PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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/--schema-only and -a/--data-only cannot be used together");
835 if (schema_only && statistics_only)
836 pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
837 if (data_only && statistics_only)
838 pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
839
840 /* reject conflicting "-only" and "no-" options */
841 if (data_only && no_data)
842 pg_fatal("options -a/--data-only and --no-data cannot be used together");
843 if (schema_only && no_schema)
844 pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
846 pg_fatal("options --statistics-only and --no-statistics cannot be used together");
847
848 /* reject conflicting "no-" options */
850 pg_fatal("options --statistics and --no-statistics cannot be used together");
851
852 /* reject conflicting "-only" options */
853 if (data_only && with_statistics)
854 pg_fatal("options %s and %s cannot be used together",
855 "-a/--data-only", "--statistics");
856 if (schema_only && with_statistics)
857 pg_fatal("options %s and %s cannot be used together",
858 "-s/--schema-only", "--statistics");
859
860 if (schema_only && foreign_servers_include_patterns.head != NULL)
861 pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
862
863 if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
864 pg_fatal("option --include-foreign-data is not supported with parallel backup");
865
866 if (data_only && dopt.outputClean)
867 pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
868
869 if (dopt.if_exists && !dopt.outputClean)
870 pg_fatal("option --if-exists requires option -c/--clean");
871
872 /*
873 * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
874 * "--schema-only --no-schema", will have already caused an error in one
875 * of the checks above.
876 */
877 dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
878 data_only) && !no_data;
879 dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
880 schema_only) && !no_schema;
881 dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
883
884
885 /*
886 * --inserts are already implied above if --column-inserts or
887 * --rows-per-insert were specified.
888 */
889 if (dopt.do_nothing && dopt.dump_inserts == 0)
890 pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
891
892 /* Identify archive format to emit */
893 archiveFormat = parseArchiveFormat(format, &archiveMode);
894
895 /* archiveFormat specific setup */
896 if (archiveFormat == archNull)
897 {
898 plainText = 1;
899
900 /*
901 * If you don't provide a restrict key, one will be appointed for you.
902 */
903 if (!dopt.restrict_key)
905 if (!dopt.restrict_key)
906 pg_fatal("could not generate restrict key");
908 pg_fatal("invalid restrict key");
909 }
910 else if (dopt.restrict_key)
911 pg_fatal("option --restrict-key can only be used with --format=plain");
912
913 /*
914 * Custom and directory formats are compressed by default with gzip when
915 * available, not the others. If gzip is not available, no compression is
916 * done by default.
917 */
918 if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
919 !user_compression_defined)
920 {
921#ifdef HAVE_LIBZ
922 compression_algorithm_str = "gzip";
923#else
924 compression_algorithm_str = "none";
925#endif
926 }
927
928 /*
929 * Compression options
930 */
931 if (!parse_compress_algorithm(compression_algorithm_str,
933 pg_fatal("unrecognized compression algorithm: \"%s\"",
934 compression_algorithm_str);
935
937 &compression_spec);
938 error_detail = validate_compress_specification(&compression_spec);
939 if (error_detail != NULL)
940 pg_fatal("invalid compression specification: %s",
941 error_detail);
942
943 error_detail = supports_compression(compression_spec);
944 if (error_detail != NULL)
945 pg_fatal("%s", error_detail);
946
947 /*
948 * Disable support for zstd workers for now - these are based on
949 * threading, and it's unclear how it interacts with parallel dumps on
950 * platforms where that relies on threads too (e.g. Windows).
951 */
952 if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
953 pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
954 "workers");
955
956 /*
957 * If emitting an archive format, we always want to emit a DATABASE item,
958 * in case --create is specified at pg_restore time.
959 */
960 if (!plainText)
961 dopt.outputCreateDB = 1;
962
963 /* Parallel backup only in the directory archive format so far */
964 if (archiveFormat != archDirectory && numWorkers > 1)
965 pg_fatal("parallel backup only supported by the directory format");
966
967 /* Open the output file */
968 fout = CreateArchive(filename, archiveFormat, compression_spec,
969 dosync, archiveMode, setupDumpWorker, sync_method);
970
971 /* Make dump options accessible right away */
972 SetArchiveOptions(fout, &dopt, NULL);
973
974 /* Register the cleanup hook */
976
977 /* Let the archiver know how noisy to be */
978 fout->verbose = g_verbose;
979
980
981 /*
982 * We allow the server to be back to 9.2, and up to any minor release of
983 * our own major version. (See also version check in pg_dumpall.c.)
984 */
985 fout->minRemoteVersion = 90200;
986 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
987
988 fout->numWorkers = numWorkers;
989
990 /*
991 * Open the database using the Archiver, so it knows about it. Errors mean
992 * death.
993 */
994 ConnectDatabaseAhx(fout, &dopt.cparams, false);
995 setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
996
997 /*
998 * On hot standbys, never try to dump unlogged table data, since it will
999 * just throw an error.
1000 */
1001 if (fout->isStandby)
1002 dopt.no_unlogged_table_data = true;
1003
1004 /*
1005 * Find the last built-in OID, if needed (prior to 8.1)
1006 *
1007 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1008 */
1010
1011 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1012
1013 /* Expand schema selection patterns into OID lists */
1014 if (schema_include_patterns.head != NULL)
1015 {
1018 strict_names);
1019 if (schema_include_oids.head == NULL)
1020 pg_fatal("no matching schemas were found");
1021 }
1024 false);
1025 /* non-matching exclusion patterns aren't an error */
1026
1027 /* Expand table selection patterns into OID lists */
1030 strict_names, false);
1033 strict_names, true);
1034 if ((table_include_patterns.head != NULL ||
1036 table_include_oids.head == NULL)
1037 pg_fatal("no matching tables were found");
1038
1041 false, false);
1044 false, true);
1045
1048 false, false);
1051 false, true);
1052
1055
1056 /* non-matching exclusion patterns aren't an error */
1057
1058 /* Expand extension selection patterns into OID lists */
1059 if (extension_include_patterns.head != NULL)
1060 {
1063 strict_names);
1064 if (extension_include_oids.head == NULL)
1065 pg_fatal("no matching extensions were found");
1066 }
1069 false);
1070 /* non-matching exclusion patterns aren't an error */
1071
1072 /*
1073 * Dumping LOs is the default for dumps where an inclusion switch is not
1074 * used (an "include everything" dump). -B can be used to exclude LOs
1075 * from those dumps. -b can be used to include LOs even when an inclusion
1076 * switch is used.
1077 *
1078 * -s means "schema only" and LOs are data, not schema, so we never
1079 * include LOs when -s is used.
1080 */
1081 if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1082 dopt.outputLOs = true;
1083
1084 /*
1085 * Collect role names so we can map object owner OIDs to names.
1086 */
1087 collectRoleNames(fout);
1088
1089 /*
1090 * Now scan the database and create DumpableObject structs for all the
1091 * objects we intend to dump.
1092 */
1093 tblinfo = getSchemaData(fout, &numTables);
1094
1095 if (dopt.dumpData)
1096 {
1097 getTableData(&dopt, tblinfo, numTables, 0);
1099 if (!dopt.dumpSchema)
1101 }
1102
1103 if (!dopt.dumpData && dopt.sequence_data)
1104 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1105
1106 /*
1107 * For binary upgrade mode, dump pg_largeobject_metadata and the
1108 * associated pg_shdepend rows. This is faster to restore than the
1109 * equivalent set of large object commands. We can only do this for
1110 * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1111 * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1112 */
1113 if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1114 {
1115 TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1116 TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1117
1118 makeTableDataInfo(&dopt, lo_metadata);
1119 makeTableDataInfo(&dopt, shdepend);
1120
1121 /*
1122 * Save pg_largeobject_metadata's dump ID for use as a dependency for
1123 * pg_shdepend and any large object comments/seclabels.
1124 */
1125 lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1127
1128 /*
1129 * Only dump large object shdepend rows for this database.
1130 */
1131 shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1132 "AND dbid = (SELECT oid FROM pg_database "
1133 " WHERE datname = current_database())";
1134
1135 /*
1136 * If upgrading from v16 or newer, only dump large objects with
1137 * comments/seclabels. For these upgrades, pg_upgrade can copy/link
1138 * pg_largeobject_metadata's files (which is usually faster) but we
1139 * still need to dump LOs with comments/seclabels here so that the
1140 * subsequent COMMENT and SECURITY LABEL commands work. pg_upgrade
1141 * can't copy/link the files from older versions because aclitem
1142 * (needed by pg_largeobject_metadata.lomacl) changed its storage
1143 * format in v16.
1144 */
1145 if (fout->remoteVersion >= 160000)
1146 lo_metadata->dataObj->filtercond = "WHERE oid IN "
1147 "(SELECT objoid FROM pg_description "
1148 "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
1149 "UNION SELECT objoid FROM pg_seclabel "
1150 "WHERE classoid = " CppAsString2(LargeObjectRelationId) ")";
1151 }
1152
1153 /*
1154 * In binary-upgrade mode, we do not have to worry about the actual LO
1155 * data or the associated metadata that resides in the pg_largeobject and
1156 * pg_largeobject_metadata tables, respectively.
1157 *
1158 * However, we do need to collect LO information as there may be comments
1159 * or other information on LOs that we do need to dump out.
1160 */
1161 if (dopt.outputLOs || dopt.binary_upgrade)
1162 getLOs(fout);
1163
1164 /*
1165 * Collect dependency data to assist in ordering the objects.
1166 */
1167 getDependencies(fout);
1168
1169 /*
1170 * Collect ACLs, comments, and security labels, if wanted.
1171 */
1172 if (!dopt.aclsSkip)
1173 getAdditionalACLs(fout);
1174 if (!dopt.no_comments)
1175 collectComments(fout);
1176 if (!dopt.no_security_labels)
1177 collectSecLabels(fout);
1178
1179 /* For binary upgrade mode, collect required pg_class information. */
1180 if (dopt.binary_upgrade)
1182
1183 /* Collect sequence information. */
1184 collectSequences(fout);
1185
1186 /* Lastly, create dummy objects to represent the section boundaries */
1187 boundaryObjs = createBoundaryObjects();
1188
1189 /* Get pointers to all the known DumpableObjects */
1190 getDumpableObjects(&dobjs, &numObjs);
1191
1192 /*
1193 * Add dummy dependencies to enforce the dump section ordering.
1194 */
1195 addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1196
1197 /*
1198 * Sort the objects into a safe dump order (no forward references).
1199 *
1200 * We rely on dependency information to help us determine a safe order, so
1201 * the initial sort is mostly for cosmetic purposes: we sort by name to
1202 * ensure that logically identical schemas will dump identically.
1203 */
1204 sortDumpableObjectsByTypeName(dobjs, numObjs);
1205
1206 sortDumpableObjects(dobjs, numObjs,
1207 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1208
1209 /*
1210 * Create archive TOC entries for all the objects to be dumped, in a safe
1211 * order.
1212 */
1213
1214 /*
1215 * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1216 */
1217 dumpEncoding(fout);
1218 dumpStdStrings(fout);
1219 dumpSearchPath(fout);
1220
1221 /* The database items are always next, unless we don't want them at all */
1222 if (dopt.outputCreateDB)
1223 dumpDatabase(fout);
1224
1225 /* Now the rearrangeable objects. */
1226 for (i = 0; i < numObjs; i++)
1227 dumpDumpableObject(fout, dobjs[i]);
1228
1229 /*
1230 * Set up options info to ensure we dump what we want.
1231 */
1232 ropt = NewRestoreOptions();
1233 ropt->filename = filename;
1234
1235 /* if you change this list, see dumpOptionsFromRestoreOptions */
1236 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1237 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1238 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1239 ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1241 ropt->dropSchema = dopt.outputClean;
1242 ropt->dumpData = dopt.dumpData;
1243 ropt->dumpSchema = dopt.dumpSchema;
1244 ropt->dumpStatistics = dopt.dumpStatistics;
1245 ropt->if_exists = dopt.if_exists;
1246 ropt->column_inserts = dopt.column_inserts;
1247 ropt->dumpSections = dopt.dumpSections;
1248 ropt->aclsSkip = dopt.aclsSkip;
1249 ropt->superuser = dopt.outputSuperuser;
1250 ropt->createDB = dopt.outputCreateDB;
1251 ropt->noOwner = dopt.outputNoOwner;
1252 ropt->noTableAm = dopt.outputNoTableAm;
1253 ropt->noTablespace = dopt.outputNoTablespaces;
1255 ropt->use_setsessauth = dopt.use_setsessauth;
1257 ropt->dump_inserts = dopt.dump_inserts;
1258 ropt->no_comments = dopt.no_comments;
1259 ropt->no_policies = dopt.no_policies;
1260 ropt->no_publications = dopt.no_publications;
1263 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1266 ropt->sequence_data = dopt.sequence_data;
1267 ropt->binary_upgrade = dopt.binary_upgrade;
1268 ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1269
1270 ropt->compression_spec = compression_spec;
1271
1272 ropt->suppressDumpWarnings = true; /* We've already shown them */
1273
1274 SetArchiveOptions(fout, &dopt, ropt);
1275
1276 /* Mark which entries should be output */
1278
1279 /*
1280 * The archive's TOC entries are now marked as to which ones will actually
1281 * be output, so we can set up their dependency lists properly. This isn't
1282 * necessary for plain-text output, though.
1283 */
1284 if (!plainText)
1286
1287 /*
1288 * And finally we can do the actual output.
1289 *
1290 * Note: for non-plain-text output formats, the output file is written
1291 * inside CloseArchive(). This is, um, bizarre; but not worth changing
1292 * right now.
1293 */
1294 if (plainText)
1295 RestoreArchive(fout);
1296
1297 CloseArchive(fout);
1298
1299 exit_nicely(0);
1300}
1301
1302
1303static void
1304help(const char *progname)
1305{
1306 printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1307 printf(_("Usage:\n"));
1308 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1309
1310 printf(_("\nGeneral options:\n"));
1311 printf(_(" -f, --file=FILENAME output file or directory name\n"));
1312 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1313 " plain text (default))\n"));
1314 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1315 printf(_(" -v, --verbose verbose mode\n"));
1316 printf(_(" -V, --version output version information, then exit\n"));
1317 printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1318 " compress as specified\n"));
1319 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1320 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1321 printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1322 printf(_(" -?, --help show this help, then exit\n"));
1323
1324 printf(_("\nOptions controlling the output content:\n"));
1325 printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1326 printf(_(" -b, --large-objects include large objects in dump\n"));
1327 printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1328 printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1329 printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1330 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1331 printf(_(" -C, --create include commands to create database in dump\n"));
1332 printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1333 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1334 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1335 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1336 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1337 " plain-text format\n"));
1338 printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1339 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1340 printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1341 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1342 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1343 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1344 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1345 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1346 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1347 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1348 " access to)\n"));
1349 printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1350 printf(_(" --exclude-table-and-children=PATTERN\n"
1351 " do NOT dump the specified table(s), including\n"
1352 " child and partition tables\n"));
1353 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1354 printf(_(" --exclude-table-data-and-children=PATTERN\n"
1355 " do NOT dump data for the specified table(s),\n"
1356 " including child and partition tables\n"));
1357 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1358 printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1359 " based on expressions in FILENAME\n"));
1360 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1361 printf(_(" --include-foreign-data=PATTERN\n"
1362 " include data of foreign tables on foreign\n"
1363 " servers matching PATTERN\n"));
1364 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1365 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1366 printf(_(" --no-comments do not dump comment commands\n"));
1367 printf(_(" --no-data do not dump data\n"));
1368 printf(_(" --no-policies do not dump row security policies\n"));
1369 printf(_(" --no-publications do not dump publications\n"));
1370 printf(_(" --no-schema do not dump schema\n"));
1371 printf(_(" --no-security-labels do not dump security label assignments\n"));
1372 printf(_(" --no-statistics do not dump statistics\n"));
1373 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1374 printf(_(" --no-table-access-method do not dump table access methods\n"));
1375 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1376 printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1377 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1378 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1379 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1380 printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1381 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1382 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1383 printf(_(" --sequence-data include sequence data in dump\n"));
1384 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1385 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1386 printf(_(" --statistics dump the statistics\n"));
1387 printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1388 printf(_(" --strict-names require table and/or schema include patterns to\n"
1389 " match at least one entity each\n"));
1390 printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1391 " child and partition tables\n"));
1392 printf(_(" --use-set-session-authorization\n"
1393 " use SET SESSION AUTHORIZATION commands instead of\n"
1394 " ALTER OWNER commands to set ownership\n"));
1395
1396 printf(_("\nConnection options:\n"));
1397 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1398 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1399 printf(_(" -p, --port=PORT database server port number\n"));
1400 printf(_(" -U, --username=NAME connect as specified database user\n"));
1401 printf(_(" -w, --no-password never prompt for password\n"));
1402 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1403 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1404
1405 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1406 "variable value is used.\n\n"));
1407 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1408 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1409}
1410
1411static void
1412setup_connection(Archive *AH, const char *dumpencoding,
1413 const char *dumpsnapshot, char *use_role)
1414{
1415 DumpOptions *dopt = AH->dopt;
1416 PGconn *conn = GetConnection(AH);
1417 const char *std_strings;
1418
1420
1421 /*
1422 * Set the client encoding if requested.
1423 */
1424 if (dumpencoding)
1425 {
1426 if (PQsetClientEncoding(conn, dumpencoding) < 0)
1427 pg_fatal("invalid client encoding \"%s\" specified",
1428 dumpencoding);
1429 }
1430
1431 /*
1432 * Get the active encoding and the standard_conforming_strings setting, so
1433 * we know how to escape strings.
1434 */
1437
1438 std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1439 AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1440
1441 /*
1442 * Set the role if requested. In a parallel dump worker, we'll be passed
1443 * use_role == NULL, but AH->use_role is already set (if user specified it
1444 * originally) and we should use that.
1445 */
1446 if (!use_role && AH->use_role)
1447 use_role = AH->use_role;
1448
1449 /* Set the role if requested */
1450 if (use_role)
1451 {
1453
1454 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1455 ExecuteSqlStatement(AH, query->data);
1456 destroyPQExpBuffer(query);
1457
1458 /* save it for possible later use by parallel workers */
1459 if (!AH->use_role)
1460 AH->use_role = pg_strdup(use_role);
1461 }
1462
1463 /* Set the datestyle to ISO to ensure the dump's portability */
1464 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1465
1466 /* Likewise, avoid using sql_standard intervalstyle */
1467 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1468
1469 /*
1470 * Use an explicitly specified extra_float_digits if it has been provided.
1471 * Otherwise, set extra_float_digits so that we can dump float data
1472 * exactly (given correctly implemented float I/O code, anyway).
1473 */
1475 {
1477
1478 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1480 ExecuteSqlStatement(AH, q->data);
1482 }
1483 else
1484 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1485
1486 /*
1487 * Disable synchronized scanning, to prevent unpredictable changes in row
1488 * ordering across a dump and reload.
1489 */
1490 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1491
1492 /*
1493 * Disable timeouts if supported.
1494 */
1495 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1496 if (AH->remoteVersion >= 90300)
1497 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1498 if (AH->remoteVersion >= 90600)
1499 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1500 if (AH->remoteVersion >= 170000)
1501 ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1502
1503 /*
1504 * Quote all identifiers, if requested.
1505 */
1507 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1508
1509 /*
1510 * Adjust row-security mode, if supported.
1511 */
1512 if (AH->remoteVersion >= 90500)
1513 {
1514 if (dopt->enable_row_security)
1515 ExecuteSqlStatement(AH, "SET row_security = on");
1516 else
1517 ExecuteSqlStatement(AH, "SET row_security = off");
1518 }
1519
1520 /*
1521 * For security reasons, we restrict the expansion of non-system views and
1522 * access to foreign tables during the pg_dump process. This restriction
1523 * is adjusted when dumping foreign table data.
1524 */
1525 set_restrict_relation_kind(AH, "view, foreign-table");
1526
1527 /*
1528 * Initialize prepared-query state to "nothing prepared". We do this here
1529 * so that a parallel dump worker will have its own state.
1530 */
1531 AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1532
1533 /*
1534 * Start transaction-snapshot mode transaction to dump consistent data.
1535 */
1536 ExecuteSqlStatement(AH, "BEGIN");
1537
1538 /*
1539 * To support the combination of serializable_deferrable with the jobs
1540 * option we use REPEATABLE READ for the worker connections that are
1541 * passed a snapshot. As long as the snapshot is acquired in a
1542 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1543 * REPEATABLE READ transaction provides the appropriate integrity
1544 * guarantees. This is a kluge, but safe for back-patching.
1545 */
1546 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1548 "SET TRANSACTION ISOLATION LEVEL "
1549 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1550 else
1552 "SET TRANSACTION ISOLATION LEVEL "
1553 "REPEATABLE READ, READ ONLY");
1554
1555 /*
1556 * If user specified a snapshot to use, select that. In a parallel dump
1557 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1558 * is already set (if the server can handle it) and we should use that.
1559 */
1560 if (dumpsnapshot)
1561 AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1562
1563 if (AH->sync_snapshot_id)
1564 {
1566
1567 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1569 ExecuteSqlStatement(AH, query->data);
1570 destroyPQExpBuffer(query);
1571 }
1572 else if (AH->numWorkers > 1)
1573 {
1574 if (AH->isStandby && AH->remoteVersion < 100000)
1575 pg_fatal("parallel dumps from standby servers are not supported by this server version");
1577 }
1578}
1579
1580/* Set up connection for a parallel worker process */
1581static void
1583{
1584 /*
1585 * We want to re-select all the same values the leader connection is
1586 * using. We'll have inherited directly-usable values in
1587 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1588 * inherited encoding value back to a string to pass to setup_connection.
1589 */
1592 NULL,
1593 NULL);
1594}
1595
1596static char *
1598{
1599 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1600 char *result;
1601 PGresult *res;
1602
1603 res = ExecuteSqlQueryForSingleRow(fout, query);
1604 result = pg_strdup(PQgetvalue(res, 0, 0));
1605 PQclear(res);
1606
1607 return result;
1608}
1609
1610static ArchiveFormat
1612{
1613 ArchiveFormat archiveFormat;
1614
1616
1617 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1618 {
1619 /* This is used by pg_dumpall, and is not documented */
1620 archiveFormat = archNull;
1622 }
1623 else if (pg_strcasecmp(format, "c") == 0)
1624 archiveFormat = archCustom;
1625 else if (pg_strcasecmp(format, "custom") == 0)
1626 archiveFormat = archCustom;
1627 else if (pg_strcasecmp(format, "d") == 0)
1628 archiveFormat = archDirectory;
1629 else if (pg_strcasecmp(format, "directory") == 0)
1630 archiveFormat = archDirectory;
1631 else if (pg_strcasecmp(format, "p") == 0)
1632 archiveFormat = archNull;
1633 else if (pg_strcasecmp(format, "plain") == 0)
1634 archiveFormat = archNull;
1635 else if (pg_strcasecmp(format, "t") == 0)
1636 archiveFormat = archTar;
1637 else if (pg_strcasecmp(format, "tar") == 0)
1638 archiveFormat = archTar;
1639 else
1640 pg_fatal("invalid output format \"%s\" specified", format);
1641 return archiveFormat;
1642}
1643
1644/*
1645 * Find the OIDs of all schemas matching the given list of patterns,
1646 * and append them to the given OID list.
1647 */
1648static void
1650 SimpleStringList *patterns,
1651 SimpleOidList *oids,
1652 bool strict_names)
1653{
1654 PQExpBuffer query;
1655 PGresult *res;
1657 int i;
1658
1659 if (patterns->head == NULL)
1660 return; /* nothing to do */
1661
1662 query = createPQExpBuffer();
1663
1664 /*
1665 * The loop below runs multiple SELECTs might sometimes result in
1666 * duplicate entries in the OID list, but we don't care.
1667 */
1668
1669 for (cell = patterns->head; cell; cell = cell->next)
1670 {
1671 PQExpBufferData dbbuf;
1672 int dotcnt;
1673
1675 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1676 initPQExpBuffer(&dbbuf);
1677 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1678 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1679 &dotcnt);
1680 if (dotcnt > 1)
1681 pg_fatal("improper qualified name (too many dotted names): %s",
1682 cell->val);
1683 else if (dotcnt == 1)
1684 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1685 termPQExpBuffer(&dbbuf);
1686
1687 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1688 if (strict_names && PQntuples(res) == 0)
1689 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1690
1691 for (i = 0; i < PQntuples(res); i++)
1692 {
1693 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1694 }
1695
1696 PQclear(res);
1697 resetPQExpBuffer(query);
1698 }
1699
1700 destroyPQExpBuffer(query);
1701}
1702
1703/*
1704 * Find the OIDs of all extensions matching the given list of patterns,
1705 * and append them to the given OID list.
1706 */
1707static void
1709 SimpleStringList *patterns,
1710 SimpleOidList *oids,
1711 bool strict_names)
1712{
1713 PQExpBuffer query;
1714 PGresult *res;
1716 int i;
1717
1718 if (patterns->head == NULL)
1719 return; /* nothing to do */
1720
1721 query = createPQExpBuffer();
1722
1723 /*
1724 * The loop below runs multiple SELECTs might sometimes result in
1725 * duplicate entries in the OID list, but we don't care.
1726 */
1727 for (cell = patterns->head; cell; cell = cell->next)
1728 {
1729 int dotcnt;
1730
1732 "SELECT oid FROM pg_catalog.pg_extension e\n");
1733 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1734 false, NULL, "e.extname", NULL, NULL, NULL,
1735 &dotcnt);
1736 if (dotcnt > 0)
1737 pg_fatal("improper qualified name (too many dotted names): %s",
1738 cell->val);
1739
1740 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1741 if (strict_names && PQntuples(res) == 0)
1742 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1743
1744 for (i = 0; i < PQntuples(res); i++)
1745 {
1746 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1747 }
1748
1749 PQclear(res);
1750 resetPQExpBuffer(query);
1751 }
1752
1753 destroyPQExpBuffer(query);
1754}
1755
1756/*
1757 * Find the OIDs of all foreign servers matching the given list of patterns,
1758 * and append them to the given OID list.
1759 */
1760static void
1762 SimpleStringList *patterns,
1763 SimpleOidList *oids)
1764{
1765 PQExpBuffer query;
1766 PGresult *res;
1768 int i;
1769
1770 if (patterns->head == NULL)
1771 return; /* nothing to do */
1772
1773 query = createPQExpBuffer();
1774
1775 /*
1776 * The loop below runs multiple SELECTs might sometimes result in
1777 * duplicate entries in the OID list, but we don't care.
1778 */
1779
1780 for (cell = patterns->head; cell; cell = cell->next)
1781 {
1782 int dotcnt;
1783
1785 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1786 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1787 false, NULL, "s.srvname", NULL, NULL, NULL,
1788 &dotcnt);
1789 if (dotcnt > 0)
1790 pg_fatal("improper qualified name (too many dotted names): %s",
1791 cell->val);
1792
1793 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1794 if (PQntuples(res) == 0)
1795 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1796
1797 for (i = 0; i < PQntuples(res); i++)
1798 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1799
1800 PQclear(res);
1801 resetPQExpBuffer(query);
1802 }
1803
1804 destroyPQExpBuffer(query);
1805}
1806
1807/*
1808 * Find the OIDs of all tables matching the given list of patterns,
1809 * and append them to the given OID list. See also expand_dbname_patterns()
1810 * in pg_dumpall.c
1811 */
1812static void
1814 SimpleStringList *patterns, SimpleOidList *oids,
1815 bool strict_names, bool with_child_tables)
1816{
1817 PQExpBuffer query;
1818 PGresult *res;
1820 int i;
1821
1822 if (patterns->head == NULL)
1823 return; /* nothing to do */
1824
1825 query = createPQExpBuffer();
1826
1827 /*
1828 * this might sometimes result in duplicate entries in the OID list, but
1829 * we don't care.
1830 */
1831
1832 for (cell = patterns->head; cell; cell = cell->next)
1833 {
1834 PQExpBufferData dbbuf;
1835 int dotcnt;
1836
1837 /*
1838 * Query must remain ABSOLUTELY devoid of unqualified names. This
1839 * would be unnecessary given a pg_table_is_visible() variant taking a
1840 * search_path argument.
1841 *
1842 * For with_child_tables, we start with the basic query's results and
1843 * recursively search the inheritance tree to add child tables.
1844 */
1845 if (with_child_tables)
1846 {
1847 appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1848 }
1849
1850 appendPQExpBuffer(query,
1851 "SELECT c.oid"
1852 "\nFROM pg_catalog.pg_class c"
1853 "\n LEFT JOIN pg_catalog.pg_namespace n"
1854 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1855 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1856 "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1857 RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1858 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1859 RELKIND_PARTITIONED_TABLE);
1860 initPQExpBuffer(&dbbuf);
1861 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1862 false, "n.nspname", "c.relname", NULL,
1863 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1864 &dotcnt);
1865 if (dotcnt > 2)
1866 pg_fatal("improper relation name (too many dotted names): %s",
1867 cell->val);
1868 else if (dotcnt == 2)
1869 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1870 termPQExpBuffer(&dbbuf);
1871
1872 if (with_child_tables)
1873 {
1874 appendPQExpBufferStr(query, "UNION"
1875 "\nSELECT i.inhrelid"
1876 "\nFROM partition_tree p"
1877 "\n JOIN pg_catalog.pg_inherits i"
1878 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1879 "\n)"
1880 "\nSELECT relid FROM partition_tree");
1881 }
1882
1883 ExecuteSqlStatement(fout, "RESET search_path");
1884 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1887 if (strict_names && PQntuples(res) == 0)
1888 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1889
1890 for (i = 0; i < PQntuples(res); i++)
1891 {
1892 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1893 }
1894
1895 PQclear(res);
1896 resetPQExpBuffer(query);
1897 }
1898
1899 destroyPQExpBuffer(query);
1900}
1901
1902/*
1903 * Verifies that the connected database name matches the given database name,
1904 * and if not, dies with an error about the given pattern.
1905 *
1906 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1907 */
1908static void
1909prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1910{
1911 const char *db;
1912
1913 db = PQdb(conn);
1914 if (db == NULL)
1915 pg_fatal("You are currently not connected to a database.");
1916
1917 if (strcmp(db, dbname) != 0)
1918 pg_fatal("cross-database references are not implemented: %s",
1919 pattern);
1920}
1921
1922/*
1923 * checkExtensionMembership
1924 * Determine whether object is an extension member, and if so,
1925 * record an appropriate dependency and set the object's dump flag.
1926 *
1927 * It's important to call this for each object that could be an extension
1928 * member. Generally, we integrate this with determining the object's
1929 * to-be-dumped-ness, since extension membership overrides other rules for that.
1930 *
1931 * Returns true if object is an extension member, else false.
1932 */
1933static bool
1935{
1937
1938 if (ext == NULL)
1939 return false;
1940
1941 dobj->ext_member = true;
1942
1943 /* Record dependency so that getDependencies needn't deal with that */
1944 addObjectDependency(dobj, ext->dobj.dumpId);
1945
1946 /*
1947 * In 9.6 and above, mark the member object to have any non-initial ACLs
1948 * dumped. (Any initial ACLs will be removed later, using data from
1949 * pg_init_privs, so that we'll dump only the delta from the extension's
1950 * initial setup.)
1951 *
1952 * Prior to 9.6, we do not include any extension member components.
1953 *
1954 * In binary upgrades, we still dump all components of the members
1955 * individually, since the idea is to exactly reproduce the database
1956 * contents rather than replace the extension contents with something
1957 * different.
1958 *
1959 * Note: it might be interesting someday to implement storage and delta
1960 * dumping of extension members' RLS policies and/or security labels.
1961 * However there is a pitfall for RLS policies: trying to dump them
1962 * requires getting a lock on their tables, and the calling user might not
1963 * have privileges for that. We need no lock to examine a table's ACLs,
1964 * so the current feature doesn't have a problem of that sort.
1965 */
1966 if (fout->dopt->binary_upgrade)
1967 dobj->dump = ext->dobj.dump;
1968 else
1969 {
1970 if (fout->remoteVersion < 90600)
1971 dobj->dump = DUMP_COMPONENT_NONE;
1972 else
1973 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1974 }
1975
1976 return true;
1977}
1978
1979/*
1980 * selectDumpableNamespace: policy-setting subroutine
1981 * Mark a namespace as to be dumped or not
1982 */
1983static void
1985{
1986 /*
1987 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1988 * and (for --clean) a DROP SCHEMA statement. (In the absence of
1989 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1990 */
1991 nsinfo->create = true;
1992
1993 /*
1994 * If specific tables are being dumped, do not dump any complete
1995 * namespaces. If specific namespaces are being dumped, dump just those
1996 * namespaces. Otherwise, dump all non-system namespaces.
1997 */
1998 if (table_include_oids.head != NULL)
1999 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2000 else if (schema_include_oids.head != NULL)
2001 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2003 nsinfo->dobj.catId.oid) ?
2005 else if (fout->remoteVersion >= 90600 &&
2006 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2007 {
2008 /*
2009 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2010 * they are interesting (and not the original ACLs which were set at
2011 * initdb time, see pg_init_privs).
2012 */
2013 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2014 }
2015 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2016 strcmp(nsinfo->dobj.name, "information_schema") == 0)
2017 {
2018 /* Other system schemas don't get dumped */
2019 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2020 }
2021 else if (strcmp(nsinfo->dobj.name, "public") == 0)
2022 {
2023 /*
2024 * The public schema is a strange beast that sits in a sort of
2025 * no-mans-land between being a system object and a user object.
2026 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2027 * a comment and an indication of ownership. If the owner is the
2028 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2029 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2030 */
2031 nsinfo->create = false;
2032 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2033 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2034 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2036
2037 /*
2038 * Also, make like it has a comment even if it doesn't; this is so
2039 * that we'll emit a command to drop the comment, if appropriate.
2040 * (Without this, we'd not call dumpCommentExtended for it.)
2041 */
2043 }
2044 else
2045 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2046
2047 /*
2048 * In any case, a namespace can be excluded by an exclusion switch
2049 */
2050 if (nsinfo->dobj.dump_contains &&
2052 nsinfo->dobj.catId.oid))
2053 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2054
2055 /*
2056 * If the schema belongs to an extension, allow extension membership to
2057 * override the dump decision for the schema itself. However, this does
2058 * not change dump_contains, so this won't change what we do with objects
2059 * within the schema. (If they belong to the extension, they'll get
2060 * suppressed by it, otherwise not.)
2061 */
2062 (void) checkExtensionMembership(&nsinfo->dobj, fout);
2063}
2064
2065/*
2066 * selectDumpableTable: policy-setting subroutine
2067 * Mark a table as to be dumped or not
2068 */
2069static void
2071{
2072 if (checkExtensionMembership(&tbinfo->dobj, fout))
2073 return; /* extension membership overrides all else */
2074
2075 /*
2076 * If specific tables are being dumped, dump just those tables; else, dump
2077 * according to the parent namespace's dump flag.
2078 */
2079 if (table_include_oids.head != NULL)
2081 tbinfo->dobj.catId.oid) ?
2083 else
2084 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2085
2086 /*
2087 * In any case, a table can be excluded by an exclusion switch
2088 */
2089 if (tbinfo->dobj.dump &&
2091 tbinfo->dobj.catId.oid))
2092 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2093}
2094
2095/*
2096 * selectDumpableType: policy-setting subroutine
2097 * Mark a type as to be dumped or not
2098 *
2099 * If it's a table's rowtype or an autogenerated array type, we also apply a
2100 * special type code to facilitate sorting into the desired order. (We don't
2101 * want to consider those to be ordinary types because that would bring tables
2102 * up into the datatype part of the dump order.) We still set the object's
2103 * dump flag; that's not going to cause the dummy type to be dumped, but we
2104 * need it so that casts involving such types will be dumped correctly -- see
2105 * dumpCast. This means the flag should be set the same as for the underlying
2106 * object (the table or base type).
2107 */
2108static void
2110{
2111 /* skip complex types, except for standalone composite types */
2112 if (OidIsValid(tyinfo->typrelid) &&
2113 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2114 {
2115 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2116
2117 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2118 if (tytable != NULL)
2119 tyinfo->dobj.dump = tytable->dobj.dump;
2120 else
2121 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2122 return;
2123 }
2124
2125 /* skip auto-generated array and multirange types */
2126 if (tyinfo->isArray || tyinfo->isMultirange)
2127 {
2128 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2129
2130 /*
2131 * Fall through to set the dump flag; we assume that the subsequent
2132 * rules will do the same thing as they would for the array's base
2133 * type or multirange's range type. (We cannot reliably look up the
2134 * base type here, since getTypes may not have processed it yet.)
2135 */
2136 }
2137
2138 if (checkExtensionMembership(&tyinfo->dobj, fout))
2139 return; /* extension membership overrides all else */
2140
2141 /* Dump based on if the contents of the namespace are being dumped */
2142 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2143}
2144
2145/*
2146 * selectDumpableDefaultACL: policy-setting subroutine
2147 * Mark a default ACL as to be dumped or not
2148 *
2149 * For per-schema default ACLs, dump if the schema is to be dumped.
2150 * Otherwise dump if we are dumping "everything". Note that dumpSchema
2151 * and aclsSkip are checked separately.
2152 */
2153static void
2155{
2156 /* Default ACLs can't be extension members */
2157
2158 if (dinfo->dobj.namespace)
2159 /* default ACLs are considered part of the namespace */
2160 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2161 else
2162 dinfo->dobj.dump = dopt->include_everything ?
2164}
2165
2166/*
2167 * selectDumpableCast: policy-setting subroutine
2168 * Mark a cast as to be dumped or not
2169 *
2170 * Casts do not belong to any particular namespace (since they haven't got
2171 * names), nor do they have identifiable owners. To distinguish user-defined
2172 * casts from built-in ones, we must resort to checking whether the cast's
2173 * OID is in the range reserved for initdb.
2174 */
2175static void
2177{
2178 if (checkExtensionMembership(&cast->dobj, fout))
2179 return; /* extension membership overrides all else */
2180
2181 /*
2182 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2183 * support ACLs currently.
2184 */
2185 if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2187 else
2188 cast->dobj.dump = fout->dopt->include_everything ?
2190}
2191
2192/*
2193 * selectDumpableProcLang: policy-setting subroutine
2194 * Mark a procedural language as to be dumped or not
2195 *
2196 * Procedural languages do not belong to any particular namespace. To
2197 * identify built-in languages, we must resort to checking whether the
2198 * language's OID is in the range reserved for initdb.
2199 */
2200static void
2202{
2203 if (checkExtensionMembership(&plang->dobj, fout))
2204 return; /* extension membership overrides all else */
2205
2206 /*
2207 * Only include procedural languages when we are dumping everything.
2208 *
2209 * For from-initdb procedural languages, only include ACLs, as we do for
2210 * the pg_catalog namespace. We need this because procedural languages do
2211 * not live in any namespace.
2212 */
2213 if (!fout->dopt->include_everything)
2214 plang->dobj.dump = DUMP_COMPONENT_NONE;
2215 else
2216 {
2217 if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2218 plang->dobj.dump = fout->remoteVersion < 90600 ?
2220 else
2221 plang->dobj.dump = DUMP_COMPONENT_ALL;
2222 }
2223}
2224
2225/*
2226 * selectDumpableAccessMethod: policy-setting subroutine
2227 * Mark an access method as to be dumped or not
2228 *
2229 * Access methods do not belong to any particular namespace. To identify
2230 * built-in access methods, we must resort to checking whether the
2231 * method's OID is in the range reserved for initdb.
2232 */
2233static void
2235{
2236 /* see getAccessMethods() comment about v9.6. */
2237 if (fout->remoteVersion < 90600)
2238 {
2239 method->dobj.dump = DUMP_COMPONENT_NONE;
2240 return;
2241 }
2242
2243 if (checkExtensionMembership(&method->dobj, fout))
2244 return; /* extension membership overrides all else */
2245
2246 /*
2247 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2248 * they do not support ACLs currently.
2249 */
2250 if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2251 method->dobj.dump = DUMP_COMPONENT_NONE;
2252 else
2253 method->dobj.dump = fout->dopt->include_everything ?
2255}
2256
2257/*
2258 * selectDumpableExtension: policy-setting subroutine
2259 * Mark an extension as to be dumped or not
2260 *
2261 * Built-in extensions should be skipped except for checking ACLs, since we
2262 * assume those will already be installed in the target database. We identify
2263 * such extensions by their having OIDs in the range reserved for initdb.
2264 * We dump all user-added extensions by default. No extensions are dumped
2265 * if include_everything is false (i.e., a --schema or --table switch was
2266 * given), except if --extension specifies a list of extensions to dump.
2267 */
2268static void
2270{
2271 /*
2272 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2273 * change permissions on their member objects, if they wish to, and have
2274 * those changes preserved.
2275 */
2276 if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2277 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2278 else
2279 {
2280 /* check if there is a list of extensions to dump */
2281 if (extension_include_oids.head != NULL)
2282 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2284 extinfo->dobj.catId.oid) ?
2286 else
2287 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2288 dopt->include_everything ?
2290
2291 /* check that the extension is not explicitly excluded */
2292 if (extinfo->dobj.dump &&
2294 extinfo->dobj.catId.oid))
2295 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2296 }
2297}
2298
2299/*
2300 * selectDumpablePublicationObject: policy-setting subroutine
2301 * Mark a publication object as to be dumped or not
2302 *
2303 * A publication can have schemas and tables which have schemas, but those are
2304 * ignored in decision making, because publications are only dumped when we are
2305 * dumping everything.
2306 */
2307static void
2309{
2310 if (checkExtensionMembership(dobj, fout))
2311 return; /* extension membership overrides all else */
2312
2313 dobj->dump = fout->dopt->include_everything ?
2315}
2316
2317/*
2318 * selectDumpableStatisticsObject: policy-setting subroutine
2319 * Mark an extended statistics object as to be dumped or not
2320 *
2321 * We dump an extended statistics object if the schema it's in and the table
2322 * it's for are being dumped. (This'll need more thought if statistics
2323 * objects ever support cross-table stats.)
2324 */
2325static void
2327{
2328 if (checkExtensionMembership(&sobj->dobj, fout))
2329 return; /* extension membership overrides all else */
2330
2331 sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2332 if (sobj->stattable == NULL ||
2335}
2336
2337/*
2338 * selectDumpableObject: policy-setting subroutine
2339 * Mark a generic dumpable object as to be dumped or not
2340 *
2341 * Use this only for object types without a special-case routine above.
2342 */
2343static void
2345{
2346 if (checkExtensionMembership(dobj, fout))
2347 return; /* extension membership overrides all else */
2348
2349 /*
2350 * Default policy is to dump if parent namespace is dumpable, or for
2351 * non-namespace-associated items, dump if we're dumping "everything".
2352 */
2353 if (dobj->namespace)
2354 dobj->dump = dobj->namespace->dobj.dump_contains;
2355 else
2356 dobj->dump = fout->dopt->include_everything ?
2358}
2359
2360/*
2361 * Dump a table's contents for loading using the COPY command
2362 * - this routine is called by the Archiver when it wants the table
2363 * to be dumped.
2364 */
2365static int
2366dumpTableData_copy(Archive *fout, const void *dcontext)
2367{
2368 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2369 TableInfo *tbinfo = tdinfo->tdtable;
2370 const char *classname = tbinfo->dobj.name;
2372
2373 /*
2374 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2375 * which uses it already.
2376 */
2377 PQExpBuffer clistBuf = createPQExpBuffer();
2378 PGconn *conn = GetConnection(fout);
2379 PGresult *res;
2380 int ret;
2381 char *copybuf;
2382 const char *column_list;
2383
2384 pg_log_info("dumping contents of table \"%s.%s\"",
2385 tbinfo->dobj.namespace->dobj.name, classname);
2386
2387 /*
2388 * Specify the column list explicitly so that we have no possibility of
2389 * retrieving data in the wrong column order. (The default column
2390 * ordering of COPY will not be what we want in certain corner cases
2391 * involving ADD COLUMN and inheritance.)
2392 */
2393 column_list = fmtCopyColumnList(tbinfo, clistBuf);
2394
2395 /*
2396 * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2397 * a filter condition was specified. For other cases a simple COPY
2398 * suffices.
2399 */
2400 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2401 {
2402 /* Temporary allows to access to foreign tables to dump data */
2403 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2404 set_restrict_relation_kind(fout, "view");
2405
2406 appendPQExpBufferStr(q, "COPY (SELECT ");
2407 /* klugery to get rid of parens in column list */
2408 if (strlen(column_list) > 2)
2409 {
2410 appendPQExpBufferStr(q, column_list + 1);
2411 q->data[q->len - 1] = ' ';
2412 }
2413 else
2414 appendPQExpBufferStr(q, "* ");
2415
2416 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2417 fmtQualifiedDumpable(tbinfo),
2418 tdinfo->filtercond ? tdinfo->filtercond : "");
2419 }
2420 else
2421 {
2422 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2423 fmtQualifiedDumpable(tbinfo),
2424 column_list);
2425 }
2426 res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2427 PQclear(res);
2428 destroyPQExpBuffer(clistBuf);
2429
2430 for (;;)
2431 {
2432 ret = PQgetCopyData(conn, &copybuf, 0);
2433
2434 if (ret < 0)
2435 break; /* done or error */
2436
2437 if (copybuf)
2438 {
2439 WriteData(fout, copybuf, ret);
2441 }
2442
2443 /* ----------
2444 * THROTTLE:
2445 *
2446 * There was considerable discussion in late July, 2000 regarding
2447 * slowing down pg_dump when backing up large tables. Users with both
2448 * slow & fast (multi-processor) machines experienced performance
2449 * degradation when doing a backup.
2450 *
2451 * Initial attempts based on sleeping for a number of ms for each ms
2452 * of work were deemed too complex, then a simple 'sleep in each loop'
2453 * implementation was suggested. The latter failed because the loop
2454 * was too tight. Finally, the following was implemented:
2455 *
2456 * If throttle is non-zero, then
2457 * See how long since the last sleep.
2458 * Work out how long to sleep (based on ratio).
2459 * If sleep is more than 100ms, then
2460 * sleep
2461 * reset timer
2462 * EndIf
2463 * EndIf
2464 *
2465 * where the throttle value was the number of ms to sleep per ms of
2466 * work. The calculation was done in each loop.
2467 *
2468 * Most of the hard work is done in the backend, and this solution
2469 * still did not work particularly well: on slow machines, the ratio
2470 * was 50:1, and on medium paced machines, 1:1, and on fast
2471 * multi-processor machines, it had little or no effect, for reasons
2472 * that were unclear.
2473 *
2474 * Further discussion ensued, and the proposal was dropped.
2475 *
2476 * For those people who want this feature, it can be implemented using
2477 * gettimeofday in each loop, calculating the time since last sleep,
2478 * multiplying that by the sleep ratio, then if the result is more
2479 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2480 * function to sleep for a subsecond period ie.
2481 *
2482 * select(0, NULL, NULL, NULL, &tvi);
2483 *
2484 * This will return after the interval specified in the structure tvi.
2485 * Finally, call gettimeofday again to save the 'last sleep time'.
2486 * ----------
2487 */
2488 }
2489 archprintf(fout, "\\.\n\n\n");
2490
2491 if (ret == -2)
2492 {
2493 /* copy data transfer failed */
2494 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2495 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2496 pg_log_error_detail("Command was: %s", q->data);
2497 exit_nicely(1);
2498 }
2499
2500 /* Check command status and return to normal libpq state */
2501 res = PQgetResult(conn);
2502 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2503 {
2504 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2505 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2506 pg_log_error_detail("Command was: %s", q->data);
2507 exit_nicely(1);
2508 }
2509 PQclear(res);
2510
2511 /* Do this to ensure we've pumped libpq back to idle state */
2512 if (PQgetResult(conn) != NULL)
2513 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2514 classname);
2515
2517
2518 /* Revert back the setting */
2519 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2520 set_restrict_relation_kind(fout, "view, foreign-table");
2521
2522 return 1;
2523}
2524
2525/*
2526 * Dump table data using INSERT commands.
2527 *
2528 * Caution: when we restore from an archive file direct to database, the
2529 * INSERT commands emitted by this function have to be parsed by
2530 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2531 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2532 */
2533static int
2534dumpTableData_insert(Archive *fout, const void *dcontext)
2535{
2536 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2537 TableInfo *tbinfo = tdinfo->tdtable;
2538 DumpOptions *dopt = fout->dopt;
2540 PQExpBuffer insertStmt = NULL;
2541 char *attgenerated;
2542 PGresult *res;
2543 int nfields,
2544 i;
2545 int rows_per_statement = dopt->dump_inserts;
2546 int rows_this_statement = 0;
2547
2548 /* Temporary allows to access to foreign tables to dump data */
2549 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2550 set_restrict_relation_kind(fout, "view");
2551
2552 /*
2553 * If we're going to emit INSERTs with column names, the most efficient
2554 * way to deal with generated columns is to exclude them entirely. For
2555 * INSERTs without column names, we have to emit DEFAULT rather than the
2556 * actual column value --- but we can save a few cycles by fetching nulls
2557 * rather than the uninteresting-to-us value.
2558 */
2559 attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2560 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2561 nfields = 0;
2562 for (i = 0; i < tbinfo->numatts; i++)
2563 {
2564 if (tbinfo->attisdropped[i])
2565 continue;
2566 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2567 continue;
2568 if (nfields > 0)
2569 appendPQExpBufferStr(q, ", ");
2570 if (tbinfo->attgenerated[i])
2571 appendPQExpBufferStr(q, "NULL");
2572 else
2573 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2574 attgenerated[nfields] = tbinfo->attgenerated[i];
2575 nfields++;
2576 }
2577 /* Servers before 9.4 will complain about zero-column SELECT */
2578 if (nfields == 0)
2579 appendPQExpBufferStr(q, "NULL");
2580 appendPQExpBuffer(q, " FROM ONLY %s",
2581 fmtQualifiedDumpable(tbinfo));
2582 if (tdinfo->filtercond)
2583 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2584
2585 ExecuteSqlStatement(fout, q->data);
2586
2587 while (1)
2588 {
2589 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2591
2592 /* cross-check field count, allowing for dummy NULL if any */
2593 if (nfields != PQnfields(res) &&
2594 !(nfields == 0 && PQnfields(res) == 1))
2595 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2596 tbinfo->dobj.name);
2597
2598 /*
2599 * First time through, we build as much of the INSERT statement as
2600 * possible in "insertStmt", which we can then just print for each
2601 * statement. If the table happens to have zero dumpable columns then
2602 * this will be a complete statement, otherwise it will end in
2603 * "VALUES" and be ready to have the row's column values printed.
2604 */
2605 if (insertStmt == NULL)
2606 {
2607 TableInfo *targettab;
2608
2609 insertStmt = createPQExpBuffer();
2610
2611 /*
2612 * When load-via-partition-root is set or forced, get the root
2613 * table name for the partition table, so that we can reload data
2614 * through the root table.
2615 */
2616 if (tbinfo->ispartition &&
2617 (dopt->load_via_partition_root ||
2618 forcePartitionRootLoad(tbinfo)))
2619 targettab = getRootTableInfo(tbinfo);
2620 else
2621 targettab = tbinfo;
2622
2623 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2624 fmtQualifiedDumpable(targettab));
2625
2626 /* corner case for zero-column table */
2627 if (nfields == 0)
2628 {
2629 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2630 }
2631 else
2632 {
2633 /* append the list of column names if required */
2634 if (dopt->column_inserts)
2635 {
2636 appendPQExpBufferChar(insertStmt, '(');
2637 for (int field = 0; field < nfields; field++)
2638 {
2639 if (field > 0)
2640 appendPQExpBufferStr(insertStmt, ", ");
2641 appendPQExpBufferStr(insertStmt,
2642 fmtId(PQfname(res, field)));
2643 }
2644 appendPQExpBufferStr(insertStmt, ") ");
2645 }
2646
2647 if (tbinfo->needs_override)
2648 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2649
2650 appendPQExpBufferStr(insertStmt, "VALUES");
2651 }
2652 }
2653
2654 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2655 {
2656 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2657 if (rows_this_statement == 0)
2658 archputs(insertStmt->data, fout);
2659
2660 /*
2661 * If it is zero-column table then we've already written the
2662 * complete statement, which will mean we've disobeyed
2663 * --rows-per-insert when it's set greater than 1. We do support
2664 * a way to make this multi-row with: SELECT UNION ALL SELECT
2665 * UNION ALL ... but that's non-standard so we should avoid it
2666 * given that using INSERTs is mostly only ever needed for
2667 * cross-database exports.
2668 */
2669 if (nfields == 0)
2670 continue;
2671
2672 /* Emit a row heading */
2673 if (rows_per_statement == 1)
2674 archputs(" (", fout);
2675 else if (rows_this_statement > 0)
2676 archputs(",\n\t(", fout);
2677 else
2678 archputs("\n\t(", fout);
2679
2680 for (int field = 0; field < nfields; field++)
2681 {
2682 if (field > 0)
2683 archputs(", ", fout);
2684 if (attgenerated[field])
2685 {
2686 archputs("DEFAULT", fout);
2687 continue;
2688 }
2689 if (PQgetisnull(res, tuple, field))
2690 {
2691 archputs("NULL", fout);
2692 continue;
2693 }
2694
2695 /* XXX This code is partially duplicated in ruleutils.c */
2696 switch (PQftype(res, field))
2697 {
2698 case INT2OID:
2699 case INT4OID:
2700 case INT8OID:
2701 case OIDOID:
2702 case FLOAT4OID:
2703 case FLOAT8OID:
2704 case NUMERICOID:
2705 {
2706 /*
2707 * These types are printed without quotes unless
2708 * they contain values that aren't accepted by the
2709 * scanner unquoted (e.g., 'NaN'). Note that
2710 * strtod() and friends might accept NaN, so we
2711 * can't use that to test.
2712 *
2713 * In reality we only need to defend against
2714 * infinity and NaN, so we need not get too crazy
2715 * about pattern matching here.
2716 */
2717 const char *s = PQgetvalue(res, tuple, field);
2718
2719 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2720 archputs(s, fout);
2721 else
2722 archprintf(fout, "'%s'", s);
2723 }
2724 break;
2725
2726 case BITOID:
2727 case VARBITOID:
2728 archprintf(fout, "B'%s'",
2729 PQgetvalue(res, tuple, field));
2730 break;
2731
2732 case BOOLOID:
2733 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2734 archputs("true", fout);
2735 else
2736 archputs("false", fout);
2737 break;
2738
2739 default:
2740 /* All other types are printed as string literals. */
2743 PQgetvalue(res, tuple, field),
2744 fout);
2745 archputs(q->data, fout);
2746 break;
2747 }
2748 }
2749
2750 /* Terminate the row ... */
2751 archputs(")", fout);
2752
2753 /* ... and the statement, if the target no. of rows is reached */
2754 if (++rows_this_statement >= rows_per_statement)
2755 {
2756 if (dopt->do_nothing)
2757 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2758 else
2759 archputs(";\n", fout);
2760 /* Reset the row counter */
2761 rows_this_statement = 0;
2762 }
2763 }
2764
2765 if (PQntuples(res) <= 0)
2766 {
2767 PQclear(res);
2768 break;
2769 }
2770 PQclear(res);
2771 }
2772
2773 /* Terminate any statements that didn't make the row count. */
2774 if (rows_this_statement > 0)
2775 {
2776 if (dopt->do_nothing)
2777 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2778 else
2779 archputs(";\n", fout);
2780 }
2781
2782 archputs("\n\n", fout);
2783
2784 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2785
2787 if (insertStmt != NULL)
2788 destroyPQExpBuffer(insertStmt);
2789 free(attgenerated);
2790
2791 /* Revert back the setting */
2792 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2793 set_restrict_relation_kind(fout, "view, foreign-table");
2794
2795 return 1;
2796}
2797
2798/*
2799 * getRootTableInfo:
2800 * get the root TableInfo for the given partition table.
2801 */
2802static TableInfo *
2804{
2805 TableInfo *parentTbinfo;
2806
2807 Assert(tbinfo->ispartition);
2808 Assert(tbinfo->numParents == 1);
2809
2810 parentTbinfo = tbinfo->parents[0];
2811 while (parentTbinfo->ispartition)
2812 {
2813 Assert(parentTbinfo->numParents == 1);
2814 parentTbinfo = parentTbinfo->parents[0];
2815 }
2816
2817 return parentTbinfo;
2818}
2819
2820/*
2821 * forcePartitionRootLoad
2822 * Check if we must force load_via_partition_root for this partition.
2823 *
2824 * This is required if any level of ancestral partitioned table has an
2825 * unsafe partitioning scheme.
2826 */
2827static bool
2829{
2830 TableInfo *parentTbinfo;
2831
2832 Assert(tbinfo->ispartition);
2833 Assert(tbinfo->numParents == 1);
2834
2835 parentTbinfo = tbinfo->parents[0];
2836 if (parentTbinfo->unsafe_partitions)
2837 return true;
2838 while (parentTbinfo->ispartition)
2839 {
2840 Assert(parentTbinfo->numParents == 1);
2841 parentTbinfo = parentTbinfo->parents[0];
2842 if (parentTbinfo->unsafe_partitions)
2843 return true;
2844 }
2845
2846 return false;
2847}
2848
2849/*
2850 * dumpTableData -
2851 * dump the contents of a single table
2852 *
2853 * Actually, this just makes an ArchiveEntry for the table contents.
2854 */
2855static void
2857{
2858 DumpOptions *dopt = fout->dopt;
2859 TableInfo *tbinfo = tdinfo->tdtable;
2860 PQExpBuffer copyBuf = createPQExpBuffer();
2861 PQExpBuffer clistBuf = createPQExpBuffer();
2862 DataDumperPtr dumpFn;
2863 char *tdDefn = NULL;
2864 char *copyStmt;
2865 const char *copyFrom;
2866
2867 /* We had better have loaded per-column details about this table */
2868 Assert(tbinfo->interesting);
2869
2870 /*
2871 * When load-via-partition-root is set or forced, get the root table name
2872 * for the partition table, so that we can reload data through the root
2873 * table. Then construct a comment to be inserted into the TOC entry's
2874 * defn field, so that such cases can be identified reliably.
2875 */
2876 if (tbinfo->ispartition &&
2877 (dopt->load_via_partition_root ||
2878 forcePartitionRootLoad(tbinfo)))
2879 {
2880 TableInfo *parentTbinfo;
2881 char *sanitized;
2882
2883 parentTbinfo = getRootTableInfo(tbinfo);
2884 copyFrom = fmtQualifiedDumpable(parentTbinfo);
2885 sanitized = sanitize_line(copyFrom, true);
2886 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2887 sanitized);
2888 free(sanitized);
2889 tdDefn = pg_strdup(copyBuf->data);
2890 }
2891 else
2892 copyFrom = fmtQualifiedDumpable(tbinfo);
2893
2894 if (dopt->dump_inserts == 0)
2895 {
2896 /* Dump/restore using COPY */
2897 dumpFn = dumpTableData_copy;
2898 /* must use 2 steps here 'cause fmtId is nonreentrant */
2899 printfPQExpBuffer(copyBuf, "COPY %s ",
2900 copyFrom);
2901 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2902 fmtCopyColumnList(tbinfo, clistBuf));
2903 copyStmt = copyBuf->data;
2904 }
2905 else
2906 {
2907 /* Restore using INSERT */
2908 dumpFn = dumpTableData_insert;
2909 copyStmt = NULL;
2910 }
2911
2912 /*
2913 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2914 * dependency on its table as "special" and pass it to ArchiveEntry now.
2915 * See comments for BuildArchiveDependencies.
2916 */
2917 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2918 {
2919 TocEntry *te;
2920
2921 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2922 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2923 .namespace = tbinfo->dobj.namespace->dobj.name,
2924 .owner = tbinfo->rolname,
2925 .description = "TABLE DATA",
2926 .section = SECTION_DATA,
2927 .createStmt = tdDefn,
2928 .copyStmt = copyStmt,
2929 .deps = &(tbinfo->dobj.dumpId),
2930 .nDeps = 1,
2931 .dumpFn = dumpFn,
2932 .dumpArg = tdinfo));
2933
2934 /*
2935 * Set the TocEntry's dataLength in case we are doing a parallel dump
2936 * and want to order dump jobs by table size. We choose to measure
2937 * dataLength in table pages (including TOAST pages) during dump, so
2938 * no scaling is needed.
2939 *
2940 * However, relpages is declared as "integer" in pg_class, and hence
2941 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2942 * Cast so that we get the right interpretation of table sizes
2943 * exceeding INT_MAX pages.
2944 */
2945 te->dataLength = (BlockNumber) tbinfo->relpages;
2946 te->dataLength += (BlockNumber) tbinfo->toastpages;
2947
2948 /*
2949 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2950 * and instead we'd better worry about integer overflow. Clamp to
2951 * INT_MAX if the correct result exceeds that.
2952 */
2953 if (sizeof(te->dataLength) == 4 &&
2954 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2955 te->dataLength < 0))
2956 te->dataLength = INT_MAX;
2957 }
2958
2959 destroyPQExpBuffer(copyBuf);
2960 destroyPQExpBuffer(clistBuf);
2961}
2962
2963/*
2964 * refreshMatViewData -
2965 * load or refresh the contents of a single materialized view
2966 *
2967 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2968 * statement.
2969 */
2970static void
2972{
2973 TableInfo *tbinfo = tdinfo->tdtable;
2974 PQExpBuffer q;
2975
2976 /* If the materialized view is not flagged as populated, skip this. */
2977 if (!tbinfo->relispopulated)
2978 return;
2979
2980 q = createPQExpBuffer();
2981
2982 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2983 fmtQualifiedDumpable(tbinfo));
2984
2985 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2986 ArchiveEntry(fout,
2987 tdinfo->dobj.catId, /* catalog ID */
2988 tdinfo->dobj.dumpId, /* dump ID */
2989 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2990 .namespace = tbinfo->dobj.namespace->dobj.name,
2991 .owner = tbinfo->rolname,
2992 .description = "MATERIALIZED VIEW DATA",
2993 .section = SECTION_POST_DATA,
2994 .createStmt = q->data,
2995 .deps = tdinfo->dobj.dependencies,
2996 .nDeps = tdinfo->dobj.nDeps));
2997
2999}
3000
3001/*
3002 * getTableData -
3003 * set up dumpable objects representing the contents of tables
3004 */
3005static void
3006getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3007{
3008 int i;
3009
3010 for (i = 0; i < numTables; i++)
3011 {
3012 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3013 (!relkind || tblinfo[i].relkind == relkind))
3014 makeTableDataInfo(dopt, &(tblinfo[i]));
3015 }
3016}
3017
3018/*
3019 * Make a dumpable object for the data of this specific table
3020 *
3021 * Note: we make a TableDataInfo if and only if we are going to dump the
3022 * table data; the "dump" field in such objects isn't very interesting.
3023 */
3024static void
3026{
3027 TableDataInfo *tdinfo;
3028
3029 /*
3030 * Nothing to do if we already decided to dump the table. This will
3031 * happen for "config" tables.
3032 */
3033 if (tbinfo->dataObj != NULL)
3034 return;
3035
3036 /* Skip VIEWs (no data to dump) */
3037 if (tbinfo->relkind == RELKIND_VIEW)
3038 return;
3039 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3040 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3043 tbinfo->foreign_server)))
3044 return;
3045 /* Skip partitioned tables (data in partitions) */
3046 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3047 return;
3048
3049 /* Don't dump data in unlogged tables, if so requested */
3050 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3052 return;
3053
3054 /* Check that the data is not explicitly excluded */
3056 tbinfo->dobj.catId.oid))
3057 return;
3058
3059 /* OK, let's dump it */
3060 tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3061
3062 if (tbinfo->relkind == RELKIND_MATVIEW)
3064 else if (tbinfo->relkind == RELKIND_SEQUENCE)
3065 tdinfo->dobj.objType = DO_SEQUENCE_SET;
3066 else
3067 tdinfo->dobj.objType = DO_TABLE_DATA;
3068
3069 /*
3070 * Note: use tableoid 0 so that this object won't be mistaken for
3071 * something that pg_depend entries apply to.
3072 */
3073 tdinfo->dobj.catId.tableoid = 0;
3074 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3075 AssignDumpId(&tdinfo->dobj);
3076 tdinfo->dobj.name = tbinfo->dobj.name;
3077 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3078 tdinfo->tdtable = tbinfo;
3079 tdinfo->filtercond = NULL; /* might get set later */
3080 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3081
3082 /* A TableDataInfo contains data, of course */
3084
3085 tbinfo->dataObj = tdinfo;
3086
3087 /*
3088 * Materialized view statistics must be restored after the data, because
3089 * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3090 *
3091 * The dependency is added here because the statistics objects are created
3092 * first.
3093 */
3094 if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3095 {
3096 tbinfo->stats->section = SECTION_POST_DATA;
3097 addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3098 }
3099
3100 /* Make sure that we'll collect per-column info for this table. */
3101 tbinfo->interesting = true;
3102}
3103
3104/*
3105 * The refresh for a materialized view must be dependent on the refresh for
3106 * any materialized view that this one is dependent on.
3107 *
3108 * This must be called after all the objects are created, but before they are
3109 * sorted.
3110 */
3111static void
3113{
3114 PQExpBuffer query;
3115 PGresult *res;
3116 int ntups,
3117 i;
3118 int i_classid,
3119 i_objid,
3120 i_refobjid;
3121
3122 /* No Mat Views before 9.3. */
3123 if (fout->remoteVersion < 90300)
3124 return;
3125
3126 query = createPQExpBuffer();
3127
3128 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3129 "( "
3130 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3131 "FROM pg_depend d1 "
3132 "JOIN pg_class c1 ON c1.oid = d1.objid "
3133 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3134 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3135 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3136 "AND d2.objid = r1.oid "
3137 "AND d2.refobjid <> d1.objid "
3138 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3139 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3140 CppAsString2(RELKIND_VIEW) ") "
3141 "WHERE d1.classid = 'pg_class'::regclass "
3142 "UNION "
3143 "SELECT w.objid, d3.refobjid, c3.relkind "
3144 "FROM w "
3145 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3146 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3147 "AND d3.objid = r3.oid "
3148 "AND d3.refobjid <> w.refobjid "
3149 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3150 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3151 CppAsString2(RELKIND_VIEW) ") "
3152 ") "
3153 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3154 "FROM w "
3155 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3156
3157 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3158
3159 ntups = PQntuples(res);
3160
3161 i_classid = PQfnumber(res, "classid");
3162 i_objid = PQfnumber(res, "objid");
3163 i_refobjid = PQfnumber(res, "refobjid");
3164
3165 for (i = 0; i < ntups; i++)
3166 {
3167 CatalogId objId;
3168 CatalogId refobjId;
3169 DumpableObject *dobj;
3170 DumpableObject *refdobj;
3171 TableInfo *tbinfo;
3172 TableInfo *reftbinfo;
3173
3174 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3175 objId.oid = atooid(PQgetvalue(res, i, i_objid));
3176 refobjId.tableoid = objId.tableoid;
3177 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3178
3179 dobj = findObjectByCatalogId(objId);
3180 if (dobj == NULL)
3181 continue;
3182
3183 Assert(dobj->objType == DO_TABLE);
3184 tbinfo = (TableInfo *) dobj;
3185 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3186 dobj = (DumpableObject *) tbinfo->dataObj;
3187 if (dobj == NULL)
3188 continue;
3190
3191 refdobj = findObjectByCatalogId(refobjId);
3192 if (refdobj == NULL)
3193 continue;
3194
3195 Assert(refdobj->objType == DO_TABLE);
3196 reftbinfo = (TableInfo *) refdobj;
3197 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3198 refdobj = (DumpableObject *) reftbinfo->dataObj;
3199 if (refdobj == NULL)
3200 continue;
3201 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3202
3203 addObjectDependency(dobj, refdobj->dumpId);
3204
3205 if (!reftbinfo->relispopulated)
3206 tbinfo->relispopulated = false;
3207 }
3208
3209 PQclear(res);
3210
3211 destroyPQExpBuffer(query);
3212}
3213
3214/*
3215 * getTableDataFKConstraints -
3216 * add dump-order dependencies reflecting foreign key constraints
3217 *
3218 * This code is executed only in a data-only dump --- in schema+data dumps
3219 * we handle foreign key issues by not creating the FK constraints until
3220 * after the data is loaded. In a data-only dump, however, we want to
3221 * order the table data objects in such a way that a table's referenced
3222 * tables are restored first. (In the presence of circular references or
3223 * self-references this may be impossible; we'll detect and complain about
3224 * that during the dependency sorting step.)
3225 */
3226static void
3228{
3229 DumpableObject **dobjs;
3230 int numObjs;
3231 int i;
3232
3233 /* Search through all the dumpable objects for FK constraints */
3234 getDumpableObjects(&dobjs, &numObjs);
3235 for (i = 0; i < numObjs; i++)
3236 {
3237 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3238 {
3239 ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3240 TableInfo *ftable;
3241
3242 /* Not interesting unless both tables are to be dumped */
3243 if (cinfo->contable == NULL ||
3244 cinfo->contable->dataObj == NULL)
3245 continue;
3246 ftable = findTableByOid(cinfo->confrelid);
3247 if (ftable == NULL ||
3248 ftable->dataObj == NULL)
3249 continue;
3250
3251 /*
3252 * Okay, make referencing table's TABLE_DATA object depend on the
3253 * referenced table's TABLE_DATA object.
3254 */
3256 ftable->dataObj->dobj.dumpId);
3257 }
3258 }
3259 free(dobjs);
3260}
3261
3262
3263/*
3264 * dumpDatabase:
3265 * dump the database definition
3266 */
3267static void
3269{
3270 DumpOptions *dopt = fout->dopt;
3272 PQExpBuffer delQry = createPQExpBuffer();
3273 PQExpBuffer creaQry = createPQExpBuffer();
3274 PQExpBuffer labelq = createPQExpBuffer();
3275 PGconn *conn = GetConnection(fout);
3276 PGresult *res;
3277 int i_tableoid,
3278 i_oid,
3279 i_datname,
3280 i_datdba,
3281 i_encoding,
3282 i_datlocprovider,
3283 i_collate,
3284 i_ctype,
3285 i_datlocale,
3286 i_daticurules,
3287 i_frozenxid,
3288 i_minmxid,
3289 i_datacl,
3290 i_acldefault,
3291 i_datistemplate,
3292 i_datconnlimit,
3293 i_datcollversion,
3294 i_tablespace;
3295 CatalogId dbCatId;
3296 DumpId dbDumpId;
3297 DumpableAcl dbdacl;
3298 const char *datname,
3299 *dba,
3300 *encoding,
3302 *collate,
3303 *ctype,
3304 *locale,
3305 *icurules,
3307 *datconnlimit,
3308 *tablespace;
3309 uint32 frozenxid,
3310 minmxid;
3311 char *qdatname;
3312
3313 pg_log_info("saving database definition");
3314
3315 /*
3316 * Fetch the database-level properties for this database.
3317 */
3318 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3319 "datdba, "
3320 "pg_encoding_to_char(encoding) AS encoding, "
3321 "datcollate, datctype, datfrozenxid, "
3322 "datacl, acldefault('d', datdba) AS acldefault, "
3323 "datistemplate, datconnlimit, ");
3324 if (fout->remoteVersion >= 90300)
3325 appendPQExpBufferStr(dbQry, "datminmxid, ");
3326 else
3327 appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3328 if (fout->remoteVersion >= 170000)
3329 appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3330 else if (fout->remoteVersion >= 150000)
3331 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3332 else
3333 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3334 if (fout->remoteVersion >= 160000)
3335 appendPQExpBufferStr(dbQry, "daticurules, ");
3336 else
3337 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3339 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3340 "shobj_description(oid, 'pg_database') AS description "
3341 "FROM pg_database "
3342 "WHERE datname = current_database()");
3343
3344 res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3345
3346 i_tableoid = PQfnumber(res, "tableoid");
3347 i_oid = PQfnumber(res, "oid");
3348 i_datname = PQfnumber(res, "datname");
3349 i_datdba = PQfnumber(res, "datdba");
3350 i_encoding = PQfnumber(res, "encoding");
3351 i_datlocprovider = PQfnumber(res, "datlocprovider");
3352 i_collate = PQfnumber(res, "datcollate");
3353 i_ctype = PQfnumber(res, "datctype");
3354 i_datlocale = PQfnumber(res, "datlocale");
3355 i_daticurules = PQfnumber(res, "daticurules");
3356 i_frozenxid = PQfnumber(res, "datfrozenxid");
3357 i_minmxid = PQfnumber(res, "datminmxid");
3358 i_datacl = PQfnumber(res, "datacl");
3359 i_acldefault = PQfnumber(res, "acldefault");
3360 i_datistemplate = PQfnumber(res, "datistemplate");
3361 i_datconnlimit = PQfnumber(res, "datconnlimit");
3362 i_datcollversion = PQfnumber(res, "datcollversion");
3363 i_tablespace = PQfnumber(res, "tablespace");
3364
3365 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3366 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3367 datname = PQgetvalue(res, 0, i_datname);
3368 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3369 encoding = PQgetvalue(res, 0, i_encoding);
3370 datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3371 collate = PQgetvalue(res, 0, i_collate);
3372 ctype = PQgetvalue(res, 0, i_ctype);
3373 if (!PQgetisnull(res, 0, i_datlocale))
3374 locale = PQgetvalue(res, 0, i_datlocale);
3375 else
3376 locale = NULL;
3377 if (!PQgetisnull(res, 0, i_daticurules))
3378 icurules = PQgetvalue(res, 0, i_daticurules);
3379 else
3380 icurules = NULL;
3381 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3382 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3383 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3384 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3385 datistemplate = PQgetvalue(res, 0, i_datistemplate);
3386 datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3387 tablespace = PQgetvalue(res, 0, i_tablespace);
3388
3389 qdatname = pg_strdup(fmtId(datname));
3390
3391 /*
3392 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3393 * to preserve that), as well as the encoding, locale, and tablespace
3394 * since those can't be altered later. Other DB properties are left to
3395 * the DATABASE PROPERTIES entry, so that they can be applied after
3396 * reconnecting to the target DB.
3397 *
3398 * For binary upgrade, we use the FILE_COPY strategy because testing has
3399 * shown it to be faster. When the server is in binary upgrade mode, it
3400 * will also skip the checkpoints this strategy ordinarily performs.
3401 */
3402 if (dopt->binary_upgrade)
3403 {
3404 appendPQExpBuffer(creaQry,
3405 "CREATE DATABASE %s WITH TEMPLATE = template0 "
3406 "OID = %u STRATEGY = FILE_COPY",
3407 qdatname, dbCatId.oid);
3408 }
3409 else
3410 {
3411 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3412 qdatname);
3413 }
3414 if (strlen(encoding) > 0)
3415 {
3416 appendPQExpBufferStr(creaQry, " ENCODING = ");
3417 appendStringLiteralAH(creaQry, encoding, fout);
3418 }
3419
3420 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3421 if (datlocprovider[0] == 'b')
3422 appendPQExpBufferStr(creaQry, "builtin");
3423 else if (datlocprovider[0] == 'c')
3424 appendPQExpBufferStr(creaQry, "libc");
3425 else if (datlocprovider[0] == 'i')
3426 appendPQExpBufferStr(creaQry, "icu");
3427 else
3428 pg_fatal("unrecognized locale provider: %s",
3430
3431 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3432 {
3433 appendPQExpBufferStr(creaQry, " LOCALE = ");
3434 appendStringLiteralAH(creaQry, collate, fout);
3435 }
3436 else
3437 {
3438 if (strlen(collate) > 0)
3439 {
3440 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3441 appendStringLiteralAH(creaQry, collate, fout);
3442 }
3443 if (strlen(ctype) > 0)
3444 {
3445 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3446 appendStringLiteralAH(creaQry, ctype, fout);
3447 }
3448 }
3449 if (locale)
3450 {
3451 if (datlocprovider[0] == 'b')
3452 appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3453 else
3454 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3455
3456 appendStringLiteralAH(creaQry, locale, fout);
3457 }
3458
3459 if (icurules)
3460 {
3461 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3462 appendStringLiteralAH(creaQry, icurules, fout);
3463 }
3464
3465 /*
3466 * For binary upgrade, carry over the collation version. For normal
3467 * dump/restore, omit the version, so that it is computed upon restore.
3468 */
3469 if (dopt->binary_upgrade)
3470 {
3471 if (!PQgetisnull(res, 0, i_datcollversion))
3472 {
3473 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3474 appendStringLiteralAH(creaQry,
3475 PQgetvalue(res, 0, i_datcollversion),
3476 fout);
3477 }
3478 }
3479
3480 /*
3481 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3482 * thing; the decision whether to specify a tablespace should be left till
3483 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3484 * label the DATABASE entry with the tablespace and let the normal
3485 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3486 * attention to default_tablespace, so that won't work.
3487 */
3488 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3489 !dopt->outputNoTablespaces)
3490 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3491 fmtId(tablespace));
3492 appendPQExpBufferStr(creaQry, ";\n");
3493
3494 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3495 qdatname);
3496
3497 dbDumpId = createDumpId();
3498
3499 ArchiveEntry(fout,
3500 dbCatId, /* catalog ID */
3501 dbDumpId, /* dump ID */
3502 ARCHIVE_OPTS(.tag = datname,
3503 .owner = dba,
3504 .description = "DATABASE",
3505 .section = SECTION_PRE_DATA,
3506 .createStmt = creaQry->data,
3507 .dropStmt = delQry->data));
3508
3509 /* Compute correct tag for archive entry */
3510 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3511
3512 /* Dump DB comment if any */
3513 {
3514 /*
3515 * 8.2 and up keep comments on shared objects in a shared table, so we
3516 * cannot use the dumpComment() code used for other database objects.
3517 * Be careful that the ArchiveEntry parameters match that function.
3518 */
3519 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3520
3521 if (comment && *comment && !dopt->no_comments)
3522 {
3523 resetPQExpBuffer(dbQry);
3524
3525 /*
3526 * Generates warning when loaded into a differently-named
3527 * database.
3528 */
3529 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3530 appendStringLiteralAH(dbQry, comment, fout);
3531 appendPQExpBufferStr(dbQry, ";\n");
3532
3534 ARCHIVE_OPTS(.tag = labelq->data,
3535 .owner = dba,
3536 .description = "COMMENT",
3537 .section = SECTION_NONE,
3538 .createStmt = dbQry->data,
3539 .deps = &dbDumpId,
3540 .nDeps = 1));
3541 }
3542 }
3543
3544 /* Dump DB security label, if enabled */
3545 if (!dopt->no_security_labels)
3546 {
3547 PGresult *shres;
3548 PQExpBuffer seclabelQry;
3549
3550 seclabelQry = createPQExpBuffer();
3551
3552 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3553 shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3554 resetPQExpBuffer(seclabelQry);
3555 emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3556 if (seclabelQry->len > 0)
3558 ARCHIVE_OPTS(.tag = labelq->data,
3559 .owner = dba,
3560 .description = "SECURITY LABEL",
3561 .section = SECTION_NONE,
3562 .createStmt = seclabelQry->data,
3563 .deps = &dbDumpId,
3564 .nDeps = 1));
3565 destroyPQExpBuffer(seclabelQry);
3566 PQclear(shres);
3567 }
3568
3569 /*
3570 * Dump ACL if any. Note that we do not support initial privileges
3571 * (pg_init_privs) on databases.
3572 */
3573 dbdacl.privtype = 0;
3574 dbdacl.initprivs = NULL;
3575
3576 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3577 qdatname, NULL, NULL,
3578 NULL, dba, &dbdacl);
3579
3580 /*
3581 * Now construct a DATABASE PROPERTIES archive entry to restore any
3582 * non-default database-level properties. (The reason this must be
3583 * separate is that we cannot put any additional commands into the TOC
3584 * entry that has CREATE DATABASE. pg_restore would execute such a group
3585 * in an implicit transaction block, and the backend won't allow CREATE
3586 * DATABASE in that context.)
3587 */
3588 resetPQExpBuffer(creaQry);
3589 resetPQExpBuffer(delQry);
3590
3591 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3592 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3593 qdatname, datconnlimit);
3594
3595 if (strcmp(datistemplate, "t") == 0)
3596 {
3597 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3598 qdatname);
3599
3600 /*
3601 * The backend won't accept DROP DATABASE on a template database. We
3602 * can deal with that by removing the template marking before the DROP
3603 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3604 * since no such command is currently supported, fake it with a direct
3605 * UPDATE on pg_database.
3606 */
3607 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3608 "SET datistemplate = false WHERE datname = ");
3609 appendStringLiteralAH(delQry, datname, fout);
3610 appendPQExpBufferStr(delQry, ";\n");
3611 }
3612
3613 /*
3614 * We do not restore pg_database.dathasloginevt because it is set
3615 * automatically on login event trigger creation.
3616 */
3617
3618 /* Add database-specific SET options */
3619 dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3620
3621 /*
3622 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3623 * entry, too, for lack of a better place.
3624 */
3625 if (dopt->binary_upgrade)
3626 {
3627 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3628 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3629 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3630 "WHERE datname = ",
3631 frozenxid, minmxid);
3632 appendStringLiteralAH(creaQry, datname, fout);
3633 appendPQExpBufferStr(creaQry, ";\n");
3634 }
3635
3636 if (creaQry->len > 0)
3638 ARCHIVE_OPTS(.tag = datname,
3639 .owner = dba,
3640 .description = "DATABASE PROPERTIES",
3641 .section = SECTION_PRE_DATA,
3642 .createStmt = creaQry->data,
3643 .dropStmt = delQry->data,
3644 .deps = &dbDumpId));
3645
3646 /*
3647 * pg_largeobject comes from the old system intact, so set its
3648 * relfrozenxids, relminmxids and relfilenode.
3649 *
3650 * pg_largeobject_metadata also comes from the old system intact for
3651 * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3652 * relfilenode, too. pg_upgrade can't copy/link the files from older
3653 * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3654 * changed its storage format in v16.
3655 */
3656 if (dopt->binary_upgrade)
3657 {
3658 PGresult *lo_res;
3659 PQExpBuffer loFrozenQry = createPQExpBuffer();
3660 PQExpBuffer loOutQry = createPQExpBuffer();
3661 PQExpBuffer lomOutQry = createPQExpBuffer();
3662 PQExpBuffer loHorizonQry = createPQExpBuffer();
3663 PQExpBuffer lomHorizonQry = createPQExpBuffer();
3664 int ii_relfrozenxid,
3665 ii_relfilenode,
3666 ii_oid,
3667 ii_relminmxid;
3668
3669 if (fout->remoteVersion >= 90300)
3670 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3671 "FROM pg_catalog.pg_class\n"
3672 "WHERE oid IN (%u, %u, %u, %u);\n",
3673 LargeObjectRelationId, LargeObjectLOidPNIndexId,
3674 LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3675 else
3676 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3677 "FROM pg_catalog.pg_class\n"
3678 "WHERE oid IN (%u, %u);\n",
3679 LargeObjectRelationId, LargeObjectLOidPNIndexId);
3680
3681 lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3682
3683 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3684 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3685 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3686 ii_oid = PQfnumber(lo_res, "oid");
3687
3688 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3689 appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3690 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3691 appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3692 for (int i = 0; i < PQntuples(lo_res); ++i)
3693 {
3694 Oid oid;
3695 RelFileNumber relfilenumber;
3696 PQExpBuffer horizonQry;
3697 PQExpBuffer outQry;
3698
3699 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3700 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3701
3702 if (oid == LargeObjectRelationId ||
3703 oid == LargeObjectLOidPNIndexId)
3704 {
3705 horizonQry = loHorizonQry;
3706 outQry = loOutQry;
3707 }
3708 else
3709 {
3710 horizonQry = lomHorizonQry;
3711 outQry = lomOutQry;
3712 }
3713
3714 appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3715 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3716 "WHERE oid = %u;\n",
3717 atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3718 atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3719 atooid(PQgetvalue(lo_res, i, ii_oid)));
3720
3721 if (oid == LargeObjectRelationId ||
3722 oid == LargeObjectMetadataRelationId)
3723 appendPQExpBuffer(outQry,
3724 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3725 relfilenumber);
3726 else if (oid == LargeObjectLOidPNIndexId ||
3727 oid == LargeObjectMetadataOidIndexId)
3728 appendPQExpBuffer(outQry,
3729 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3730 relfilenumber);
3731 }
3732
3733 appendPQExpBufferStr(loOutQry,
3734 "TRUNCATE pg_catalog.pg_largeobject;\n");
3735 appendPQExpBufferStr(lomOutQry,
3736 "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3737
3738 appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3739 appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3740
3742 ARCHIVE_OPTS(.tag = "pg_largeobject",
3743 .description = "pg_largeobject",
3744 .section = SECTION_PRE_DATA,
3745 .createStmt = loOutQry->data));
3746
3747 if (fout->remoteVersion >= 160000)
3749 ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3750 .description = "pg_largeobject_metadata",
3751 .section = SECTION_PRE_DATA,
3752 .createStmt = lomOutQry->data));
3753
3754 PQclear(lo_res);
3755
3756 destroyPQExpBuffer(loFrozenQry);
3757 destroyPQExpBuffer(loHorizonQry);
3758 destroyPQExpBuffer(lomHorizonQry);
3759 destroyPQExpBuffer(loOutQry);
3760 destroyPQExpBuffer(lomOutQry);
3761 }
3762
3763 PQclear(res);
3764
3765 free(qdatname);
3766 destroyPQExpBuffer(dbQry);
3767 destroyPQExpBuffer(delQry);
3768 destroyPQExpBuffer(creaQry);
3769 destroyPQExpBuffer(labelq);
3770}
3771
3772/*
3773 * Collect any database-specific or role-and-database-specific SET options
3774 * for this database, and append them to outbuf.
3775 */
3776static void
3778 const char *dbname, Oid dboid)
3779{
3780 PGconn *conn = GetConnection(AH);
3782 PGresult *res;
3783
3784 /* First collect database-specific options */
3785 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3786 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3787 dboid);
3788
3789 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3790
3791 for (int i = 0; i < PQntuples(res); i++)
3793 "DATABASE", dbname, NULL, NULL,
3794 outbuf);
3795
3796 PQclear(res);
3797
3798 /* Now look for role-and-database-specific options */
3799 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3800 "FROM pg_db_role_setting s, pg_roles r "
3801 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3802 dboid);
3803
3804 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3805
3806 for (int i = 0; i < PQntuples(res); i++)
3808 "ROLE", PQgetvalue(res, i, 0),
3809 "DATABASE", dbname,
3810 outbuf);
3811
3812 PQclear(res);
3813
3815}
3816
3817/*
3818 * dumpEncoding: put the correct encoding into the archive
3819 */
3820static void
3822{
3823 const char *encname = pg_encoding_to_char(AH->encoding);
3825
3826 pg_log_info("saving encoding = %s", encname);
3827
3828 appendPQExpBufferStr(qry, "SET client_encoding = ");
3829 appendStringLiteralAH(qry, encname, AH);
3830 appendPQExpBufferStr(qry, ";\n");
3831
3833 ARCHIVE_OPTS(.tag = "ENCODING",
3834 .description = "ENCODING",
3835 .section = SECTION_PRE_DATA,
3836 .createStmt = qry->data));
3837
3838 destroyPQExpBuffer(qry);
3839}
3840
3841
3842/*
3843 * dumpStdStrings: put the correct escape string behavior into the archive
3844 */
3845static void
3847{
3848 const char *stdstrings = AH->std_strings ? "on" : "off";
3850
3851 pg_log_info("saving \"standard_conforming_strings = %s\"",
3852 stdstrings);
3853
3854 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3855 stdstrings);
3856
3858 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3859 .description = "STDSTRINGS",
3860 .section = SECTION_PRE_DATA,
3861 .createStmt = qry->data));
3862
3863 destroyPQExpBuffer(qry);
3864}
3865
3866/*
3867 * dumpSearchPath: record the active search_path in the archive
3868 */
3869static void
3871{
3874 PGresult *res;
3875 char **schemanames = NULL;
3876 int nschemanames = 0;
3877 int i;
3878
3879 /*
3880 * We use the result of current_schemas(), not the search_path GUC,
3881 * because that might contain wildcards such as "$user", which won't
3882 * necessarily have the same value during restore. Also, this way avoids
3883 * listing schemas that may appear in search_path but not actually exist,
3884 * which seems like a prudent exclusion.
3885 */
3887 "SELECT pg_catalog.current_schemas(false)");
3888
3889 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3890 pg_fatal("could not parse result of current_schemas()");
3891
3892 /*
3893 * We use set_config(), not a simple "SET search_path" command, because
3894 * the latter has less-clean behavior if the search path is empty. While
3895 * that's likely to get fixed at some point, it seems like a good idea to
3896 * be as backwards-compatible as possible in what we put into archives.
3897 */
3898 for (i = 0; i < nschemanames; i++)
3899 {
3900 if (i > 0)
3901 appendPQExpBufferStr(path, ", ");
3902 appendPQExpBufferStr(path, fmtId(schemanames[i]));
3903 }
3904
3905 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3906 appendStringLiteralAH(qry, path->data, AH);
3907 appendPQExpBufferStr(qry, ", false);\n");
3908
3909 pg_log_info("saving \"search_path = %s\"", path->data);
3910
3912 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3913 .description = "SEARCHPATH",
3914 .section = SECTION_PRE_DATA,
3915 .createStmt = qry->data));
3916
3917 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3918 AH->searchpath = pg_strdup(qry->data);
3919
3920 free(schemanames);
3921 PQclear(res);
3922 destroyPQExpBuffer(qry);
3923 destroyPQExpBuffer(path);
3924}
3925
3926
3927/*
3928 * getLOs:
3929 * Collect schema-level data about large objects
3930 */
3931static void
3933{
3934 DumpOptions *dopt = fout->dopt;
3936 PGresult *res;
3937 int ntups;
3938 int i;
3939 int n;
3940 int i_oid;
3941 int i_lomowner;
3942 int i_lomacl;
3943 int i_acldefault;
3944
3945 pg_log_info("reading large objects");
3946
3947 /*
3948 * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3949 * with the same owner/ACL appear together.
3950 */
3952 "SELECT oid, lomowner, lomacl, "
3953 "acldefault('L', lomowner) AS acldefault "
3954 "FROM pg_largeobject_metadata "
3955 "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3956
3957 res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3958
3959 i_oid = PQfnumber(res, "oid");
3960 i_lomowner = PQfnumber(res, "lomowner");
3961 i_lomacl = PQfnumber(res, "lomacl");
3962 i_acldefault = PQfnumber(res, "acldefault");
3963
3964 ntups = PQntuples(res);
3965
3966 /*
3967 * Group the blobs into suitably-sized groups that have the same owner and
3968 * ACL setting, and build a metadata and a data DumpableObject for each
3969 * group. (If we supported initprivs for blobs, we'd have to insist that
3970 * groups also share initprivs settings, since the DumpableObject only has
3971 * room for one.) i is the index of the first tuple in the current group,
3972 * and n is the number of tuples we include in the group.
3973 */
3974 for (i = 0; i < ntups; i += n)
3975 {
3976 Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3977 char *thisowner = PQgetvalue(res, i, i_lomowner);
3978 char *thisacl = PQgetvalue(res, i, i_lomacl);
3979 LoInfo *loinfo;
3980 DumpableObject *lodata;
3981 char namebuf[64];
3982
3983 /* Scan to find first tuple not to be included in group */
3984 n = 1;
3985 while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3986 {
3987 if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3988 strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3989 break;
3990 n++;
3991 }
3992
3993 /* Build the metadata DumpableObject */
3994 loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3995
3996 loinfo->dobj.objType = DO_LARGE_OBJECT;
3997 loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3998 loinfo->dobj.catId.oid = thisoid;
3999 AssignDumpId(&loinfo->dobj);
4000
4001 if (n > 1)
4002 snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4003 atooid(PQgetvalue(res, i + n - 1, i_oid)));
4004 else
4005 snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4006 loinfo->dobj.name = pg_strdup(namebuf);
4007 loinfo->dacl.acl = pg_strdup(thisacl);
4008 loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4009 loinfo->dacl.privtype = 0;
4010 loinfo->dacl.initprivs = NULL;
4011 loinfo->rolname = getRoleName(thisowner);
4012 loinfo->numlos = n;
4013 loinfo->looids[0] = thisoid;
4014 /* Collect OIDs of the remaining blobs in this group */
4015 for (int k = 1; k < n; k++)
4016 {
4017 CatalogId extraID;
4018
4019 loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4020
4021 /* Make sure we can look up loinfo by any of the blobs' OIDs */
4022 extraID.tableoid = LargeObjectRelationId;
4023 extraID.oid = loinfo->looids[k];
4024 recordAdditionalCatalogID(extraID, &loinfo->dobj);
4025 }
4026
4027 /* LOs have data */
4029
4030 /* Mark whether LO group has a non-empty ACL */
4031 if (!PQgetisnull(res, i, i_lomacl))
4033
4034 /*
4035 * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4036 * as it will be copied by pg_upgrade, which simply copies the
4037 * pg_largeobject table. We *do* however dump out anything but the
4038 * data, as pg_upgrade copies just pg_largeobject, but not
4039 * pg_largeobject_metadata, after the dump is restored. In versions
4040 * before v12, this is done via proper large object commands. In
4041 * newer versions, we dump the content of pg_largeobject_metadata and
4042 * any associated pg_shdepend rows, which is faster to restore. (On
4043 * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4044 * column is hidden and won't be dumped.)
4045 */
4046 if (dopt->binary_upgrade)
4047 {
4048 if (fout->remoteVersion >= 120000)
4049 {
4050 /*
4051 * We should've saved pg_largeobject_metadata's dump ID before
4052 * this point.
4053 */
4055
4057
4058 /*
4059 * Mark the large object as dependent on
4060 * pg_largeobject_metadata so that any large object
4061 * comments/seclables are dumped after it.
4062 */
4063 loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4065 loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4066 }
4067 else
4068 loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4069 }
4070
4071 /*
4072 * Create a "BLOBS" data item for the group, too. This is just a
4073 * placeholder for sorting; it carries no data now.
4074 */
4075 lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4076 lodata->objType = DO_LARGE_OBJECT_DATA;
4077 lodata->catId = nilCatalogId;
4078 AssignDumpId(lodata);
4079 lodata->name = pg_strdup(namebuf);
4081 /* Set up explicit dependency from data to metadata */
4082 lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4083 lodata->dependencies[0] = loinfo->dobj.dumpId;
4084 lodata->nDeps = lodata->allocDeps = 1;
4085 }
4086
4087 PQclear(res);
4088 destroyPQExpBuffer(loQry);
4089}
4090
4091/*
4092 * dumpLO
4093 *
4094 * dump the definition (metadata) of the given large object group
4095 */
4096static void
4097dumpLO(Archive *fout, const LoInfo *loinfo)
4098{
4099 PQExpBuffer cquery = createPQExpBuffer();
4100
4101 /*
4102 * The "definition" is just a newline-separated list of OIDs. We need to
4103 * put something into the dropStmt too, but it can just be a comment.
4104 */
4105 for (int i = 0; i < loinfo->numlos; i++)
4106 appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4107
4108 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4109 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4110 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4111 .owner = loinfo->rolname,
4112 .description = "BLOB METADATA",
4113 .section = SECTION_DATA,
4114 .createStmt = cquery->data,
4115 .dropStmt = "-- dummy"));
4116
4117 /*
4118 * Dump per-blob comments and seclabels if any. We assume these are rare
4119 * enough that it's okay to generate retail TOC entries for them.
4120 */
4121 if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4123 {
4124 for (int i = 0; i < loinfo->numlos; i++)
4125 {
4126 CatalogId catId;
4127 char namebuf[32];
4128
4129 /* Build identifying info for this blob */
4130 catId.tableoid = loinfo->dobj.catId.tableoid;
4131 catId.oid = loinfo->looids[i];
4132 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4133
4134 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4135 dumpComment(fout, "LARGE OBJECT", namebuf,
4136 NULL, loinfo->rolname,
4137 catId, 0, loinfo->dobj.dumpId);
4138
4139 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4140 dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4141 NULL, loinfo->rolname,
4142 catId, 0, loinfo->dobj.dumpId);
4143 }
4144 }
4145
4146 /*
4147 * Dump the ACLs if any (remember that all blobs in the group will have
4148 * the same ACL). If there's just one blob, dump a simple ACL entry; if
4149 * there's more, make a "LARGE OBJECTS" entry that really contains only
4150 * the ACL for the first blob. _printTocEntry() will be cued by the tag
4151 * string to emit a mutated version for each blob.
4152 */
4153 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4154 {
4155 char namebuf[32];
4156
4157 /* Build identifying info for the first blob */
4158 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4159
4160 if (loinfo->numlos > 1)
4161 {
4162 char tagbuf[64];
4163
4164 snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4165 loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4166
4167 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4168 "LARGE OBJECT", namebuf, NULL, NULL,
4169 tagbuf, loinfo->rolname, &loinfo->dacl);
4170 }
4171 else
4172 {
4173 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4174 "LARGE OBJECT", namebuf, NULL, NULL,
4175 NULL, loinfo->rolname, &loinfo->dacl);
4176 }
4177 }
4178
4179 destroyPQExpBuffer(cquery);
4180}
4181
4182/*
4183 * dumpLOs:
4184 * dump the data contents of the large objects in the given group
4185 */
4186static int
4187dumpLOs(Archive *fout, const void *arg)
4188{
4189 const LoInfo *loinfo = (const LoInfo *) arg;
4190 PGconn *conn = GetConnection(fout);
4191 char buf[LOBBUFSIZE];
4192
4193 pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4194
4195 for (int i = 0; i < loinfo->numlos; i++)
4196 {
4197 Oid loOid = loinfo->looids[i];
4198 int loFd;
4199 int cnt;
4200
4201 /* Open the LO */
4202 loFd = lo_open(conn, loOid, INV_READ);
4203 if (loFd == -1)
4204 pg_fatal("could not open large object %u: %s",
4205 loOid, PQerrorMessage(conn));
4206
4207 StartLO(fout, loOid);
4208
4209 /* Now read it in chunks, sending data to archive */
4210 do
4211 {
4212 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4213 if (cnt < 0)
4214 pg_fatal("error reading large object %u: %s",
4215 loOid, PQerrorMessage(conn));
4216
4217 WriteData(fout, buf, cnt);
4218 } while (cnt > 0);
4219
4220 lo_close(conn, loFd);
4221
4222 EndLO(fout, loOid);
4223 }
4224
4225 return 1;
4226}
4227
4228/*
4229 * getPolicies
4230 * get information about all RLS policies on dumpable tables.
4231 */
4232void
4233getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4234{
4235 DumpOptions *dopt = fout->dopt;
4236 PQExpBuffer query;
4237 PQExpBuffer tbloids;
4238 PGresult *res;
4239 PolicyInfo *polinfo;
4240 int i_oid;
4241 int i_tableoid;
4242 int i_polrelid;
4243 int i_polname;
4244 int i_polcmd;
4245 int i_polpermissive;
4246 int i_polroles;
4247 int i_polqual;
4248 int i_polwithcheck;
4249 int i,
4250 j,
4251 ntups;
4252
4253 /* No policies before 9.5 */
4254 if (fout->remoteVersion < 90500)
4255 return;
4256
4257 /* Skip if --no-policies was specified */
4258 if (dopt->no_policies)
4259 return;
4260
4261 query = createPQExpBuffer();
4262 tbloids = createPQExpBuffer();
4263
4264 /*
4265 * Identify tables of interest, and check which ones have RLS enabled.
4266 */
4267 appendPQExpBufferChar(tbloids, '{');
4268 for (i = 0; i < numTables; i++)
4269 {
4270 TableInfo *tbinfo = &tblinfo[i];
4271
4272 /* Ignore row security on tables not to be dumped */
4273 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4274 continue;
4275
4276 /* It can't have RLS or policies if it's not a table */
4277 if (tbinfo->relkind != RELKIND_RELATION &&
4278 tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4279 continue;
4280
4281 /* Add it to the list of table OIDs to be probed below */
4282 if (tbloids->len > 1) /* do we have more than the '{'? */
4283 appendPQExpBufferChar(tbloids, ',');
4284 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4285
4286 /* Is RLS enabled? (That's separate from whether it has policies) */
4287 if (tbinfo->rowsec)
4288 {
4290
4291 /*
4292 * We represent RLS being enabled on a table by creating a
4293 * PolicyInfo object with null polname.
4294 *
4295 * Note: use tableoid 0 so that this object won't be mistaken for
4296 * something that pg_depend entries apply to.
4297 */
4298 polinfo = pg_malloc(sizeof(PolicyInfo));
4299 polinfo->dobj.objType = DO_POLICY;
4300 polinfo->dobj.catId.tableoid = 0;
4301 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4302 AssignDumpId(&polinfo->dobj);
4303 polinfo->dobj.namespace = tbinfo->dobj.namespace;
4304 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4305 polinfo->poltable = tbinfo;
4306 polinfo->polname = NULL;
4307 polinfo->polcmd = '\0';
4308 polinfo->polpermissive = 0;
4309 polinfo->polroles = NULL;
4310 polinfo->polqual = NULL;
4311 polinfo->polwithcheck = NULL;
4312 }
4313 }
4314 appendPQExpBufferChar(tbloids, '}');
4315
4316 /*
4317 * Now, read all RLS policies belonging to the tables of interest, and
4318 * create PolicyInfo objects for them. (Note that we must filter the
4319 * results server-side not locally, because we dare not apply pg_get_expr
4320 * to tables we don't have lock on.)
4321 */
4322 pg_log_info("reading row-level security policies");
4323
4324 printfPQExpBuffer(query,
4325 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4326 if (fout->remoteVersion >= 100000)
4327 appendPQExpBufferStr(query, "pol.polpermissive, ");
4328 else
4329 appendPQExpBufferStr(query, "'t' as polpermissive, ");
4330 appendPQExpBuffer(query,
4331 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4332 " 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, "
4333 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4334 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4335 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4336 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4337 tbloids->data);
4338
4339 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4340
4341 ntups = PQntuples(res);
4342 if (ntups > 0)
4343 {
4344 i_oid = PQfnumber(res, "oid");
4345 i_tableoid = PQfnumber(res, "tableoid");
4346 i_polrelid = PQfnumber(res, "polrelid");
4347 i_polname = PQfnumber(res, "polname");
4348 i_polcmd = PQfnumber(res, "polcmd");
4349 i_polpermissive = PQfnumber(res, "polpermissive");
4350 i_polroles = PQfnumber(res, "polroles");
4351 i_polqual = PQfnumber(res, "polqual");
4352 i_polwithcheck = PQfnumber(res, "polwithcheck");
4353
4354 polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4355
4356 for (j = 0; j < ntups; j++)
4357 {
4358 Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4359 TableInfo *tbinfo = findTableByOid(polrelid);
4360
4362
4363 polinfo[j].dobj.objType = DO_POLICY;
4364 polinfo[j].dobj.catId.tableoid =
4365 atooid(PQgetvalue(res, j, i_tableoid));
4366 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4367 AssignDumpId(&polinfo[j].dobj);
4368 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4369 polinfo[j].poltable = tbinfo;
4370 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4371 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4372
4373 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4374 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4375
4376 if (PQgetisnull(res, j, i_polroles))
4377 polinfo[j].polroles = NULL;
4378 else
4379 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4380
4381 if (PQgetisnull(res, j, i_polqual))
4382 polinfo[j].polqual = NULL;
4383 else
4384 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4385
4386 if (PQgetisnull(res, j, i_polwithcheck))
4387 polinfo[j].polwithcheck = NULL;
4388 else
4389 polinfo[j].polwithcheck
4390 = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4391 }
4392 }
4393
4394 PQclear(res);
4395
4396 destroyPQExpBuffer(query);
4397 destroyPQExpBuffer(tbloids);
4398}
4399
4400/*
4401 * dumpPolicy
4402 * dump the definition of the given policy
4403 */
4404static void
4405dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4406{
4407 DumpOptions *dopt = fout->dopt;
4408 TableInfo *tbinfo = polinfo->poltable;
4409 PQExpBuffer query;
4410 PQExpBuffer delqry;
4411 PQExpBuffer polprefix;
4412 char *qtabname;
4413 const char *cmd;
4414 char *tag;
4415
4416 /* Do nothing if not dumping schema */
4417 if (!dopt->dumpSchema)
4418 return;
4419
4420 /*
4421 * If polname is NULL, then this record is just indicating that ROW LEVEL
4422 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4423 * ROW LEVEL SECURITY.
4424 */
4425 if (polinfo->polname == NULL)
4426 {
4427 query = createPQExpBuffer();
4428
4429 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4430 fmtQualifiedDumpable(tbinfo));
4431
4432 /*
4433 * We must emit the ROW SECURITY object's dependency on its table
4434 * explicitly, because it will not match anything in pg_depend (unlike
4435 * the case for other PolicyInfo objects).
4436 */
4437 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4438 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4439 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4440 .namespace = polinfo->dobj.namespace->dobj.name,
4441 .owner = tbinfo->rolname,
4442 .description = "ROW SECURITY",
4443 .section = SECTION_POST_DATA,
4444 .createStmt = query->data,
4445 .deps = &(tbinfo->dobj.dumpId),
4446 .nDeps = 1));
4447
4448 destroyPQExpBuffer(query);
4449 return;
4450 }
4451
4452 if (polinfo->polcmd == '*')
4453 cmd = "";
4454 else if (polinfo->polcmd == 'r')
4455 cmd = " FOR SELECT";
4456 else if (polinfo->polcmd == 'a')
4457 cmd = " FOR INSERT";
4458 else if (polinfo->polcmd == 'w')
4459 cmd = " FOR UPDATE";
4460 else if (polinfo->polcmd == 'd')
4461 cmd = " FOR DELETE";
4462 else
4463 pg_fatal("unexpected policy command type: %c",
4464 polinfo->polcmd);
4465
4466 query = createPQExpBuffer();
4467 delqry = createPQExpBuffer();
4468 polprefix = createPQExpBuffer();
4469
4470 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4471
4472 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4473
4474 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4475 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4476
4477 if (polinfo->polroles != NULL)
4478 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4479
4480 if (polinfo->polqual != NULL)
4481 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4482
4483 if (polinfo->polwithcheck != NULL)
4484 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4485
4486 appendPQExpBufferStr(query, ";\n");
4487
4488 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4489 appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4490
4491 appendPQExpBuffer(polprefix, "POLICY %s ON",
4492 fmtId(polinfo->polname));
4493
4494 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4495
4496 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4497 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4498 ARCHIVE_OPTS(.tag = tag,
4499 .namespace = polinfo->dobj.namespace->dobj.name,
4500 .owner = tbinfo->rolname,
4501 .description = "POLICY",
4502 .section = SECTION_POST_DATA,
4503 .createStmt = query->data,
4504 .dropStmt = delqry->data));
4505
4506 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4507 dumpComment(fout, polprefix->data, qtabname,
4508 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4509 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4510
4511 free(tag);
4512 destroyPQExpBuffer(query);
4513 destroyPQExpBuffer(delqry);
4514 destroyPQExpBuffer(polprefix);
4515 free(qtabname);
4516}
4517
4518/*
4519 * getPublications
4520 * get information about publications
4521 */
4522void
4524{
4525 DumpOptions *dopt = fout->dopt;
4526 PQExpBuffer query;
4527 PGresult *res;
4528 PublicationInfo *pubinfo;
4529 int i_tableoid;
4530 int i_oid;
4531 int i_pubname;
4532 int i_pubowner;
4533 int i_puballtables;
4534 int i_puballsequences;
4535 int i_pubinsert;
4536 int i_pubupdate;
4537 int i_pubdelete;
4538 int i_pubtruncate;
4539 int i_pubviaroot;
4540 int i_pubgencols;
4541 int i,
4542 ntups;
4543
4544 if (dopt->no_publications || fout->remoteVersion < 100000)
4545 return;
4546
4547 query = createPQExpBuffer();
4548
4549 /* Get the publications. */
4550 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4551 "p.pubowner, p.puballtables, p.pubinsert, "
4552 "p.pubupdate, p.pubdelete, ");
4553
4554 if (fout->remoteVersion >= 110000)
4555 appendPQExpBufferStr(query, "p.pubtruncate, ");
4556 else
4557 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4558
4559 if (fout->remoteVersion >= 130000)
4560 appendPQExpBufferStr(query, "p.pubviaroot, ");
4561 else
4562 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4563
4564 if (fout->remoteVersion >= 180000)
4565 appendPQExpBufferStr(query, "p.pubgencols, ");
4566 else
4567 appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4568
4569 if (fout->remoteVersion >= 190000)
4570 appendPQExpBufferStr(query, "p.puballsequences ");
4571 else
4572 appendPQExpBufferStr(query, "false AS puballsequences ");
4573
4574 appendPQExpBufferStr(query, "FROM pg_publication p");
4575
4576 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4577
4578 ntups = PQntuples(res);
4579
4580 if (ntups == 0)
4581 goto cleanup;
4582
4583 i_tableoid = PQfnumber(res, "tableoid");
4584 i_oid = PQfnumber(res, "oid");
4585 i_pubname = PQfnumber(res, "pubname");
4586 i_pubowner = PQfnumber(res, "pubowner");
4587 i_puballtables = PQfnumber(res, "puballtables");
4588 i_puballsequences = PQfnumber(res, "puballsequences");
4589 i_pubinsert = PQfnumber(res, "pubinsert");
4590 i_pubupdate = PQfnumber(res, "pubupdate");
4591 i_pubdelete = PQfnumber(res, "pubdelete");
4592 i_pubtruncate = PQfnumber(res, "pubtruncate");
4593 i_pubviaroot = PQfnumber(res, "pubviaroot");
4594 i_pubgencols = PQfnumber(res, "pubgencols");
4595
4596 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4597
4598 for (i = 0; i < ntups; i++)
4599 {
4600 pubinfo[i].dobj.objType = DO_PUBLICATION;
4601 pubinfo[i].dobj.catId.tableoid =
4602 atooid(PQgetvalue(res, i, i_tableoid));
4603 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4604 AssignDumpId(&pubinfo[i].dobj);
4605 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4606 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4607 pubinfo[i].puballtables =
4608 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4609 pubinfo[i].puballsequences =
4610 (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4611 pubinfo[i].pubinsert =
4612 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4613 pubinfo[i].pubupdate =
4614 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4615 pubinfo[i].pubdelete =
4616 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4617 pubinfo[i].pubtruncate =
4618 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4619 pubinfo[i].pubviaroot =
4620 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4621 pubinfo[i].pubgencols_type =
4622 *(PQgetvalue(res, i, i_pubgencols));
4623
4624 /* Decide whether we want to dump it */
4625 selectDumpableObject(&(pubinfo[i].dobj), fout);
4626 }
4627
4628cleanup:
4629 PQclear(res);
4630
4631 destroyPQExpBuffer(query);
4632}
4633
4634/*
4635 * dumpPublication
4636 * dump the definition of the given publication
4637 */
4638static void
4640{
4641 DumpOptions *dopt = fout->dopt;
4642 PQExpBuffer delq;
4643 PQExpBuffer query;
4644 char *qpubname;
4645 bool first = true;
4646
4647 /* Do nothing if not dumping schema */
4648 if (!dopt->dumpSchema)
4649 return;
4650
4651 delq = createPQExpBuffer();
4652 query = createPQExpBuffer();
4653
4654 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4655
4656 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4657 qpubname);
4658
4659 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4660 qpubname);
4661
4662 if (pubinfo->puballtables && pubinfo->puballsequences)
4663 appendPQExpBufferStr(query, " FOR ALL TABLES, ALL SEQUENCES");
4664 else if (pubinfo->puballtables)
4665 appendPQExpBufferStr(query, " FOR ALL TABLES");
4666 else if (pubinfo->puballsequences)
4667 appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4668
4669 appendPQExpBufferStr(query, " WITH (publish = '");
4670 if (pubinfo->pubinsert)
4671 {
4672 appendPQExpBufferStr(query, "insert");
4673 first = false;
4674 }
4675
4676 if (pubinfo->pubupdate)
4677 {
4678 if (!first)
4679 appendPQExpBufferStr(query, ", ");
4680
4681 appendPQExpBufferStr(query, "update");
4682 first = false;
4683 }
4684
4685 if (pubinfo->pubdelete)
4686 {
4687 if (!first)
4688 appendPQExpBufferStr(query, ", ");
4689
4690 appendPQExpBufferStr(query, "delete");
4691 first = false;
4692 }
4693
4694 if (pubinfo->pubtruncate)
4695 {
4696 if (!first)
4697 appendPQExpBufferStr(query, ", ");
4698
4699 appendPQExpBufferStr(query, "truncate");
4700 first = false;
4701 }
4702
4703 appendPQExpBufferChar(query, '\'');
4704
4705 if (pubinfo->pubviaroot)
4706 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4707
4708 if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4709 appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4710
4711 appendPQExpBufferStr(query, ");\n");
4712
4713 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4714 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4715 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4716 .owner = pubinfo->rolname,
4717 .description = "PUBLICATION",
4718 .section = SECTION_POST_DATA,
4719 .createStmt = query->data,
4720 .dropStmt = delq->data));
4721
4722 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4723 dumpComment(fout, "PUBLICATION", qpubname,
4724 NULL, pubinfo->rolname,
4725 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4726
4727 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4728 dumpSecLabel(fout, "PUBLICATION", qpubname,
4729 NULL, pubinfo->rolname,
4730 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4731
4732 destroyPQExpBuffer(delq);
4733 destroyPQExpBuffer(query);
4734 free(qpubname);
4735}
4736
4737/*
4738 * getPublicationNamespaces
4739 * get information about publication membership for dumpable schemas.
4740 */
4741void
4743{
4744 PQExpBuffer query;
4745 PGresult *res;
4746 PublicationSchemaInfo *pubsinfo;
4747 DumpOptions *dopt = fout->dopt;
4748 int i_tableoid;
4749 int i_oid;
4750 int i_pnpubid;
4751 int i_pnnspid;
4752 int i,
4753 j,
4754 ntups;
4755
4756 if (dopt->no_publications || fout->remoteVersion < 150000)
4757 return;
4758
4759 query = createPQExpBuffer();
4760
4761 /* Collect all publication membership info. */
4763 "SELECT tableoid, oid, pnpubid, pnnspid "
4764 "FROM pg_catalog.pg_publication_namespace");
4765 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4766
4767 ntups = PQntuples(res);
4768
4769 i_tableoid = PQfnumber(res, "tableoid");
4770 i_oid = PQfnumber(res, "oid");
4771 i_pnpubid = PQfnumber(res, "pnpubid");
4772 i_pnnspid = PQfnumber(res, "pnnspid");
4773
4774 /* this allocation may be more than we need */
4775 pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4776 j = 0;
4777
4778 for (i = 0; i < ntups; i++)
4779 {
4780 Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4781 Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4782 PublicationInfo *pubinfo;
4783 NamespaceInfo *nspinfo;
4784
4785 /*
4786 * Ignore any entries for which we aren't interested in either the
4787 * publication or the rel.
4788 */
4789 pubinfo = findPublicationByOid(pnpubid);
4790 if (pubinfo == NULL)
4791 continue;
4792 nspinfo = findNamespaceByOid(pnnspid);
4793 if (nspinfo == NULL)
4794 continue;
4795
4796 /* OK, make a DumpableObject for this relationship */
4798 pubsinfo[j].dobj.catId.tableoid =
4799 atooid(PQgetvalue(res, i, i_tableoid));
4800 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4801 AssignDumpId(&pubsinfo[j].dobj);
4802 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4803 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4804 pubsinfo[j].publication = pubinfo;
4805 pubsinfo[j].pubschema = nspinfo;
4806
4807 /* Decide whether we want to dump it */
4808 selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4809
4810 j++;
4811 }
4812
4813 PQclear(res);
4814 destroyPQExpBuffer(query);
4815}
4816
4817/*
4818 * getPublicationTables
4819 * get information about publication membership for dumpable tables.
4820 */
4821void
4822getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4823{
4824 PQExpBuffer query;
4825 PGresult *res;
4826 PublicationRelInfo *pubrinfo;
4827 DumpOptions *dopt = fout->dopt;
4828 int i_tableoid;
4829 int i_oid;
4830 int i_prpubid;
4831 int i_prrelid;
4832 int i_prrelqual;
4833 int i_prattrs;
4834 int i,
4835 j,
4836 ntups;
4837
4838 if (dopt->no_publications || fout->remoteVersion < 100000)
4839 return;
4840
4841 query = createPQExpBuffer();
4842
4843 /* Collect all publication membership info. */
4844 if (fout->remoteVersion >= 150000)
4846 "SELECT tableoid, oid, prpubid, prrelid, "
4847 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4848 "(CASE\n"
4849 " WHEN pr.prattrs IS NOT NULL THEN\n"
4850 " (SELECT array_agg(attname)\n"
4851 " FROM\n"
4852 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4853 " pg_catalog.pg_attribute\n"
4854 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4855 " ELSE NULL END) prattrs "
4856 "FROM pg_catalog.pg_publication_rel pr");
4857 else
4859 "SELECT tableoid, oid, prpubid, prrelid, "
4860 "NULL AS prrelqual, NULL AS prattrs "
4861 "FROM pg_catalog.pg_publication_rel");
4862 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4863
4864 ntups = PQntuples(res);
4865
4866 i_tableoid = PQfnumber(res, "tableoid");
4867 i_oid = PQfnumber(res, "oid");
4868 i_prpubid = PQfnumber(res, "prpubid");
4869 i_prrelid = PQfnumber(res, "prrelid");
4870 i_prrelqual = PQfnumber(res, "prrelqual");
4871 i_prattrs = PQfnumber(res, "prattrs");
4872
4873 /* this allocation may be more than we need */
4874 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4875 j = 0;
4876
4877 for (i = 0; i < ntups; i++)
4878 {
4879 Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4880 Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4881 PublicationInfo *pubinfo;
4882 TableInfo *tbinfo;
4883
4884 /*
4885 * Ignore any entries for which we aren't interested in either the
4886 * publication or the rel.
4887 */
4888 pubinfo = findPublicationByOid(prpubid);
4889 if (pubinfo == NULL)
4890 continue;
4891 tbinfo = findTableByOid(prrelid);
4892 if (tbinfo == NULL)
4893 continue;
4894
4895 /* OK, make a DumpableObject for this relationship */
4896 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4897 pubrinfo[j].dobj.catId.tableoid =
4898 atooid(PQgetvalue(res, i, i_tableoid));
4899 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4900 AssignDumpId(&pubrinfo[j].dobj);
4901 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4902 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4903 pubrinfo[j].publication = pubinfo;
4904 pubrinfo[j].pubtable = tbinfo;
4905 if (PQgetisnull(res, i, i_prrelqual))
4906 pubrinfo[j].pubrelqual = NULL;
4907 else
4908 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4909
4910 if (!PQgetisnull(res, i, i_prattrs))
4911 {
4912 char **attnames;
4913 int nattnames;
4914 PQExpBuffer attribs;
4915
4916 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4917 &attnames, &nattnames))
4918 pg_fatal("could not parse %s array", "prattrs");
4919 attribs = createPQExpBuffer();
4920 for (int k = 0; k < nattnames; k++)
4921 {
4922 if (k > 0)
4923 appendPQExpBufferStr(attribs, ", ");
4924
4925 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4926 }
4927 pubrinfo[j].pubrattrs = attribs->data;
4928 free(attribs); /* but not attribs->data */
4929 free(attnames);
4930 }
4931 else
4932 pubrinfo[j].pubrattrs = NULL;
4933
4934 /* Decide whether we want to dump it */
4935 selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4936
4937 j++;
4938 }
4939
4940 PQclear(res);
4941 destroyPQExpBuffer(query);
4942}
4943
4944/*
4945 * dumpPublicationNamespace
4946 * dump the definition of the given publication schema mapping.
4947 */
4948static void
4950{
4951 DumpOptions *dopt = fout->dopt;
4952 NamespaceInfo *schemainfo = pubsinfo->pubschema;
4953 PublicationInfo *pubinfo = pubsinfo->publication;
4954 PQExpBuffer query;
4955 char *tag;
4956
4957 /* Do nothing if not dumping schema */
4958 if (!dopt->dumpSchema)
4959 return;
4960
4961 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4962
4963 query = createPQExpBuffer();
4964
4965 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4966 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4967
4968 /*
4969 * There is no point in creating drop query as the drop is done by schema
4970 * drop.
4971 */
4972 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4973 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4974 ARCHIVE_OPTS(.tag = tag,
4975 .namespace = schemainfo->dobj.name,
4976 .owner = pubinfo->rolname,
4977 .description = "PUBLICATION TABLES IN SCHEMA",
4978 .section = SECTION_POST_DATA,
4979 .createStmt = query->data));
4980
4981 /* These objects can't currently have comments or seclabels */
4982
4983 free(tag);
4984 destroyPQExpBuffer(query);
4985}
4986
4987/*
4988 * dumpPublicationTable
4989 * dump the definition of the given publication table mapping
4990 */
4991static void
4993{
4994 DumpOptions *dopt = fout->dopt;
4995 PublicationInfo *pubinfo = pubrinfo->publication;
4996 TableInfo *tbinfo = pubrinfo->pubtable;
4997 PQExpBuffer query;
4998 char *tag;
4999
5000 /* Do nothing if not dumping schema */
5001 if (!dopt->dumpSchema)
5002 return;
5003
5004 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5005
5006 query = createPQExpBuffer();
5007
5008 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5009 fmtId(pubinfo->dobj.name));
5010 appendPQExpBuffer(query, " %s",
5011 fmtQualifiedDumpable(tbinfo));
5012
5013 if (pubrinfo->pubrattrs)
5014 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5015
5016 if (pubrinfo->pubrelqual)
5017 {
5018 /*
5019 * It's necessary to add parentheses around the expression because
5020 * pg_get_expr won't supply the parentheses for things like WHERE
5021 * TRUE.
5022 */
5023 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5024 }
5025 appendPQExpBufferStr(query, ";\n");
5026
5027 /*
5028 * There is no point in creating a drop query as the drop is done by table
5029 * drop. (If you think to change this, see also _printTocEntry().)
5030 * Although this object doesn't really have ownership as such, set the
5031 * owner field anyway to ensure that the command is run by the correct
5032 * role at restore time.
5033 */
5034 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5035 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5036 ARCHIVE_OPTS(.tag = tag,
5037 .namespace = tbinfo->dobj.namespace->dobj.name,
5038 .owner = pubinfo->rolname,
5039 .description = "PUBLICATION TABLE",
5040 .section = SECTION_POST_DATA,
5041 .createStmt = query->data));
5042
5043 /* These objects can't currently have comments or seclabels */
5044
5045 free(tag);
5046 destroyPQExpBuffer(query);
5047}
5048
5049/*
5050 * Is the currently connected user a superuser?
5051 */
5052static bool
5054{
5055 ArchiveHandle *AH = (ArchiveHandle *) fout;
5056 const char *val;
5057
5058 val = PQparameterStatus(AH->connection, "is_superuser");
5059
5060 if (val && strcmp(val, "on") == 0)
5061 return true;
5062
5063 return false;
5064}
5065
5066/*
5067 * Set the given value to restrict_nonsystem_relation_kind value. Since
5068 * restrict_nonsystem_relation_kind is introduced in minor version releases,
5069 * the setting query is effective only where available.
5070 */
5071static void
5073{
5075 PGresult *res;
5076
5077 appendPQExpBuffer(query,
5078 "SELECT set_config(name, '%s', false) "
5079 "FROM pg_settings "
5080 "WHERE name = 'restrict_nonsystem_relation_kind'",
5081 value);
5082 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5083
5084 PQclear(res);
5085 destroyPQExpBuffer(query);
5086}
5087
5088/*
5089 * getSubscriptions
5090 * get information about subscriptions
5091 */
5092void
5094{
5095 DumpOptions *dopt = fout->dopt;
5096 PQExpBuffer query;
5097 PGresult *res;
5098 SubscriptionInfo *subinfo;
5099 int i_tableoid;
5100 int i_oid;
5101 int i_subname;
5102 int i_subowner;
5103 int i_subbinary;
5104 int i_substream;
5105 int i_subtwophasestate;
5106 int i_subdisableonerr;
5107 int i_subpasswordrequired;
5108 int i_subrunasowner;
5109 int i_subconninfo;
5110 int i_subslotname;
5111 int i_subsynccommit;
5112 int i_subpublications;
5113 int i_suborigin;
5114 int i_suboriginremotelsn;
5115 int i_subenabled;
5116 int i_subfailover;
5117 int i_subretaindeadtuples;
5118 int i_submaxretention;
5119 int i,
5120 ntups;
5121
5122 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5123 return;
5124
5125 if (!is_superuser(fout))
5126 {
5127 int n;
5128
5129 res = ExecuteSqlQuery(fout,
5130 "SELECT count(*) FROM pg_subscription "
5131 "WHERE subdbid = (SELECT oid FROM pg_database"
5132 " WHERE datname = current_database())",
5134 n = atoi(PQgetvalue(res, 0, 0));
5135 if (n > 0)
5136 pg_log_warning("subscriptions not dumped because current user is not a superuser");
5137 PQclear(res);
5138 return;
5139 }
5140
5141 query = createPQExpBuffer();
5142
5143 /* Get the subscriptions in current database. */
5145 "SELECT s.tableoid, s.oid, s.subname,\n"
5146 " s.subowner,\n"
5147 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5148 " s.subpublications,\n");
5149
5150 if (fout->remoteVersion >= 140000)
5151 appendPQExpBufferStr(query, " s.subbinary,\n");
5152 else
5153 appendPQExpBufferStr(query, " false AS subbinary,\n");
5154
5155 if (fout->remoteVersion >= 140000)
5156 appendPQExpBufferStr(query, " s.substream,\n");
5157 else
5158 appendPQExpBufferStr(query, " 'f' AS substream,\n");
5159
5160 if (fout->remoteVersion >= 150000)
5162 " s.subtwophasestate,\n"
5163 " s.subdisableonerr,\n");
5164 else
5165 appendPQExpBuffer(query,
5166 " '%c' AS subtwophasestate,\n"
5167 " false AS subdisableonerr,\n",
5168 LOGICALREP_TWOPHASE_STATE_DISABLED);
5169
5170 if (fout->remoteVersion >= 160000)
5172 " s.subpasswordrequired,\n"
5173 " s.subrunasowner,\n"
5174 " s.suborigin,\n");
5175 else
5176 appendPQExpBuffer(query,
5177 " 't' AS subpasswordrequired,\n"
5178 " 't' AS subrunasowner,\n"
5179 " '%s' AS suborigin,\n",
5180 LOGICALREP_ORIGIN_ANY);
5181
5182 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5183 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5184 " s.subenabled,\n");
5185 else
5186 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5187 " false AS subenabled,\n");
5188
5189 if (fout->remoteVersion >= 170000)
5191 " s.subfailover,\n");
5192 else
5194 " false AS subfailover,\n");
5195
5196 if (fout->remoteVersion >= 190000)
5198 " s.subretaindeadtuples,\n");
5199 else
5201 " false AS subretaindeadtuples,\n");
5202
5203 if (fout->remoteVersion >= 190000)
5205 " s.submaxretention\n");
5206 else
5207 appendPQExpBuffer(query,
5208 " 0 AS submaxretention\n");
5209
5211 "FROM pg_subscription s\n");
5212
5213 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5215 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5216 " ON o.external_id = 'pg_' || s.oid::text \n");
5217
5219 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5220 " WHERE datname = current_database())");
5221
5222 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5223
5224 ntups = PQntuples(res);
5225
5226 /*
5227 * Get subscription fields. We don't include subskiplsn in the dump as
5228 * after restoring the dump this value may no longer be relevant.
5229 */
5230 i_tableoid = PQfnumber(res, "tableoid");
5231 i_oid = PQfnumber(res, "oid");
5232 i_subname = PQfnumber(res, "subname");
5233 i_subowner = PQfnumber(res, "subowner");
5234 i_subenabled = PQfnumber(res, "subenabled");
5235 i_subbinary = PQfnumber(res, "subbinary");
5236 i_substream = PQfnumber(res, "substream");
5237 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5238 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5239 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5240 i_subrunasowner = PQfnumber(res, "subrunasowner");
5241 i_subfailover = PQfnumber(res, "subfailover");
5242 i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5243 i_submaxretention = PQfnumber(res, "submaxretention");
5244 i_subconninfo = PQfnumber(res, "subconninfo");
5245 i_subslotname = PQfnumber(res, "subslotname");
5246 i_subsynccommit = PQfnumber(res, "subsynccommit");
5247 i_subpublications = PQfnumber(res, "subpublications");
5248 i_suborigin = PQfnumber(res, "suborigin");
5249 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5250
5251 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5252
5253 for (i = 0; i < ntups; i++)
5254 {
5255 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5256 subinfo[i].dobj.catId.tableoid =
5257 atooid(PQgetvalue(res, i, i_tableoid));
5258 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5259 AssignDumpId(&subinfo[i].dobj);
5260 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5261 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5262
5263 subinfo[i].subenabled =
5264 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5265 subinfo[i].subbinary =
5266 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5267 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5268 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5269 subinfo[i].subdisableonerr =
5270 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5271 subinfo[i].subpasswordrequired =
5272 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5273 subinfo[i].subrunasowner =
5274 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5275 subinfo[i].subfailover =
5276 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5277 subinfo[i].subretaindeadtuples =
5278 (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5279 subinfo[i].submaxretention =
5280 atoi(PQgetvalue(res, i, i_submaxretention));
5281 subinfo[i].subconninfo =
5282 pg_strdup(PQgetvalue(res, i, i_subconninfo));
5283 if (PQgetisnull(res, i, i_subslotname))
5284 subinfo[i].subslotname = NULL;
5285 else
5286 subinfo[i].subslotname =
5287 pg_strdup(PQgetvalue(res, i, i_subslotname));
5288 subinfo[i].subsynccommit =
5289 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5290 subinfo[i].subpublications =
5291 pg_strdup(PQgetvalue(res, i, i_subpublications));
5292 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5293 if (PQgetisnull(res, i, i_suboriginremotelsn))
5294 subinfo[i].suboriginremotelsn = NULL;
5295 else
5296 subinfo[i].suboriginremotelsn =
5297 pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5298
5299 /* Decide whether we want to dump it */
5300 selectDumpableObject(&(subinfo[i].dobj), fout);
5301 }
5302 PQclear(res);
5303
5304 destroyPQExpBuffer(query);
5305}
5306
5307/*
5308 * getSubscriptionRelations
5309 * Get information about subscription membership for dumpable relations. This
5310 * will be used only in binary-upgrade mode for PG17 or later versions.
5311 */
5312void
5314{
5315 DumpOptions *dopt = fout->dopt;
5316 SubscriptionInfo *subinfo = NULL;
5317 SubRelInfo *subrinfo;
5318 PGresult *res;
5319 int i_srsubid;
5320 int i_srrelid;
5321 int i_srsubstate;
5322 int i_srsublsn;
5323 int ntups;
5324 Oid last_srsubid = InvalidOid;
5325
5326 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5327 fout->remoteVersion < 170000)
5328 return;
5329
5330 res = ExecuteSqlQuery(fout,
5331 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5332 "FROM pg_catalog.pg_subscription_rel "
5333 "ORDER BY srsubid",
5335 ntups = PQntuples(res);
5336 if (ntups == 0)
5337 goto cleanup;
5338
5339 /* Get pg_subscription_rel attributes */
5340 i_srsubid = PQfnumber(res, "srsubid");
5341 i_srrelid = PQfnumber(res, "srrelid");
5342 i_srsubstate = PQfnumber(res, "srsubstate");
5343 i_srsublsn = PQfnumber(res, "srsublsn");
5344
5345 subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5346 for (int i = 0; i < ntups; i++)
5347 {
5348 Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5349 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5350 TableInfo *tblinfo;
5351
5352 /*
5353 * If we switched to a new subscription, check if the subscription
5354 * exists.
5355 */
5356 if (cur_srsubid != last_srsubid)
5357 {
5358 subinfo = findSubscriptionByOid(cur_srsubid);
5359 if (subinfo == NULL)
5360 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5361
5362 last_srsubid = cur_srsubid;
5363 }
5364
5365 tblinfo = findTableByOid(relid);
5366 if (tblinfo == NULL)
5367 pg_fatal("failed sanity check, relation with OID %u not found",
5368 relid);
5369
5370 /* OK, make a DumpableObject for this relationship */
5371 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5372 subrinfo[i].dobj.catId.tableoid = relid;
5373 subrinfo[i].dobj.catId.oid = cur_srsubid;
5374 AssignDumpId(&subrinfo[i].dobj);
5375 subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5376 subrinfo[i].tblinfo = tblinfo;
5377 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5378 if (PQgetisnull(res, i, i_srsublsn))
5379 subrinfo[i].srsublsn = NULL;
5380 else
5381 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5382
5383 subrinfo[i].subinfo = subinfo;
5384
5385 /* Decide whether we want to dump it */
5386 selectDumpableObject(&(subrinfo[i].dobj), fout);
5387 }
5388
5389cleanup:
5390 PQclear(res);
5391}
5392
5393/*
5394 * dumpSubscriptionTable
5395 * Dump the definition of the given subscription table mapping. This will be
5396 * used only in binary-upgrade mode for PG17 or later versions.
5397 */
5398static void
5400{
5401 DumpOptions *dopt = fout->dopt;
5402 SubscriptionInfo *subinfo = subrinfo->subinfo;
5403 PQExpBuffer query;
5404 char *tag;
5405
5406 /* Do nothing if not dumping schema */
5407 if (!dopt->dumpSchema)
5408 return;
5409
5410 Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5411
5412 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5413
5414 query = createPQExpBuffer();
5415
5416 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5417 {
5418 /*
5419 * binary_upgrade_add_sub_rel_state will add the subscription relation
5420 * to pg_subscription_rel table. This will be used only in
5421 * binary-upgrade mode.
5422 */
5424 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5426 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5427 appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5428 appendPQExpBuffer(query,
5429 ", %u, '%c'",
5430 subrinfo->tblinfo->dobj.catId.oid,
5431 subrinfo->srsubstate);
5432
5433 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5434 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5435 else
5436 appendPQExpBufferStr(query, ", NULL");
5437
5438 appendPQExpBufferStr(query, ");\n");
5439 }
5440
5441 /*
5442 * There is no point in creating a drop query as the drop is done by table
5443 * drop. (If you think to change this, see also _printTocEntry().)
5444 * Although this object doesn't really have ownership as such, set the
5445 * owner field anyway to ensure that the command is run by the correct
5446 * role at restore time.
5447 */
5448 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5449 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5450 ARCHIVE_OPTS(.tag = tag,
5451 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5452 .owner = subinfo->rolname,
5453 .description = "SUBSCRIPTION TABLE",
5454 .section = SECTION_POST_DATA,
5455 .createStmt = query->data));
5456
5457 /* These objects can't currently have comments or seclabels */
5458
5459 free(tag);
5460 destroyPQExpBuffer(query);
5461}
5462
5463/*
5464 * dumpSubscription
5465 * dump the definition of the given subscription
5466 */
5467static void
5469{
5470 DumpOptions *dopt = fout->dopt;
5471 PQExpBuffer delq;
5472 PQExpBuffer query;
5473 PQExpBuffer publications;
5474 char *qsubname;
5475 char **pubnames = NULL;
5476 int npubnames = 0;
5477 int i;
5478
5479 /* Do nothing if not dumping schema */
5480 if (!dopt->dumpSchema)
5481 return;
5482
5483 delq = createPQExpBuffer();
5484 query = createPQExpBuffer();
5485
5486 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5487
5488 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5489 qsubname);
5490
5491 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5492 qsubname);
5493 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5494
5495 /* Build list of quoted publications and append them to query. */
5496 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5497 pg_fatal("could not parse %s array", "subpublications");
5498
5499 publications = createPQExpBuffer();
5500 for (i = 0; i < npubnames; i++)
5501 {
5502 if (i > 0)
5503 appendPQExpBufferStr(publications, ", ");
5504
5505 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5506 }
5507
5508 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5509 if (subinfo->subslotname)
5510 appendStringLiteralAH(query, subinfo->subslotname, fout);
5511 else
5512 appendPQExpBufferStr(query, "NONE");
5513
5514 if (subinfo->subbinary)
5515 appendPQExpBufferStr(query, ", binary = true");
5516
5517 if (subinfo->substream == LOGICALREP_STREAM_ON)
5518 appendPQExpBufferStr(query, ", streaming = on");
5519 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5520 appendPQExpBufferStr(query, ", streaming = parallel");
5521 else
5522 appendPQExpBufferStr(query, ", streaming = off");
5523
5524 if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5525 appendPQExpBufferStr(query, ", two_phase = on");
5526
5527 if (subinfo->subdisableonerr)
5528 appendPQExpBufferStr(query, ", disable_on_error = true");
5529
5530 if (!subinfo->subpasswordrequired)
5531 appendPQExpBufferStr(query, ", password_required = false");
5532
5533 if (subinfo->subrunasowner)
5534 appendPQExpBufferStr(query, ", run_as_owner = true");
5535
5536 if (subinfo->subfailover)
5537 appendPQExpBufferStr(query, ", failover = true");
5538
5539 if (subinfo->subretaindeadtuples)
5540 appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5541
5542 if (subinfo->submaxretention)
5543 appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5544
5545 if (strcmp(subinfo->subsynccommit, "off") != 0)
5546 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5547
5548 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5549 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5550
5551 appendPQExpBufferStr(query, ");\n");
5552
5553 /*
5554 * In binary-upgrade mode, we allow the replication to continue after the
5555 * upgrade.
5556 */
5557 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5558 {
5559 if (subinfo->suboriginremotelsn)
5560 {
5561 /*
5562 * Preserve the remote_lsn for the subscriber's replication
5563 * origin. This value is required to start the replication from
5564 * the position before the upgrade. This value will be stale if
5565 * the publisher gets upgraded before the subscriber node.
5566 * However, this shouldn't be a problem as the upgrade of the
5567 * publisher ensures that all the transactions were replicated
5568 * before upgrading it.
5569 */
5571 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5573 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5574 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5575 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5576 }
5577
5578 if (subinfo->subenabled)
5579 {
5580 /*
5581 * Enable the subscription to allow the replication to continue
5582 * after the upgrade.
5583 */
5585 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5586 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5587 }
5588 }
5589
5590 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5591 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5592 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5593 .owner = subinfo->rolname,
5594 .description = "SUBSCRIPTION",
5595 .section = SECTION_POST_DATA,
5596 .createStmt = query->data,
5597 .dropStmt = delq->data));
5598
5599 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5600 dumpComment(fout, "SUBSCRIPTION", qsubname,
5601 NULL, subinfo->rolname,
5602 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5603
5604 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5605 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5606 NULL, subinfo->rolname,
5607 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5608
5609 destroyPQExpBuffer(publications);
5610 free(pubnames);
5611
5612 destroyPQExpBuffer(delq);
5613 destroyPQExpBuffer(query);
5614 free(qsubname);
5615}
5616
5617/*
5618 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5619 * the object needs.
5620 */
5621static void
5623 PQExpBuffer create,
5624 const DumpableObject *dobj,
5625 const char *catalog,
5626 const char *keyword,
5627 const char *objname)
5628{
5629 if (dobj->depends_on_ext)
5630 {
5631 char *nm;
5632 PGresult *res;
5633 PQExpBuffer query;
5634 int ntups;
5635 int i_extname;
5636 int i;
5637
5638 /* dodge fmtId() non-reentrancy */
5639 nm = pg_strdup(objname);
5640
5641 query = createPQExpBuffer();
5642 appendPQExpBuffer(query,
5643 "SELECT e.extname "
5644 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5645 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5646 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5647 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5648 catalog,
5649 dobj->catId.oid);
5650 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5651 ntups = PQntuples(res);
5652 i_extname = PQfnumber(res, "extname");
5653 for (i = 0; i < ntups; i++)
5654 {
5655 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5656 keyword, nm,
5657 fmtId(PQgetvalue(res, i, i_extname)));
5658 }
5659
5660 PQclear(res);
5661 destroyPQExpBuffer(query);
5662 pg_free(nm);
5663 }
5664}
5665
5666static Oid
5668{
5669 /*
5670 * If the old version didn't assign an array type, but the new version
5671 * does, we must select an unused type OID to assign. This currently only
5672 * happens for domains, when upgrading pre-v11 to v11 and up.
5673 *
5674 * Note: local state here is kind of ugly, but we must have some, since we
5675 * mustn't choose the same unused OID more than once.
5676 */
5677 static Oid next_possible_free_oid = FirstNormalObjectId;
5678 PGresult *res;
5679 bool is_dup;
5680
5681 do
5682 {
5683 ++next_possible_free_oid;
5684 printfPQExpBuffer(upgrade_query,
5685 "SELECT EXISTS(SELECT 1 "
5686 "FROM pg_catalog.pg_type "
5687 "WHERE oid = '%u'::pg_catalog.oid);",
5688 next_possible_free_oid);
5689 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5690 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5691 PQclear(res);
5692 } while (is_dup);
5693
5694 return next_possible_free_oid;
5695}
5696
5697static void
5699 PQExpBuffer upgrade_buffer,
5700 Oid pg_type_oid,
5701 bool force_array_type,
5702 bool include_multirange_type)
5703{
5704 PQExpBuffer upgrade_query = createPQExpBuffer();
5705 PGresult *res;
5706 Oid pg_type_array_oid;
5707 Oid pg_type_multirange_oid;
5708 Oid pg_type_multirange_array_oid;
5709 TypeInfo *tinfo;
5710
5711 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5712 appendPQExpBuffer(upgrade_buffer,
5713 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5714 pg_type_oid);
5715
5716 tinfo = findTypeByOid(pg_type_oid);
5717 if (tinfo)
5718 pg_type_array_oid = tinfo->typarray;
5719 else
5720 pg_type_array_oid = InvalidOid;
5721
5722 if (!OidIsValid(pg_type_array_oid) && force_array_type)
5723 pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5724
5725 if (OidIsValid(pg_type_array_oid))
5726 {
5727 appendPQExpBufferStr(upgrade_buffer,
5728 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5729 appendPQExpBuffer(upgrade_buffer,
5730 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5731 pg_type_array_oid);
5732 }
5733
5734 /*
5735 * Pre-set the multirange type oid and its own array type oid.
5736 */
5737 if (include_multirange_type)
5738 {
5739 if (fout->remoteVersion >= 140000)
5740 {
5741 printfPQExpBuffer(upgrade_query,
5742 "SELECT t.oid, t.typarray "
5743 "FROM pg_catalog.pg_type t "
5744 "JOIN pg_catalog.pg_range r "
5745 "ON t.oid = r.rngmultitypid "
5746 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5747 pg_type_oid);
5748
5749 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5750
5751 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5752 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5753
5754 PQclear(res);
5755 }
5756 else
5757 {
5758 pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5759 pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5760 }
5761
5762 appendPQExpBufferStr(upgrade_buffer,
5763 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5764 appendPQExpBuffer(upgrade_buffer,
5765 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5766 pg_type_multirange_oid);
5767 appendPQExpBufferStr(upgrade_buffer,
5768 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5769 appendPQExpBuffer(upgrade_buffer,
5770 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5771 pg_type_multirange_array_oid);
5772 }
5773
5774 destroyPQExpBuffer(upgrade_query);
5775}
5776
5777static void
5779 PQExpBuffer upgrade_buffer,
5780 const TableInfo *tbinfo)
5781{
5782 Oid pg_type_oid = tbinfo->reltype;
5783
5784 if (OidIsValid(pg_type_oid))
5785 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5786 pg_type_oid, false, false);
5787}
5788
5789/*
5790 * bsearch() comparator for BinaryUpgradeClassOidItem
5791 */
5792static int
5793BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5794{
5797
5798 return pg_cmp_u32(v1.oid, v2.oid);
5799}
5800
5801/*
5802 * collectBinaryUpgradeClassOids
5803 *
5804 * Construct a table of pg_class information required for
5805 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5806 * lookup.
5807 */
5808static void
5810{
5811 PGresult *res;
5812 const char *query;
5813
5814 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5815 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5816 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5817 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5818 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5819 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5820 "ORDER BY c.oid;";
5821
5822 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5823
5827
5828 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5829 {
5837 }
5838
5839 PQclear(res);
5840}
5841
5842static void
5844 PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5845{
5848
5850
5851 /*
5852 * Preserve the OID and relfilenumber of the table, table's index, table's
5853 * toast table and toast table's index if any.
5854 *
5855 * One complexity is that the current table definition might not require
5856 * the creation of a TOAST table, but the old database might have a TOAST
5857 * table that was created earlier, before some wide columns were dropped.
5858 * By setting the TOAST oid we force creation of the TOAST heap and index
5859 * by the new backend, so we can copy the files during binary upgrade
5860 * without worrying about this case.
5861 */
5862 key.oid = pg_class_oid;
5866
5867 appendPQExpBufferStr(upgrade_buffer,
5868 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5869
5870 if (entry->relkind != RELKIND_INDEX &&
5871 entry->relkind != RELKIND_PARTITIONED_INDEX)
5872 {
5873 appendPQExpBuffer(upgrade_buffer,
5874 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5875 pg_class_oid);
5876
5877 /*
5878 * Not every relation has storage. Also, in a pre-v12 database,
5879 * partitioned tables have a relfilenumber, which should not be
5880 * preserved when upgrading.
5881 */
5882 if (RelFileNumberIsValid(entry->relfilenumber) &&
5883 entry->relkind != RELKIND_PARTITIONED_TABLE)
5884 appendPQExpBuffer(upgrade_buffer,
5885 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5886 entry->relfilenumber);
5887
5888 /*
5889 * In a pre-v12 database, partitioned tables might be marked as having
5890 * toast tables, but we should ignore them if so.
5891 */
5892 if (OidIsValid(entry->toast_oid) &&
5893 entry->relkind != RELKIND_PARTITIONED_TABLE)
5894 {
5895 appendPQExpBuffer(upgrade_buffer,
5896 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5897 entry->toast_oid);
5898 appendPQExpBuffer(upgrade_buffer,
5899 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5900 entry->toast_relfilenumber);
5901
5902 /* every toast table has an index */
5903 appendPQExpBuffer(upgrade_buffer,
5904 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5905 entry->toast_index_oid);
5906 appendPQExpBuffer(upgrade_buffer,
5907 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5909 }
5910 }
5911 else
5912 {
5913 /* Preserve the OID and relfilenumber of the index */
5914 appendPQExpBuffer(upgrade_buffer,
5915 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5916 pg_class_oid);
5917 appendPQExpBuffer(upgrade_buffer,
5918 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5919 entry->relfilenumber);
5920 }
5921
5922 appendPQExpBufferChar(upgrade_buffer, '\n');
5923}
5924
5925/*
5926 * If the DumpableObject is a member of an extension, add a suitable
5927 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5928 *
5929 * For somewhat historical reasons, objname should already be quoted,
5930 * but not objnamespace (if any).
5931 */
5932static void
5934 const DumpableObject *dobj,
5935 const char *objtype,
5936 const char *objname,
5937 const char *objnamespace)
5938{
5939 DumpableObject *extobj = NULL;
5940 int i;
5941
5942 if (!dobj->ext_member)
5943 return;
5944
5945 /*
5946 * Find the parent extension. We could avoid this search if we wanted to
5947 * add a link field to DumpableObject, but the space costs of that would
5948 * be considerable. We assume that member objects could only have a
5949 * direct dependency on their own extension, not any others.
5950 */
5951 for (i = 0; i < dobj->nDeps; i++)
5952 {
5953 extobj = findObjectByDumpId(dobj->dependencies[i]);
5954 if (extobj && extobj->objType == DO_EXTENSION)
5955 break;
5956 extobj = NULL;
5957 }
5958 if (extobj == NULL)
5959 pg_fatal("could not find parent extension for %s %s",
5960 objtype, objname);
5961
5962 appendPQExpBufferStr(upgrade_buffer,
5963 "\n-- For binary upgrade, handle extension membership the hard way\n");
5964 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5965 fmtId(extobj->name),
5966 objtype);
5967 if (objnamespace && *objnamespace)
5968 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5969 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5970}
5971
5972/*
5973 * getNamespaces:
5974 * get information about all namespaces in the system catalogs
5975 */
5976void
5978{
5979 PGresult *res;
5980 int ntups;
5981 int i;
5982 PQExpBuffer query;
5983 NamespaceInfo *nsinfo;
5984 int i_tableoid;
5985 int i_oid;
5986 int i_nspname;
5987 int i_nspowner;
5988 int i_nspacl;
5989 int i_acldefault;
5990
5991 query = createPQExpBuffer();
5992
5993 /*
5994 * we fetch all namespaces including system ones, so that every object we
5995 * read in can be linked to a containing namespace.
5996 */
5997 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5998 "n.nspowner, "
5999 "n.nspacl, "
6000 "acldefault('n', n.nspowner) AS acldefault "
6001 "FROM pg_namespace n");
6002
6003 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6004
6005 ntups = PQntuples(res);
6006
6007 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
6008
6009 i_tableoid = PQfnumber(res, "tableoid");
6010 i_oid = PQfnumber(res, "oid");
6011 i_nspname = PQfnumber(res, "nspname");
6012 i_nspowner = PQfnumber(res, "nspowner");
6013 i_nspacl = PQfnumber(res, "nspacl");
6014 i_acldefault = PQfnumber(res, "acldefault");
6015
6016 for (i = 0; i < ntups; i++)
6017 {
6018 const char *nspowner;
6019
6020 nsinfo[i].dobj.objType = DO_NAMESPACE;
6021 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6022 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6023 AssignDumpId(&nsinfo[i].dobj);
6024 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6025 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6026 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6027 nsinfo[i].dacl.privtype = 0;
6028 nsinfo[i].dacl.initprivs = NULL;
6029 nspowner = PQgetvalue(res, i, i_nspowner);
6030 nsinfo[i].nspowner = atooid(nspowner);
6031 nsinfo[i].rolname = getRoleName(nspowner);
6032
6033 /* Decide whether to dump this namespace */
6034 selectDumpableNamespace(&nsinfo[i], fout);
6035
6036 /* Mark whether namespace has an ACL */
6037 if (!PQgetisnull(res, i, i_nspacl))
6039
6040 /*
6041 * We ignore any pg_init_privs.initprivs entry for the public schema
6042 * and assume a predetermined default, for several reasons. First,
6043 * dropping and recreating the schema removes its pg_init_privs entry,
6044 * but an empty destination database starts with this ACL nonetheless.
6045 * Second, we support dump/reload of public schema ownership changes.
6046 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6047 * initprivs continues to reflect the initial owner. Hence,
6048 * synthesize the value that nspacl will have after the restore's
6049 * ALTER SCHEMA OWNER. Third, this makes the destination database
6050 * match the source's ACL, even if the latter was an initdb-default
6051 * ACL, which changed in v15. An upgrade pulls in changes to most
6052 * system object ACLs that the DBA had not customized. We've made the
6053 * public schema depart from that, because changing its ACL so easily
6054 * breaks applications.
6055 */
6056 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6057 {
6058 PQExpBuffer aclarray = createPQExpBuffer();
6059 PQExpBuffer aclitem = createPQExpBuffer();
6060
6061 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6062 appendPQExpBufferChar(aclarray, '{');
6063 quoteAclUserName(aclitem, nsinfo[i].rolname);
6064 appendPQExpBufferStr(aclitem, "=UC/");
6065 quoteAclUserName(aclitem, nsinfo[i].rolname);
6066 appendPGArray(aclarray, aclitem->data);
6067 resetPQExpBuffer(aclitem);
6068 appendPQExpBufferStr(aclitem, "=U/");
6069 quoteAclUserName(aclitem, nsinfo[i].rolname);
6070 appendPGArray(aclarray, aclitem->data);
6071 appendPQExpBufferChar(aclarray, '}');
6072
6073 nsinfo[i].dacl.privtype = 'i';
6074 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6076
6077 destroyPQExpBuffer(aclarray);
6078 destroyPQExpBuffer(aclitem);
6079 }
6080 }
6081
6082 PQclear(res);
6083 destroyPQExpBuffer(query);
6084}
6085
6086/*
6087 * findNamespace:
6088 * given a namespace OID, look up the info read by getNamespaces
6089 */
6090static NamespaceInfo *
6092{
6093 NamespaceInfo *nsinfo;
6094
6095 nsinfo = findNamespaceByOid(nsoid);
6096 if (nsinfo == NULL)
6097 pg_fatal("schema with OID %u does not exist", nsoid);
6098 return nsinfo;
6099}
6100
6101/*
6102 * getExtensions:
6103 * read all extensions in the system catalogs and return them in the
6104 * ExtensionInfo* structure
6105 *
6106 * numExtensions is set to the number of extensions read in
6107 */
6109getExtensions(Archive *fout, int *numExtensions)
6110{
6111 DumpOptions *dopt = fout->dopt;
6112 PGresult *res;
6113 int ntups;
6114 int i;
6115 PQExpBuffer query;
6116 ExtensionInfo *extinfo = NULL;
6117 int i_tableoid;
6118 int i_oid;
6119 int i_extname;
6120 int i_nspname;
6121 int i_extrelocatable;
6122 int i_extversion;
6123 int i_extconfig;
6124 int i_extcondition;
6125
6126 query = createPQExpBuffer();
6127
6128 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6129 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6130 "FROM pg_extension x "
6131 "JOIN pg_namespace n ON n.oid = x.extnamespace");
6132
6133 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6134
6135 ntups = PQntuples(res);
6136 if (ntups == 0)
6137 goto cleanup;
6138
6139 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6140
6141 i_tableoid = PQfnumber(res, "tableoid");
6142 i_oid = PQfnumber(res, "oid");
6143 i_extname = PQfnumber(res, "extname");
6144 i_nspname = PQfnumber(res, "nspname");
6145 i_extrelocatable = PQfnumber(res, "extrelocatable");
6146 i_extversion = PQfnumber(res, "extversion");
6147 i_extconfig = PQfnumber(res, "extconfig");
6148 i_extcondition = PQfnumber(res, "extcondition");
6149
6150 for (i = 0; i < ntups; i++)
6151 {
6152 extinfo[i].dobj.objType = DO_EXTENSION;
6153 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6154 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6155 AssignDumpId(&extinfo[i].dobj);
6156 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6157 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6158 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6159 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6160 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6161 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6162
6163 /* Decide whether we want to dump it */
6164 selectDumpableExtension(&(extinfo[i]), dopt);
6165 }
6166
6167cleanup:
6168 PQclear(res);
6169 destroyPQExpBuffer(query);
6170
6171 *numExtensions = ntups;
6172
6173 return extinfo;
6174}
6175
6176/*
6177 * getTypes:
6178 * get information about all types in the system catalogs
6179 *
6180 * NB: this must run after getFuncs() because we assume we can do
6181 * findFuncByOid().
6182 */
6183void
6185{
6186 PGresult *res;
6187 int ntups;
6188 int i;
6190 TypeInfo *tyinfo;
6191 ShellTypeInfo *stinfo;
6192 int i_tableoid;
6193 int i_oid;
6194 int i_typname;
6195 int i_typnamespace;
6196 int i_typacl;
6197 int i_acldefault;
6198 int i_typowner;
6199 int i_typelem;
6200 int i_typrelid;
6201 int i_typrelkind;
6202 int i_typtype;
6203 int i_typisdefined;
6204 int i_isarray;
6205 int i_typarray;
6206
6207 /*
6208 * we include even the built-in types because those may be used as array
6209 * elements by user-defined types
6210 *
6211 * we filter out the built-in types when we dump out the types
6212 *
6213 * same approach for undefined (shell) types and array types
6214 *
6215 * Note: as of 8.3 we can reliably detect whether a type is an
6216 * auto-generated array type by checking the element type's typarray.
6217 * (Before that the test is capable of generating false positives.) We
6218 * still check for name beginning with '_', though, so as to avoid the
6219 * cost of the subselect probe for all standard types. This would have to
6220 * be revisited if the backend ever allows renaming of array types.
6221 */
6222 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6223 "typnamespace, typacl, "
6224 "acldefault('T', typowner) AS acldefault, "
6225 "typowner, "
6226 "typelem, typrelid, typarray, "
6227 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6228 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6229 "typtype, typisdefined, "
6230 "typname[0] = '_' AND typelem != 0 AND "
6231 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6232 "FROM pg_type");
6233
6234 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6235
6236 ntups = PQntuples(res);
6237
6238 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6239
6240 i_tableoid = PQfnumber(res, "tableoid");
6241 i_oid = PQfnumber(res, "oid");
6242 i_typname = PQfnumber(res, "typname");
6243 i_typnamespace = PQfnumber(res, "typnamespace");
6244 i_typacl = PQfnumber(res, "typacl");
6245 i_acldefault = PQfnumber(res, "acldefault");
6246 i_typowner = PQfnumber(res, "typowner");
6247 i_typelem = PQfnumber(res, "typelem");
6248 i_typrelid = PQfnumber(res, "typrelid");
6249 i_typrelkind = PQfnumber(res, "typrelkind");
6250 i_typtype = PQfnumber(res, "typtype");
6251 i_typisdefined = PQfnumber(res, "typisdefined");
6252 i_isarray = PQfnumber(res, "isarray");
6253 i_typarray = PQfnumber(res, "typarray");
6254
6255 for (i = 0; i < ntups; i++)
6256 {
6257 tyinfo[i].dobj.objType = DO_TYPE;
6258 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6259 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6260 AssignDumpId(&tyinfo[i].dobj);
6261 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6262 tyinfo[i].dobj.namespace =
6263 findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6264 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6265 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6266 tyinfo[i].dacl.privtype = 0;
6267 tyinfo[i].dacl.initprivs = NULL;
6268 tyinfo[i].ftypname = NULL; /* may get filled later */
6269 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6270 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6271 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6272 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6273 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6274 tyinfo[i].shellType = NULL;
6275
6276 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6277 tyinfo[i].isDefined = true;
6278 else
6279 tyinfo[i].isDefined = false;
6280
6281 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6282 tyinfo[i].isArray = true;
6283 else
6284 tyinfo[i].isArray = false;
6285
6286 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6287
6288 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6289 tyinfo[i].isMultirange = true;
6290 else
6291 tyinfo[i].isMultirange = false;
6292
6293 /* Decide whether we want to dump it */
6294 selectDumpableType(&tyinfo[i], fout);
6295
6296 /* Mark whether type has an ACL */
6297 if (!PQgetisnull(res, i, i_typacl))
6299
6300 /*
6301 * If it's a domain, fetch info about its constraints, if any
6302 */
6303 tyinfo[i].nDomChecks = 0;
6304 tyinfo[i].domChecks = NULL;
6305 tyinfo[i].notnull = NULL;
6306 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6307 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6308 getDomainConstraints(fout, &(tyinfo[i]));
6309
6310 /*
6311 * If it's a base type, make a DumpableObject representing a shell
6312 * definition of the type. We will need to dump that ahead of the I/O
6313 * functions for the type. Similarly, range types need a shell
6314 * definition in case they have a canonicalize function.
6315 *
6316 * Note: the shell type doesn't have a catId. You might think it
6317 * should copy the base type's catId, but then it might capture the
6318 * pg_depend entries for the type, which we don't want.
6319 */
6320 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6321 (tyinfo[i].typtype == TYPTYPE_BASE ||
6322 tyinfo[i].typtype == TYPTYPE_RANGE))
6323 {
6324 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6325 stinfo->dobj.objType = DO_SHELL_TYPE;
6326 stinfo->dobj.catId = nilCatalogId;
6327 AssignDumpId(&stinfo->dobj);
6328 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6329 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6330 stinfo->baseType = &(tyinfo[i]);
6331 tyinfo[i].shellType = stinfo;
6332
6333 /*
6334 * Initially mark the shell type as not to be dumped. We'll only
6335 * dump it if the I/O or canonicalize functions need to be dumped;
6336 * this is taken care of while sorting dependencies.
6337 */
6338 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6339 }
6340 }
6341
6342 PQclear(res);
6343
6344 destroyPQExpBuffer(query);
6345}
6346
6347/*
6348 * getOperators:
6349 * get information about all operators in the system catalogs
6350 */
6351void
6353{
6354 PGresult *res;
6355 int ntups;
6356 int i;
6358 OprInfo *oprinfo;
6359 int i_tableoid;
6360 int i_oid;
6361 int i_oprname;
6362 int i_oprnamespace;
6363 int i_oprowner;
6364 int i_oprkind;
6365 int i_oprleft;
6366 int i_oprright;
6367 int i_oprcode;
6368
6369 /*
6370 * find all operators, including builtin operators; we filter out
6371 * system-defined operators at dump-out time.
6372 */
6373
6374 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6375 "oprnamespace, "
6376 "oprowner, "
6377 "oprkind, "
6378 "oprleft, "
6379 "oprright, "
6380 "oprcode::oid AS oprcode "
6381 "FROM pg_operator");
6382
6383 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6384
6385 ntups = PQntuples(res);
6386
6387 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6388
6389 i_tableoid = PQfnumber(res, "tableoid");
6390 i_oid = PQfnumber(res, "oid");
6391 i_oprname = PQfnumber(res, "oprname");
6392 i_oprnamespace = PQfnumber(res, "oprnamespace");
6393 i_oprowner = PQfnumber(res, "oprowner");
6394 i_oprkind = PQfnumber(res, "oprkind");
6395 i_oprleft = PQfnumber(res, "oprleft");
6396 i_oprright = PQfnumber(res, "oprright");
6397 i_oprcode = PQfnumber(res, "oprcode");
6398
6399 for (i = 0; i < ntups; i++)
6400 {
6401 oprinfo[i].dobj.objType = DO_OPERATOR;
6402 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6403 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6404 AssignDumpId(&oprinfo[i].dobj);
6405 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6406 oprinfo[i].dobj.namespace =
6407 findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6408 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6409 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6410 oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6411 oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6412 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6413
6414 /* Decide whether we want to dump it */
6415 selectDumpableObject(&(oprinfo[i].dobj), fout);
6416 }
6417
6418 PQclear(res);
6419
6420 destroyPQExpBuffer(query);
6421}
6422
6423/*
6424 * getCollations:
6425 * get information about all collations in the system catalogs
6426 */
6427void
6429{
6430 PGresult *res;
6431 int ntups;
6432 int i;
6433 PQExpBuffer query;
6434 CollInfo *collinfo;
6435 int i_tableoid;
6436 int i_oid;
6437 int i_collname;
6438 int i_collnamespace;
6439 int i_collowner;
6440 int i_collencoding;
6441
6442 query = createPQExpBuffer();
6443
6444 /*
6445 * find all collations, including builtin collations; we filter out
6446 * system-defined collations at dump-out time.
6447 */
6448
6449 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6450 "collnamespace, "
6451 "collowner, "
6452 "collencoding "
6453 "FROM pg_collation");
6454
6455 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6456
6457 ntups = PQntuples(res);
6458
6459 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6460
6461 i_tableoid = PQfnumber(res, "tableoid");
6462 i_oid = PQfnumber(res, "oid");
6463 i_collname = PQfnumber(res, "collname");
6464 i_collnamespace = PQfnumber(res, "collnamespace");
6465 i_collowner = PQfnumber(res, "collowner");
6466 i_collencoding = PQfnumber(res, "collencoding");
6467
6468 for (i = 0; i < ntups; i++)
6469 {
6470 collinfo[i].dobj.objType = DO_COLLATION;
6471 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6472 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6473 AssignDumpId(&collinfo[i].dobj);
6474 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6475 collinfo[i].dobj.namespace =
6476 findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6477 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6478 collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6479
6480 /* Decide whether we want to dump it */
6481 selectDumpableObject(&(collinfo[i].dobj), fout);
6482 }
6483
6484 PQclear(res);
6485
6486 destroyPQExpBuffer(query);
6487}
6488
6489/*
6490 * getConversions:
6491 * get information about all conversions in the system catalogs
6492 */
6493void
6495{
6496 PGresult *res;
6497 int ntups;
6498 int i;
6499 PQExpBuffer query;
6500 ConvInfo *convinfo;
6501 int i_tableoid;
6502 int i_oid;
6503 int i_conname;
6504 int i_connamespace;
6505 int i_conowner;
6506
6507 query = createPQExpBuffer();
6508
6509 /*
6510 * find all conversions, including builtin conversions; we filter out
6511 * system-defined conversions at dump-out time.
6512 */
6513
6514 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6515 "connamespace, "
6516 "conowner "
6517 "FROM pg_conversion");
6518
6519 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6520
6521 ntups = PQntuples(res);
6522
6523 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6524
6525 i_tableoid = PQfnumber(res, "tableoid");
6526 i_oid = PQfnumber(res, "oid");
6527 i_conname = PQfnumber(res, "conname");
6528 i_connamespace = PQfnumber(res, "connamespace");
6529 i_conowner = PQfnumber(res, "conowner");
6530
6531 for (i = 0; i < ntups; i++)
6532 {
6533 convinfo[i].dobj.objType = DO_CONVERSION;
6534 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6535 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6536 AssignDumpId(&convinfo[i].dobj);
6537 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6538 convinfo[i].dobj.namespace =
6539 findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6540 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6541
6542 /* Decide whether we want to dump it */
6543 selectDumpableObject(&(convinfo[i].dobj), fout);
6544 }
6545
6546 PQclear(res);
6547
6548 destroyPQExpBuffer(query);
6549}
6550
6551/*
6552 * getAccessMethods:
6553 * get information about all user-defined access methods
6554 */
6555void
6557{
6558 PGresult *res;
6559 int ntups;
6560 int i;
6561 PQExpBuffer query;
6562 AccessMethodInfo *aminfo;
6563 int i_tableoid;
6564 int i_oid;
6565 int i_amname;
6566 int i_amhandler;
6567 int i_amtype;
6568
6569 query = createPQExpBuffer();
6570
6571 /*
6572 * Select all access methods from pg_am table. v9.6 introduced CREATE
6573 * ACCESS METHOD, so earlier versions usually have only built-in access
6574 * methods. v9.6 also changed the access method API, replacing dozens of
6575 * pg_am columns with amhandler. Even if a user created an access method
6576 * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6577 * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6578 * pg_am just to facilitate findAccessMethodByOid() providing the
6579 * OID-to-name mapping.
6580 */
6581 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6582 if (fout->remoteVersion >= 90600)
6584 "amtype, "
6585 "amhandler::pg_catalog.regproc AS amhandler ");
6586 else
6588 "'i'::pg_catalog.\"char\" AS amtype, "
6589 "'-'::pg_catalog.regproc AS amhandler ");
6590 appendPQExpBufferStr(query, "FROM pg_am");
6591
6592 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6593
6594 ntups = PQntuples(res);
6595
6596 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6597
6598 i_tableoid = PQfnumber(res, "tableoid");
6599 i_oid = PQfnumber(res, "oid");
6600 i_amname = PQfnumber(res, "amname");
6601 i_amhandler = PQfnumber(res, "amhandler");
6602 i_amtype = PQfnumber(res, "amtype");
6603
6604 for (i = 0; i < ntups; i++)
6605 {
6606 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6607 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6608 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6609 AssignDumpId(&aminfo[i].dobj);
6610 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6611 aminfo[i].dobj.namespace = NULL;
6612 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6613 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6614
6615 /* Decide whether we want to dump it */
6616 selectDumpableAccessMethod(&(aminfo[i]), fout);
6617 }
6618
6619 PQclear(res);
6620
6621 destroyPQExpBuffer(query);
6622}
6623
6624
6625/*
6626 * getOpclasses:
6627 * get information about all opclasses in the system catalogs
6628 */
6629void
6631{
6632 PGresult *res;
6633 int ntups;
6634 int i;
6636 OpclassInfo *opcinfo;
6637 int i_tableoid;
6638 int i_oid;
6639 int i_opcmethod;
6640 int i_opcname;
6641 int i_opcnamespace;
6642 int i_opcowner;
6643
6644 /*
6645 * find all opclasses, including builtin opclasses; we filter out
6646 * system-defined opclasses at dump-out time.
6647 */
6648
6649 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6650 "opcnamespace, "
6651 "opcowner "
6652 "FROM pg_opclass");
6653
6654 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6655
6656 ntups = PQntuples(res);
6657
6658 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6659
6660 i_tableoid = PQfnumber(res, "tableoid");
6661 i_oid = PQfnumber(res, "oid");
6662 i_opcmethod = PQfnumber(res, "opcmethod");
6663 i_opcname = PQfnumber(res, "opcname");
6664 i_opcnamespace = PQfnumber(res, "opcnamespace");
6665 i_opcowner = PQfnumber(res, "opcowner");
6666
6667 for (i = 0; i < ntups; i++)
6668 {
6669 opcinfo[i].dobj.objType = DO_OPCLASS;
6670 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6671 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6672 AssignDumpId(&opcinfo[i].dobj);
6673 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6674 opcinfo[i].dobj.namespace =
6675 findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6676 opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6677 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6678
6679 /* Decide whether we want to dump it */
6680 selectDumpableObject(&(opcinfo[i].dobj), fout);
6681 }
6682
6683 PQclear(res);
6684
6685 destroyPQExpBuffer(query);
6686}
6687
6688/*
6689 * getOpfamilies:
6690 * get information about all opfamilies in the system catalogs
6691 */
6692void
6694{
6695 PGresult *res;
6696 int ntups;
6697 int i;
6698 PQExpBuffer query;
6699 OpfamilyInfo *opfinfo;
6700 int i_tableoid;
6701 int i_oid;
6702 int i_opfmethod;
6703 int i_opfname;
6704 int i_opfnamespace;
6705 int i_opfowner;
6706
6707 query = createPQExpBuffer();
6708
6709 /*
6710 * find all opfamilies, including builtin opfamilies; we filter out
6711 * system-defined opfamilies at dump-out time.
6712 */
6713
6714 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6715 "opfnamespace, "
6716 "opfowner "
6717 "FROM pg_opfamily");
6718
6719 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6720
6721 ntups = PQntuples(res);
6722
6723 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6724
6725 i_tableoid = PQfnumber(res, "tableoid");
6726 i_oid = PQfnumber(res, "oid");
6727 i_opfname = PQfnumber(res, "opfname");
6728 i_opfmethod = PQfnumber(res, "opfmethod");
6729 i_opfnamespace = PQfnumber(res, "opfnamespace");
6730 i_opfowner = PQfnumber(res, "opfowner");
6731
6732 for (i = 0; i < ntups; i++)
6733 {
6734 opfinfo[i].dobj.objType = DO_OPFAMILY;
6735 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6736 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6737 AssignDumpId(&opfinfo[i].dobj);
6738 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6739 opfinfo[i].dobj.namespace =
6740 findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6741 opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6742 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6743
6744 /* Decide whether we want to dump it */
6745 selectDumpableObject(&(opfinfo[i].dobj), fout);
6746 }
6747
6748 PQclear(res);
6749
6750 destroyPQExpBuffer(query);
6751}
6752
6753/*
6754 * getAggregates:
6755 * get information about all user-defined aggregates in the system catalogs
6756 */
6757void
6759{
6760 DumpOptions *dopt = fout->dopt;
6761 PGresult *res;
6762 int ntups;
6763 int i;
6765 AggInfo *agginfo;
6766 int i_tableoid;
6767 int i_oid;
6768 int i_aggname;
6769 int i_aggnamespace;
6770 int i_pronargs;
6771 int i_proargtypes;
6772 int i_proowner;
6773 int i_aggacl;
6774 int i_acldefault;
6775
6776 /*
6777 * Find all interesting aggregates. See comment in getFuncs() for the
6778 * rationale behind the filtering logic.
6779 */
6780 if (fout->remoteVersion >= 90600)
6781 {
6782 const char *agg_check;
6783
6784 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6785 : "p.proisagg");
6786
6787 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6788 "p.proname AS aggname, "
6789 "p.pronamespace AS aggnamespace, "
6790 "p.pronargs, p.proargtypes, "
6791 "p.proowner, "
6792 "p.proacl AS aggacl, "
6793 "acldefault('f', p.proowner) AS acldefault "
6794 "FROM pg_proc p "
6795 "LEFT JOIN pg_init_privs pip ON "
6796 "(p.oid = pip.objoid "
6797 "AND pip.classoid = 'pg_proc'::regclass "
6798 "AND pip.objsubid = 0) "
6799 "WHERE %s AND ("
6800 "p.pronamespace != "
6801 "(SELECT oid FROM pg_namespace "
6802 "WHERE nspname = 'pg_catalog') OR "
6803 "p.proacl IS DISTINCT FROM pip.initprivs",
6804 agg_check);
6805 if (dopt->binary_upgrade)
6807 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6808 "classid = 'pg_proc'::regclass AND "
6809 "objid = p.oid AND "
6810 "refclassid = 'pg_extension'::regclass AND "
6811 "deptype = 'e')");
6812 appendPQExpBufferChar(query, ')');
6813 }
6814 else
6815 {
6816 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6817 "pronamespace AS aggnamespace, "
6818 "pronargs, proargtypes, "
6819 "proowner, "
6820 "proacl AS aggacl, "
6821 "acldefault('f', proowner) AS acldefault "
6822 "FROM pg_proc p "
6823 "WHERE proisagg AND ("
6824 "pronamespace != "
6825 "(SELECT oid FROM pg_namespace "
6826 "WHERE nspname = 'pg_catalog')");
6827 if (dopt->binary_upgrade)
6829 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6830 "classid = 'pg_proc'::regclass AND "
6831 "objid = p.oid AND "
6832 "refclassid = 'pg_extension'::regclass AND "
6833 "deptype = 'e')");
6834 appendPQExpBufferChar(query, ')');
6835 }
6836
6837 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6838
6839 ntups = PQntuples(res);
6840
6841 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6842
6843 i_tableoid = PQfnumber(res, "tableoid");
6844 i_oid = PQfnumber(res, "oid");
6845 i_aggname = PQfnumber(res, "aggname");
6846 i_aggnamespace = PQfnumber(res, "aggnamespace");
6847 i_pronargs = PQfnumber(res, "pronargs");
6848 i_proargtypes = PQfnumber(res, "proargtypes");
6849 i_proowner = PQfnumber(res, "proowner");
6850 i_aggacl = PQfnumber(res, "aggacl");
6851 i_acldefault = PQfnumber(res, "acldefault");
6852
6853 for (i = 0; i < ntups; i++)
6854 {
6855 agginfo[i].aggfn.dobj.objType = DO_AGG;
6856 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6857 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6858 AssignDumpId(&agginfo[i].aggfn.dobj);
6859 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6860 agginfo[i].aggfn.dobj.namespace =
6861 findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6862 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6863 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6864 agginfo[i].aggfn.dacl.privtype = 0;
6865 agginfo[i].aggfn.dacl.initprivs = NULL;
6866 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6867 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6868 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6869 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6870 if (agginfo[i].aggfn.nargs == 0)
6871 agginfo[i].aggfn.argtypes = NULL;
6872 else
6873 {
6874 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6875 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6876 agginfo[i].aggfn.argtypes,
6877 agginfo[i].aggfn.nargs);
6878 }
6879 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6880
6881 /* Decide whether we want to dump it */
6882 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6883
6884 /* Mark whether aggregate has an ACL */
6885 if (!PQgetisnull(res, i, i_aggacl))
6886 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6887 }
6888
6889 PQclear(res);
6890
6891 destroyPQExpBuffer(query);
6892}
6893
6894/*
6895 * getFuncs:
6896 * get information about all user-defined functions in the system catalogs
6897 */
6898void
6900{
6901 DumpOptions *dopt = fout->dopt;
6902 PGresult *res;
6903 int ntups;
6904 int i;
6906 FuncInfo *finfo;
6907 int i_tableoid;
6908 int i_oid;
6909 int i_proname;
6910 int i_pronamespace;
6911 int i_proowner;
6912 int i_prolang;
6913 int i_pronargs;
6914 int i_proargtypes;
6915 int i_prorettype;
6916 int i_proacl;
6917 int i_acldefault;
6918
6919 /*
6920 * Find all interesting functions. This is a bit complicated:
6921 *
6922 * 1. Always exclude aggregates; those are handled elsewhere.
6923 *
6924 * 2. Always exclude functions that are internally dependent on something
6925 * else, since presumably those will be created as a result of creating
6926 * the something else. This currently acts only to suppress constructor
6927 * functions for range types. Note this is OK only because the
6928 * constructors don't have any dependencies the range type doesn't have;
6929 * otherwise we might not get creation ordering correct.
6930 *
6931 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6932 * they're members of extensions and we are in binary-upgrade mode then
6933 * include them, since we want to dump extension members individually in
6934 * that mode. Also, if they are used by casts or transforms then we need
6935 * to gather the information about them, though they won't be dumped if
6936 * they are built-in. Also, in 9.6 and up, include functions in
6937 * pg_catalog if they have an ACL different from what's shown in
6938 * pg_init_privs (so we have to join to pg_init_privs; annoying).
6939 */
6940 if (fout->remoteVersion >= 90600)
6941 {
6942 const char *not_agg_check;
6943
6944 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6945 : "NOT p.proisagg");
6946
6947 appendPQExpBuffer(query,
6948 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6949 "p.pronargs, p.proargtypes, p.prorettype, "
6950 "p.proacl, "
6951 "acldefault('f', p.proowner) AS acldefault, "
6952 "p.pronamespace, "
6953 "p.proowner "
6954 "FROM pg_proc p "
6955 "LEFT JOIN pg_init_privs pip ON "
6956 "(p.oid = pip.objoid "
6957 "AND pip.classoid = 'pg_proc'::regclass "
6958 "AND pip.objsubid = 0) "
6959 "WHERE %s"
6960 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6961 "WHERE classid = 'pg_proc'::regclass AND "
6962 "objid = p.oid AND deptype = 'i')"
6963 "\n AND ("
6964 "\n pronamespace != "
6965 "(SELECT oid FROM pg_namespace "
6966 "WHERE nspname = 'pg_catalog')"
6967 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6968 "\n WHERE pg_cast.oid > %u "
6969 "\n AND p.oid = pg_cast.castfunc)"
6970 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6971 "\n WHERE pg_transform.oid > %u AND "
6972 "\n (p.oid = pg_transform.trffromsql"
6973 "\n OR p.oid = pg_transform.trftosql))",
6974 not_agg_check,
6977 if (dopt->binary_upgrade)
6979 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6980 "classid = 'pg_proc'::regclass AND "
6981 "objid = p.oid AND "
6982 "refclassid = 'pg_extension'::regclass AND "
6983 "deptype = 'e')");
6985 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6986 appendPQExpBufferChar(query, ')');
6987 }
6988 else
6989 {
6990 appendPQExpBuffer(query,
6991 "SELECT tableoid, oid, proname, prolang, "
6992 "pronargs, proargtypes, prorettype, proacl, "
6993 "acldefault('f', proowner) AS acldefault, "
6994 "pronamespace, "
6995 "proowner "
6996 "FROM pg_proc p "
6997 "WHERE NOT proisagg"
6998 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6999 "WHERE classid = 'pg_proc'::regclass AND "
7000 "objid = p.oid AND deptype = 'i')"
7001 "\n AND ("
7002 "\n pronamespace != "
7003 "(SELECT oid FROM pg_namespace "
7004 "WHERE nspname = 'pg_catalog')"
7005 "\n OR EXISTS (SELECT 1 FROM pg_cast"
7006 "\n WHERE pg_cast.oid > '%u'::oid"
7007 "\n AND p.oid = pg_cast.castfunc)",
7009
7010 if (fout->remoteVersion >= 90500)
7011 appendPQExpBuffer(query,
7012 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7013 "\n WHERE pg_transform.oid > '%u'::oid"
7014 "\n AND (p.oid = pg_transform.trffromsql"
7015 "\n OR p.oid = pg_transform.trftosql))",
7017
7018 if (dopt->binary_upgrade)
7020 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7021 "classid = 'pg_proc'::regclass AND "
7022 "objid = p.oid AND "
7023 "refclassid = 'pg_extension'::regclass AND "
7024 "deptype = 'e')");
7025 appendPQExpBufferChar(query, ')');
7026 }
7027
7028 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7029
7030 ntups = PQntuples(res);
7031
7032 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7033
7034 i_tableoid = PQfnumber(res, "tableoid");
7035 i_oid = PQfnumber(res, "oid");
7036 i_proname = PQfnumber(res, "proname");
7037 i_pronamespace = PQfnumber(res, "pronamespace");
7038 i_proowner = PQfnumber(res, "proowner");
7039 i_prolang = PQfnumber(res, "prolang");
7040 i_pronargs = PQfnumber(res, "pronargs");
7041 i_proargtypes = PQfnumber(res, "proargtypes");
7042 i_prorettype = PQfnumber(res, "prorettype");
7043 i_proacl = PQfnumber(res, "proacl");
7044 i_acldefault = PQfnumber(res, "acldefault");
7045
7046 for (i = 0; i < ntups; i++)
7047 {
7048 finfo[i].dobj.objType = DO_FUNC;
7049 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7050 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7051 AssignDumpId(&finfo[i].dobj);
7052 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7053 finfo[i].dobj.namespace =
7054 findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7055 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7056 finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7057 finfo[i].dacl.privtype = 0;
7058 finfo[i].dacl.initprivs = NULL;
7059 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7060 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7061 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7062 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7063 if (finfo[i].nargs == 0)
7064 finfo[i].argtypes = NULL;
7065 else
7066 {
7067 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7068 parseOidArray(PQgetvalue(res, i, i_proargtypes),
7069 finfo[i].argtypes, finfo[i].nargs);
7070 }
7071 finfo[i].postponed_def = false; /* might get set during sort */
7072
7073 /* Decide whether we want to dump it */
7074 selectDumpableObject(&(finfo[i].dobj), fout);
7075
7076 /* Mark whether function has an ACL */
7077 if (!PQgetisnull(res, i, i_proacl))
7079 }
7080
7081 PQclear(res);
7082
7083 destroyPQExpBuffer(query);
7084}
7085
7086/*
7087 * getRelationStatistics
7088 * register the statistics object as a dependent of the relation.
7089 *
7090 * reltuples is passed as a string to avoid complexities in converting from/to
7091 * floating point.
7092 */
7093static RelStatsInfo *
7095 char *reltuples, int32 relallvisible,
7096 int32 relallfrozen, char relkind,
7097 char **indAttNames, int nindAttNames)
7098{
7099 if (!fout->dopt->dumpStatistics)
7100 return NULL;
7101
7102 if ((relkind == RELKIND_RELATION) ||
7103 (relkind == RELKIND_PARTITIONED_TABLE) ||
7104 (relkind == RELKIND_INDEX) ||
7105 (relkind == RELKIND_PARTITIONED_INDEX) ||
7106 (relkind == RELKIND_MATVIEW ||
7107 relkind == RELKIND_FOREIGN_TABLE))
7108 {
7109 RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7110 DumpableObject *dobj = &info->dobj;
7111
7112 dobj->objType = DO_REL_STATS;
7113 dobj->catId.tableoid = 0;
7114 dobj->catId.oid = 0;
7115 AssignDumpId(dobj);
7116 dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7117 dobj->dependencies[0] = rel->dumpId;
7118 dobj->nDeps = 1;
7119 dobj->allocDeps = 1;
7121 dobj->name = pg_strdup(rel->name);
7122 dobj->namespace = rel->namespace;
7123 info->relpages = relpages;
7124 info->reltuples = pstrdup(reltuples);
7125 info->relallvisible = relallvisible;
7126 info->relallfrozen = relallfrozen;
7127 info->relkind = relkind;
7128 info->indAttNames = indAttNames;
7129 info->nindAttNames = nindAttNames;
7130
7131 /*
7132 * Ordinarily, stats go in SECTION_DATA for tables and
7133 * SECTION_POST_DATA for indexes.
7134 *
7135 * However, the section may be updated later for materialized view
7136 * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7137 * the stats, so the stats must be restored after the data. Also, the
7138 * materialized view definition may be postponed to SECTION_POST_DATA
7139 * (see repairMatViewBoundaryMultiLoop()).
7140 */
7141 switch (info->relkind)
7142 {
7143 case RELKIND_RELATION:
7144 case RELKIND_PARTITIONED_TABLE:
7145 case RELKIND_MATVIEW:
7146 case RELKIND_FOREIGN_TABLE:
7147 info->section = SECTION_DATA;
7148 break;
7149 case RELKIND_INDEX:
7150 case RELKIND_PARTITIONED_INDEX:
7151 info->section = SECTION_POST_DATA;
7152 break;
7153 default:
7154 pg_fatal("cannot dump statistics for relation kind \"%c\"",
7155 info->relkind);
7156 }
7157
7158 return info;
7159 }
7160 return NULL;
7161}
7162
7163/*
7164 * getTables
7165 * read all the tables (no indexes) in the system catalogs,
7166 * and return them as an array of TableInfo structures
7167 *
7168 * *numTables is set to the number of tables read in
7169 */
7170TableInfo *
7171getTables(Archive *fout, int *numTables)
7172{
7173 DumpOptions *dopt = fout->dopt;
7174 PGresult *res;
7175 int ntups;
7176 int i;
7178 TableInfo *tblinfo;
7179 int i_reltableoid;
7180 int i_reloid;
7181 int i_relname;
7182 int i_relnamespace;
7183 int i_relkind;
7184 int i_reltype;
7185 int i_relowner;
7186 int i_relchecks;
7187 int i_relhasindex;
7188 int i_relhasrules;
7189 int i_relpages;
7190 int i_reltuples;
7191 int i_relallvisible;
7192 int i_relallfrozen;
7193 int i_toastpages;
7194 int i_owning_tab;
7195 int i_owning_col;
7196 int i_reltablespace;
7197 int i_relhasoids;
7198 int i_relhastriggers;
7199 int i_relpersistence;
7200 int i_relispopulated;
7201 int i_relreplident;
7202 int i_relrowsec;
7203 int i_relforcerowsec;
7204 int i_relfrozenxid;
7205 int i_toastfrozenxid;
7206 int i_toastoid;
7207 int i_relminmxid;
7208 int i_toastminmxid;
7209 int i_reloptions;
7210 int i_checkoption;
7211 int i_toastreloptions;
7212 int i_reloftype;
7213 int i_foreignserver;
7214 int i_amname;
7215 int i_is_identity_sequence;
7216 int i_relacl;
7217 int i_acldefault;
7218 int i_ispartition;
7219
7220 /*
7221 * Find all the tables and table-like objects.
7222 *
7223 * We must fetch all tables in this phase because otherwise we cannot
7224 * correctly identify inherited columns, owned sequences, etc.
7225 *
7226 * We include system catalogs, so that we can work if a user table is
7227 * defined to inherit from a system catalog (pretty weird, but...)
7228 *
7229 * Note: in this phase we should collect only a minimal amount of
7230 * information about each table, basically just enough to decide if it is
7231 * interesting. In particular, since we do not yet have lock on any user
7232 * table, we MUST NOT invoke any server-side data collection functions
7233 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7234 * wrong answers if any concurrent DDL is happening.
7235 */
7236
7238 "SELECT c.tableoid, c.oid, c.relname, "
7239 "c.relnamespace, c.relkind, c.reltype, "
7240 "c.relowner, "
7241 "c.relchecks, "
7242 "c.relhasindex, c.relhasrules, c.relpages, "
7243 "c.reltuples, c.relallvisible, ");
7244
7245 if (fout->remoteVersion >= 180000)
7246 appendPQExpBufferStr(query, "c.relallfrozen, ");
7247 else
7248 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7249
7251 "c.relhastriggers, c.relpersistence, "
7252 "c.reloftype, "
7253 "c.relacl, "
7254 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7255 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7256 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7257 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7258 "ELSE 0 END AS foreignserver, "
7259 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7260 "tc.oid AS toid, "
7261 "tc.relpages AS toastpages, "
7262 "tc.reloptions AS toast_reloptions, "
7263 "d.refobjid AS owning_tab, "
7264 "d.refobjsubid AS owning_col, "
7265 "tsp.spcname AS reltablespace, ");
7266
7267 if (fout->remoteVersion >= 120000)
7269 "false AS relhasoids, ");
7270 else
7272 "c.relhasoids, ");
7273
7274 if (fout->remoteVersion >= 90300)
7276 "c.relispopulated, ");
7277 else
7279 "'t' as relispopulated, ");
7280
7281 if (fout->remoteVersion >= 90400)
7283 "c.relreplident, ");
7284 else
7286 "'d' AS relreplident, ");
7287
7288 if (fout->remoteVersion >= 90500)
7290 "c.relrowsecurity, c.relforcerowsecurity, ");
7291 else
7293 "false AS relrowsecurity, "
7294 "false AS relforcerowsecurity, ");
7295
7296 if (fout->remoteVersion >= 90300)
7298 "c.relminmxid, tc.relminmxid AS tminmxid, ");
7299 else
7301 "0 AS relminmxid, 0 AS tminmxid, ");
7302
7303 if (fout->remoteVersion >= 90300)
7305 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7306 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7307 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7308 else
7310 "c.reloptions, NULL AS checkoption, ");
7311
7312 if (fout->remoteVersion >= 90600)
7314 "am.amname, ");
7315 else
7317 "NULL AS amname, ");
7318
7319 if (fout->remoteVersion >= 90600)
7321 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7322 else
7324 "false AS is_identity_sequence, ");
7325
7326 if (fout->remoteVersion >= 100000)
7328 "c.relispartition AS ispartition ");
7329 else
7331 "false AS ispartition ");
7332
7333 /*
7334 * Left join to pg_depend to pick up dependency info linking sequences to
7335 * their owning column, if any (note this dependency is AUTO except for
7336 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7337 * collect the spcname.
7338 */
7340 "\nFROM pg_class c\n"
7341 "LEFT JOIN pg_depend d ON "
7342 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7343 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7344 "d.objsubid = 0 AND "
7345 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7346 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7347
7348 /*
7349 * In 9.6 and up, left join to pg_am to pick up the amname.
7350 */
7351 if (fout->remoteVersion >= 90600)
7353 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7354
7355 /*
7356 * We purposefully ignore toast OIDs for partitioned tables; the reason is
7357 * that versions 10 and 11 have them, but later versions do not, so
7358 * emitting them causes the upgrade to fail.
7359 */
7361 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7362 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7363 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7364
7365 /*
7366 * Restrict to interesting relkinds (in particular, not indexes). Not all
7367 * relkinds are possible in older servers, but it's not worth the trouble
7368 * to emit a version-dependent list.
7369 *
7370 * Composite-type table entries won't be dumped as such, but we have to
7371 * make a DumpableObject for them so that we can track dependencies of the
7372 * composite type (pg_depend entries for columns of the composite type
7373 * link to the pg_class entry not the pg_type entry).
7374 */
7376 "WHERE c.relkind IN ("
7377 CppAsString2(RELKIND_RELATION) ", "
7378 CppAsString2(RELKIND_SEQUENCE) ", "
7379 CppAsString2(RELKIND_VIEW) ", "
7380 CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7381 CppAsString2(RELKIND_MATVIEW) ", "
7382 CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7383 CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7384 "ORDER BY c.oid");
7385
7386 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7387
7388 ntups = PQntuples(res);
7389
7390 *numTables = ntups;
7391
7392 /*
7393 * Extract data from result and lock dumpable tables. We do the locking
7394 * before anything else, to minimize the window wherein a table could
7395 * disappear under us.
7396 *
7397 * Note that we have to save info about all tables here, even when dumping
7398 * only one, because we don't yet know which tables might be inheritance
7399 * ancestors of the target table.
7400 */
7401 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7402
7403 i_reltableoid = PQfnumber(res, "tableoid");
7404 i_reloid = PQfnumber(res, "oid");
7405 i_relname = PQfnumber(res, "relname");
7406 i_relnamespace = PQfnumber(res, "relnamespace");
7407 i_relkind = PQfnumber(res, "relkind");
7408 i_reltype = PQfnumber(res, "reltype");
7409 i_relowner = PQfnumber(res, "relowner");
7410 i_relchecks = PQfnumber(res, "relchecks");
7411 i_relhasindex = PQfnumber(res, "relhasindex");
7412 i_relhasrules = PQfnumber(res, "relhasrules");
7413 i_relpages = PQfnumber(res, "relpages");
7414 i_reltuples = PQfnumber(res, "reltuples");
7415 i_relallvisible = PQfnumber(res, "relallvisible");
7416 i_relallfrozen = PQfnumber(res, "relallfrozen");
7417 i_toastpages = PQfnumber(res, "toastpages");
7418 i_owning_tab = PQfnumber(res, "owning_tab");
7419 i_owning_col = PQfnumber(res, "owning_col");
7420 i_reltablespace = PQfnumber(res, "reltablespace");
7421 i_relhasoids = PQfnumber(res, "relhasoids");
7422 i_relhastriggers = PQfnumber(res, "relhastriggers");
7423 i_relpersistence = PQfnumber(res, "relpersistence");
7424 i_relispopulated = PQfnumber(res, "relispopulated");
7425 i_relreplident = PQfnumber(res, "relreplident");
7426 i_relrowsec = PQfnumber(res, "relrowsecurity");
7427 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7428 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7429 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7430 i_toastoid = PQfnumber(res, "toid");
7431 i_relminmxid = PQfnumber(res, "relminmxid");
7432 i_toastminmxid = PQfnumber(res, "tminmxid");
7433 i_reloptions = PQfnumber(res, "reloptions");
7434 i_checkoption = PQfnumber(res, "checkoption");
7435 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7436 i_reloftype = PQfnumber(res, "reloftype");
7437 i_foreignserver = PQfnumber(res, "foreignserver");
7438 i_amname = PQfnumber(res, "amname");
7439 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7440 i_relacl = PQfnumber(res, "relacl");
7441 i_acldefault = PQfnumber(res, "acldefault");
7442 i_ispartition = PQfnumber(res, "ispartition");
7443
7444 if (dopt->lockWaitTimeout)
7445 {
7446 /*
7447 * Arrange to fail instead of waiting forever for a table lock.
7448 *
7449 * NB: this coding assumes that the only queries issued within the
7450 * following loop are LOCK TABLEs; else the timeout may be undesirably
7451 * applied to other things too.
7452 */
7453 resetPQExpBuffer(query);
7454 appendPQExpBufferStr(query, "SET statement_timeout = ");
7456 ExecuteSqlStatement(fout, query->data);
7457 }
7458
7459 resetPQExpBuffer(query);
7460
7461 for (i = 0; i < ntups; i++)
7462 {
7463 int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7464 int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7465
7466 tblinfo[i].dobj.objType = DO_TABLE;
7467 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7468 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7469 AssignDumpId(&tblinfo[i].dobj);
7470 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7471 tblinfo[i].dobj.namespace =
7472 findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7473 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7474 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7475 tblinfo[i].dacl.privtype = 0;
7476 tblinfo[i].dacl.initprivs = NULL;
7477 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7478 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7479 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7480 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7481 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7482 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7483 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7484 if (PQgetisnull(res, i, i_toastpages))
7485 tblinfo[i].toastpages = 0;
7486 else
7487 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7488 if (PQgetisnull(res, i, i_owning_tab))
7489 {
7490 tblinfo[i].owning_tab = InvalidOid;
7491 tblinfo[i].owning_col = 0;
7492 }
7493 else
7494 {
7495 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7496 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7497 }
7498 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7499 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7500 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7501 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7502 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7503 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7504 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7505 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7506 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7507 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7508 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7509 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7510 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7511 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7512 if (PQgetisnull(res, i, i_checkoption))
7513 tblinfo[i].checkoption = NULL;
7514 else
7515 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7516 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7517 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7518 tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7519 if (PQgetisnull(res, i, i_amname))
7520 tblinfo[i].amname = NULL;
7521 else
7522 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7523 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7524 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7525
7526 /* other fields were zeroed above */
7527
7528 /*
7529 * Decide whether we want to dump this table.
7530 */
7531 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7532 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7533 else
7534 selectDumpableTable(&tblinfo[i], fout);
7535
7536 /*
7537 * Now, consider the table "interesting" if we need to dump its
7538 * definition, data or its statistics. Later on, we'll skip a lot of
7539 * data collection for uninteresting tables.
7540 *
7541 * Note: the "interesting" flag will also be set by flagInhTables for
7542 * parents of interesting tables, so that we collect necessary
7543 * inheritance info even when the parents are not themselves being
7544 * dumped. This is the main reason why we need an "interesting" flag
7545 * that's separate from the components-to-dump bitmask.
7546 */
7547 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7551
7552 tblinfo[i].dummy_view = false; /* might get set during sort */
7553 tblinfo[i].postponed_def = false; /* might get set during sort */
7554
7555 /* Tables have data */
7557
7558 /* Mark whether table has an ACL */
7559 if (!PQgetisnull(res, i, i_relacl))
7560 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7561 tblinfo[i].hascolumnACLs = false; /* may get set later */
7562
7563 /* Add statistics */
7564 if (tblinfo[i].interesting)
7565 {
7566 RelStatsInfo *stats;
7567
7568 stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7569 tblinfo[i].relpages,
7570 PQgetvalue(res, i, i_reltuples),
7571 relallvisible, relallfrozen,
7572 tblinfo[i].relkind, NULL, 0);
7573 if (tblinfo[i].relkind == RELKIND_MATVIEW)
7574 tblinfo[i].stats = stats;
7575 }
7576
7577 /*
7578 * Read-lock target tables to make sure they aren't DROPPED or altered
7579 * in schema before we get around to dumping them.
7580 *
7581 * Note that we don't explicitly lock parents of the target tables; we
7582 * assume our lock on the child is enough to prevent schema
7583 * alterations to parent tables.
7584 *
7585 * NOTE: it'd be kinda nice to lock other relations too, not only
7586 * plain or partitioned tables, but the backend doesn't presently
7587 * allow that.
7588 *
7589 * We only need to lock the table for certain components; see
7590 * pg_dump.h
7591 */
7592 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7593 (tblinfo[i].relkind == RELKIND_RELATION ||
7594 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7595 {
7596 /*
7597 * Tables are locked in batches. When dumping from a remote
7598 * server this can save a significant amount of time by reducing
7599 * the number of round trips.
7600 */
7601 if (query->len == 0)
7602 appendPQExpBuffer(query, "LOCK TABLE %s",
7603 fmtQualifiedDumpable(&tblinfo[i]));
7604 else
7605 {
7606 appendPQExpBuffer(query, ", %s",
7607 fmtQualifiedDumpable(&tblinfo[i]));
7608
7609 /* Arbitrarily end a batch when query length reaches 100K. */
7610 if (query->len >= 100000)
7611 {
7612 /* Lock another batch of tables. */
7613 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7614 ExecuteSqlStatement(fout, query->data);
7615 resetPQExpBuffer(query);
7616 }
7617 }
7618 }
7619 }
7620
7621 if (query->len != 0)
7622 {
7623 /* Lock the tables in the last batch. */
7624 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7625 ExecuteSqlStatement(fout, query->data);
7626 }
7627
7628 if (dopt->lockWaitTimeout)
7629 {
7630 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7631 }
7632
7633 PQclear(res);
7634
7635 destroyPQExpBuffer(query);
7636
7637 return tblinfo;
7638}
7639
7640/*
7641 * getOwnedSeqs
7642 * identify owned sequences and mark them as dumpable if owning table is
7643 *
7644 * We used to do this in getTables(), but it's better to do it after the
7645 * index used by findTableByOid() has been set up.
7646 */
7647void
7648getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7649{
7650 int i;
7651
7652 /*
7653 * Force sequences that are "owned" by table columns to be dumped whenever
7654 * their owning table is being dumped.
7655 */
7656 for (i = 0; i < numTables; i++)
7657 {
7658 TableInfo *seqinfo = &tblinfo[i];
7659 TableInfo *owning_tab;
7660
7661 if (!OidIsValid(seqinfo->owning_tab))
7662 continue; /* not an owned sequence */
7663
7664 owning_tab = findTableByOid(seqinfo->owning_tab);
7665 if (owning_tab == NULL)
7666 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7667 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7668
7669 /*
7670 * For an identity sequence, dump exactly the same components for the
7671 * sequence as for the owning table. This is important because we
7672 * treat the identity sequence as an integral part of the table. For
7673 * example, there is not any DDL command that allows creation of such
7674 * a sequence independently of the table.
7675 *
7676 * For other owned sequences such as serial sequences, we need to dump
7677 * the components that are being dumped for the table and any
7678 * components that the sequence is explicitly marked with.
7679 *
7680 * We can't simply use the set of components which are being dumped
7681 * for the table as the table might be in an extension (and only the
7682 * non-extension components, eg: ACLs if changed, security labels, and
7683 * policies, are being dumped) while the sequence is not (and
7684 * therefore the definition and other components should also be
7685 * dumped).
7686 *
7687 * If the sequence is part of the extension then it should be properly
7688 * marked by checkExtensionMembership() and this will be a no-op as
7689 * the table will be equivalently marked.
7690 */
7691 if (seqinfo->is_identity_sequence)
7692 seqinfo->dobj.dump = owning_tab->dobj.dump;
7693 else
7694 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7695
7696 /* Make sure that necessary data is available if we're dumping it */
7697 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7698 {
7699 seqinfo->interesting = true;
7700 owning_tab->interesting = true;
7701 }
7702 }
7703}
7704
7705/*
7706 * getInherits
7707 * read all the inheritance information
7708 * from the system catalogs return them in the InhInfo* structure
7709 *
7710 * numInherits is set to the number of pairs read in
7711 */
7712InhInfo *
7713getInherits(Archive *fout, int *numInherits)
7714{
7715 PGresult *res;
7716 int ntups;
7717 int i;
7719 InhInfo *inhinfo;
7720
7721 int i_inhrelid;
7722 int i_inhparent;
7723
7724 /* find all the inheritance information */
7725 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7726
7727 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7728
7729 ntups = PQntuples(res);
7730
7731 *numInherits = ntups;
7732
7733 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7734
7735 i_inhrelid = PQfnumber(res, "inhrelid");
7736 i_inhparent = PQfnumber(res, "inhparent");
7737
7738 for (i = 0; i < ntups; i++)
7739 {
7740 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7741 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7742 }
7743
7744 PQclear(res);
7745
7746 destroyPQExpBuffer(query);
7747
7748 return inhinfo;
7749}
7750
7751/*
7752 * getPartitioningInfo
7753 * get information about partitioning
7754 *
7755 * For the most part, we only collect partitioning info about tables we
7756 * intend to dump. However, this function has to consider all partitioned
7757 * tables in the database, because we need to know about parents of partitions
7758 * we are going to dump even if the parents themselves won't be dumped.
7759 *
7760 * Specifically, what we need to know is whether each partitioned table
7761 * has an "unsafe" partitioning scheme that requires us to force
7762 * load-via-partition-root mode for its children. Currently the only case
7763 * for which we force that is hash partitioning on enum columns, since the
7764 * hash codes depend on enum value OIDs which won't be replicated across
7765 * dump-and-reload. There are other cases in which load-via-partition-root
7766 * might be necessary, but we expect users to cope with them.
7767 */
7768void
7770{
7771 PQExpBuffer query;
7772 PGresult *res;
7773 int ntups;
7774
7775 /* hash partitioning didn't exist before v11 */
7776 if (fout->remoteVersion < 110000)
7777 return;
7778 /* needn't bother if not dumping data */
7779 if (!fout->dopt->dumpData)
7780 return;
7781
7782 query = createPQExpBuffer();
7783
7784 /*
7785 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7786 * appears among the partition opclasses. We needn't check partstrat.
7787 *
7788 * Note that this query may well retrieve info about tables we aren't
7789 * going to dump and hence have no lock on. That's okay since we need not
7790 * invoke any unsafe server-side functions.
7791 */
7793 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7794 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7795 "ON c.opcmethod = a.oid\n"
7796 "WHERE opcname = 'enum_ops' "
7797 "AND opcnamespace = 'pg_catalog'::regnamespace "
7798 "AND amname = 'hash') = ANY(partclass)");
7799
7800 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7801
7802 ntups = PQntuples(res);
7803
7804 for (int i = 0; i < ntups; i++)
7805 {
7806 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7807 TableInfo *tbinfo;
7808
7809 tbinfo = findTableByOid(tabrelid);
7810 if (tbinfo == NULL)
7811 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7812 tabrelid);
7813 tbinfo->unsafe_partitions = true;
7814 }
7815
7816 PQclear(res);
7817
7818 destroyPQExpBuffer(query);
7819}
7820
7821/*
7822 * getIndexes
7823 * get information about every index on a dumpable table
7824 *
7825 * Note: index data is not returned directly to the caller, but it
7826 * does get entered into the DumpableObject tables.
7827 */
7828void
7829getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7830{
7832 PQExpBuffer tbloids = createPQExpBuffer();
7833 PGresult *res;
7834 int ntups;
7835 int curtblindx;
7836 IndxInfo *indxinfo;
7837 int i_tableoid,
7838 i_oid,
7839 i_indrelid,
7840 i_indexname,
7841 i_relpages,
7842 i_reltuples,
7843 i_relallvisible,
7844 i_relallfrozen,
7845 i_parentidx,
7846 i_indexdef,
7847 i_indnkeyatts,
7848 i_indnatts,
7849 i_indkey,
7850 i_indisclustered,
7851 i_indisreplident,
7852 i_indnullsnotdistinct,
7853 i_contype,
7854 i_conname,
7855 i_condeferrable,
7856 i_condeferred,
7857 i_conperiod,
7858 i_contableoid,
7859 i_conoid,
7860 i_condef,
7861 i_indattnames,
7862 i_tablespace,
7863 i_indreloptions,
7864 i_indstatcols,
7865 i_indstatvals;
7866
7867 /*
7868 * We want to perform just one query against pg_index. However, we
7869 * mustn't try to select every row of the catalog and then sort it out on
7870 * the client side, because some of the server-side functions we need
7871 * would be unsafe to apply to tables we don't have lock on. Hence, we
7872 * build an array of the OIDs of tables we care about (and now have lock
7873 * on!), and use a WHERE clause to constrain which rows are selected.
7874 */
7875 appendPQExpBufferChar(tbloids, '{');
7876 for (int i = 0; i < numTables; i++)
7877 {
7878 TableInfo *tbinfo = &tblinfo[i];
7879
7880 if (!tbinfo->hasindex)
7881 continue;
7882
7883 /*
7884 * We can ignore indexes of uninteresting tables.
7885 */
7886 if (!tbinfo->interesting)
7887 continue;
7888
7889 /* OK, we need info for this table */
7890 if (tbloids->len > 1) /* do we have more than the '{'? */
7891 appendPQExpBufferChar(tbloids, ',');
7892 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7893 }
7894 appendPQExpBufferChar(tbloids, '}');
7895
7897 "SELECT t.tableoid, t.oid, i.indrelid, "
7898 "t.relname AS indexname, "
7899 "t.relpages, t.reltuples, t.relallvisible, ");
7900
7901 if (fout->remoteVersion >= 180000)
7902 appendPQExpBufferStr(query, "t.relallfrozen, ");
7903 else
7904 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7905
7907 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7908 "i.indkey, i.indisclustered, "
7909 "c.contype, c.conname, "
7910 "c.condeferrable, c.condeferred, "
7911 "c.tableoid AS contableoid, "
7912 "c.oid AS conoid, "
7913 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7914 "CASE WHEN i.indexprs IS NOT NULL THEN "
7915 "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7916 " FROM pg_catalog.pg_attribute "
7917 " WHERE attrelid = i.indexrelid) "
7918 "ELSE NULL END AS indattnames, "
7919 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7920 "t.reloptions AS indreloptions, ");
7921
7922
7923 if (fout->remoteVersion >= 90400)
7925 "i.indisreplident, ");
7926 else
7928 "false AS indisreplident, ");
7929
7930 if (fout->remoteVersion >= 110000)
7932 "inh.inhparent AS parentidx, "
7933 "i.indnkeyatts AS indnkeyatts, "
7934 "i.indnatts AS indnatts, "
7935 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7936 " FROM pg_catalog.pg_attribute "
7937 " WHERE attrelid = i.indexrelid AND "
7938 " attstattarget >= 0) AS indstatcols, "
7939 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7940 " FROM pg_catalog.pg_attribute "
7941 " WHERE attrelid = i.indexrelid AND "
7942 " attstattarget >= 0) AS indstatvals, ");
7943 else
7945 "0 AS parentidx, "
7946 "i.indnatts AS indnkeyatts, "
7947 "i.indnatts AS indnatts, "
7948 "'' AS indstatcols, "
7949 "'' AS indstatvals, ");
7950
7951 if (fout->remoteVersion >= 150000)
7953 "i.indnullsnotdistinct, ");
7954 else
7956 "false AS indnullsnotdistinct, ");
7957
7958 if (fout->remoteVersion >= 180000)
7960 "c.conperiod ");
7961 else
7963 "NULL AS conperiod ");
7964
7965 /*
7966 * The point of the messy-looking outer join is to find a constraint that
7967 * is related by an internal dependency link to the index. If we find one,
7968 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7969 * index won't have more than one internal dependency.
7970 *
7971 * Note: the check on conrelid is redundant, but useful because that
7972 * column is indexed while conindid is not.
7973 */
7974 if (fout->remoteVersion >= 110000)
7975 {
7976 appendPQExpBuffer(query,
7977 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7978 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7979 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7980 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7981 "LEFT JOIN pg_catalog.pg_constraint c "
7982 "ON (i.indrelid = c.conrelid AND "
7983 "i.indexrelid = c.conindid AND "
7984 "c.contype IN ('p','u','x')) "
7985 "LEFT JOIN pg_catalog.pg_inherits inh "
7986 "ON (inh.inhrelid = indexrelid) "
7987 "WHERE (i.indisvalid OR t2.relkind = 'p') "
7988 "AND i.indisready "
7989 "ORDER BY i.indrelid, indexname",
7990 tbloids->data);
7991 }
7992 else
7993 {
7994 /*
7995 * the test on indisready is necessary in 9.2, and harmless in
7996 * earlier/later versions
7997 */
7998 appendPQExpBuffer(query,
7999 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8000 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8001 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8002 "LEFT JOIN pg_catalog.pg_constraint c "
8003 "ON (i.indrelid = c.conrelid AND "
8004 "i.indexrelid = c.conindid AND "
8005 "c.contype IN ('p','u','x')) "
8006 "WHERE i.indisvalid AND i.indisready "
8007 "ORDER BY i.indrelid, indexname",
8008 tbloids->data);
8009 }
8010
8011 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8012
8013 ntups = PQntuples(res);
8014
8015 i_tableoid = PQfnumber(res, "tableoid");
8016 i_oid = PQfnumber(res, "oid");
8017 i_indrelid = PQfnumber(res, "indrelid");
8018 i_indexname = PQfnumber(res, "indexname");
8019 i_relpages = PQfnumber(res, "relpages");
8020 i_reltuples = PQfnumber(res, "reltuples");
8021 i_relallvisible = PQfnumber(res, "relallvisible");
8022 i_relallfrozen = PQfnumber(res, "relallfrozen");
8023 i_parentidx = PQfnumber(res, "parentidx");
8024 i_indexdef = PQfnumber(res, "indexdef");
8025 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8026 i_indnatts = PQfnumber(res, "indnatts");
8027 i_indkey = PQfnumber(res, "indkey");
8028 i_indisclustered = PQfnumber(res, "indisclustered");
8029 i_indisreplident = PQfnumber(res, "indisreplident");
8030 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8031 i_contype = PQfnumber(res, "contype");
8032 i_conname = PQfnumber(res, "conname");
8033 i_condeferrable = PQfnumber(res, "condeferrable");
8034 i_condeferred = PQfnumber(res, "condeferred");
8035 i_conperiod = PQfnumber(res, "conperiod");
8036 i_contableoid = PQfnumber(res, "contableoid");
8037 i_conoid = PQfnumber(res, "conoid");
8038 i_condef = PQfnumber(res, "condef");
8039 i_indattnames = PQfnumber(res, "indattnames");
8040 i_tablespace = PQfnumber(res, "tablespace");
8041 i_indreloptions = PQfnumber(res, "indreloptions");
8042 i_indstatcols = PQfnumber(res, "indstatcols");
8043 i_indstatvals = PQfnumber(res, "indstatvals");
8044
8045 indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8046
8047 /*
8048 * Outer loop iterates once per table, not once per row. Incrementing of
8049 * j is handled by the inner loop.
8050 */
8051 curtblindx = -1;
8052 for (int j = 0; j < ntups;)
8053 {
8054 Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8055 TableInfo *tbinfo = NULL;
8056 char **indAttNames = NULL;
8057 int nindAttNames = 0;
8058 int numinds;
8059
8060 /* Count rows for this table */
8061 for (numinds = 1; numinds < ntups - j; numinds++)
8062 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8063 break;
8064
8065 /*
8066 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8067 * order.
8068 */
8069 while (++curtblindx < numTables)
8070 {
8071 tbinfo = &tblinfo[curtblindx];
8072 if (tbinfo->dobj.catId.oid == indrelid)
8073 break;
8074 }
8075 if (curtblindx >= numTables)
8076 pg_fatal("unrecognized table OID %u", indrelid);
8077 /* cross-check that we only got requested tables */
8078 if (!tbinfo->hasindex ||
8079 !tbinfo->interesting)
8080 pg_fatal("unexpected index data for table \"%s\"",
8081 tbinfo->dobj.name);
8082
8083 /* Save data for this table */
8084 tbinfo->indexes = indxinfo + j;
8085 tbinfo->numIndexes = numinds;
8086
8087 for (int c = 0; c < numinds; c++, j++)
8088 {
8089 char contype;
8090 char indexkind;
8091 RelStatsInfo *relstats;
8092 int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8093 int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8094 int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8095
8096 indxinfo[j].dobj.objType = DO_INDEX;
8097 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8098 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8099 AssignDumpId(&indxinfo[j].dobj);
8100 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8101 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8102 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8103 indxinfo[j].indextable = tbinfo;
8104 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8105 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8106 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8107 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8108 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8109 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8110 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8111 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8112 parseOidArray(PQgetvalue(res, j, i_indkey),
8113 indxinfo[j].indkeys, indxinfo[j].indnattrs);
8114 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8115 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8116 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8117 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8118 indxinfo[j].partattaches = (SimplePtrList)
8119 {
8120 NULL, NULL
8121 };
8122
8123 if (indxinfo[j].parentidx == 0)
8124 indexkind = RELKIND_INDEX;
8125 else
8126 indexkind = RELKIND_PARTITIONED_INDEX;
8127
8128 if (!PQgetisnull(res, j, i_indattnames))
8129 {
8130 if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8131 &indAttNames, &nindAttNames))
8132 pg_fatal("could not parse %s array", "indattnames");
8133 }
8134
8135 relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8136 PQgetvalue(res, j, i_reltuples),
8137 relallvisible, relallfrozen, indexkind,
8138 indAttNames, nindAttNames);
8139
8140 contype = *(PQgetvalue(res, j, i_contype));
8141 if (contype == 'p' || contype == 'u' || contype == 'x')
8142 {
8143 /*
8144 * If we found a constraint matching the index, create an
8145 * entry for it.
8146 */
8147 ConstraintInfo *constrinfo;
8148
8149 constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8150 constrinfo->dobj.objType = DO_CONSTRAINT;
8151 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8152 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8153 AssignDumpId(&constrinfo->dobj);
8154 constrinfo->dobj.dump = tbinfo->dobj.dump;
8155 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8156 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8157 constrinfo->contable = tbinfo;
8158 constrinfo->condomain = NULL;
8159 constrinfo->contype = contype;
8160 if (contype == 'x')
8161 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8162 else
8163 constrinfo->condef = NULL;
8164 constrinfo->confrelid = InvalidOid;
8165 constrinfo->conindex = indxinfo[j].dobj.dumpId;
8166 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8167 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8168 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8169 constrinfo->conislocal = true;
8170 constrinfo->separate = true;
8171
8172 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8173 if (relstats != NULL)
8174 addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8175 }
8176 else
8177 {
8178 /* Plain secondary index */
8179 indxinfo[j].indexconstraint = 0;
8180 }
8181 }
8182 }
8183
8184 PQclear(res);
8185
8186 destroyPQExpBuffer(query);
8187 destroyPQExpBuffer(tbloids);
8188}
8189
8190/*
8191 * getExtendedStatistics
8192 * get information about extended-statistics objects.
8193 *
8194 * Note: extended statistics data is not returned directly to the caller, but
8195 * it does get entered into the DumpableObject tables.
8196 */
8197void
8199{
8200 PQExpBuffer query;
8201 PGresult *res;
8202 StatsExtInfo *statsextinfo;
8203 int ntups;
8204 int i_tableoid;
8205 int i_oid;
8206 int i_stxname;
8207 int i_stxnamespace;
8208 int i_stxowner;
8209 int i_stxrelid;
8210 int i_stattarget;
8211 int i;
8212
8213 /* Extended statistics were new in v10 */
8214 if (fout->remoteVersion < 100000)
8215 return;
8216
8217 query = createPQExpBuffer();
8218
8219 if (fout->remoteVersion < 130000)
8220 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8221 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8222 "FROM pg_catalog.pg_statistic_ext");
8223 else
8224 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8225 "stxnamespace, stxowner, stxrelid, stxstattarget "
8226 "FROM pg_catalog.pg_statistic_ext");
8227
8228 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8229
8230 ntups = PQntuples(res);
8231
8232 i_tableoid = PQfnumber(res, "tableoid");
8233 i_oid = PQfnumber(res, "oid");
8234 i_stxname = PQfnumber(res, "stxname");
8235 i_stxnamespace = PQfnumber(res, "stxnamespace");
8236 i_stxowner = PQfnumber(res, "stxowner");
8237 i_stxrelid = PQfnumber(res, "stxrelid");
8238 i_stattarget = PQfnumber(res, "stxstattarget");
8239
8240 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8241
8242 for (i = 0; i < ntups; i++)
8243 {
8244 statsextinfo[i].dobj.objType = DO_STATSEXT;
8245 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8246 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8247 AssignDumpId(&statsextinfo[i].dobj);
8248 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8249 statsextinfo[i].dobj.namespace =
8250 findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8251 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8252 statsextinfo[i].stattable =
8253 findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8254 if (PQgetisnull(res, i, i_stattarget))
8255 statsextinfo[i].stattarget = -1;
8256 else
8257 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8258
8259 /* Decide whether we want to dump it */
8260 selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8261 }
8262
8263 PQclear(res);
8264 destroyPQExpBuffer(query);
8265}
8266
8267/*
8268 * getConstraints
8269 *
8270 * Get info about constraints on dumpable tables.
8271 *
8272 * Currently handles foreign keys only.
8273 * Unique and primary key constraints are handled with indexes,
8274 * while check constraints are processed in getTableAttrs().
8275 */
8276void
8277getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8278{
8280 PQExpBuffer tbloids = createPQExpBuffer();
8281 PGresult *res;
8282 int ntups;
8283 int curtblindx;
8284 TableInfo *tbinfo = NULL;
8285 ConstraintInfo *constrinfo;
8286 int i_contableoid,
8287 i_conoid,
8288 i_conrelid,
8289 i_conname,
8290 i_confrelid,
8291 i_conindid,
8292 i_condef;
8293
8294 /*
8295 * We want to perform just one query against pg_constraint. However, we
8296 * mustn't try to select every row of the catalog and then sort it out on
8297 * the client side, because some of the server-side functions we need
8298 * would be unsafe to apply to tables we don't have lock on. Hence, we
8299 * build an array of the OIDs of tables we care about (and now have lock
8300 * on!), and use a WHERE clause to constrain which rows are selected.
8301 */
8302 appendPQExpBufferChar(tbloids, '{');
8303 for (int i = 0; i < numTables; i++)
8304 {
8305 TableInfo *tinfo = &tblinfo[i];
8306
8307 if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8308 continue;
8309
8310 /* OK, we need info for this table */
8311 if (tbloids->len > 1) /* do we have more than the '{'? */
8312 appendPQExpBufferChar(tbloids, ',');
8313 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8314 }
8315 appendPQExpBufferChar(tbloids, '}');
8316
8318 "SELECT c.tableoid, c.oid, "
8319 "conrelid, conname, confrelid, ");
8320 if (fout->remoteVersion >= 110000)
8321 appendPQExpBufferStr(query, "conindid, ");
8322 else
8323 appendPQExpBufferStr(query, "0 AS conindid, ");
8324 appendPQExpBuffer(query,
8325 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8326 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8327 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8328 "WHERE contype = 'f' ",
8329 tbloids->data);
8330 if (fout->remoteVersion >= 110000)
8332 "AND conparentid = 0 ");
8334 "ORDER BY conrelid, conname");
8335
8336 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8337
8338 ntups = PQntuples(res);
8339
8340 i_contableoid = PQfnumber(res, "tableoid");
8341 i_conoid = PQfnumber(res, "oid");
8342 i_conrelid = PQfnumber(res, "conrelid");
8343 i_conname = PQfnumber(res, "conname");
8344 i_confrelid = PQfnumber(res, "confrelid");
8345 i_conindid = PQfnumber(res, "conindid");
8346 i_condef = PQfnumber(res, "condef");
8347
8348 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8349
8350 curtblindx = -1;
8351 for (int j = 0; j < ntups; j++)
8352 {
8353 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8354 TableInfo *reftable;
8355
8356 /*
8357 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8358 * order.
8359 */
8360 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8361 {
8362 while (++curtblindx < numTables)
8363 {
8364 tbinfo = &tblinfo[curtblindx];
8365 if (tbinfo->dobj.catId.oid == conrelid)
8366 break;
8367 }
8368 if (curtblindx >= numTables)
8369 pg_fatal("unrecognized table OID %u", conrelid);
8370 }
8371
8372 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8373 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8374 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8375 AssignDumpId(&constrinfo[j].dobj);
8376 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8377 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8378 constrinfo[j].contable = tbinfo;
8379 constrinfo[j].condomain = NULL;
8380 constrinfo[j].contype = 'f';
8381 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8382 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8383 constrinfo[j].conindex = 0;
8384 constrinfo[j].condeferrable = false;
8385 constrinfo[j].condeferred = false;
8386 constrinfo[j].conislocal = true;
8387 constrinfo[j].separate = true;
8388
8389 /*
8390 * Restoring an FK that points to a partitioned table requires that
8391 * all partition indexes have been attached beforehand. Ensure that
8392 * happens by making the constraint depend on each index partition
8393 * attach object.
8394 */
8395 reftable = findTableByOid(constrinfo[j].confrelid);
8396 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8397 {
8398 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8399
8400 if (indexOid != InvalidOid)
8401 {
8402 for (int k = 0; k < reftable->numIndexes; k++)
8403 {
8404 IndxInfo *refidx;
8405
8406 /* not our index? */
8407 if (reftable->indexes[k].dobj.catId.oid != indexOid)
8408 continue;
8409
8410 refidx = &reftable->indexes[k];
8411 addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8412 break;
8413 }
8414 }
8415 }
8416 }
8417
8418 PQclear(res);
8419
8420 destroyPQExpBuffer(query);
8421 destroyPQExpBuffer(tbloids);
8422}
8423
8424/*
8425 * addConstrChildIdxDeps
8426 *
8427 * Recursive subroutine for getConstraints
8428 *
8429 * Given an object representing a foreign key constraint and an index on the
8430 * partitioned table it references, mark the constraint object as dependent
8431 * on the DO_INDEX_ATTACH object of each index partition, recursively
8432 * drilling down to their partitions if any. This ensures that the FK is not
8433 * restored until the index is fully marked valid.
8434 */
8435static void
8437{
8438 SimplePtrListCell *cell;
8439
8441
8442 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8443 {
8444 IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8445
8446 addObjectDependency(dobj, attach->dobj.dumpId);
8447
8448 if (attach->partitionIdx->partattaches.head != NULL)
8449 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8450 }
8451}
8452
8453/*
8454 * getDomainConstraints
8455 *
8456 * Get info about constraints on a domain.
8457 */
8458static void
8460{
8461 ConstraintInfo *constrinfo;
8463 PGresult *res;
8464 int i_tableoid,
8465 i_oid,
8466 i_conname,
8467 i_consrc,
8468 i_convalidated,
8469 i_contype;
8470 int ntups;
8471
8473 {
8474 /*
8475 * Set up query for constraint-specific details. For servers 17 and
8476 * up, domains have constraints of type 'n' as well as 'c', otherwise
8477 * just the latter.
8478 */
8479 appendPQExpBuffer(query,
8480 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8481 "SELECT tableoid, oid, conname, "
8482 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8483 "convalidated, contype "
8484 "FROM pg_catalog.pg_constraint "
8485 "WHERE contypid = $1 AND contype IN (%s) "
8486 "ORDER BY conname",
8487 fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8488
8489 ExecuteSqlStatement(fout, query->data);
8490
8492 }
8493
8494 printfPQExpBuffer(query,
8495 "EXECUTE getDomainConstraints('%u')",
8496 tyinfo->dobj.catId.oid);
8497
8498 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8499
8500 ntups = PQntuples(res);
8501
8502 i_tableoid = PQfnumber(res, "tableoid");
8503 i_oid = PQfnumber(res, "oid");
8504 i_conname = PQfnumber(res, "conname");
8505 i_consrc = PQfnumber(res, "consrc");
8506 i_convalidated = PQfnumber(res, "convalidated");
8507 i_contype = PQfnumber(res, "contype");
8508
8509 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8510 tyinfo->domChecks = constrinfo;
8511
8512 /* 'i' tracks result rows; 'j' counts CHECK constraints */
8513 for (int i = 0, j = 0; i < ntups; i++)
8514 {
8515 bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8516 char contype = (PQgetvalue(res, i, i_contype))[0];
8517 ConstraintInfo *constraint;
8518
8519 if (contype == CONSTRAINT_CHECK)
8520 {
8521 constraint = &constrinfo[j++];
8522 tyinfo->nDomChecks++;
8523 }
8524 else
8525 {
8526 Assert(contype == CONSTRAINT_NOTNULL);
8527 Assert(tyinfo->notnull == NULL);
8528 /* use last item in array for the not-null constraint */
8529 tyinfo->notnull = &(constrinfo[ntups - 1]);
8530 constraint = tyinfo->notnull;
8531 }
8532
8533 constraint->dobj.objType = DO_CONSTRAINT;
8534 constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8535 constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8536 AssignDumpId(&(constraint->dobj));
8537 constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8538 constraint->dobj.namespace = tyinfo->dobj.namespace;
8539 constraint->contable = NULL;
8540 constraint->condomain = tyinfo;
8541 constraint->contype = contype;
8542 constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8543 constraint->confrelid = InvalidOid;
8544 constraint->conindex = 0;
8545 constraint->condeferrable = false;
8546 constraint->condeferred = false;
8547 constraint->conislocal = true;
8548
8549 constraint->separate = !validated;
8550
8551 /*
8552 * Make the domain depend on the constraint, ensuring it won't be
8553 * output till any constraint dependencies are OK. If the constraint
8554 * has not been validated, it's going to be dumped after the domain
8555 * anyway, so this doesn't matter.
8556 */
8557 if (validated)
8558 addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8559 }
8560
8561 PQclear(res);
8562
8563 destroyPQExpBuffer(query);
8564}
8565
8566/*
8567 * getRules
8568 * get basic information about every rule in the system
8569 */
8570void
8572{
8573 PGresult *res;
8574 int ntups;
8575 int i;
8577 RuleInfo *ruleinfo;
8578 int i_tableoid;
8579 int i_oid;
8580 int i_rulename;
8581 int i_ruletable;
8582 int i_ev_type;
8583 int i_is_instead;
8584 int i_ev_enabled;
8585
8586 appendPQExpBufferStr(query, "SELECT "
8587 "tableoid, oid, rulename, "
8588 "ev_class AS ruletable, ev_type, is_instead, "
8589 "ev_enabled "
8590 "FROM pg_rewrite "
8591 "ORDER BY oid");
8592
8593 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8594
8595 ntups = PQntuples(res);
8596
8597 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8598
8599 i_tableoid = PQfnumber(res, "tableoid");
8600 i_oid = PQfnumber(res, "oid");
8601 i_rulename = PQfnumber(res, "rulename");
8602 i_ruletable = PQfnumber(res, "ruletable");
8603 i_ev_type = PQfnumber(res, "ev_type");
8604 i_is_instead = PQfnumber(res, "is_instead");
8605 i_ev_enabled = PQfnumber(res, "ev_enabled");
8606
8607 for (i = 0; i < ntups; i++)
8608 {
8609 Oid ruletableoid;
8610
8611 ruleinfo[i].dobj.objType = DO_RULE;
8612 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8613 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8614 AssignDumpId(&ruleinfo[i].dobj);
8615 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8616 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8617 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8618 if (ruleinfo[i].ruletable == NULL)
8619 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8620 ruletableoid, ruleinfo[i].dobj.catId.oid);
8621 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8622 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8623 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8624 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8625 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8626 if (ruleinfo[i].ruletable)
8627 {
8628 /*
8629 * If the table is a view or materialized view, force its ON
8630 * SELECT rule to be sorted before the view itself --- this
8631 * ensures that any dependencies for the rule affect the table's
8632 * positioning. Other rules are forced to appear after their
8633 * table.
8634 */
8635 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8636 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8637 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8638 {
8639 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8640 ruleinfo[i].dobj.dumpId);
8641 /* We'll merge the rule into CREATE VIEW, if possible */
8642 ruleinfo[i].separate = false;
8643 }
8644 else
8645 {
8646 addObjectDependency(&ruleinfo[i].dobj,
8647 ruleinfo[i].ruletable->dobj.dumpId);
8648 ruleinfo[i].separate = true;
8649 }
8650 }
8651 else
8652 ruleinfo[i].separate = true;
8653 }
8654
8655 PQclear(res);
8656
8657 destroyPQExpBuffer(query);
8658}
8659
8660/*
8661 * getTriggers
8662 * get information about every trigger on a dumpable table
8663 *
8664 * Note: trigger data is not returned directly to the caller, but it
8665 * does get entered into the DumpableObject tables.
8666 */
8667void
8668getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8669{
8671 PQExpBuffer tbloids = createPQExpBuffer();
8672 PGresult *res;
8673 int ntups;
8674 int curtblindx;
8675 TriggerInfo *tginfo;
8676 int i_tableoid,
8677 i_oid,
8678 i_tgrelid,
8679 i_tgname,
8680 i_tgenabled,
8681 i_tgispartition,
8682 i_tgdef;
8683
8684 /*
8685 * We want to perform just one query against pg_trigger. However, we
8686 * mustn't try to select every row of the catalog and then sort it out on
8687 * the client side, because some of the server-side functions we need
8688 * would be unsafe to apply to tables we don't have lock on. Hence, we
8689 * build an array of the OIDs of tables we care about (and now have lock
8690 * on!), and use a WHERE clause to constrain which rows are selected.
8691 */
8692 appendPQExpBufferChar(tbloids, '{');
8693 for (int i = 0; i < numTables; i++)
8694 {
8695 TableInfo *tbinfo = &tblinfo[i];
8696
8697 if (!tbinfo->hastriggers ||
8698 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8699 continue;
8700
8701 /* OK, we need info for this table */
8702 if (tbloids->len > 1) /* do we have more than the '{'? */
8703 appendPQExpBufferChar(tbloids, ',');
8704 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8705 }
8706 appendPQExpBufferChar(tbloids, '}');
8707
8708 if (fout->remoteVersion >= 150000)
8709 {
8710 /*
8711 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8712 * result in non-forward-compatible dumps of WHEN clauses due to
8713 * under-parenthesization.
8714 *
8715 * NB: We need to see partition triggers in case the tgenabled flag
8716 * has been changed from the parent.
8717 */
8718 appendPQExpBuffer(query,
8719 "SELECT t.tgrelid, t.tgname, "
8720 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8721 "t.tgenabled, t.tableoid, t.oid, "
8722 "t.tgparentid <> 0 AS tgispartition\n"
8723 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8724 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8725 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8726 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8727 "OR t.tgenabled != u.tgenabled) "
8728 "ORDER BY t.tgrelid, t.tgname",
8729 tbloids->data);
8730 }
8731 else if (fout->remoteVersion >= 130000)
8732 {
8733 /*
8734 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8735 * result in non-forward-compatible dumps of WHEN clauses due to
8736 * under-parenthesization.
8737 *
8738 * NB: We need to see tgisinternal triggers in partitions, in case the
8739 * tgenabled flag has been changed from the parent.
8740 */
8741 appendPQExpBuffer(query,
8742 "SELECT t.tgrelid, t.tgname, "
8743 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8744 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8745 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8746 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8747 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8748 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8749 "ORDER BY t.tgrelid, t.tgname",
8750 tbloids->data);
8751 }
8752 else if (fout->remoteVersion >= 110000)
8753 {
8754 /*
8755 * NB: We need to see tgisinternal triggers in partitions, in case the
8756 * tgenabled flag has been changed from the parent. No tgparentid in
8757 * version 11-12, so we have to match them via pg_depend.
8758 *
8759 * See above about pretty=true in pg_get_triggerdef.
8760 */
8761 appendPQExpBuffer(query,
8762 "SELECT t.tgrelid, t.tgname, "
8763 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8764 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8765 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8766 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8767 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8768 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8769 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8770 " d.objid = t.oid "
8771 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8772 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8773 "ORDER BY t.tgrelid, t.tgname",
8774 tbloids->data);
8775 }
8776 else
8777 {
8778 /* See above about pretty=true in pg_get_triggerdef */
8779 appendPQExpBuffer(query,
8780 "SELECT t.tgrelid, t.tgname, "
8781 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8782 "t.tgenabled, false as tgispartition, "
8783 "t.tableoid, t.oid "
8784 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8785 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8786 "WHERE NOT tgisinternal "
8787 "ORDER BY t.tgrelid, t.tgname",
8788 tbloids->data);
8789 }
8790
8791 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8792
8793 ntups = PQntuples(res);
8794
8795 i_tableoid = PQfnumber(res, "tableoid");
8796 i_oid = PQfnumber(res, "oid");
8797 i_tgrelid = PQfnumber(res, "tgrelid");
8798 i_tgname = PQfnumber(res, "tgname");
8799 i_tgenabled = PQfnumber(res, "tgenabled");
8800 i_tgispartition = PQfnumber(res, "tgispartition");
8801 i_tgdef = PQfnumber(res, "tgdef");
8802
8803 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8804
8805 /*
8806 * Outer loop iterates once per table, not once per row. Incrementing of
8807 * j is handled by the inner loop.
8808 */
8809 curtblindx = -1;
8810 for (int j = 0; j < ntups;)
8811 {
8812 Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8813 TableInfo *tbinfo = NULL;
8814 int numtrigs;
8815
8816 /* Count rows for this table */
8817 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8818 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8819 break;
8820
8821 /*
8822 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8823 * order.
8824 */
8825 while (++curtblindx < numTables)
8826 {
8827 tbinfo = &tblinfo[curtblindx];
8828 if (tbinfo->dobj.catId.oid == tgrelid)
8829 break;
8830 }
8831 if (curtblindx >= numTables)
8832 pg_fatal("unrecognized table OID %u", tgrelid);
8833
8834 /* Save data for this table */
8835 tbinfo->triggers = tginfo + j;
8836 tbinfo->numTriggers = numtrigs;
8837
8838 for (int c = 0; c < numtrigs; c++, j++)
8839 {
8840 tginfo[j].dobj.objType = DO_TRIGGER;
8841 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8842 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8843 AssignDumpId(&tginfo[j].dobj);
8844 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8845 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8846 tginfo[j].tgtable = tbinfo;
8847 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8848 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8849 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8850 }
8851 }
8852
8853 PQclear(res);
8854
8855 destroyPQExpBuffer(query);
8856 destroyPQExpBuffer(tbloids);
8857}
8858
8859/*
8860 * getEventTriggers
8861 * get information about event triggers
8862 */
8863void
8865{
8866 int i;
8867 PQExpBuffer query;
8868 PGresult *res;
8869 EventTriggerInfo *evtinfo;
8870 int i_tableoid,
8871 i_oid,
8872 i_evtname,
8873 i_evtevent,
8874 i_evtowner,
8875 i_evttags,
8876 i_evtfname,
8877 i_evtenabled;
8878 int ntups;
8879
8880 /* Before 9.3, there are no event triggers */
8881 if (fout->remoteVersion < 90300)
8882 return;
8883
8884 query = createPQExpBuffer();
8885
8887 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8888 "evtevent, evtowner, "
8889 "array_to_string(array("
8890 "select quote_literal(x) "
8891 " from unnest(evttags) as t(x)), ', ') as evttags, "
8892 "e.evtfoid::regproc as evtfname "
8893 "FROM pg_event_trigger e "
8894 "ORDER BY e.oid");
8895
8896 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8897
8898 ntups = PQntuples(res);
8899
8900 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8901
8902 i_tableoid = PQfnumber(res, "tableoid");
8903 i_oid = PQfnumber(res, "oid");
8904 i_evtname = PQfnumber(res, "evtname");
8905 i_evtevent = PQfnumber(res, "evtevent");
8906 i_evtowner = PQfnumber(res, "evtowner");
8907 i_evttags = PQfnumber(res, "evttags");
8908 i_evtfname = PQfnumber(res, "evtfname");
8909 i_evtenabled = PQfnumber(res, "evtenabled");
8910
8911 for (i = 0; i < ntups; i++)
8912 {
8913 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8914 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8915 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8916 AssignDumpId(&evtinfo[i].dobj);
8917 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8918 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8919 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8920 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8921 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8922 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8923 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8924
8925 /* Decide whether we want to dump it */
8926 selectDumpableObject(&(evtinfo[i].dobj), fout);
8927 }
8928
8929 PQclear(res);
8930
8931 destroyPQExpBuffer(query);
8932}
8933
8934/*
8935 * getProcLangs
8936 * get basic information about every procedural language in the system
8937 *
8938 * NB: this must run after getFuncs() because we assume we can do
8939 * findFuncByOid().
8940 */
8941void
8943{
8944 PGresult *res;
8945 int ntups;
8946 int i;
8948 ProcLangInfo *planginfo;
8949 int i_tableoid;
8950 int i_oid;
8951 int i_lanname;
8952 int i_lanpltrusted;
8953 int i_lanplcallfoid;
8954 int i_laninline;
8955 int i_lanvalidator;
8956 int i_lanacl;
8957 int i_acldefault;
8958 int i_lanowner;
8959
8960 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8961 "lanname, lanpltrusted, lanplcallfoid, "
8962 "laninline, lanvalidator, "
8963 "lanacl, "
8964 "acldefault('l', lanowner) AS acldefault, "
8965 "lanowner "
8966 "FROM pg_language "
8967 "WHERE lanispl "
8968 "ORDER BY oid");
8969
8970 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8971
8972 ntups = PQntuples(res);
8973
8974 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8975
8976 i_tableoid = PQfnumber(res, "tableoid");
8977 i_oid = PQfnumber(res, "oid");
8978 i_lanname = PQfnumber(res, "lanname");
8979 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8980 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8981 i_laninline = PQfnumber(res, "laninline");
8982 i_lanvalidator = PQfnumber(res, "lanvalidator");
8983 i_lanacl = PQfnumber(res, "lanacl");
8984 i_acldefault = PQfnumber(res, "acldefault");
8985 i_lanowner = PQfnumber(res, "lanowner");
8986
8987 for (i = 0; i < ntups; i++)
8988 {
8989 planginfo[i].dobj.objType = DO_PROCLANG;
8990 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8991 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8992 AssignDumpId(&planginfo[i].dobj);
8993
8994 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8995 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8996 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8997 planginfo[i].dacl.privtype = 0;
8998 planginfo[i].dacl.initprivs = NULL;
8999 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9000 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9001 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9002 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9003 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9004
9005 /* Decide whether we want to dump it */
9006 selectDumpableProcLang(&(planginfo[i]), fout);
9007
9008 /* Mark whether language has an ACL */
9009 if (!PQgetisnull(res, i, i_lanacl))
9010 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9011 }
9012
9013 PQclear(res);
9014
9015 destroyPQExpBuffer(query);
9016}
9017
9018/*
9019 * getCasts
9020 * get basic information about most casts in the system
9021 *
9022 * Skip casts from a range to its multirange, since we'll create those
9023 * automatically.
9024 */
9025void
9027{
9028 PGresult *res;
9029 int ntups;
9030 int i;
9032 CastInfo *castinfo;
9033 int i_tableoid;
9034 int i_oid;
9035 int i_castsource;
9036 int i_casttarget;
9037 int i_castfunc;
9038 int i_castcontext;
9039 int i_castmethod;
9040
9041 if (fout->remoteVersion >= 140000)
9042 {
9043 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9044 "castsource, casttarget, castfunc, castcontext, "
9045 "castmethod "
9046 "FROM pg_cast c "
9047 "WHERE NOT EXISTS ( "
9048 "SELECT 1 FROM pg_range r "
9049 "WHERE c.castsource = r.rngtypid "
9050 "AND c.casttarget = r.rngmultitypid "
9051 ") "
9052 "ORDER BY 3,4");
9053 }
9054 else
9055 {
9056 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9057 "castsource, casttarget, castfunc, castcontext, "
9058 "castmethod "
9059 "FROM pg_cast ORDER BY 3,4");
9060 }
9061
9062 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9063
9064 ntups = PQntuples(res);
9065
9066 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9067
9068 i_tableoid = PQfnumber(res, "tableoid");
9069 i_oid = PQfnumber(res, "oid");
9070 i_castsource = PQfnumber(res, "castsource");
9071 i_casttarget = PQfnumber(res, "casttarget");
9072 i_castfunc = PQfnumber(res, "castfunc");
9073 i_castcontext = PQfnumber(res, "castcontext");
9074 i_castmethod = PQfnumber(res, "castmethod");
9075
9076 for (i = 0; i < ntups; i++)
9077 {
9078 PQExpBufferData namebuf;
9079 TypeInfo *sTypeInfo;
9080 TypeInfo *tTypeInfo;
9081
9082 castinfo[i].dobj.objType = DO_CAST;
9083 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9084 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9085 AssignDumpId(&castinfo[i].dobj);
9086 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9087 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9088 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9089 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9090 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9091
9092 /*
9093 * Try to name cast as concatenation of typnames. This is only used
9094 * for purposes of sorting. If we fail to find either type, the name
9095 * will be an empty string.
9096 */
9097 initPQExpBuffer(&namebuf);
9098 sTypeInfo = findTypeByOid(castinfo[i].castsource);
9099 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9100 if (sTypeInfo && tTypeInfo)
9101 appendPQExpBuffer(&namebuf, "%s %s",
9102 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9103 castinfo[i].dobj.name = namebuf.data;
9104
9105 /* Decide whether we want to dump it */
9106 selectDumpableCast(&(castinfo[i]), fout);
9107 }
9108
9109 PQclear(res);
9110
9111 destroyPQExpBuffer(query);
9112}
9113
9114static char *
9116{
9117 PQExpBuffer query;
9118 PGresult *res;
9119 char *lanname;
9120
9121 query = createPQExpBuffer();
9122 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9123 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9124 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9125 destroyPQExpBuffer(query);
9126 PQclear(res);
9127
9128 return lanname;
9129}
9130
9131/*
9132 * getTransforms
9133 * get basic information about every transform in the system
9134 */
9135void
9137{
9138 PGresult *res;
9139 int ntups;
9140 int i;
9141 PQExpBuffer query;
9142 TransformInfo *transforminfo;
9143 int i_tableoid;
9144 int i_oid;
9145 int i_trftype;
9146 int i_trflang;
9147 int i_trffromsql;
9148 int i_trftosql;
9149
9150 /* Transforms didn't exist pre-9.5 */
9151 if (fout->remoteVersion < 90500)
9152 return;
9153
9154 query = createPQExpBuffer();
9155
9156 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9157 "trftype, trflang, trffromsql::oid, trftosql::oid "
9158 "FROM pg_transform "
9159 "ORDER BY 3,4");
9160
9161 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9162
9163 ntups = PQntuples(res);
9164
9165 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9166
9167 i_tableoid = PQfnumber(res, "tableoid");
9168 i_oid = PQfnumber(res, "oid");
9169 i_trftype = PQfnumber(res, "trftype");
9170 i_trflang = PQfnumber(res, "trflang");
9171 i_trffromsql = PQfnumber(res, "trffromsql");
9172 i_trftosql = PQfnumber(res, "trftosql");
9173
9174 for (i = 0; i < ntups; i++)
9175 {
9176 PQExpBufferData namebuf;
9177 TypeInfo *typeInfo;
9178 char *lanname;
9179
9180 transforminfo[i].dobj.objType = DO_TRANSFORM;
9181 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9182 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9183 AssignDumpId(&transforminfo[i].dobj);
9184 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9185 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9186 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9187 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9188
9189 /*
9190 * Try to name transform as concatenation of type and language name.
9191 * This is only used for purposes of sorting. If we fail to find
9192 * either, the name will be an empty string.
9193 */
9194 initPQExpBuffer(&namebuf);
9195 typeInfo = findTypeByOid(transforminfo[i].trftype);
9196 lanname = get_language_name(fout, transforminfo[i].trflang);
9197 if (typeInfo && lanname)
9198 appendPQExpBuffer(&namebuf, "%s %s",
9199 typeInfo->dobj.name, lanname);
9200 transforminfo[i].dobj.name = namebuf.data;
9201 free(lanname);
9202
9203 /* Decide whether we want to dump it */
9204 selectDumpableObject(&(transforminfo[i].dobj), fout);
9205 }
9206
9207 PQclear(res);
9208
9209 destroyPQExpBuffer(query);
9210}
9211
9212/*
9213 * getTableAttrs -
9214 * for each interesting table, read info about its attributes
9215 * (names, types, default values, CHECK constraints, etc)
9216 *
9217 * modifies tblinfo
9218 */
9219void
9220getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9221{
9222 DumpOptions *dopt = fout->dopt;
9224 PQExpBuffer tbloids = createPQExpBuffer();
9225 PQExpBuffer checkoids = createPQExpBuffer();
9226 PQExpBuffer invalidnotnulloids = NULL;
9227 PGresult *res;
9228 int ntups;
9229 int curtblindx;
9230 int i_attrelid;
9231 int i_attnum;
9232 int i_attname;
9233 int i_atttypname;
9234 int i_attstattarget;
9235 int i_attstorage;
9236 int i_typstorage;
9237 int i_attidentity;
9238 int i_attgenerated;
9239 int i_attisdropped;
9240 int i_attlen;
9241 int i_attalign;
9242 int i_attislocal;
9243 int i_notnull_name;
9244 int i_notnull_comment;
9245 int i_notnull_noinherit;
9246 int i_notnull_islocal;
9247 int i_notnull_invalidoid;
9248 int i_attoptions;
9249 int i_attcollation;
9250 int i_attcompression;
9251 int i_attfdwoptions;
9252 int i_attmissingval;
9253 int i_atthasdef;
9254
9255 /*
9256 * We want to perform just one query against pg_attribute, and then just
9257 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9258 * (for CHECK constraints and for NOT NULL constraints). However, we
9259 * mustn't try to select every row of those catalogs and then sort it out
9260 * on the client side, because some of the server-side functions we need
9261 * would be unsafe to apply to tables we don't have lock on. Hence, we
9262 * build an array of the OIDs of tables we care about (and now have lock
9263 * on!), and use a WHERE clause to constrain which rows are selected.
9264 */
9265 appendPQExpBufferChar(tbloids, '{');
9266 appendPQExpBufferChar(checkoids, '{');
9267 for (int i = 0; i < numTables; i++)
9268 {
9269 TableInfo *tbinfo = &tblinfo[i];
9270
9271 /* Don't bother to collect info for sequences */
9272 if (tbinfo->relkind == RELKIND_SEQUENCE)
9273 continue;
9274
9275 /*
9276 * Don't bother with uninteresting tables, either. For binary
9277 * upgrades, this is bypassed for pg_largeobject_metadata and
9278 * pg_shdepend so that the columns names are collected for the
9279 * corresponding COPY commands. Restoring the data for those catalogs
9280 * is faster than restoring the equivalent set of large object
9281 * commands. We can only do this for upgrades from v12 and newer; in
9282 * older versions, pg_largeobject_metadata was created WITH OIDS, so
9283 * the OID column is hidden and won't be dumped.
9284 */
9285 if (!tbinfo->interesting &&
9286 !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9287 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9288 tbinfo->dobj.catId.oid == SharedDependRelationId)))
9289 continue;
9290
9291 /* OK, we need info for this table */
9292 if (tbloids->len > 1) /* do we have more than the '{'? */
9293 appendPQExpBufferChar(tbloids, ',');
9294 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9295
9296 if (tbinfo->ncheck > 0)
9297 {
9298 /* Also make a list of the ones with check constraints */
9299 if (checkoids->len > 1) /* do we have more than the '{'? */
9300 appendPQExpBufferChar(checkoids, ',');
9301 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9302 }
9303 }
9304 appendPQExpBufferChar(tbloids, '}');
9305 appendPQExpBufferChar(checkoids, '}');
9306
9307 /*
9308 * Find all the user attributes and their types.
9309 *
9310 * Since we only want to dump COLLATE clauses for attributes whose
9311 * collation is different from their type's default, we use a CASE here to
9312 * suppress uninteresting attcollations cheaply.
9313 */
9315 "SELECT\n"
9316 "a.attrelid,\n"
9317 "a.attnum,\n"
9318 "a.attname,\n"
9319 "a.attstattarget,\n"
9320 "a.attstorage,\n"
9321 "t.typstorage,\n"
9322 "a.atthasdef,\n"
9323 "a.attisdropped,\n"
9324 "a.attlen,\n"
9325 "a.attalign,\n"
9326 "a.attislocal,\n"
9327 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9328 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9329 "CASE WHEN a.attcollation <> t.typcollation "
9330 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9331 "pg_catalog.array_to_string(ARRAY("
9332 "SELECT pg_catalog.quote_ident(option_name) || "
9333 "' ' || pg_catalog.quote_literal(option_value) "
9334 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9335 "ORDER BY option_name"
9336 "), E',\n ') AS attfdwoptions,\n");
9337
9338 /*
9339 * Find out any NOT NULL markings for each column. In 18 and up we read
9340 * pg_constraint to obtain the constraint name, and for valid constraints
9341 * also pg_description to obtain its comment. notnull_noinherit is set
9342 * according to the NO INHERIT property. For versions prior to 18, we
9343 * store an empty string as the name when a constraint is marked as
9344 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9345 * without a name); also, such cases are never NO INHERIT.
9346 *
9347 * For invalid constraints, we need to store their OIDs for processing
9348 * elsewhere, so we bring the pg_constraint.oid value when the constraint
9349 * is invalid, and NULL otherwise. Their comments are handled not here
9350 * but by collectComments, because they're their own dumpable object.
9351 *
9352 * We track in notnull_islocal whether the constraint was defined directly
9353 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9354 * might modify this later.
9355 */
9356 if (fout->remoteVersion >= 180000)
9358 "co.conname AS notnull_name,\n"
9359 "CASE WHEN co.convalidated THEN pt.description"
9360 " ELSE NULL END AS notnull_comment,\n"
9361 "CASE WHEN NOT co.convalidated THEN co.oid "
9362 "ELSE NULL END AS notnull_invalidoid,\n"
9363 "co.connoinherit AS notnull_noinherit,\n"
9364 "co.conislocal AS notnull_islocal,\n");
9365 else
9367 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9368 "NULL AS notnull_comment,\n"
9369 "NULL AS notnull_invalidoid,\n"
9370 "false AS notnull_noinherit,\n"
9371 "CASE WHEN a.attislocal THEN true\n"
9372 " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9373 " ELSE false\n"
9374 "END AS notnull_islocal,\n");
9375
9376 if (fout->remoteVersion >= 140000)
9378 "a.attcompression AS attcompression,\n");
9379 else
9381 "'' AS attcompression,\n");
9382
9383 if (fout->remoteVersion >= 100000)
9385 "a.attidentity,\n");
9386 else
9388 "'' AS attidentity,\n");
9389
9390 if (fout->remoteVersion >= 110000)
9392 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9393 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9394 else
9396 "NULL AS attmissingval,\n");
9397
9398 if (fout->remoteVersion >= 120000)
9400 "a.attgenerated\n");
9401 else
9403 "'' AS attgenerated\n");
9404
9405 /* need left join to pg_type to not fail on dropped columns ... */
9407 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9408 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9409 "LEFT JOIN pg_catalog.pg_type t "
9410 "ON (a.atttypid = t.oid)\n",
9411 tbloids->data);
9412
9413 /*
9414 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9415 * entries and pg_description to get their comments.
9416 */
9417 if (fout->remoteVersion >= 180000)
9419 " LEFT JOIN pg_catalog.pg_constraint co ON "
9420 "(a.attrelid = co.conrelid\n"
9421 " AND co.contype = 'n' AND "
9422 "co.conkey = array[a.attnum])\n"
9423 " LEFT JOIN pg_catalog.pg_description pt ON "
9424 "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9425
9427 "WHERE a.attnum > 0::pg_catalog.int2\n"
9428 "ORDER BY a.attrelid, a.attnum");
9429
9430 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9431
9432 ntups = PQntuples(res);
9433
9434 i_attrelid = PQfnumber(res, "attrelid");
9435 i_attnum = PQfnumber(res, "attnum");
9436 i_attname = PQfnumber(res, "attname");
9437 i_atttypname = PQfnumber(res, "atttypname");
9438 i_attstattarget = PQfnumber(res, "attstattarget");
9439 i_attstorage = PQfnumber(res, "attstorage");
9440 i_typstorage = PQfnumber(res, "typstorage");
9441 i_attidentity = PQfnumber(res, "attidentity");
9442 i_attgenerated = PQfnumber(res, "attgenerated");
9443 i_attisdropped = PQfnumber(res, "attisdropped");
9444 i_attlen = PQfnumber(res, "attlen");
9445 i_attalign = PQfnumber(res, "attalign");
9446 i_attislocal = PQfnumber(res, "attislocal");
9447 i_notnull_name = PQfnumber(res, "notnull_name");
9448 i_notnull_comment = PQfnumber(res, "notnull_comment");
9449 i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9450 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9451 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9452 i_attoptions = PQfnumber(res, "attoptions");
9453 i_attcollation = PQfnumber(res, "attcollation");
9454 i_attcompression = PQfnumber(res, "attcompression");
9455 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9456 i_attmissingval = PQfnumber(res, "attmissingval");
9457 i_atthasdef = PQfnumber(res, "atthasdef");
9458
9459 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9460 resetPQExpBuffer(tbloids);
9461 appendPQExpBufferChar(tbloids, '{');
9462
9463 /*
9464 * Outer loop iterates once per table, not once per row. Incrementing of
9465 * r is handled by the inner loop.
9466 */
9467 curtblindx = -1;
9468 for (int r = 0; r < ntups;)
9469 {
9470 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9471 TableInfo *tbinfo = NULL;
9472 int numatts;
9473 bool hasdefaults;
9474
9475 /* Count rows for this table */
9476 for (numatts = 1; numatts < ntups - r; numatts++)
9477 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9478 break;
9479
9480 /*
9481 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9482 * order.
9483 */
9484 while (++curtblindx < numTables)
9485 {
9486 tbinfo = &tblinfo[curtblindx];
9487 if (tbinfo->dobj.catId.oid == attrelid)
9488 break;
9489 }
9490 if (curtblindx >= numTables)
9491 pg_fatal("unrecognized table OID %u", attrelid);
9492 /* cross-check that we only got requested tables */
9493 if (tbinfo->relkind == RELKIND_SEQUENCE ||
9494 (!tbinfo->interesting &&
9495 !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9496 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9497 tbinfo->dobj.catId.oid == SharedDependRelationId))))
9498 pg_fatal("unexpected column data for table \"%s\"",
9499 tbinfo->dobj.name);
9500
9501 /* Save data for this table */
9502 tbinfo->numatts = numatts;
9503 tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9504 tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9505 tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9506 tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9507 tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9508 tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9509 tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9510 tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9511 tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9512 tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9513 tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9514 tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9515 tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9516 tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9517 tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9518 tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9519 tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9520 tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9521 tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9522 tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9523 tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9524 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9525 hasdefaults = false;
9526
9527 for (int j = 0; j < numatts; j++, r++)
9528 {
9529 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9530 pg_fatal("invalid column numbering in table \"%s\"",
9531 tbinfo->dobj.name);
9532 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9533 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9534 if (PQgetisnull(res, r, i_attstattarget))
9535 tbinfo->attstattarget[j] = -1;
9536 else
9537 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9538 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9539 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9540 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9541 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9542 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9543 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9544 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9545 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9546 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9547
9548 /* Handle not-null constraint name and flags */
9549 determineNotNullFlags(fout, res, r,
9550 tbinfo, j,
9551 i_notnull_name,
9552 i_notnull_comment,
9553 i_notnull_invalidoid,
9554 i_notnull_noinherit,
9555 i_notnull_islocal,
9556 &invalidnotnulloids);
9557
9558 tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9559 NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9560 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9561 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9562 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9563 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9564 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9565 tbinfo->attrdefs[j] = NULL; /* fix below */
9566 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9567 hasdefaults = true;
9568 }
9569
9570 if (hasdefaults)
9571 {
9572 /* Collect OIDs of interesting tables that have defaults */
9573 if (tbloids->len > 1) /* do we have more than the '{'? */
9574 appendPQExpBufferChar(tbloids, ',');
9575 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9576 }
9577 }
9578
9579 /* If invalidnotnulloids has any data, finalize it */
9580 if (invalidnotnulloids != NULL)
9581 appendPQExpBufferChar(invalidnotnulloids, '}');
9582
9583 PQclear(res);
9584
9585 /*
9586 * Now get info about column defaults. This is skipped for a data-only
9587 * dump, as it is only needed for table schemas.
9588 */
9589 if (dopt->dumpSchema && tbloids->len > 1)
9590 {
9591 AttrDefInfo *attrdefs;
9592 int numDefaults;
9593 TableInfo *tbinfo = NULL;
9594
9595 pg_log_info("finding table default expressions");
9596
9597 appendPQExpBufferChar(tbloids, '}');
9598
9599 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9600 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9601 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9602 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9603 "ORDER BY a.adrelid, a.adnum",
9604 tbloids->data);
9605
9606 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9607
9608 numDefaults = PQntuples(res);
9609 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9610
9611 curtblindx = -1;
9612 for (int j = 0; j < numDefaults; j++)
9613 {
9614 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9615 Oid adoid = atooid(PQgetvalue(res, j, 1));
9616 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9617 int adnum = atoi(PQgetvalue(res, j, 3));
9618 char *adsrc = PQgetvalue(res, j, 4);
9619
9620 /*
9621 * Locate the associated TableInfo; we rely on tblinfo[] being in
9622 * OID order.
9623 */
9624 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9625 {
9626 while (++curtblindx < numTables)
9627 {
9628 tbinfo = &tblinfo[curtblindx];
9629 if (tbinfo->dobj.catId.oid == adrelid)
9630 break;
9631 }
9632 if (curtblindx >= numTables)
9633 pg_fatal("unrecognized table OID %u", adrelid);
9634 }
9635
9636 if (adnum <= 0 || adnum > tbinfo->numatts)
9637 pg_fatal("invalid adnum value %d for table \"%s\"",
9638 adnum, tbinfo->dobj.name);
9639
9640 /*
9641 * dropped columns shouldn't have defaults, but just in case,
9642 * ignore 'em
9643 */
9644 if (tbinfo->attisdropped[adnum - 1])
9645 continue;
9646
9647 attrdefs[j].dobj.objType = DO_ATTRDEF;
9648 attrdefs[j].dobj.catId.tableoid = adtableoid;
9649 attrdefs[j].dobj.catId.oid = adoid;
9650 AssignDumpId(&attrdefs[j].dobj);
9651 attrdefs[j].adtable = tbinfo;
9652 attrdefs[j].adnum = adnum;
9653 attrdefs[j].adef_expr = pg_strdup(adsrc);
9654
9655 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9656 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9657
9658 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9659
9660 /*
9661 * Figure out whether the default/generation expression should be
9662 * dumped as part of the main CREATE TABLE (or similar) command or
9663 * as a separate ALTER TABLE (or similar) command. The preference
9664 * is to put it into the CREATE command, but in some cases that's
9665 * not possible.
9666 */
9667 if (tbinfo->attgenerated[adnum - 1])
9668 {
9669 /*
9670 * Column generation expressions cannot be dumped separately,
9671 * because there is no syntax for it. By setting separate to
9672 * false here we prevent the "default" from being processed as
9673 * its own dumpable object. Later, flagInhAttrs() will mark
9674 * it as not to be dumped at all, if possible (that is, if it
9675 * can be inherited from a parent).
9676 */
9677 attrdefs[j].separate = false;
9678 }
9679 else if (tbinfo->relkind == RELKIND_VIEW)
9680 {
9681 /*
9682 * Defaults on a VIEW must always be dumped as separate ALTER
9683 * TABLE commands.
9684 */
9685 attrdefs[j].separate = true;
9686 }
9687 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9688 {
9689 /* column will be suppressed, print default separately */
9690 attrdefs[j].separate = true;
9691 }
9692 else
9693 {
9694 attrdefs[j].separate = false;
9695 }
9696
9697 if (!attrdefs[j].separate)
9698 {
9699 /*
9700 * Mark the default as needing to appear before the table, so
9701 * that any dependencies it has must be emitted before the
9702 * CREATE TABLE. If this is not possible, we'll change to
9703 * "separate" mode while sorting dependencies.
9704 */
9705 addObjectDependency(&tbinfo->dobj,
9706 attrdefs[j].dobj.dumpId);
9707 }
9708
9709 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9710 }
9711
9712 PQclear(res);
9713 }
9714
9715 /*
9716 * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9717 * data-only dump, as it is only needed for table schemas.
9718 */
9719 if (dopt->dumpSchema && invalidnotnulloids)
9720 {
9721 ConstraintInfo *constrs;
9722 int numConstrs;
9723 int i_tableoid;
9724 int i_oid;
9725 int i_conrelid;
9726 int i_conname;
9727 int i_consrc;
9728 int i_conislocal;
9729
9730 pg_log_info("finding invalid not-null constraints");
9731
9734 "SELECT c.tableoid, c.oid, conrelid, conname, "
9735 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9736 "conislocal, convalidated "
9737 "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9738 "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9739 "ORDER BY c.conrelid, c.conname",
9740 invalidnotnulloids->data);
9741
9742 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9743
9744 numConstrs = PQntuples(res);
9745 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9746
9747 i_tableoid = PQfnumber(res, "tableoid");
9748 i_oid = PQfnumber(res, "oid");
9749 i_conrelid = PQfnumber(res, "conrelid");
9750 i_conname = PQfnumber(res, "conname");
9751 i_consrc = PQfnumber(res, "consrc");
9752 i_conislocal = PQfnumber(res, "conislocal");
9753
9754 /* As above, this loop iterates once per table, not once per row */
9755 curtblindx = -1;
9756 for (int j = 0; j < numConstrs;)
9757 {
9758 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9759 TableInfo *tbinfo = NULL;
9760 int numcons;
9761
9762 /* Count rows for this table */
9763 for (numcons = 1; numcons < numConstrs - j; numcons++)
9764 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9765 break;
9766
9767 /*
9768 * Locate the associated TableInfo; we rely on tblinfo[] being in
9769 * OID order.
9770 */
9771 while (++curtblindx < numTables)
9772 {
9773 tbinfo = &tblinfo[curtblindx];
9774 if (tbinfo->dobj.catId.oid == conrelid)
9775 break;
9776 }
9777 if (curtblindx >= numTables)
9778 pg_fatal("unrecognized table OID %u", conrelid);
9779
9780 for (int c = 0; c < numcons; c++, j++)
9781 {
9782 constrs[j].dobj.objType = DO_CONSTRAINT;
9783 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9784 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9785 AssignDumpId(&constrs[j].dobj);
9786 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9787 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9788 constrs[j].contable = tbinfo;
9789 constrs[j].condomain = NULL;
9790 constrs[j].contype = 'n';
9791 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9792 constrs[j].confrelid = InvalidOid;
9793 constrs[j].conindex = 0;
9794 constrs[j].condeferrable = false;
9795 constrs[j].condeferred = false;
9796 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9797
9798 /*
9799 * All invalid not-null constraints must be dumped separately,
9800 * because CREATE TABLE would not create them as invalid, and
9801 * also because they must be created after potentially
9802 * violating data has been loaded.
9803 */
9804 constrs[j].separate = true;
9805
9806 constrs[j].dobj.dump = tbinfo->dobj.dump;
9807 }
9808 }
9809 PQclear(res);
9810 }
9811
9812 /*
9813 * Get info about table CHECK constraints. This is skipped for a
9814 * data-only dump, as it is only needed for table schemas.
9815 */
9816 if (dopt->dumpSchema && checkoids->len > 2)
9817 {
9818 ConstraintInfo *constrs;
9819 int numConstrs;
9820 int i_tableoid;
9821 int i_oid;
9822 int i_conrelid;
9823 int i_conname;
9824 int i_consrc;
9825 int i_conislocal;
9826 int i_convalidated;
9827
9828 pg_log_info("finding table check constraints");
9829
9832 "SELECT c.tableoid, c.oid, conrelid, conname, "
9833 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9834 "conislocal, convalidated "
9835 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9836 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9837 "WHERE contype = 'c' "
9838 "ORDER BY c.conrelid, c.conname",
9839 checkoids->data);
9840
9841 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9842
9843 numConstrs = PQntuples(res);
9844 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9845
9846 i_tableoid = PQfnumber(res, "tableoid");
9847 i_oid = PQfnumber(res, "oid");
9848 i_conrelid = PQfnumber(res, "conrelid");
9849 i_conname = PQfnumber(res, "conname");
9850 i_consrc = PQfnumber(res, "consrc");
9851 i_conislocal = PQfnumber(res, "conislocal");
9852 i_convalidated = PQfnumber(res, "convalidated");
9853
9854 /* As above, this loop iterates once per table, not once per row */
9855 curtblindx = -1;
9856 for (int j = 0; j < numConstrs;)
9857 {
9858 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9859 TableInfo *tbinfo = NULL;
9860 int numcons;
9861
9862 /* Count rows for this table */
9863 for (numcons = 1; numcons < numConstrs - j; numcons++)
9864 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9865 break;
9866
9867 /*
9868 * Locate the associated TableInfo; we rely on tblinfo[] being in
9869 * OID order.
9870 */
9871 while (++curtblindx < numTables)
9872 {
9873 tbinfo = &tblinfo[curtblindx];
9874 if (tbinfo->dobj.catId.oid == conrelid)
9875 break;
9876 }
9877 if (curtblindx >= numTables)
9878 pg_fatal("unrecognized table OID %u", conrelid);
9879
9880 if (numcons != tbinfo->ncheck)
9881 {
9882 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9883 "expected %d check constraints on table \"%s\" but found %d",
9884 tbinfo->ncheck),
9885 tbinfo->ncheck, tbinfo->dobj.name, numcons);
9886 pg_log_error_hint("The system catalogs might be corrupted.");
9887 exit_nicely(1);
9888 }
9889
9890 tbinfo->checkexprs = constrs + j;
9891
9892 for (int c = 0; c < numcons; c++, j++)
9893 {
9894 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9895
9896 constrs[j].dobj.objType = DO_CONSTRAINT;
9897 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9898 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9899 AssignDumpId(&constrs[j].dobj);
9900 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9901 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9902 constrs[j].contable = tbinfo;
9903 constrs[j].condomain = NULL;
9904 constrs[j].contype = 'c';
9905 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9906 constrs[j].confrelid = InvalidOid;
9907 constrs[j].conindex = 0;
9908 constrs[j].condeferrable = false;
9909 constrs[j].condeferred = false;
9910 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9911
9912 /*
9913 * An unvalidated constraint needs to be dumped separately, so
9914 * that potentially-violating existing data is loaded before
9915 * the constraint.
9916 */
9917 constrs[j].separate = !validated;
9918
9919 constrs[j].dobj.dump = tbinfo->dobj.dump;
9920
9921 /*
9922 * Mark the constraint as needing to appear before the table
9923 * --- this is so that any other dependencies of the
9924 * constraint will be emitted before we try to create the
9925 * table. If the constraint is to be dumped separately, it
9926 * will be dumped after data is loaded anyway, so don't do it.
9927 * (There's an automatic dependency in the opposite direction
9928 * anyway, so don't need to add one manually here.)
9929 */
9930 if (!constrs[j].separate)
9931 addObjectDependency(&tbinfo->dobj,
9932 constrs[j].dobj.dumpId);
9933
9934 /*
9935 * We will detect later whether the constraint must be split
9936 * out from the table definition.
9937 */
9938 }
9939 }
9940
9941 PQclear(res);
9942 }
9943
9945 destroyPQExpBuffer(tbloids);
9946 destroyPQExpBuffer(checkoids);
9947}
9948
9949/*
9950 * Based on the getTableAttrs query's row corresponding to one column, set
9951 * the name and flags to handle a not-null constraint for that column in
9952 * the tbinfo struct.
9953 *
9954 * Result row 'r' is for tbinfo's attribute 'j'.
9955 *
9956 * There are four possibilities:
9957 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9958 * (the constraint name) remains NULL.
9959 * 2) The column has a constraint with no name (this is the case when
9960 * constraints come from pre-18 servers). In this case, ->notnull_constrs
9961 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9962 * 3) The column has an invalid not-null constraint. This must be treated
9963 * as a separate object (because it must be created after the table data
9964 * is loaded). So we add its OID to invalidnotnulloids for processing
9965 * elsewhere and do nothing further with it here. We distinguish this
9966 * case because the "notnull_invalidoid" column has been set to a non-NULL
9967 * value, which is the constraint OID. Valid constraints have a null OID.
9968 * 4) The column has a constraint with a known name; in that case
9969 * notnull_constrs carries that name and dumpTableSchema will print
9970 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9971 * (table_column_not_null) and there's no comment on the constraint,
9972 * there's no need to print that name in the dump, so notnull_constrs
9973 * is set to the empty string and it behaves as case 2.
9974 *
9975 * In a child table that inherits from a parent already containing NOT NULL
9976 * constraints and the columns in the child don't have their own NOT NULL
9977 * declarations, we suppress printing constraints in the child: the
9978 * constraints are acquired at the point where the child is attached to the
9979 * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9980 * set not here but in flagInhAttrs. That flag is also used when the
9981 * constraint was validated in a child but all its parent have it as NOT
9982 * VALID.
9983 *
9984 * Any of these constraints might have the NO INHERIT bit. If so we set
9985 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9986 *
9987 * In case 4 above, the name comparison is a bit of a hack; it actually fails
9988 * to do the right thing in all but the trivial case. However, the downside
9989 * of getting it wrong is simply that the name is printed rather than
9990 * suppressed, so it's not a big deal.
9991 *
9992 * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9993 * constraints are found, it is initialized and filled with the array of
9994 * OIDs of such constraints, for later processing.
9995 */
9996static void
9998 TableInfo *tbinfo, int j,
9999 int i_notnull_name,
10000 int i_notnull_comment,
10001 int i_notnull_invalidoid,
10002 int i_notnull_noinherit,
10003 int i_notnull_islocal,
10004 PQExpBuffer *invalidnotnulloids)
10005{
10006 DumpOptions *dopt = fout->dopt;
10007
10008 /*
10009 * If this not-null constraint is not valid, list its OID in
10010 * invalidnotnulloids and do nothing further. It'll be processed
10011 * elsewhere later.
10012 *
10013 * Because invalid not-null constraints are rare, we don't want to malloc
10014 * invalidnotnulloids until we're sure we're going it need it, which
10015 * happens here.
10016 */
10017 if (!PQgetisnull(res, r, i_notnull_invalidoid))
10018 {
10019 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10020
10021 if (*invalidnotnulloids == NULL)
10022 {
10023 *invalidnotnulloids = createPQExpBuffer();
10024 appendPQExpBufferChar(*invalidnotnulloids, '{');
10025 appendPQExpBufferStr(*invalidnotnulloids, constroid);
10026 }
10027 else
10028 appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10029
10030 /*
10031 * Track when a parent constraint is invalid for the cases where a
10032 * child constraint has been validated independenly.
10033 */
10034 tbinfo->notnull_invalid[j] = true;
10035
10036 /* nothing else to do */
10037 tbinfo->notnull_constrs[j] = NULL;
10038 return;
10039 }
10040
10041 /*
10042 * notnull_noinh is straight from the query result. notnull_islocal also,
10043 * though flagInhAttrs may change that one later.
10044 */
10045 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10046 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10047 tbinfo->notnull_invalid[j] = false;
10048
10049 /*
10050 * Determine a constraint name to use. If the column is not marked not-
10051 * null, we set NULL which cues ... to do nothing. An empty string says
10052 * to print an unnamed NOT NULL, and anything else is a constraint name to
10053 * use.
10054 */
10055 if (fout->remoteVersion < 180000)
10056 {
10057 /*
10058 * < 18 doesn't have not-null names, so an unnamed constraint is
10059 * sufficient.
10060 */
10061 if (PQgetisnull(res, r, i_notnull_name))
10062 tbinfo->notnull_constrs[j] = NULL;
10063 else
10064 tbinfo->notnull_constrs[j] = "";
10065 }
10066 else
10067 {
10068 if (PQgetisnull(res, r, i_notnull_name))
10069 tbinfo->notnull_constrs[j] = NULL;
10070 else
10071 {
10072 /*
10073 * In binary upgrade of inheritance child tables, must have a
10074 * constraint name that we can UPDATE later; same if there's a
10075 * comment on the constraint.
10076 */
10077 if ((dopt->binary_upgrade &&
10078 !tbinfo->ispartition &&
10079 !tbinfo->notnull_islocal) ||
10080 !PQgetisnull(res, r, i_notnull_comment))
10081 {
10082 tbinfo->notnull_constrs[j] =
10083 pstrdup(PQgetvalue(res, r, i_notnull_name));
10084 }
10085 else
10086 {
10087 char *default_name;
10088
10089 /* XXX should match ChooseConstraintName better */
10090 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10091 tbinfo->attnames[j]);
10092 if (strcmp(default_name,
10093 PQgetvalue(res, r, i_notnull_name)) == 0)
10094 tbinfo->notnull_constrs[j] = "";
10095 else
10096 {
10097 tbinfo->notnull_constrs[j] =
10098 pstrdup(PQgetvalue(res, r, i_notnull_name));
10099 }
10100 free(default_name);
10101 }
10102 }
10103 }
10104}
10105
10106/*
10107 * Test whether a column should be printed as part of table's CREATE TABLE.
10108 * Column number is zero-based.
10109 *
10110 * Normally this is always true, but it's false for dropped columns, as well
10111 * as those that were inherited without any local definition. (If we print
10112 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10113 * For partitions, it's always true, because we want the partitions to be
10114 * created independently and ATTACH PARTITION used afterwards.
10115 *
10116 * In binary_upgrade mode, we must print all columns and fix the attislocal/
10117 * attisdropped state later, so as to keep control of the physical column
10118 * order.
10119 *
10120 * This function exists because there are scattered nonobvious places that
10121 * must be kept in sync with this decision.
10122 */
10123bool
10124shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10125{
10126 if (dopt->binary_upgrade)
10127 return true;
10128 if (tbinfo->attisdropped[colno])
10129 return false;
10130 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10131}
10132
10133
10134/*
10135 * getTSParsers:
10136 * get information about all text search parsers in the system catalogs
10137 */
10138void
10140{
10141 PGresult *res;
10142 int ntups;
10143 int i;
10144 PQExpBuffer query;
10145 TSParserInfo *prsinfo;
10146 int i_tableoid;
10147 int i_oid;
10148 int i_prsname;
10149 int i_prsnamespace;
10150 int i_prsstart;
10151 int i_prstoken;
10152 int i_prsend;
10153 int i_prsheadline;
10154 int i_prslextype;
10155
10156 query = createPQExpBuffer();
10157
10158 /*
10159 * find all text search objects, including builtin ones; we filter out
10160 * system-defined objects at dump-out time.
10161 */
10162
10163 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10164 "prsstart::oid, prstoken::oid, "
10165 "prsend::oid, prsheadline::oid, prslextype::oid "
10166 "FROM pg_ts_parser");
10167
10168 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10169
10170 ntups = PQntuples(res);
10171
10172 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10173
10174 i_tableoid = PQfnumber(res, "tableoid");
10175 i_oid = PQfnumber(res, "oid");
10176 i_prsname = PQfnumber(res, "prsname");
10177 i_prsnamespace = PQfnumber(res, "prsnamespace");
10178 i_prsstart = PQfnumber(res, "prsstart");
10179 i_prstoken = PQfnumber(res, "prstoken");
10180 i_prsend = PQfnumber(res, "prsend");
10181 i_prsheadline = PQfnumber(res, "prsheadline");
10182 i_prslextype = PQfnumber(res, "prslextype");
10183
10184 for (i = 0; i < ntups; i++)
10185 {
10186 prsinfo[i].dobj.objType = DO_TSPARSER;
10187 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10188 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10189 AssignDumpId(&prsinfo[i].dobj);
10190 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10191 prsinfo[i].dobj.namespace =
10192 findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10193 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10194 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10195 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10196 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10197 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10198
10199 /* Decide whether we want to dump it */
10200 selectDumpableObject(&(prsinfo[i].dobj), fout);
10201 }
10202
10203 PQclear(res);
10204
10205 destroyPQExpBuffer(query);
10206}
10207
10208/*
10209 * getTSDictionaries:
10210 * get information about all text search dictionaries in the system catalogs
10211 */
10212void
10214{
10215 PGresult *res;
10216 int ntups;
10217 int i;
10218 PQExpBuffer query;
10219 TSDictInfo *dictinfo;
10220 int i_tableoid;
10221 int i_oid;
10222 int i_dictname;
10223 int i_dictnamespace;
10224 int i_dictowner;
10225 int i_dicttemplate;
10226 int i_dictinitoption;
10227
10228 query = createPQExpBuffer();
10229
10230 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10231 "dictnamespace, dictowner, "
10232 "dicttemplate, dictinitoption "
10233 "FROM pg_ts_dict");
10234
10235 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10236
10237 ntups = PQntuples(res);
10238
10239 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10240
10241 i_tableoid = PQfnumber(res, "tableoid");
10242 i_oid = PQfnumber(res, "oid");
10243 i_dictname = PQfnumber(res, "dictname");
10244 i_dictnamespace = PQfnumber(res, "dictnamespace");
10245 i_dictowner = PQfnumber(res, "dictowner");
10246 i_dictinitoption = PQfnumber(res, "dictinitoption");
10247 i_dicttemplate = PQfnumber(res, "dicttemplate");
10248
10249 for (i = 0; i < ntups; i++)
10250 {
10251 dictinfo[i].dobj.objType = DO_TSDICT;
10252 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10253 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10254 AssignDumpId(&dictinfo[i].dobj);
10255 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10256 dictinfo[i].dobj.namespace =
10257 findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10258 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10259 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10260 if (PQgetisnull(res, i, i_dictinitoption))
10261 dictinfo[i].dictinitoption = NULL;
10262 else
10263 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10264
10265 /* Decide whether we want to dump it */
10266 selectDumpableObject(&(dictinfo[i].dobj), fout);
10267 }
10268
10269 PQclear(res);
10270
10271 destroyPQExpBuffer(query);
10272}
10273
10274/*
10275 * getTSTemplates:
10276 * get information about all text search templates in the system catalogs
10277 */
10278void
10280{
10281 PGresult *res;
10282 int ntups;
10283 int i;
10284 PQExpBuffer query;
10285 TSTemplateInfo *tmplinfo;
10286 int i_tableoid;
10287 int i_oid;
10288 int i_tmplname;
10289 int i_tmplnamespace;
10290 int i_tmplinit;
10291 int i_tmpllexize;
10292
10293 query = createPQExpBuffer();
10294
10295 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10296 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10297 "FROM pg_ts_template");
10298
10299 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10300
10301 ntups = PQntuples(res);
10302
10303 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10304
10305 i_tableoid = PQfnumber(res, "tableoid");
10306 i_oid = PQfnumber(res, "oid");
10307 i_tmplname = PQfnumber(res, "tmplname");
10308 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10309 i_tmplinit = PQfnumber(res, "tmplinit");
10310 i_tmpllexize = PQfnumber(res, "tmpllexize");
10311
10312 for (i = 0; i < ntups; i++)
10313 {
10314 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10315 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10316 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10317 AssignDumpId(&tmplinfo[i].dobj);
10318 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10319 tmplinfo[i].dobj.namespace =
10320 findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10321 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10322 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10323
10324 /* Decide whether we want to dump it */
10325 selectDumpableObject(&(tmplinfo[i].dobj), fout);
10326 }
10327
10328 PQclear(res);
10329
10330 destroyPQExpBuffer(query);
10331}
10332
10333/*
10334 * getTSConfigurations:
10335 * get information about all text search configurations
10336 */
10337void
10339{
10340 PGresult *res;
10341 int ntups;
10342 int i;
10343 PQExpBuffer query;
10344 TSConfigInfo *cfginfo;
10345 int i_tableoid;
10346 int i_oid;
10347 int i_cfgname;
10348 int i_cfgnamespace;
10349 int i_cfgowner;
10350 int i_cfgparser;
10351
10352 query = createPQExpBuffer();
10353
10354 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10355 "cfgnamespace, cfgowner, cfgparser "
10356 "FROM pg_ts_config");
10357
10358 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10359
10360 ntups = PQntuples(res);
10361
10362 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10363
10364 i_tableoid = PQfnumber(res, "tableoid");
10365 i_oid = PQfnumber(res, "oid");
10366 i_cfgname = PQfnumber(res, "cfgname");
10367 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10368 i_cfgowner = PQfnumber(res, "cfgowner");
10369 i_cfgparser = PQfnumber(res, "cfgparser");
10370
10371 for (i = 0; i < ntups; i++)
10372 {
10373 cfginfo[i].dobj.objType = DO_TSCONFIG;
10374 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10375 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10376 AssignDumpId(&cfginfo[i].dobj);
10377 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10378 cfginfo[i].dobj.namespace =
10379 findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10380 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10381 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10382
10383 /* Decide whether we want to dump it */
10384 selectDumpableObject(&(cfginfo[i].dobj), fout);
10385 }
10386
10387 PQclear(res);
10388
10389 destroyPQExpBuffer(query);
10390}
10391
10392/*
10393 * getForeignDataWrappers:
10394 * get information about all foreign-data wrappers in the system catalogs
10395 */
10396void
10398{
10399 PGresult *res;
10400 int ntups;
10401 int i;
10402 PQExpBuffer query;
10403 FdwInfo *fdwinfo;
10404 int i_tableoid;
10405 int i_oid;
10406 int i_fdwname;
10407 int i_fdwowner;
10408 int i_fdwhandler;
10409 int i_fdwvalidator;
10410 int i_fdwacl;
10411 int i_acldefault;
10412 int i_fdwoptions;
10413
10414 query = createPQExpBuffer();
10415
10416 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10417 "fdwowner, "
10418 "fdwhandler::pg_catalog.regproc, "
10419 "fdwvalidator::pg_catalog.regproc, "
10420 "fdwacl, "
10421 "acldefault('F', fdwowner) AS acldefault, "
10422 "array_to_string(ARRAY("
10423 "SELECT quote_ident(option_name) || ' ' || "
10424 "quote_literal(option_value) "
10425 "FROM pg_options_to_table(fdwoptions) "
10426 "ORDER BY option_name"
10427 "), E',\n ') AS fdwoptions "
10428 "FROM pg_foreign_data_wrapper");
10429
10430 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10431
10432 ntups = PQntuples(res);
10433
10434 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10435
10436 i_tableoid = PQfnumber(res, "tableoid");
10437 i_oid = PQfnumber(res, "oid");
10438 i_fdwname = PQfnumber(res, "fdwname");
10439 i_fdwowner = PQfnumber(res, "fdwowner");
10440 i_fdwhandler = PQfnumber(res, "fdwhandler");
10441 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10442 i_fdwacl = PQfnumber(res, "fdwacl");
10443 i_acldefault = PQfnumber(res, "acldefault");
10444 i_fdwoptions = PQfnumber(res, "fdwoptions");
10445
10446 for (i = 0; i < ntups; i++)
10447 {
10448 fdwinfo[i].dobj.objType = DO_FDW;
10449 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10450 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10451 AssignDumpId(&fdwinfo[i].dobj);
10452 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10453 fdwinfo[i].dobj.namespace = NULL;
10454 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10455 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10456 fdwinfo[i].dacl.privtype = 0;
10457 fdwinfo[i].dacl.initprivs = NULL;
10458 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10459 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10460 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10461 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10462
10463 /* Decide whether we want to dump it */
10464 selectDumpableObject(&(fdwinfo[i].dobj), fout);
10465
10466 /* Mark whether FDW has an ACL */
10467 if (!PQgetisnull(res, i, i_fdwacl))
10468 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10469 }
10470
10471 PQclear(res);
10472
10473 destroyPQExpBuffer(query);
10474}
10475
10476/*
10477 * getForeignServers:
10478 * get information about all foreign servers in the system catalogs
10479 */
10480void
10482{
10483 PGresult *res;
10484 int ntups;
10485 int i;
10486 PQExpBuffer query;
10487 ForeignServerInfo *srvinfo;
10488 int i_tableoid;
10489 int i_oid;
10490 int i_srvname;
10491 int i_srvowner;
10492 int i_srvfdw;
10493 int i_srvtype;
10494 int i_srvversion;
10495 int i_srvacl;
10496 int i_acldefault;
10497 int i_srvoptions;
10498
10499 query = createPQExpBuffer();
10500
10501 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10502 "srvowner, "
10503 "srvfdw, srvtype, srvversion, srvacl, "
10504 "acldefault('S', srvowner) AS acldefault, "
10505 "array_to_string(ARRAY("
10506 "SELECT quote_ident(option_name) || ' ' || "
10507 "quote_literal(option_value) "
10508 "FROM pg_options_to_table(srvoptions) "
10509 "ORDER BY option_name"
10510 "), E',\n ') AS srvoptions "
10511 "FROM pg_foreign_server");
10512
10513 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10514
10515 ntups = PQntuples(res);
10516
10517 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10518
10519 i_tableoid = PQfnumber(res, "tableoid");
10520 i_oid = PQfnumber(res, "oid");
10521 i_srvname = PQfnumber(res, "srvname");
10522 i_srvowner = PQfnumber(res, "srvowner");
10523 i_srvfdw = PQfnumber(res, "srvfdw");
10524 i_srvtype = PQfnumber(res, "srvtype");
10525 i_srvversion = PQfnumber(res, "srvversion");
10526 i_srvacl = PQfnumber(res, "srvacl");
10527 i_acldefault = PQfnumber(res, "acldefault");
10528 i_srvoptions = PQfnumber(res, "srvoptions");
10529
10530 for (i = 0; i < ntups; i++)
10531 {
10532 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10533 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10534 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10535 AssignDumpId(&srvinfo[i].dobj);
10536 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10537 srvinfo[i].dobj.namespace = NULL;
10538 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10539 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10540 srvinfo[i].dacl.privtype = 0;
10541 srvinfo[i].dacl.initprivs = NULL;
10542 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10543 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10544 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10545 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10546 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10547
10548 /* Decide whether we want to dump it */
10549 selectDumpableObject(&(srvinfo[i].dobj), fout);
10550
10551 /* Servers have user mappings */
10553
10554 /* Mark whether server has an ACL */
10555 if (!PQgetisnull(res, i, i_srvacl))
10556 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10557 }
10558
10559 PQclear(res);
10560
10561 destroyPQExpBuffer(query);
10562}
10563
10564/*
10565 * getDefaultACLs:
10566 * get information about all default ACL information in the system catalogs
10567 */
10568void
10570{
10571 DumpOptions *dopt = fout->dopt;
10572 DefaultACLInfo *daclinfo;
10573 PQExpBuffer query;
10574 PGresult *res;
10575 int i_oid;
10576 int i_tableoid;
10577 int i_defaclrole;
10578 int i_defaclnamespace;
10579 int i_defaclobjtype;
10580 int i_defaclacl;
10581 int i_acldefault;
10582 int i,
10583 ntups;
10584
10585 query = createPQExpBuffer();
10586
10587 /*
10588 * Global entries (with defaclnamespace=0) replace the hard-wired default
10589 * ACL for their object type. We should dump them as deltas from the
10590 * default ACL, since that will be used as a starting point for
10591 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10592 * non-global entries can only add privileges not revoke them. We must
10593 * dump those as-is (i.e., as deltas from an empty ACL).
10594 *
10595 * We can use defaclobjtype as the object type for acldefault(), except
10596 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10597 * 's'.
10598 */
10600 "SELECT oid, tableoid, "
10601 "defaclrole, "
10602 "defaclnamespace, "
10603 "defaclobjtype, "
10604 "defaclacl, "
10605 "CASE WHEN defaclnamespace = 0 THEN "
10606 "acldefault(CASE WHEN defaclobjtype = 'S' "
10607 "THEN 's'::\"char\" ELSE defaclobjtype END, "
10608 "defaclrole) ELSE '{}' END AS acldefault "
10609 "FROM pg_default_acl");
10610
10611 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10612
10613 ntups = PQntuples(res);
10614
10615 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10616
10617 i_oid = PQfnumber(res, "oid");
10618 i_tableoid = PQfnumber(res, "tableoid");
10619 i_defaclrole = PQfnumber(res, "defaclrole");
10620 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10621 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10622 i_defaclacl = PQfnumber(res, "defaclacl");
10623 i_acldefault = PQfnumber(res, "acldefault");
10624
10625 for (i = 0; i < ntups; i++)
10626 {
10627 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10628
10629 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10630 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10631 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10632 AssignDumpId(&daclinfo[i].dobj);
10633 /* cheesy ... is it worth coming up with a better object name? */
10634 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10635
10636 if (nspid != InvalidOid)
10637 daclinfo[i].dobj.namespace = findNamespace(nspid);
10638 else
10639 daclinfo[i].dobj.namespace = NULL;
10640
10641 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10642 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10643 daclinfo[i].dacl.privtype = 0;
10644 daclinfo[i].dacl.initprivs = NULL;
10645 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10646 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10647
10648 /* Default ACLs are ACLs, of course */
10649 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10650
10651 /* Decide whether we want to dump it */
10652 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10653 }
10654
10655 PQclear(res);
10656
10657 destroyPQExpBuffer(query);
10658}
10659
10660/*
10661 * getRoleName -- look up the name of a role, given its OID
10662 *
10663 * In current usage, we don't expect failures, so error out for a bad OID.
10664 */
10665static const char *
10666getRoleName(const char *roleoid_str)
10667{
10668 Oid roleoid = atooid(roleoid_str);
10669
10670 /*
10671 * Do binary search to find the appropriate item.
10672 */
10673 if (nrolenames > 0)
10674 {
10675 RoleNameItem *low = &rolenames[0];
10676 RoleNameItem *high = &rolenames[nrolenames - 1];
10677
10678 while (low <= high)
10679 {
10680 RoleNameItem *middle = low + (high - low) / 2;
10681
10682 if (roleoid < middle->roleoid)
10683 high = middle - 1;
10684 else if (roleoid > middle->roleoid)
10685 low = middle + 1;
10686 else
10687 return middle->rolename; /* found a match */
10688 }
10689 }
10690
10691 pg_fatal("role with OID %u does not exist", roleoid);
10692 return NULL; /* keep compiler quiet */
10693}
10694
10695/*
10696 * collectRoleNames --
10697 *
10698 * Construct a table of all known roles.
10699 * The table is sorted by OID for speed in lookup.
10700 */
10701static void
10703{
10704 PGresult *res;
10705 const char *query;
10706 int i;
10707
10708 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10709
10710 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10711
10712 nrolenames = PQntuples(res);
10713
10715
10716 for (i = 0; i < nrolenames; i++)
10717 {
10718 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10720 }
10721
10722 PQclear(res);
10723}
10724
10725/*
10726 * getAdditionalACLs
10727 *
10728 * We have now created all the DumpableObjects, and collected the ACL data
10729 * that appears in the directly-associated catalog entries. However, there's
10730 * more ACL-related info to collect. If any of a table's columns have ACLs,
10731 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10732 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10733 * Also, in versions having the pg_init_privs catalog, read that and load the
10734 * information into the relevant DumpableObjects.
10735 */
10736static void
10738{
10740 PGresult *res;
10741 int ntups,
10742 i;
10743
10744 /* Check for per-column ACLs */
10746 "SELECT DISTINCT attrelid FROM pg_attribute "
10747 "WHERE attacl IS NOT NULL");
10748
10749 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10750
10751 ntups = PQntuples(res);
10752 for (i = 0; i < ntups; i++)
10753 {
10754 Oid relid = atooid(PQgetvalue(res, i, 0));
10755 TableInfo *tblinfo;
10756
10757 tblinfo = findTableByOid(relid);
10758 /* OK to ignore tables we haven't got a DumpableObject for */
10759 if (tblinfo)
10760 {
10762 tblinfo->hascolumnACLs = true;
10763 }
10764 }
10765 PQclear(res);
10766
10767 /* Fetch initial-privileges data */
10768 if (fout->remoteVersion >= 90600)
10769 {
10770 printfPQExpBuffer(query,
10771 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10772 "FROM pg_init_privs");
10773
10774 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10775
10776 ntups = PQntuples(res);
10777 for (i = 0; i < ntups; i++)
10778 {
10779 Oid objoid = atooid(PQgetvalue(res, i, 0));
10780 Oid classoid = atooid(PQgetvalue(res, i, 1));
10781 int objsubid = atoi(PQgetvalue(res, i, 2));
10782 char privtype = *(PQgetvalue(res, i, 3));
10783 char *initprivs = PQgetvalue(res, i, 4);
10784 CatalogId objId;
10785 DumpableObject *dobj;
10786
10787 objId.tableoid = classoid;
10788 objId.oid = objoid;
10789 dobj = findObjectByCatalogId(objId);
10790 /* OK to ignore entries we haven't got a DumpableObject for */
10791 if (dobj)
10792 {
10793 /* Cope with sub-object initprivs */
10794 if (objsubid != 0)
10795 {
10796 if (dobj->objType == DO_TABLE)
10797 {
10798 /* For a column initprivs, set the table's ACL flags */
10800 ((TableInfo *) dobj)->hascolumnACLs = true;
10801 }
10802 else
10803 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10804 classoid, objoid, objsubid);
10805 continue;
10806 }
10807
10808 /*
10809 * We ignore any pg_init_privs.initprivs entry for the public
10810 * schema, as explained in getNamespaces().
10811 */
10812 if (dobj->objType == DO_NAMESPACE &&
10813 strcmp(dobj->name, "public") == 0)
10814 continue;
10815
10816 /* Else it had better be of a type we think has ACLs */
10817 if (dobj->objType == DO_NAMESPACE ||
10818 dobj->objType == DO_TYPE ||
10819 dobj->objType == DO_FUNC ||
10820 dobj->objType == DO_AGG ||
10821 dobj->objType == DO_TABLE ||
10822 dobj->objType == DO_PROCLANG ||
10823 dobj->objType == DO_FDW ||
10824 dobj->objType == DO_FOREIGN_SERVER)
10825 {
10827
10828 daobj->dacl.privtype = privtype;
10829 daobj->dacl.initprivs = pstrdup(initprivs);
10830 }
10831 else
10832 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10833 classoid, objoid, objsubid);
10834 }
10835 }
10836 PQclear(res);
10837 }
10838
10839 destroyPQExpBuffer(query);
10840}
10841
10842/*
10843 * dumpCommentExtended --
10844 *
10845 * This routine is used to dump any comments associated with the
10846 * object handed to this routine. The routine takes the object type
10847 * and object name (ready to print, except for schema decoration), plus
10848 * the namespace and owner of the object (for labeling the ArchiveEntry),
10849 * plus catalog ID and subid which are the lookup key for pg_description,
10850 * plus the dump ID for the object (for setting a dependency).
10851 * If a matching pg_description entry is found, it is dumped.
10852 *
10853 * Note: in some cases, such as comments for triggers and rules, the "type"
10854 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10855 * but it doesn't seem worth complicating the API for all callers to make
10856 * it cleaner.
10857 *
10858 * Note: although this routine takes a dumpId for dependency purposes,
10859 * that purpose is just to mark the dependency in the emitted dump file
10860 * for possible future use by pg_restore. We do NOT use it for determining
10861 * ordering of the comment in the dump file, because this routine is called
10862 * after dependency sorting occurs. This routine should be called just after
10863 * calling ArchiveEntry() for the specified object.
10864 */
10865static void
10867 const char *name, const char *namespace,
10868 const char *owner, CatalogId catalogId,
10869 int subid, DumpId dumpId,
10870 const char *initdb_comment)
10871{
10872 DumpOptions *dopt = fout->dopt;
10874 int ncomments;
10875
10876 /* do nothing, if --no-comments is supplied */
10877 if (dopt->no_comments)
10878 return;
10879
10880 /* Comments are schema not data ... except LO comments are data */
10881 if (strcmp(type, "LARGE OBJECT") != 0)
10882 {
10883 if (!dopt->dumpSchema)
10884 return;
10885 }
10886 else
10887 {
10888 /* We do dump LO comments in binary-upgrade mode */
10889 if (!dopt->dumpData && !dopt->binary_upgrade)
10890 return;
10891 }
10892
10893 /* Search for comments associated with catalogId, using table */
10894 ncomments = findComments(catalogId.tableoid, catalogId.oid,
10895 &comments);
10896
10897 /* Is there one matching the subid? */
10898 while (ncomments > 0)
10899 {
10900 if (comments->objsubid == subid)
10901 break;
10902 comments++;
10903 ncomments--;
10904 }
10905
10906 if (initdb_comment != NULL)
10907 {
10908 static CommentItem empty_comment = {.descr = ""};
10909
10910 /*
10911 * initdb creates this object with a comment. Skip dumping the
10912 * initdb-provided comment, which would complicate matters for
10913 * non-superuser use of pg_dump. When the DBA has removed initdb's
10914 * comment, replicate that.
10915 */
10916 if (ncomments == 0)
10917 {
10918 comments = &empty_comment;
10919 ncomments = 1;
10920 }
10921 else if (strcmp(comments->descr, initdb_comment) == 0)
10922 ncomments = 0;
10923 }
10924
10925 /* If a comment exists, build COMMENT ON statement */
10926 if (ncomments > 0)
10927 {
10930
10931 appendPQExpBuffer(query, "COMMENT ON %s ", type);
10932 if (namespace && *namespace)
10933 appendPQExpBuffer(query, "%s.", fmtId(namespace));
10934 appendPQExpBuffer(query, "%s IS ", name);
10935 appendStringLiteralAH(query, comments->descr, fout);
10936 appendPQExpBufferStr(query, ";\n");
10937
10938 appendPQExpBuffer(tag, "%s %s", type, name);
10939
10940 /*
10941 * We mark comments as SECTION_NONE because they really belong in the
10942 * same section as their parent, whether that is pre-data or
10943 * post-data.
10944 */
10946 ARCHIVE_OPTS(.tag = tag->data,
10947 .namespace = namespace,
10948 .owner = owner,
10949 .description = "COMMENT",
10950 .section = SECTION_NONE,
10951 .createStmt = query->data,
10952 .deps = &dumpId,
10953 .nDeps = 1));
10954
10955 destroyPQExpBuffer(query);
10956 destroyPQExpBuffer(tag);
10957 }
10958}
10959
10960/*
10961 * dumpComment --
10962 *
10963 * Typical simplification of the above function.
10964 */
10965static inline void
10966dumpComment(Archive *fout, const char *type,
10967 const char *name, const char *namespace,
10968 const char *owner, CatalogId catalogId,
10969 int subid, DumpId dumpId)
10970{
10971 dumpCommentExtended(fout, type, name, namespace, owner,
10972 catalogId, subid, dumpId, NULL);
10973}
10974
10975/*
10976 * appendNamedArgument --
10977 *
10978 * Convenience routine for constructing parameters of the form:
10979 * 'paraname', 'value'::type
10980 */
10981static void
10982appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10983 const char *argtype, const char *argval)
10984{
10985 appendPQExpBufferStr(out, ",\n\t");
10986
10987 appendStringLiteralAH(out, argname, fout);
10988 appendPQExpBufferStr(out, ", ");
10989
10990 appendStringLiteralAH(out, argval, fout);
10991 appendPQExpBuffer(out, "::%s", argtype);
10992}
10993
10994/*
10995 * fetchAttributeStats --
10996 *
10997 * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10998 */
10999static PGresult *
11001{
11002 ArchiveHandle *AH = (ArchiveHandle *) fout;
11003 PQExpBuffer nspnames = createPQExpBuffer();
11004 PQExpBuffer relnames = createPQExpBuffer();
11005 int count = 0;
11006 PGresult *res = NULL;
11007 static TocEntry *te;
11008 static bool restarted;
11009 int max_rels = MAX_ATTR_STATS_RELS;
11010
11011 /*
11012 * Our query for retrieving statistics for multiple relations uses WITH
11013 * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11014 * in v9.4. For older versions, we resort to gathering statistics for a
11015 * single relation at a time.
11016 */
11017 if (fout->remoteVersion < 90400)
11018 max_rels = 1;
11019
11020 /* If we're just starting, set our TOC pointer. */
11021 if (!te)
11022 te = AH->toc->next;
11023
11024 /*
11025 * We can't easily avoid a second TOC scan for the tar format because it
11026 * writes restore.sql separately, which means we must execute the queries
11027 * twice. This feels risky, but there is no known reason it should
11028 * generate different output than the first pass. Even if it does, the
11029 * worst-case scenario is that restore.sql might have different statistics
11030 * data than the archive.
11031 */
11032 if (!restarted && te == AH->toc && AH->format == archTar)
11033 {
11034 te = AH->toc->next;
11035 restarted = true;
11036 }
11037
11038 appendPQExpBufferChar(nspnames, '{');
11039 appendPQExpBufferChar(relnames, '{');
11040
11041 /*
11042 * Scan the TOC for the next set of relevant stats entries. We assume
11043 * that statistics are dumped in the order they are listed in the TOC.
11044 * This is perhaps not the sturdiest assumption, so we verify it matches
11045 * reality in dumpRelationStats_dumper().
11046 */
11047 for (; te != AH->toc && count < max_rels; te = te->next)
11048 {
11049 if ((te->reqs & REQ_STATS) != 0 &&
11050 strcmp(te->desc, "STATISTICS DATA") == 0)
11051 {
11052 appendPGArray(nspnames, te->namespace);
11053 appendPGArray(relnames, te->tag);
11054 count++;
11055 }
11056 }
11057
11058 appendPQExpBufferChar(nspnames, '}');
11059 appendPQExpBufferChar(relnames, '}');
11060
11061 /* Execute the query for the next batch of relations. */
11062 if (count > 0)
11063 {
11065
11066 appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11067 appendStringLiteralAH(query, nspnames->data, fout);
11068 appendPQExpBufferStr(query, "::pg_catalog.name[],");
11069 appendStringLiteralAH(query, relnames->data, fout);
11070 appendPQExpBufferStr(query, "::pg_catalog.name[])");
11071 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11072 destroyPQExpBuffer(query);
11073 }
11074
11075 destroyPQExpBuffer(nspnames);
11076 destroyPQExpBuffer(relnames);
11077 return res;
11078}
11079
11080/*
11081 * dumpRelationStats_dumper --
11082 *
11083 * Generate command to import stats into the relation on the new database.
11084 * This routine is called by the Archiver when it wants the statistics to be
11085 * dumped.
11086 */
11087static char *
11088dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11089{
11090 const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11091 static PGresult *res;
11092 static int rownum;
11093 PQExpBuffer query;
11094 PQExpBufferData out_data;
11095 PQExpBuffer out = &out_data;
11096 int i_schemaname;
11097 int i_tablename;
11098 int i_attname;
11099 int i_inherited;
11100 int i_null_frac;
11101 int i_avg_width;
11102 int i_n_distinct;
11103 int i_most_common_vals;
11104 int i_most_common_freqs;
11105 int i_histogram_bounds;
11106 int i_correlation;
11107 int i_most_common_elems;
11108 int i_most_common_elem_freqs;
11109 int i_elem_count_histogram;
11110 int i_range_length_histogram;
11111 int i_range_empty_frac;
11112 int i_range_bounds_histogram;
11113 static TocEntry *expected_te;
11114
11115 /*
11116 * fetchAttributeStats() assumes that the statistics are dumped in the
11117 * order they are listed in the TOC. We verify that here for safety.
11118 */
11119 if (!expected_te)
11120 expected_te = ((ArchiveHandle *) fout)->toc;
11121
11122 expected_te = expected_te->next;
11123 while ((expected_te->reqs & REQ_STATS) == 0 ||
11124 strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11125 expected_te = expected_te->next;
11126
11127 if (te != expected_te)
11128 pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11129 te->dumpId, te->desc, te->tag,
11130 expected_te->dumpId, expected_te->desc, expected_te->tag);
11131
11132 query = createPQExpBuffer();
11134 {
11136 "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11137 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11138 "s.null_frac, s.avg_width, s.n_distinct, "
11139 "s.most_common_vals, s.most_common_freqs, "
11140 "s.histogram_bounds, s.correlation, "
11141 "s.most_common_elems, s.most_common_elem_freqs, "
11142 "s.elem_count_histogram, ");
11143
11144 if (fout->remoteVersion >= 170000)
11146 "s.range_length_histogram, "
11147 "s.range_empty_frac, "
11148 "s.range_bounds_histogram ");
11149 else
11151 "NULL AS range_length_histogram,"
11152 "NULL AS range_empty_frac,"
11153 "NULL AS range_bounds_histogram ");
11154
11155 /*
11156 * The results must be in the order of the relations supplied in the
11157 * parameters to ensure we remain in sync as we walk through the TOC.
11158 * The redundant filter clause on s.tablename = ANY(...) seems
11159 * sufficient to convince the planner to use
11160 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11161 * This may not work for all versions.
11162 *
11163 * Our query for retrieving statistics for multiple relations uses
11164 * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11165 * introduced in v9.4. For older versions, we resort to gathering
11166 * statistics for a single relation at a time.
11167 */
11168 if (fout->remoteVersion >= 90400)
11170 "FROM pg_catalog.pg_stats s "
11171 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11172 "ON s.schemaname = u.schemaname "
11173 "AND s.tablename = u.tablename "
11174 "WHERE s.tablename = ANY($2) "
11175 "ORDER BY u.ord, s.attname, s.inherited");
11176 else
11178 "FROM pg_catalog.pg_stats s "
11179 "WHERE s.schemaname = $1[1] "
11180 "AND s.tablename = $2[1] "
11181 "ORDER BY s.attname, s.inherited");
11182
11183 ExecuteSqlStatement(fout, query->data);
11184
11186 resetPQExpBuffer(query);
11187 }
11188
11189 initPQExpBuffer(out);
11190
11191 /* restore relation stats */
11192 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11193 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11194 fout->remoteVersion);
11195 appendPQExpBufferStr(out, "\t'schemaname', ");
11196 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11197 appendPQExpBufferStr(out, ",\n");
11198 appendPQExpBufferStr(out, "\t'relname', ");
11199 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11200 appendPQExpBufferStr(out, ",\n");
11201 appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11202
11203 /*
11204 * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11205 * the relation is empty, or it could mean that it hadn't yet been
11206 * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11207 * This ambiguity allegedly can cause the planner to choose inefficient
11208 * plans after restoring to v18 or newer. To deal with this, let's just
11209 * set reltuples to -1 in that case.
11210 */
11211 if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11212 appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11213 else
11214 appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11215
11216 appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11217 rsinfo->relallvisible);
11218
11219 if (fout->remoteVersion >= 180000)
11220 appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11221
11222 appendPQExpBufferStr(out, "\n);\n");
11223
11224 /* Fetch the next batch of attribute statistics if needed. */
11225 if (rownum >= PQntuples(res))
11226 {
11227 PQclear(res);
11228 res = fetchAttributeStats(fout);
11229 rownum = 0;
11230 }
11231
11232 i_schemaname = PQfnumber(res, "schemaname");
11233 i_tablename = PQfnumber(res, "tablename");
11234 i_attname = PQfnumber(res, "attname");
11235 i_inherited = PQfnumber(res, "inherited");
11236 i_null_frac = PQfnumber(res, "null_frac");
11237 i_avg_width = PQfnumber(res, "avg_width");
11238 i_n_distinct = PQfnumber(res, "n_distinct");
11239 i_most_common_vals = PQfnumber(res, "most_common_vals");
11240 i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11241 i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11242 i_correlation = PQfnumber(res, "correlation");
11243 i_most_common_elems = PQfnumber(res, "most_common_elems");
11244 i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11245 i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11246 i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11247 i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11248 i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11249
11250 /* restore attribute stats */
11251 for (; rownum < PQntuples(res); rownum++)
11252 {
11253 const char *attname;
11254
11255 /* Stop if the next stat row in our cache isn't for this relation. */
11256 if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11257 strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11258 break;
11259
11260 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11261 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11262 fout->remoteVersion);
11263 appendPQExpBufferStr(out, "\t'schemaname', ");
11264 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11265 appendPQExpBufferStr(out, ",\n\t'relname', ");
11266 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11267
11268 if (PQgetisnull(res, rownum, i_attname))
11269 pg_fatal("unexpected null attname");
11270 attname = PQgetvalue(res, rownum, i_attname);
11271
11272 /*
11273 * Indexes look up attname in indAttNames to derive attnum, all others
11274 * use attname directly. We must specify attnum for indexes, since
11275 * their attnames are not necessarily stable across dump/reload.
11276 */
11277 if (rsinfo->nindAttNames == 0)
11278 {
11279 appendPQExpBufferStr(out, ",\n\t'attname', ");
11280 appendStringLiteralAH(out, attname, fout);
11281 }
11282 else
11283 {
11284 bool found = false;
11285
11286 for (int i = 0; i < rsinfo->nindAttNames; i++)
11287 {
11288 if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11289 {
11290 appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11291 i + 1);
11292 found = true;
11293 break;
11294 }
11295 }
11296
11297 if (!found)
11298 pg_fatal("could not find index attname \"%s\"", attname);
11299 }
11300
11301 if (!PQgetisnull(res, rownum, i_inherited))
11302 appendNamedArgument(out, fout, "inherited", "boolean",
11303 PQgetvalue(res, rownum, i_inherited));
11304 if (!PQgetisnull(res, rownum, i_null_frac))
11305 appendNamedArgument(out, fout, "null_frac", "real",
11306 PQgetvalue(res, rownum, i_null_frac));
11307 if (!PQgetisnull(res, rownum, i_avg_width))
11308 appendNamedArgument(out, fout, "avg_width", "integer",
11309 PQgetvalue(res, rownum, i_avg_width));
11310 if (!PQgetisnull(res, rownum, i_n_distinct))
11311 appendNamedArgument(out, fout, "n_distinct", "real",
11312 PQgetvalue(res, rownum, i_n_distinct));
11313 if (!PQgetisnull(res, rownum, i_most_common_vals))
11314 appendNamedArgument(out, fout, "most_common_vals", "text",
11315 PQgetvalue(res, rownum, i_most_common_vals));
11316 if (!PQgetisnull(res, rownum, i_most_common_freqs))
11317 appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11318 PQgetvalue(res, rownum, i_most_common_freqs));
11319 if (!PQgetisnull(res, rownum, i_histogram_bounds))
11320 appendNamedArgument(out, fout, "histogram_bounds", "text",
11321 PQgetvalue(res, rownum, i_histogram_bounds));
11322 if (!PQgetisnull(res, rownum, i_correlation))
11323 appendNamedArgument(out, fout, "correlation", "real",
11324 PQgetvalue(res, rownum, i_correlation));
11325 if (!PQgetisnull(res, rownum, i_most_common_elems))
11326 appendNamedArgument(out, fout, "most_common_elems", "text",
11327 PQgetvalue(res, rownum, i_most_common_elems));
11328 if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11329 appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11330 PQgetvalue(res, rownum, i_most_common_elem_freqs));
11331 if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11332 appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11333 PQgetvalue(res, rownum, i_elem_count_histogram));
11334 if (fout->remoteVersion >= 170000)
11335 {
11336 if (!PQgetisnull(res, rownum, i_range_length_histogram))
11337 appendNamedArgument(out, fout, "range_length_histogram", "text",
11338 PQgetvalue(res, rownum, i_range_length_histogram));
11339 if (!PQgetisnull(res, rownum, i_range_empty_frac))
11340 appendNamedArgument(out, fout, "range_empty_frac", "real",
11341 PQgetvalue(res, rownum, i_range_empty_frac));
11342 if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11343 appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11344 PQgetvalue(res, rownum, i_range_bounds_histogram));
11345 }
11346 appendPQExpBufferStr(out, "\n);\n");
11347 }
11348
11349 destroyPQExpBuffer(query);
11350 return out->data;
11351}
11352
11353/*
11354 * dumpRelationStats --
11355 *
11356 * Make an ArchiveEntry for the relation statistics. The Archiver will take
11357 * care of gathering the statistics and generating the restore commands when
11358 * they are needed.
11359 */
11360static void
11362{
11363 const DumpableObject *dobj = &rsinfo->dobj;
11364
11365 /* nothing to do if we are not dumping statistics */
11366 if (!fout->dopt->dumpStatistics)
11367 return;
11368
11370 ARCHIVE_OPTS(.tag = dobj->name,
11371 .namespace = dobj->namespace->dobj.name,
11372 .description = "STATISTICS DATA",
11373 .section = rsinfo->section,
11374 .defnFn = dumpRelationStats_dumper,
11375 .defnArg = rsinfo,
11376 .deps = dobj->dependencies,
11377 .nDeps = dobj->nDeps));
11378}
11379
11380/*
11381 * dumpTableComment --
11382 *
11383 * As above, but dump comments for both the specified table (or view)
11384 * and its columns.
11385 */
11386static void
11388 const char *reltypename)
11389{
11390 DumpOptions *dopt = fout->dopt;
11392 int ncomments;
11393 PQExpBuffer query;
11394 PQExpBuffer tag;
11395
11396 /* do nothing, if --no-comments is supplied */
11397 if (dopt->no_comments)
11398 return;
11399
11400 /* Comments are SCHEMA not data */
11401 if (!dopt->dumpSchema)
11402 return;
11403
11404 /* Search for comments associated with relation, using table */
11406 tbinfo->dobj.catId.oid,
11407 &comments);
11408
11409 /* If comments exist, build COMMENT ON statements */
11410 if (ncomments <= 0)
11411 return;
11412
11413 query = createPQExpBuffer();
11414 tag = createPQExpBuffer();
11415
11416 while (ncomments > 0)
11417 {
11418 const char *descr = comments->descr;
11419 int objsubid = comments->objsubid;
11420
11421 if (objsubid == 0)
11422 {
11423 resetPQExpBuffer(tag);
11424 appendPQExpBuffer(tag, "%s %s", reltypename,
11425 fmtId(tbinfo->dobj.name));
11426
11427 resetPQExpBuffer(query);
11428 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11429 fmtQualifiedDumpable(tbinfo));
11430 appendStringLiteralAH(query, descr, fout);
11431 appendPQExpBufferStr(query, ";\n");
11432
11434 ARCHIVE_OPTS(.tag = tag->data,
11435 .namespace = tbinfo->dobj.namespace->dobj.name,
11436 .owner = tbinfo->rolname,
11437 .description = "COMMENT",
11438 .section = SECTION_NONE,
11439 .createStmt = query->data,
11440 .deps = &(tbinfo->dobj.dumpId),
11441 .nDeps = 1));
11442 }
11443 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11444 {
11445 resetPQExpBuffer(tag);
11446 appendPQExpBuffer(tag, "COLUMN %s.",
11447 fmtId(tbinfo->dobj.name));
11448 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11449
11450 resetPQExpBuffer(query);
11451 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11452 fmtQualifiedDumpable(tbinfo));
11453 appendPQExpBuffer(query, "%s IS ",
11454 fmtId(tbinfo->attnames[objsubid - 1]));
11455 appendStringLiteralAH(query, descr, fout);
11456 appendPQExpBufferStr(query, ";\n");
11457
11459 ARCHIVE_OPTS(.tag = tag->data,
11460 .namespace = tbinfo->dobj.namespace->dobj.name,
11461 .owner = tbinfo->rolname,
11462 .description = "COMMENT",
11463 .section = SECTION_NONE,
11464 .createStmt = query->data,
11465 .deps = &(tbinfo->dobj.dumpId),
11466 .nDeps = 1));
11467 }
11468
11469 comments++;
11470 ncomments--;
11471 }
11472
11473 destroyPQExpBuffer(query);
11474 destroyPQExpBuffer(tag);
11475}
11476
11477/*
11478 * findComments --
11479 *
11480 * Find the comment(s), if any, associated with the given object. All the
11481 * objsubid values associated with the given classoid/objoid are found with
11482 * one search.
11483 */
11484static int
11486{
11487 CommentItem *middle = NULL;
11488 CommentItem *low;
11489 CommentItem *high;
11490 int nmatch;
11491
11492 /*
11493 * Do binary search to find some item matching the object.
11494 */
11495 low = &comments[0];
11496 high = &comments[ncomments - 1];
11497 while (low <= high)
11498 {
11499 middle = low + (high - low) / 2;
11500
11501 if (classoid < middle->classoid)
11502 high = middle - 1;
11503 else if (classoid > middle->classoid)
11504 low = middle + 1;
11505 else if (objoid < middle->objoid)
11506 high = middle - 1;
11507 else if (objoid > middle->objoid)
11508 low = middle + 1;
11509 else
11510 break; /* found a match */
11511 }
11512
11513 if (low > high) /* no matches */
11514 {
11515 *items = NULL;
11516 return 0;
11517 }
11518
11519 /*
11520 * Now determine how many items match the object. The search loop
11521 * invariant still holds: only items between low and high inclusive could
11522 * match.
11523 */
11524 nmatch = 1;
11525 while (middle > low)
11526 {
11527 if (classoid != middle[-1].classoid ||
11528 objoid != middle[-1].objoid)
11529 break;
11530 middle--;
11531 nmatch++;
11532 }
11533
11534 *items = middle;
11535
11536 middle += nmatch;
11537 while (middle <= high)
11538 {
11539 if (classoid != middle->classoid ||
11540 objoid != middle->objoid)
11541 break;
11542 middle++;
11543 nmatch++;
11544 }
11545
11546 return nmatch;
11547}
11548
11549/*
11550 * collectComments --
11551 *
11552 * Construct a table of all comments available for database objects;
11553 * also set the has-comment component flag for each relevant object.
11554 *
11555 * We used to do per-object queries for the comments, but it's much faster
11556 * to pull them all over at once, and on most databases the memory cost
11557 * isn't high.
11558 *
11559 * The table is sorted by classoid/objid/objsubid for speed in lookup.
11560 */
11561static void
11563{
11564 PGresult *res;
11565 PQExpBuffer query;
11566 int i_description;
11567 int i_classoid;
11568 int i_objoid;
11569 int i_objsubid;
11570 int ntups;
11571 int i;
11572 DumpableObject *dobj;
11573
11574 query = createPQExpBuffer();
11575
11576 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11577 "FROM pg_catalog.pg_description "
11578 "ORDER BY classoid, objoid, objsubid");
11579
11580 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11581
11582 /* Construct lookup table containing OIDs in numeric form */
11583
11584 i_description = PQfnumber(res, "description");
11585 i_classoid = PQfnumber(res, "classoid");
11586 i_objoid = PQfnumber(res, "objoid");
11587 i_objsubid = PQfnumber(res, "objsubid");
11588
11589 ntups = PQntuples(res);
11590
11591 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11592 ncomments = 0;
11593 dobj = NULL;
11594
11595 for (i = 0; i < ntups; i++)
11596 {
11597 CatalogId objId;
11598 int subid;
11599
11600 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11601 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11602 subid = atoi(PQgetvalue(res, i, i_objsubid));
11603
11604 /* We needn't remember comments that don't match any dumpable object */
11605 if (dobj == NULL ||
11606 dobj->catId.tableoid != objId.tableoid ||
11607 dobj->catId.oid != objId.oid)
11608 dobj = findObjectByCatalogId(objId);
11609 if (dobj == NULL)
11610 continue;
11611
11612 /*
11613 * Comments on columns of composite types are linked to the type's
11614 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11615 * in the type's own DumpableObject.
11616 */
11617 if (subid != 0 && dobj->objType == DO_TABLE &&
11618 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11619 {
11620 TypeInfo *cTypeInfo;
11621
11622 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11623 if (cTypeInfo)
11625 }
11626 else
11627 dobj->components |= DUMP_COMPONENT_COMMENT;
11628
11629 comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11631 comments[ncomments].objoid = objId.oid;
11632 comments[ncomments].objsubid = subid;
11633 ncomments++;
11634 }
11635
11636 PQclear(res);
11637 destroyPQExpBuffer(query);
11638}
11639
11640/*
11641 * dumpDumpableObject
11642 *
11643 * This routine and its subsidiaries are responsible for creating
11644 * ArchiveEntries (TOC objects) for each object to be dumped.
11645 */
11646static void
11648{
11649 /*
11650 * Clear any dump-request bits for components that don't exist for this
11651 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11652 * request for every kind of object.)
11653 */
11654 dobj->dump &= dobj->components;
11655
11656 /* Now, short-circuit if there's nothing to be done here. */
11657 if (dobj->dump == 0)
11658 return;
11659
11660 switch (dobj->objType)
11661 {
11662 case DO_NAMESPACE:
11663 dumpNamespace(fout, (const NamespaceInfo *) dobj);
11664 break;
11665 case DO_EXTENSION:
11666 dumpExtension(fout, (const ExtensionInfo *) dobj);
11667 break;
11668 case DO_TYPE:
11669 dumpType(fout, (const TypeInfo *) dobj);
11670 break;
11671 case DO_SHELL_TYPE:
11672 dumpShellType(fout, (const ShellTypeInfo *) dobj);
11673 break;
11674 case DO_FUNC:
11675 dumpFunc(fout, (const FuncInfo *) dobj);
11676 break;
11677 case DO_AGG:
11678 dumpAgg(fout, (const AggInfo *) dobj);
11679 break;
11680 case DO_OPERATOR:
11681 dumpOpr(fout, (const OprInfo *) dobj);
11682 break;
11683 case DO_ACCESS_METHOD:
11684 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11685 break;
11686 case DO_OPCLASS:
11687 dumpOpclass(fout, (const OpclassInfo *) dobj);
11688 break;
11689 case DO_OPFAMILY:
11690 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11691 break;
11692 case DO_COLLATION:
11693 dumpCollation(fout, (const CollInfo *) dobj);
11694 break;
11695 case DO_CONVERSION:
11696 dumpConversion(fout, (const ConvInfo *) dobj);
11697 break;
11698 case DO_TABLE:
11699 dumpTable(fout, (const TableInfo *) dobj);
11700 break;
11701 case DO_TABLE_ATTACH:
11702 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11703 break;
11704 case DO_ATTRDEF:
11705 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11706 break;
11707 case DO_INDEX:
11708 dumpIndex(fout, (const IndxInfo *) dobj);
11709 break;
11710 case DO_INDEX_ATTACH:
11711 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11712 break;
11713 case DO_STATSEXT:
11714 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11715 break;
11716 case DO_REFRESH_MATVIEW:
11717 refreshMatViewData(fout, (const TableDataInfo *) dobj);
11718 break;
11719 case DO_RULE:
11720 dumpRule(fout, (const RuleInfo *) dobj);
11721 break;
11722 case DO_TRIGGER:
11723 dumpTrigger(fout, (const TriggerInfo *) dobj);
11724 break;
11725 case DO_EVENT_TRIGGER:
11726 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11727 break;
11728 case DO_CONSTRAINT:
11729 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11730 break;
11731 case DO_FK_CONSTRAINT:
11732 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11733 break;
11734 case DO_PROCLANG:
11735 dumpProcLang(fout, (const ProcLangInfo *) dobj);
11736 break;
11737 case DO_CAST:
11738 dumpCast(fout, (const CastInfo *) dobj);
11739 break;
11740 case DO_TRANSFORM:
11741 dumpTransform(fout, (const TransformInfo *) dobj);
11742 break;
11743 case DO_SEQUENCE_SET:
11744 dumpSequenceData(fout, (const TableDataInfo *) dobj);
11745 break;
11746 case DO_TABLE_DATA:
11747 dumpTableData(fout, (const TableDataInfo *) dobj);
11748 break;
11749 case DO_DUMMY_TYPE:
11750 /* table rowtypes and array types are never dumped separately */
11751 break;
11752 case DO_TSPARSER:
11753 dumpTSParser(fout, (const TSParserInfo *) dobj);
11754 break;
11755 case DO_TSDICT:
11756 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11757 break;
11758 case DO_TSTEMPLATE:
11759 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11760 break;
11761 case DO_TSCONFIG:
11762 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11763 break;
11764 case DO_FDW:
11765 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11766 break;
11767 case DO_FOREIGN_SERVER:
11768 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11769 break;
11770 case DO_DEFAULT_ACL:
11771 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11772 break;
11773 case DO_LARGE_OBJECT:
11774 dumpLO(fout, (const LoInfo *) dobj);
11775 break;
11777 if (dobj->dump & DUMP_COMPONENT_DATA)
11778 {
11779 LoInfo *loinfo;
11780 TocEntry *te;
11781
11782 loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11783 if (loinfo == NULL)
11784 pg_fatal("missing metadata for large objects \"%s\"",
11785 dobj->name);
11786
11787 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11788 ARCHIVE_OPTS(.tag = dobj->name,
11789 .owner = loinfo->rolname,
11790 .description = "BLOBS",
11791 .section = SECTION_DATA,
11792 .deps = dobj->dependencies,
11793 .nDeps = dobj->nDeps,
11794 .dumpFn = dumpLOs,
11795 .dumpArg = loinfo));
11796
11797 /*
11798 * Set the TocEntry's dataLength in case we are doing a
11799 * parallel dump and want to order dump jobs by table size.
11800 * (We need some size estimate for every TocEntry with a
11801 * DataDumper function.) We don't currently have any cheap
11802 * way to estimate the size of LOs, but fortunately it doesn't
11803 * matter too much as long as we get large batches of LOs
11804 * processed reasonably early. Assume 8K per blob.
11805 */
11806 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11807 }
11808 break;
11809 case DO_POLICY:
11810 dumpPolicy(fout, (const PolicyInfo *) dobj);
11811 break;
11812 case DO_PUBLICATION:
11813 dumpPublication(fout, (const PublicationInfo *) dobj);
11814 break;
11815 case DO_PUBLICATION_REL:
11816 dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11817 break;
11820 (const PublicationSchemaInfo *) dobj);
11821 break;
11822 case DO_SUBSCRIPTION:
11823 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11824 break;
11826 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11827 break;
11828 case DO_REL_STATS:
11829 dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11830 break;
11833 /* never dumped, nothing to do */
11834 break;
11835 }
11836}
11837
11838/*
11839 * dumpNamespace
11840 * writes out to fout the queries to recreate a user-defined namespace
11841 */
11842static void
11844{
11845 DumpOptions *dopt = fout->dopt;
11846 PQExpBuffer q;
11847 PQExpBuffer delq;
11848 char *qnspname;
11849
11850 /* Do nothing if not dumping schema */
11851 if (!dopt->dumpSchema)
11852 return;
11853
11854 q = createPQExpBuffer();
11855 delq = createPQExpBuffer();
11856
11857 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11858
11859 if (nspinfo->create)
11860 {
11861 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11862 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11863 }
11864 else
11865 {
11866 /* see selectDumpableNamespace() */
11868 "-- *not* dropping schema, since initdb creates it\n");
11870 "-- *not* creating schema, since initdb creates it\n");
11871 }
11872
11873 if (dopt->binary_upgrade)
11875 "SCHEMA", qnspname, NULL);
11876
11877 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11878 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11879 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11880 .owner = nspinfo->rolname,
11881 .description = "SCHEMA",
11882 .section = SECTION_PRE_DATA,
11883 .createStmt = q->data,
11884 .dropStmt = delq->data));
11885
11886 /* Dump Schema Comments and Security Labels */
11887 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11888 {
11889 const char *initdb_comment = NULL;
11890
11891 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11892 initdb_comment = "standard public schema";
11893 dumpCommentExtended(fout, "SCHEMA", qnspname,
11894 NULL, nspinfo->rolname,
11895 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11896 initdb_comment);
11897 }
11898
11899 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11900 dumpSecLabel(fout, "SCHEMA", qnspname,
11901 NULL, nspinfo->rolname,
11902 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11903
11904 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11905 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11906 qnspname, NULL, NULL,
11907 NULL, nspinfo->rolname, &nspinfo->dacl);
11908
11909 free(qnspname);
11910
11912 destroyPQExpBuffer(delq);
11913}
11914
11915/*
11916 * dumpExtension
11917 * writes out to fout the queries to recreate an extension
11918 */
11919static void
11921{
11922 DumpOptions *dopt = fout->dopt;
11923 PQExpBuffer q;
11924 PQExpBuffer delq;
11925 char *qextname;
11926
11927 /* Do nothing if not dumping schema */
11928 if (!dopt->dumpSchema)
11929 return;
11930
11931 q = createPQExpBuffer();
11932 delq = createPQExpBuffer();
11933
11934 qextname = pg_strdup(fmtId(extinfo->dobj.name));
11935
11936 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11937
11938 if (!dopt->binary_upgrade)
11939 {
11940 /*
11941 * In a regular dump, we simply create the extension, intentionally
11942 * not specifying a version, so that the destination installation's
11943 * default version is used.
11944 *
11945 * Use of IF NOT EXISTS here is unlike our behavior for other object
11946 * types; but there are various scenarios in which it's convenient to
11947 * manually create the desired extension before restoring, so we
11948 * prefer to allow it to exist already.
11949 */
11950 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11951 qextname, fmtId(extinfo->namespace));
11952 }
11953 else
11954 {
11955 /*
11956 * In binary-upgrade mode, it's critical to reproduce the state of the
11957 * database exactly, so our procedure is to create an empty extension,
11958 * restore all the contained objects normally, and add them to the
11959 * extension one by one. This function performs just the first of
11960 * those steps. binary_upgrade_extension_member() takes care of
11961 * adding member objects as they're created.
11962 */
11963 int i;
11964 int n;
11965
11966 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11967
11968 /*
11969 * We unconditionally create the extension, so we must drop it if it
11970 * exists. This could happen if the user deleted 'plpgsql' and then
11971 * readded it, causing its oid to be greater than g_last_builtin_oid.
11972 */
11973 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11974
11976 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11977 appendStringLiteralAH(q, extinfo->dobj.name, fout);
11978 appendPQExpBufferStr(q, ", ");
11979 appendStringLiteralAH(q, extinfo->namespace, fout);
11980 appendPQExpBufferStr(q, ", ");
11981 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11982 appendStringLiteralAH(q, extinfo->extversion, fout);
11983 appendPQExpBufferStr(q, ", ");
11984
11985 /*
11986 * Note that we're pushing extconfig (an OID array) back into
11987 * pg_extension exactly as-is. This is OK because pg_class OIDs are
11988 * preserved in binary upgrade.
11989 */
11990 if (strlen(extinfo->extconfig) > 2)
11991 appendStringLiteralAH(q, extinfo->extconfig, fout);
11992 else
11993 appendPQExpBufferStr(q, "NULL");
11994 appendPQExpBufferStr(q, ", ");
11995 if (strlen(extinfo->extcondition) > 2)
11996 appendStringLiteralAH(q, extinfo->extcondition, fout);
11997 else
11998 appendPQExpBufferStr(q, "NULL");
11999 appendPQExpBufferStr(q, ", ");
12000 appendPQExpBufferStr(q, "ARRAY[");
12001 n = 0;
12002 for (i = 0; i < extinfo->dobj.nDeps; i++)
12003 {
12004 DumpableObject *extobj;
12005
12006 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12007 if (extobj && extobj->objType == DO_EXTENSION)
12008 {
12009 if (n++ > 0)
12010 appendPQExpBufferChar(q, ',');
12011 appendStringLiteralAH(q, extobj->name, fout);
12012 }
12013 }
12014 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12015 appendPQExpBufferStr(q, ");\n");
12016 }
12017
12018 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12019 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12020 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12021 .description = "EXTENSION",
12022 .section = SECTION_PRE_DATA,
12023 .createStmt = q->data,
12024 .dropStmt = delq->data));
12025
12026 /* Dump Extension Comments */
12027 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12028 dumpComment(fout, "EXTENSION", qextname,
12029 NULL, "",
12030 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12031
12032 free(qextname);
12033
12035 destroyPQExpBuffer(delq);
12036}
12037
12038/*
12039 * dumpType
12040 * writes out to fout the queries to recreate a user-defined type
12041 */
12042static void
12043dumpType(Archive *fout, const TypeInfo *tyinfo)
12044{
12045 DumpOptions *dopt = fout->dopt;
12046
12047 /* Do nothing if not dumping schema */
12048 if (!dopt->dumpSchema)
12049 return;
12050
12051 /* Dump out in proper style */
12052 if (tyinfo->typtype == TYPTYPE_BASE)
12053 dumpBaseType(fout, tyinfo);
12054 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12055 dumpDomain(fout, tyinfo);
12056 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12057 dumpCompositeType(fout, tyinfo);
12058 else if (tyinfo->typtype == TYPTYPE_ENUM)
12059 dumpEnumType(fout, tyinfo);
12060 else if (tyinfo->typtype == TYPTYPE_RANGE)
12061 dumpRangeType(fout, tyinfo);
12062 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12063 dumpUndefinedType(fout, tyinfo);
12064 else
12065 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12066 tyinfo->dobj.name);
12067}
12068
12069/*
12070 * dumpEnumType
12071 * writes out to fout the queries to recreate a user-defined enum type
12072 */
12073static void
12074dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12075{
12076 DumpOptions *dopt = fout->dopt;
12080 PGresult *res;
12081 int num,
12082 i;
12083 Oid enum_oid;
12084 char *qtypname;
12085 char *qualtypname;
12086 char *label;
12087 int i_enumlabel;
12088 int i_oid;
12089
12091 {
12092 /* Set up query for enum-specific details */
12094 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12095 "SELECT oid, enumlabel "
12096 "FROM pg_catalog.pg_enum "
12097 "WHERE enumtypid = $1 "
12098 "ORDER BY enumsortorder");
12099
12100 ExecuteSqlStatement(fout, query->data);
12101
12102 fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12103 }
12104
12105 printfPQExpBuffer(query,
12106 "EXECUTE dumpEnumType('%u')",
12107 tyinfo->dobj.catId.oid);
12108
12109 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12110
12111 num = PQntuples(res);
12112
12113 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12114 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12115
12116 /*
12117 * CASCADE shouldn't be required here as for normal types since the I/O
12118 * functions are generic and do not get dropped.
12119 */
12120 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12121
12122 if (dopt->binary_upgrade)
12124 tyinfo->dobj.catId.oid,
12125 false, false);
12126
12127 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12128 qualtypname);
12129
12130 if (!dopt->binary_upgrade)
12131 {
12132 i_enumlabel = PQfnumber(res, "enumlabel");
12133
12134 /* Labels with server-assigned oids */
12135 for (i = 0; i < num; i++)
12136 {
12137 label = PQgetvalue(res, i, i_enumlabel);
12138 if (i > 0)
12139 appendPQExpBufferChar(q, ',');
12140 appendPQExpBufferStr(q, "\n ");
12141 appendStringLiteralAH(q, label, fout);
12142 }
12143 }
12144
12145 appendPQExpBufferStr(q, "\n);\n");
12146
12147 if (dopt->binary_upgrade)
12148 {
12149 i_oid = PQfnumber(res, "oid");
12150 i_enumlabel = PQfnumber(res, "enumlabel");
12151
12152 /* Labels with dump-assigned (preserved) oids */
12153 for (i = 0; i < num; i++)
12154 {
12155 enum_oid = atooid(PQgetvalue(res, i, i_oid));
12156 label = PQgetvalue(res, i, i_enumlabel);
12157
12158 if (i == 0)
12159 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12161 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12162 enum_oid);
12163 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12164 appendStringLiteralAH(q, label, fout);
12165 appendPQExpBufferStr(q, ";\n\n");
12166 }
12167 }
12168
12169 if (dopt->binary_upgrade)
12171 "TYPE", qtypname,
12172 tyinfo->dobj.namespace->dobj.name);
12173
12174 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12175 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12176 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12177 .namespace = tyinfo->dobj.namespace->dobj.name,
12178 .owner = tyinfo->rolname,
12179 .description = "TYPE",
12180 .section = SECTION_PRE_DATA,
12181 .createStmt = q->data,
12182 .dropStmt = delq->data));
12183
12184 /* Dump Type Comments and Security Labels */
12185 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12186 dumpComment(fout, "TYPE", qtypname,
12187 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12188 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12189
12190 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12191 dumpSecLabel(fout, "TYPE", qtypname,
12192 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12193 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12194
12195 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12196 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12197 qtypname, NULL,
12198 tyinfo->dobj.namespace->dobj.name,
12199 NULL, tyinfo->rolname, &tyinfo->dacl);
12200
12201 PQclear(res);
12203 destroyPQExpBuffer(delq);
12204 destroyPQExpBuffer(query);
12205 free(qtypname);
12206 free(qualtypname);
12207}
12208
12209/*
12210 * dumpRangeType
12211 * writes out to fout the queries to recreate a user-defined range type
12212 */
12213static void
12214dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12215{
12216 DumpOptions *dopt = fout->dopt;
12220 PGresult *res;
12221 Oid collationOid;
12222 char *qtypname;
12223 char *qualtypname;
12224 char *procname;
12225
12227 {
12228 /* Set up query for range-specific details */
12230 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12231
12233 "SELECT ");
12234
12235 if (fout->remoteVersion >= 140000)
12237 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12238 else
12240 "NULL AS rngmultitype, ");
12241
12243 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12244 "opc.opcname AS opcname, "
12245 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12246 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12247 "opc.opcdefault, "
12248 "CASE WHEN rngcollation = st.typcollation THEN 0 "
12249 " ELSE rngcollation END AS collation, "
12250 "rngcanonical, rngsubdiff "
12251 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12252 " pg_catalog.pg_opclass opc "
12253 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12254 "rngtypid = $1");
12255
12256 ExecuteSqlStatement(fout, query->data);
12257
12259 }
12260
12261 printfPQExpBuffer(query,
12262 "EXECUTE dumpRangeType('%u')",
12263 tyinfo->dobj.catId.oid);
12264
12265 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12266
12267 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12268 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12269
12270 /*
12271 * CASCADE shouldn't be required here as for normal types since the I/O
12272 * functions are generic and do not get dropped.
12273 */
12274 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12275
12276 if (dopt->binary_upgrade)
12278 tyinfo->dobj.catId.oid,
12279 false, true);
12280
12281 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12282 qualtypname);
12283
12284 appendPQExpBuffer(q, "\n subtype = %s",
12285 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12286
12287 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12288 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12289 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12290
12291 /* print subtype_opclass only if not default for subtype */
12292 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12293 {
12294 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12295 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12296
12297 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12298 fmtId(nspname));
12299 appendPQExpBufferStr(q, fmtId(opcname));
12300 }
12301
12302 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12303 if (OidIsValid(collationOid))
12304 {
12305 CollInfo *coll = findCollationByOid(collationOid);
12306
12307 if (coll)
12308 appendPQExpBuffer(q, ",\n collation = %s",
12309 fmtQualifiedDumpable(coll));
12310 }
12311
12312 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12313 if (strcmp(procname, "-") != 0)
12314 appendPQExpBuffer(q, ",\n canonical = %s", procname);
12315
12316 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12317 if (strcmp(procname, "-") != 0)
12318 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12319
12320 appendPQExpBufferStr(q, "\n);\n");
12321
12322 if (dopt->binary_upgrade)
12324 "TYPE", qtypname,
12325 tyinfo->dobj.namespace->dobj.name);
12326
12327 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12328 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12329 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12330 .namespace = tyinfo->dobj.namespace->dobj.name,
12331 .owner = tyinfo->rolname,
12332 .description = "TYPE",
12333 .section = SECTION_PRE_DATA,
12334 .createStmt = q->data,
12335 .dropStmt = delq->data));
12336
12337 /* Dump Type Comments and Security Labels */
12338 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12339 dumpComment(fout, "TYPE", qtypname,
12340 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12341 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12342
12343 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12344 dumpSecLabel(fout, "TYPE", qtypname,
12345 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12346 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12347
12348 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12349 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12350 qtypname, NULL,
12351 tyinfo->dobj.namespace->dobj.name,
12352 NULL, tyinfo->rolname, &tyinfo->dacl);
12353
12354 PQclear(res);
12356 destroyPQExpBuffer(delq);
12357 destroyPQExpBuffer(query);
12358 free(qtypname);
12359 free(qualtypname);
12360}
12361
12362/*
12363 * dumpUndefinedType
12364 * writes out to fout the queries to recreate a !typisdefined type
12365 *
12366 * This is a shell type, but we use different terminology to distinguish
12367 * this case from where we have to emit a shell type definition to break
12368 * circular dependencies. An undefined type shouldn't ever have anything
12369 * depending on it.
12370 */
12371static void
12373{
12374 DumpOptions *dopt = fout->dopt;
12377 char *qtypname;
12378 char *qualtypname;
12379
12380 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12381 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12382
12383 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12384
12385 if (dopt->binary_upgrade)
12387 tyinfo->dobj.catId.oid,
12388 false, false);
12389
12390 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12391 qualtypname);
12392
12393 if (dopt->binary_upgrade)
12395 "TYPE", qtypname,
12396 tyinfo->dobj.namespace->dobj.name);
12397
12398 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12399 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12400 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12401 .namespace = tyinfo->dobj.namespace->dobj.name,
12402 .owner = tyinfo->rolname,
12403 .description = "TYPE",
12404 .section = SECTION_PRE_DATA,
12405 .createStmt = q->data,
12406 .dropStmt = delq->data));
12407
12408 /* Dump Type Comments and Security Labels */
12409 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12410 dumpComment(fout, "TYPE", qtypname,
12411 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12412 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12413
12414 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12415 dumpSecLabel(fout, "TYPE", qtypname,
12416 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12417 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12418
12419 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12420 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12421 qtypname, NULL,
12422 tyinfo->dobj.namespace->dobj.name,
12423 NULL, tyinfo->rolname, &tyinfo->dacl);
12424
12426 destroyPQExpBuffer(delq);
12427 free(qtypname);
12428 free(qualtypname);
12429}
12430
12431/*
12432 * dumpBaseType
12433 * writes out to fout the queries to recreate a user-defined base type
12434 */
12435static void
12436dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12437{
12438 DumpOptions *dopt = fout->dopt;
12442 PGresult *res;
12443 char *qtypname;
12444 char *qualtypname;
12445 char *typlen;
12446 char *typinput;
12447 char *typoutput;
12448 char *typreceive;
12449 char *typsend;
12450 char *typmodin;
12451 char *typmodout;
12452 char *typanalyze;
12453 char *typsubscript;
12454 Oid typreceiveoid;
12455 Oid typsendoid;
12456 Oid typmodinoid;
12457 Oid typmodoutoid;
12458 Oid typanalyzeoid;
12459 Oid typsubscriptoid;
12460 char *typcategory;
12461 char *typispreferred;
12462 char *typdelim;
12463 char *typbyval;
12464 char *typalign;
12465 char *typstorage;
12466 char *typcollatable;
12467 char *typdefault;
12468 bool typdefault_is_literal = false;
12469
12471 {
12472 /* Set up query for type-specific details */
12474 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12475 "SELECT typlen, "
12476 "typinput, typoutput, typreceive, typsend, "
12477 "typreceive::pg_catalog.oid AS typreceiveoid, "
12478 "typsend::pg_catalog.oid AS typsendoid, "
12479 "typanalyze, "
12480 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12481 "typdelim, typbyval, typalign, typstorage, "
12482 "typmodin, typmodout, "
12483 "typmodin::pg_catalog.oid AS typmodinoid, "
12484 "typmodout::pg_catalog.oid AS typmodoutoid, "
12485 "typcategory, typispreferred, "
12486 "(typcollation <> 0) AS typcollatable, "
12487 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12488
12489 if (fout->remoteVersion >= 140000)
12491 "typsubscript, "
12492 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12493 else
12495 "'-' AS typsubscript, 0 AS typsubscriptoid ");
12496
12497 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12498 "WHERE oid = $1");
12499
12500 ExecuteSqlStatement(fout, query->data);
12501
12502 fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12503 }
12504
12505 printfPQExpBuffer(query,
12506 "EXECUTE dumpBaseType('%u')",
12507 tyinfo->dobj.catId.oid);
12508
12509 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12510
12511 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12512 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12513 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12514 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12515 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12516 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12517 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12518 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12519 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12520 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12521 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12522 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12523 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12524 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12525 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12526 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12527 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12528 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12529 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12530 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12531 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12532 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12533 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12534 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12535 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12536 {
12537 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12538 typdefault_is_literal = true; /* it needs quotes */
12539 }
12540 else
12541 typdefault = NULL;
12542
12543 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12544 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12545
12546 /*
12547 * The reason we include CASCADE is that the circular dependency between
12548 * the type and its I/O functions makes it impossible to drop the type any
12549 * other way.
12550 */
12551 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12552
12553 /*
12554 * We might already have a shell type, but setting pg_type_oid is
12555 * harmless, and in any case we'd better set the array type OID.
12556 */
12557 if (dopt->binary_upgrade)
12559 tyinfo->dobj.catId.oid,
12560 false, false);
12561
12563 "CREATE TYPE %s (\n"
12564 " INTERNALLENGTH = %s",
12565 qualtypname,
12566 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12567
12568 /* regproc result is sufficiently quoted already */
12569 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12570 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12571 if (OidIsValid(typreceiveoid))
12572 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12573 if (OidIsValid(typsendoid))
12574 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12575 if (OidIsValid(typmodinoid))
12576 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12577 if (OidIsValid(typmodoutoid))
12578 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12579 if (OidIsValid(typanalyzeoid))
12580 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12581
12582 if (strcmp(typcollatable, "t") == 0)
12583 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12584
12585 if (typdefault != NULL)
12586 {
12587 appendPQExpBufferStr(q, ",\n DEFAULT = ");
12588 if (typdefault_is_literal)
12589 appendStringLiteralAH(q, typdefault, fout);
12590 else
12591 appendPQExpBufferStr(q, typdefault);
12592 }
12593
12594 if (OidIsValid(typsubscriptoid))
12595 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12596
12597 if (OidIsValid(tyinfo->typelem))
12598 appendPQExpBuffer(q, ",\n ELEMENT = %s",
12599 getFormattedTypeName(fout, tyinfo->typelem,
12600 zeroIsError));
12601
12602 if (strcmp(typcategory, "U") != 0)
12603 {
12604 appendPQExpBufferStr(q, ",\n CATEGORY = ");
12605 appendStringLiteralAH(q, typcategory, fout);
12606 }
12607
12608 if (strcmp(typispreferred, "t") == 0)
12609 appendPQExpBufferStr(q, ",\n PREFERRED = true");
12610
12611 if (typdelim && strcmp(typdelim, ",") != 0)
12612 {
12613 appendPQExpBufferStr(q, ",\n DELIMITER = ");
12614 appendStringLiteralAH(q, typdelim, fout);
12615 }
12616
12617 if (*typalign == TYPALIGN_CHAR)
12618 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12619 else if (*typalign == TYPALIGN_SHORT)
12620 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12621 else if (*typalign == TYPALIGN_INT)
12622 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12623 else if (*typalign == TYPALIGN_DOUBLE)
12624 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12625
12626 if (*typstorage == TYPSTORAGE_PLAIN)
12627 appendPQExpBufferStr(q, ",\n STORAGE = plain");
12628 else if (*typstorage == TYPSTORAGE_EXTERNAL)
12629 appendPQExpBufferStr(q, ",\n STORAGE = external");
12630 else if (*typstorage == TYPSTORAGE_EXTENDED)
12631 appendPQExpBufferStr(q, ",\n STORAGE = extended");
12632 else if (*typstorage == TYPSTORAGE_MAIN)
12633 appendPQExpBufferStr(q, ",\n STORAGE = main");
12634
12635 if (strcmp(typbyval, "t") == 0)
12636 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12637
12638 appendPQExpBufferStr(q, "\n);\n");
12639
12640 if (dopt->binary_upgrade)
12642 "TYPE", qtypname,
12643 tyinfo->dobj.namespace->dobj.name);
12644
12645 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12646 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12647 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12648 .namespace = tyinfo->dobj.namespace->dobj.name,
12649 .owner = tyinfo->rolname,
12650 .description = "TYPE",
12651 .section = SECTION_PRE_DATA,
12652 .createStmt = q->data,
12653 .dropStmt = delq->data));
12654
12655 /* Dump Type Comments and Security Labels */
12656 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12657 dumpComment(fout, "TYPE", qtypname,
12658 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12659 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12660
12661 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12662 dumpSecLabel(fout, "TYPE", qtypname,
12663 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12664 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12665
12666 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12667 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12668 qtypname, NULL,
12669 tyinfo->dobj.namespace->dobj.name,
12670 NULL, tyinfo->rolname, &tyinfo->dacl);
12671
12672 PQclear(res);
12674 destroyPQExpBuffer(delq);
12675 destroyPQExpBuffer(query);
12676 free(qtypname);
12677 free(qualtypname);
12678}
12679
12680/*
12681 * dumpDomain
12682 * writes out to fout the queries to recreate a user-defined domain
12683 */
12684static void
12685dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12686{
12687 DumpOptions *dopt = fout->dopt;
12691 PGresult *res;
12692 int i;
12693 char *qtypname;
12694 char *qualtypname;
12695 char *typnotnull;
12696 char *typdefn;
12697 char *typdefault;
12698 Oid typcollation;
12699 bool typdefault_is_literal = false;
12700
12702 {
12703 /* Set up query for domain-specific details */
12705 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12706
12707 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12708 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12709 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12710 "t.typdefault, "
12711 "CASE WHEN t.typcollation <> u.typcollation "
12712 "THEN t.typcollation ELSE 0 END AS typcollation "
12713 "FROM pg_catalog.pg_type t "
12714 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12715 "WHERE t.oid = $1");
12716
12717 ExecuteSqlStatement(fout, query->data);
12718
12719 fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12720 }
12721
12722 printfPQExpBuffer(query,
12723 "EXECUTE dumpDomain('%u')",
12724 tyinfo->dobj.catId.oid);
12725
12726 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12727
12728 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12729 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12730 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12731 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12732 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12733 {
12734 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12735 typdefault_is_literal = true; /* it needs quotes */
12736 }
12737 else
12738 typdefault = NULL;
12739 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12740
12741 if (dopt->binary_upgrade)
12743 tyinfo->dobj.catId.oid,
12744 true, /* force array type */
12745 false); /* force multirange type */
12746
12747 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12748 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12749
12751 "CREATE DOMAIN %s AS %s",
12752 qualtypname,
12753 typdefn);
12754
12755 /* Print collation only if different from base type's collation */
12756 if (OidIsValid(typcollation))
12757 {
12758 CollInfo *coll;
12759
12760 coll = findCollationByOid(typcollation);
12761 if (coll)
12762 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12763 }
12764
12765 /*
12766 * Print a not-null constraint if there's one. In servers older than 17
12767 * these don't have names, so just print it unadorned; in newer ones they
12768 * do, but most of the time it's going to be the standard generated one,
12769 * so omit the name in that case also.
12770 */
12771 if (typnotnull[0] == 't')
12772 {
12773 if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12774 appendPQExpBufferStr(q, " NOT NULL");
12775 else
12776 {
12777 ConstraintInfo *notnull = tyinfo->notnull;
12778
12779 if (!notnull->separate)
12780 {
12781 char *default_name;
12782
12783 /* XXX should match ChooseConstraintName better */
12784 default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12785
12786 if (strcmp(default_name, notnull->dobj.name) == 0)
12787 appendPQExpBufferStr(q, " NOT NULL");
12788 else
12789 appendPQExpBuffer(q, " CONSTRAINT %s %s",
12790 fmtId(notnull->dobj.name), notnull->condef);
12791 free(default_name);
12792 }
12793 }
12794 }
12795
12796 if (typdefault != NULL)
12797 {
12798 appendPQExpBufferStr(q, " DEFAULT ");
12799 if (typdefault_is_literal)
12800 appendStringLiteralAH(q, typdefault, fout);
12801 else
12802 appendPQExpBufferStr(q, typdefault);
12803 }
12804
12805 PQclear(res);
12806
12807 /*
12808 * Add any CHECK constraints for the domain
12809 */
12810 for (i = 0; i < tyinfo->nDomChecks; i++)
12811 {
12812 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12813
12814 if (!domcheck->separate && domcheck->contype == 'c')
12815 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12816 fmtId(domcheck->dobj.name), domcheck->condef);
12817 }
12818
12819 appendPQExpBufferStr(q, ";\n");
12820
12821 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12822
12823 if (dopt->binary_upgrade)
12825 "DOMAIN", qtypname,
12826 tyinfo->dobj.namespace->dobj.name);
12827
12828 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12829 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12830 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12831 .namespace = tyinfo->dobj.namespace->dobj.name,
12832 .owner = tyinfo->rolname,
12833 .description = "DOMAIN",
12834 .section = SECTION_PRE_DATA,
12835 .createStmt = q->data,
12836 .dropStmt = delq->data));
12837
12838 /* Dump Domain Comments and Security Labels */
12839 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12840 dumpComment(fout, "DOMAIN", qtypname,
12841 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12842 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12843
12844 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12845 dumpSecLabel(fout, "DOMAIN", qtypname,
12846 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12847 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12848
12849 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12850 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12851 qtypname, NULL,
12852 tyinfo->dobj.namespace->dobj.name,
12853 NULL, tyinfo->rolname, &tyinfo->dacl);
12854
12855 /* Dump any per-constraint comments */
12856 for (i = 0; i < tyinfo->nDomChecks; i++)
12857 {
12858 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12859 PQExpBuffer conprefix;
12860
12861 /* but only if the constraint itself was dumped here */
12862 if (domcheck->separate)
12863 continue;
12864
12865 conprefix = createPQExpBuffer();
12866 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12867 fmtId(domcheck->dobj.name));
12868
12869 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12870 dumpComment(fout, conprefix->data, qtypname,
12871 tyinfo->dobj.namespace->dobj.name,
12872 tyinfo->rolname,
12873 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12874
12875 destroyPQExpBuffer(conprefix);
12876 }
12877
12878 /*
12879 * And a comment on the not-null constraint, if there's one -- but only if
12880 * the constraint itself was dumped here
12881 */
12882 if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12883 {
12884 PQExpBuffer conprefix = createPQExpBuffer();
12885
12886 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12887 fmtId(tyinfo->notnull->dobj.name));
12888
12889 if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12890 dumpComment(fout, conprefix->data, qtypname,
12891 tyinfo->dobj.namespace->dobj.name,
12892 tyinfo->rolname,
12893 tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12894 destroyPQExpBuffer(conprefix);
12895 }
12896
12898 destroyPQExpBuffer(delq);
12899 destroyPQExpBuffer(query);
12900 free(qtypname);
12901 free(qualtypname);
12902}
12903
12904/*
12905 * dumpCompositeType
12906 * writes out to fout the queries to recreate a user-defined stand-alone
12907 * composite type
12908 */
12909static void
12911{
12912 DumpOptions *dopt = fout->dopt;
12914 PQExpBuffer dropped = createPQExpBuffer();
12917 PGresult *res;
12918 char *qtypname;
12919 char *qualtypname;
12920 int ntups;
12921 int i_attname;
12922 int i_atttypdefn;
12923 int i_attlen;
12924 int i_attalign;
12925 int i_attisdropped;
12926 int i_attcollation;
12927 int i;
12928 int actual_atts;
12929
12931 {
12932 /*
12933 * Set up query for type-specific details.
12934 *
12935 * Since we only want to dump COLLATE clauses for attributes whose
12936 * collation is different from their type's default, we use a CASE
12937 * here to suppress uninteresting attcollations cheaply. atttypid
12938 * will be 0 for dropped columns; collation does not matter for those.
12939 */
12941 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12942 "SELECT a.attname, a.attnum, "
12943 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12944 "a.attlen, a.attalign, a.attisdropped, "
12945 "CASE WHEN a.attcollation <> at.typcollation "
12946 "THEN a.attcollation ELSE 0 END AS attcollation "
12947 "FROM pg_catalog.pg_type ct "
12948 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12949 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12950 "WHERE ct.oid = $1 "
12951 "ORDER BY a.attnum");
12952
12953 ExecuteSqlStatement(fout, query->data);
12954
12956 }
12957
12958 printfPQExpBuffer(query,
12959 "EXECUTE dumpCompositeType('%u')",
12960 tyinfo->dobj.catId.oid);
12961
12962 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12963
12964 ntups = PQntuples(res);
12965
12966 i_attname = PQfnumber(res, "attname");
12967 i_atttypdefn = PQfnumber(res, "atttypdefn");
12968 i_attlen = PQfnumber(res, "attlen");
12969 i_attalign = PQfnumber(res, "attalign");
12970 i_attisdropped = PQfnumber(res, "attisdropped");
12971 i_attcollation = PQfnumber(res, "attcollation");
12972
12973 if (dopt->binary_upgrade)
12974 {
12976 tyinfo->dobj.catId.oid,
12977 false, false);
12979 }
12980
12981 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12982 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12983
12984 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12985 qualtypname);
12986
12987 actual_atts = 0;
12988 for (i = 0; i < ntups; i++)
12989 {
12990 char *attname;
12991 char *atttypdefn;
12992 char *attlen;
12993 char *attalign;
12994 bool attisdropped;
12995 Oid attcollation;
12996
12997 attname = PQgetvalue(res, i, i_attname);
12998 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12999 attlen = PQgetvalue(res, i, i_attlen);
13000 attalign = PQgetvalue(res, i, i_attalign);
13001 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13002 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13003
13004 if (attisdropped && !dopt->binary_upgrade)
13005 continue;
13006
13007 /* Format properly if not first attr */
13008 if (actual_atts++ > 0)
13009 appendPQExpBufferChar(q, ',');
13010 appendPQExpBufferStr(q, "\n\t");
13011
13012 if (!attisdropped)
13013 {
13014 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13015
13016 /* Add collation if not default for the column type */
13017 if (OidIsValid(attcollation))
13018 {
13019 CollInfo *coll;
13020
13021 coll = findCollationByOid(attcollation);
13022 if (coll)
13023 appendPQExpBuffer(q, " COLLATE %s",
13024 fmtQualifiedDumpable(coll));
13025 }
13026 }
13027 else
13028 {
13029 /*
13030 * This is a dropped attribute and we're in binary_upgrade mode.
13031 * Insert a placeholder for it in the CREATE TYPE command, and set
13032 * length and alignment with direct UPDATE to the catalogs
13033 * afterwards. See similar code in dumpTableSchema().
13034 */
13035 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13036
13037 /* stash separately for insertion after the CREATE TYPE */
13038 appendPQExpBufferStr(dropped,
13039 "\n-- For binary upgrade, recreate dropped column.\n");
13040 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13041 "SET attlen = %s, "
13042 "attalign = '%s', attbyval = false\n"
13043 "WHERE attname = ", attlen, attalign);
13044 appendStringLiteralAH(dropped, attname, fout);
13045 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13046 appendStringLiteralAH(dropped, qualtypname, fout);
13047 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13048
13049 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13050 qualtypname);
13051 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13052 fmtId(attname));
13053 }
13054 }
13055 appendPQExpBufferStr(q, "\n);\n");
13056 appendPQExpBufferStr(q, dropped->data);
13057
13058 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13059
13060 if (dopt->binary_upgrade)
13062 "TYPE", qtypname,
13063 tyinfo->dobj.namespace->dobj.name);
13064
13065 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13066 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13067 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13068 .namespace = tyinfo->dobj.namespace->dobj.name,
13069 .owner = tyinfo->rolname,
13070 .description = "TYPE",
13071 .section = SECTION_PRE_DATA,
13072 .createStmt = q->data,
13073 .dropStmt = delq->data));
13074
13075
13076 /* Dump Type Comments and Security Labels */
13077 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13078 dumpComment(fout, "TYPE", qtypname,
13079 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13080 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13081
13082 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13083 dumpSecLabel(fout, "TYPE", qtypname,
13084 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13085 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13086
13087 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13088 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13089 qtypname, NULL,
13090 tyinfo->dobj.namespace->dobj.name,
13091 NULL, tyinfo->rolname, &tyinfo->dacl);
13092
13093 /* Dump any per-column comments */
13094 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13095 dumpCompositeTypeColComments(fout, tyinfo, res);
13096
13097 PQclear(res);
13099 destroyPQExpBuffer(dropped);
13100 destroyPQExpBuffer(delq);
13101 destroyPQExpBuffer(query);
13102 free(qtypname);
13103 free(qualtypname);
13104}
13105
13106/*
13107 * dumpCompositeTypeColComments
13108 * writes out to fout the queries to recreate comments on the columns of
13109 * a user-defined stand-alone composite type.
13110 *
13111 * The caller has already made a query to collect the names and attnums
13112 * of the type's columns, so we just pass that result into here rather
13113 * than reading them again.
13114 */
13115static void
13117 PGresult *res)
13118{
13120 int ncomments;
13121 PQExpBuffer query;
13122 PQExpBuffer target;
13123 int i;
13124 int ntups;
13125 int i_attname;
13126 int i_attnum;
13127 int i_attisdropped;
13128
13129 /* do nothing, if --no-comments is supplied */
13130 if (fout->dopt->no_comments)
13131 return;
13132
13133 /* Search for comments associated with type's pg_class OID */
13134 ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13135 &comments);
13136
13137 /* If no comments exist, we're done */
13138 if (ncomments <= 0)
13139 return;
13140
13141 /* Build COMMENT ON statements */
13142 query = createPQExpBuffer();
13143 target = createPQExpBuffer();
13144
13145 ntups = PQntuples(res);
13146 i_attnum = PQfnumber(res, "attnum");
13147 i_attname = PQfnumber(res, "attname");
13148 i_attisdropped = PQfnumber(res, "attisdropped");
13149 while (ncomments > 0)
13150 {
13151 const char *attname;
13152
13153 attname = NULL;
13154 for (i = 0; i < ntups; i++)
13155 {
13156 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13157 PQgetvalue(res, i, i_attisdropped)[0] != 't')
13158 {
13159 attname = PQgetvalue(res, i, i_attname);
13160 break;
13161 }
13162 }
13163 if (attname) /* just in case we don't find it */
13164 {
13165 const char *descr = comments->descr;
13166
13167 resetPQExpBuffer(target);
13168 appendPQExpBuffer(target, "COLUMN %s.",
13169 fmtId(tyinfo->dobj.name));
13171
13172 resetPQExpBuffer(query);
13173 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13174 fmtQualifiedDumpable(tyinfo));
13175 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13176 appendStringLiteralAH(query, descr, fout);
13177 appendPQExpBufferStr(query, ";\n");
13178
13180 ARCHIVE_OPTS(.tag = target->data,
13181 .namespace = tyinfo->dobj.namespace->dobj.name,
13182 .owner = tyinfo->rolname,
13183 .description = "COMMENT",
13184 .section = SECTION_NONE,
13185 .createStmt = query->data,
13186 .deps = &(tyinfo->dobj.dumpId),
13187 .nDeps = 1));
13188 }
13189
13190 comments++;
13191 ncomments--;
13192 }
13193
13194 destroyPQExpBuffer(query);
13195 destroyPQExpBuffer(target);
13196}
13197
13198/*
13199 * dumpShellType
13200 * writes out to fout the queries to create a shell type
13201 *
13202 * We dump a shell definition in advance of the I/O functions for the type.
13203 */
13204static void
13206{
13207 DumpOptions *dopt = fout->dopt;
13208 PQExpBuffer q;
13209
13210 /* Do nothing if not dumping schema */
13211 if (!dopt->dumpSchema)
13212 return;
13213
13214 q = createPQExpBuffer();
13215
13216 /*
13217 * Note the lack of a DROP command for the shell type; any required DROP
13218 * is driven off the base type entry, instead. This interacts with
13219 * _printTocEntry()'s use of the presence of a DROP command to decide
13220 * whether an entry needs an ALTER OWNER command. We don't want to alter
13221 * the shell type's owner immediately on creation; that should happen only
13222 * after it's filled in, otherwise the backend complains.
13223 */
13224
13225 if (dopt->binary_upgrade)
13227 stinfo->baseType->dobj.catId.oid,
13228 false, false);
13229
13230 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13231 fmtQualifiedDumpable(stinfo));
13232
13233 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13234 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13235 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13236 .namespace = stinfo->dobj.namespace->dobj.name,
13237 .owner = stinfo->baseType->rolname,
13238 .description = "SHELL TYPE",
13239 .section = SECTION_PRE_DATA,
13240 .createStmt = q->data));
13241
13243}
13244
13245/*
13246 * dumpProcLang
13247 * writes out to fout the queries to recreate a user-defined
13248 * procedural language
13249 */
13250static void
13252{
13253 DumpOptions *dopt = fout->dopt;
13254 PQExpBuffer defqry;
13255 PQExpBuffer delqry;
13256 bool useParams;
13257 char *qlanname;
13258 FuncInfo *funcInfo;
13259 FuncInfo *inlineInfo = NULL;
13260 FuncInfo *validatorInfo = NULL;
13261
13262 /* Do nothing if not dumping schema */
13263 if (!dopt->dumpSchema)
13264 return;
13265
13266 /*
13267 * Try to find the support function(s). It is not an error if we don't
13268 * find them --- if the functions are in the pg_catalog schema, as is
13269 * standard in 8.1 and up, then we won't have loaded them. (In this case
13270 * we will emit a parameterless CREATE LANGUAGE command, which will
13271 * require PL template knowledge in the backend to reload.)
13272 */
13273
13274 funcInfo = findFuncByOid(plang->lanplcallfoid);
13275 if (funcInfo != NULL && !funcInfo->dobj.dump)
13276 funcInfo = NULL; /* treat not-dumped same as not-found */
13277
13278 if (OidIsValid(plang->laninline))
13279 {
13280 inlineInfo = findFuncByOid(plang->laninline);
13281 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13282 inlineInfo = NULL;
13283 }
13284
13285 if (OidIsValid(plang->lanvalidator))
13286 {
13287 validatorInfo = findFuncByOid(plang->lanvalidator);
13288 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13289 validatorInfo = NULL;
13290 }
13291
13292 /*
13293 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13294 * parameters. Otherwise, we'll write a parameterless command, which will
13295 * be interpreted as CREATE EXTENSION.
13296 */
13297 useParams = (funcInfo != NULL &&
13298 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13299 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13300
13301 defqry = createPQExpBuffer();
13302 delqry = createPQExpBuffer();
13303
13304 qlanname = pg_strdup(fmtId(plang->dobj.name));
13305
13306 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13307 qlanname);
13308
13309 if (useParams)
13310 {
13311 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13312 plang->lanpltrusted ? "TRUSTED " : "",
13313 qlanname);
13314 appendPQExpBuffer(defqry, " HANDLER %s",
13315 fmtQualifiedDumpable(funcInfo));
13316 if (OidIsValid(plang->laninline))
13317 appendPQExpBuffer(defqry, " INLINE %s",
13318 fmtQualifiedDumpable(inlineInfo));
13319 if (OidIsValid(plang->lanvalidator))
13320 appendPQExpBuffer(defqry, " VALIDATOR %s",
13321 fmtQualifiedDumpable(validatorInfo));
13322 }
13323 else
13324 {
13325 /*
13326 * If not dumping parameters, then use CREATE OR REPLACE so that the
13327 * command will not fail if the language is preinstalled in the target
13328 * database.
13329 *
13330 * Modern servers will interpret this as CREATE EXTENSION IF NOT
13331 * EXISTS; perhaps we should emit that instead? But it might just add
13332 * confusion.
13333 */
13334 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13335 qlanname);
13336 }
13337 appendPQExpBufferStr(defqry, ";\n");
13338
13339 if (dopt->binary_upgrade)
13340 binary_upgrade_extension_member(defqry, &plang->dobj,
13341 "LANGUAGE", qlanname, NULL);
13342
13343 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13344 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13345 ARCHIVE_OPTS(.tag = plang->dobj.name,
13346 .owner = plang->lanowner,
13347 .description = "PROCEDURAL LANGUAGE",
13348 .section = SECTION_PRE_DATA,
13349 .createStmt = defqry->data,
13350 .dropStmt = delqry->data,
13351 ));
13352
13353 /* Dump Proc Lang Comments and Security Labels */
13354 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13355 dumpComment(fout, "LANGUAGE", qlanname,
13356 NULL, plang->lanowner,
13357 plang->dobj.catId, 0, plang->dobj.dumpId);
13358
13359 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13360 dumpSecLabel(fout, "LANGUAGE", qlanname,
13361 NULL, plang->lanowner,
13362 plang->dobj.catId, 0, plang->dobj.dumpId);
13363
13364 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13365 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13366 qlanname, NULL, NULL,
13367 NULL, plang->lanowner, &plang->dacl);
13368
13369 free(qlanname);
13370
13371 destroyPQExpBuffer(defqry);
13372 destroyPQExpBuffer(delqry);
13373}
13374
13375/*
13376 * format_function_arguments: generate function name and argument list
13377 *
13378 * This is used when we can rely on pg_get_function_arguments to format
13379 * the argument list. Note, however, that pg_get_function_arguments
13380 * does not special-case zero-argument aggregates.
13381 */
13382static char *
13383format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13384{
13386
13389 if (is_agg && finfo->nargs == 0)
13390 appendPQExpBufferStr(&fn, "(*)");
13391 else
13392 appendPQExpBuffer(&fn, "(%s)", funcargs);
13393 return fn.data;
13394}
13395
13396/*
13397 * format_function_signature: generate function name and argument list
13398 *
13399 * Only a minimal list of input argument types is generated; this is
13400 * sufficient to reference the function, but not to define it.
13401 *
13402 * If honor_quotes is false then the function name is never quoted.
13403 * This is appropriate for use in TOC tags, but not in SQL commands.
13404 */
13405static char *
13406format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13407{
13409 int j;
13410
13412 if (honor_quotes)
13413 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13414 else
13415 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13416 for (j = 0; j < finfo->nargs; j++)
13417 {
13418 if (j > 0)
13419 appendPQExpBufferStr(&fn, ", ");
13420
13422 getFormattedTypeName(fout, finfo->argtypes[j],
13423 zeroIsError));
13424 }
13426 return fn.data;
13427}
13428
13429
13430/*
13431 * dumpFunc:
13432 * dump out one function
13433 */
13434static void
13435dumpFunc(Archive *fout, const FuncInfo *finfo)
13436{
13437 DumpOptions *dopt = fout->dopt;
13438 PQExpBuffer query;
13439 PQExpBuffer q;
13440 PQExpBuffer delqry;
13441 PQExpBuffer asPart;
13442 PGresult *res;
13443 char *funcsig; /* identity signature */
13444 char *funcfullsig = NULL; /* full signature */
13445 char *funcsig_tag;
13446 char *qual_funcsig;
13447 char *proretset;
13448 char *prosrc;
13449 char *probin;
13450 char *prosqlbody;
13451 char *funcargs;
13452 char *funciargs;
13453 char *funcresult;
13454 char *protrftypes;
13455 char *prokind;
13456 char *provolatile;
13457 char *proisstrict;
13458 char *prosecdef;
13459 char *proleakproof;
13460 char *proconfig;
13461 char *procost;
13462 char *prorows;
13463 char *prosupport;
13464 char *proparallel;
13465 char *lanname;
13466 char **configitems = NULL;
13467 int nconfigitems = 0;
13468 const char *keyword;
13469
13470 /* Do nothing if not dumping schema */
13471 if (!dopt->dumpSchema)
13472 return;
13473
13474 query = createPQExpBuffer();
13475 q = createPQExpBuffer();
13476 delqry = createPQExpBuffer();
13477 asPart = createPQExpBuffer();
13478
13479 if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13480 {
13481 /* Set up query for function-specific details */
13483 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13484
13486 "SELECT\n"
13487 "proretset,\n"
13488 "prosrc,\n"
13489 "probin,\n"
13490 "provolatile,\n"
13491 "proisstrict,\n"
13492 "prosecdef,\n"
13493 "lanname,\n"
13494 "proconfig,\n"
13495 "procost,\n"
13496 "prorows,\n"
13497 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13498 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13499 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13500 "proleakproof,\n");
13501
13502 if (fout->remoteVersion >= 90500)
13504 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13505 else
13507 "NULL AS protrftypes,\n");
13508
13509 if (fout->remoteVersion >= 90600)
13511 "proparallel,\n");
13512 else
13514 "'u' AS proparallel,\n");
13515
13516 if (fout->remoteVersion >= 110000)
13518 "prokind,\n");
13519 else
13521 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13522
13523 if (fout->remoteVersion >= 120000)
13525 "prosupport,\n");
13526 else
13528 "'-' AS prosupport,\n");
13529
13530 if (fout->remoteVersion >= 140000)
13532 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13533 else
13535 "NULL AS prosqlbody\n");
13536
13538 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13539 "WHERE p.oid = $1 "
13540 "AND l.oid = p.prolang");
13541
13542 ExecuteSqlStatement(fout, query->data);
13543
13544 fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13545 }
13546
13547 printfPQExpBuffer(query,
13548 "EXECUTE dumpFunc('%u')",
13549 finfo->dobj.catId.oid);
13550
13551 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13552
13553 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13554 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13555 {
13556 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13557 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13558 prosqlbody = NULL;
13559 }
13560 else
13561 {
13562 prosrc = NULL;
13563 probin = NULL;
13564 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13565 }
13566 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13567 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13568 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13569 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13570 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13571 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13572 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13573 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13574 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13575 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13576 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13577 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13578 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13579 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13580 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13581
13582 /*
13583 * See backend/commands/functioncmds.c for details of how the 'AS' clause
13584 * is used.
13585 */
13586 if (prosqlbody)
13587 {
13588 appendPQExpBufferStr(asPart, prosqlbody);
13589 }
13590 else if (probin[0] != '\0')
13591 {
13592 appendPQExpBufferStr(asPart, "AS ");
13593 appendStringLiteralAH(asPart, probin, fout);
13594 if (prosrc[0] != '\0')
13595 {
13596 appendPQExpBufferStr(asPart, ", ");
13597
13598 /*
13599 * where we have bin, use dollar quoting if allowed and src
13600 * contains quote or backslash; else use regular quoting.
13601 */
13602 if (dopt->disable_dollar_quoting ||
13603 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13604 appendStringLiteralAH(asPart, prosrc, fout);
13605 else
13606 appendStringLiteralDQ(asPart, prosrc, NULL);
13607 }
13608 }
13609 else
13610 {
13611 appendPQExpBufferStr(asPart, "AS ");
13612 /* with no bin, dollar quote src unconditionally if allowed */
13613 if (dopt->disable_dollar_quoting)
13614 appendStringLiteralAH(asPart, prosrc, fout);
13615 else
13616 appendStringLiteralDQ(asPart, prosrc, NULL);
13617 }
13618
13619 if (*proconfig)
13620 {
13621 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13622 pg_fatal("could not parse %s array", "proconfig");
13623 }
13624 else
13625 {
13626 configitems = NULL;
13627 nconfigitems = 0;
13628 }
13629
13630 funcfullsig = format_function_arguments(finfo, funcargs, false);
13631 funcsig = format_function_arguments(finfo, funciargs, false);
13632
13633 funcsig_tag = format_function_signature(fout, finfo, false);
13634
13635 qual_funcsig = psprintf("%s.%s",
13636 fmtId(finfo->dobj.namespace->dobj.name),
13637 funcsig);
13638
13639 if (prokind[0] == PROKIND_PROCEDURE)
13640 keyword = "PROCEDURE";
13641 else
13642 keyword = "FUNCTION"; /* works for window functions too */
13643
13644 appendPQExpBuffer(delqry, "DROP %s %s;\n",
13645 keyword, qual_funcsig);
13646
13647 appendPQExpBuffer(q, "CREATE %s %s.%s",
13648 keyword,
13649 fmtId(finfo->dobj.namespace->dobj.name),
13650 funcfullsig ? funcfullsig :
13651 funcsig);
13652
13653 if (prokind[0] == PROKIND_PROCEDURE)
13654 /* no result type to output */ ;
13655 else if (funcresult)
13656 appendPQExpBuffer(q, " RETURNS %s", funcresult);
13657 else
13658 appendPQExpBuffer(q, " RETURNS %s%s",
13659 (proretset[0] == 't') ? "SETOF " : "",
13660 getFormattedTypeName(fout, finfo->prorettype,
13661 zeroIsError));
13662
13663 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13664
13665 if (*protrftypes)
13666 {
13667 Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13668 int i;
13669
13670 appendPQExpBufferStr(q, " TRANSFORM ");
13671 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13672 for (i = 0; typeids[i]; i++)
13673 {
13674 if (i != 0)
13675 appendPQExpBufferStr(q, ", ");
13676 appendPQExpBuffer(q, "FOR TYPE %s",
13677 getFormattedTypeName(fout, typeids[i], zeroAsNone));
13678 }
13679
13680 free(typeids);
13681 }
13682
13683 if (prokind[0] == PROKIND_WINDOW)
13684 appendPQExpBufferStr(q, " WINDOW");
13685
13686 if (provolatile[0] != PROVOLATILE_VOLATILE)
13687 {
13688 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13689 appendPQExpBufferStr(q, " IMMUTABLE");
13690 else if (provolatile[0] == PROVOLATILE_STABLE)
13691 appendPQExpBufferStr(q, " STABLE");
13692 else if (provolatile[0] != PROVOLATILE_VOLATILE)
13693 pg_fatal("unrecognized provolatile value for function \"%s\"",
13694 finfo->dobj.name);
13695 }
13696
13697 if (proisstrict[0] == 't')
13698 appendPQExpBufferStr(q, " STRICT");
13699
13700 if (prosecdef[0] == 't')
13701 appendPQExpBufferStr(q, " SECURITY DEFINER");
13702
13703 if (proleakproof[0] == 't')
13704 appendPQExpBufferStr(q, " LEAKPROOF");
13705
13706 /*
13707 * COST and ROWS are emitted only if present and not default, so as not to
13708 * break backwards-compatibility of the dump without need. Keep this code
13709 * in sync with the defaults in functioncmds.c.
13710 */
13711 if (strcmp(procost, "0") != 0)
13712 {
13713 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13714 {
13715 /* default cost is 1 */
13716 if (strcmp(procost, "1") != 0)
13717 appendPQExpBuffer(q, " COST %s", procost);
13718 }
13719 else
13720 {
13721 /* default cost is 100 */
13722 if (strcmp(procost, "100") != 0)
13723 appendPQExpBuffer(q, " COST %s", procost);
13724 }
13725 }
13726 if (proretset[0] == 't' &&
13727 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13728 appendPQExpBuffer(q, " ROWS %s", prorows);
13729
13730 if (strcmp(prosupport, "-") != 0)
13731 {
13732 /* We rely on regprocout to provide quoting and qualification */
13733 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13734 }
13735
13736 if (proparallel[0] != PROPARALLEL_UNSAFE)
13737 {
13738 if (proparallel[0] == PROPARALLEL_SAFE)
13739 appendPQExpBufferStr(q, " PARALLEL SAFE");
13740 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13741 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13742 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13743 pg_fatal("unrecognized proparallel value for function \"%s\"",
13744 finfo->dobj.name);
13745 }
13746
13747 for (int i = 0; i < nconfigitems; i++)
13748 {
13749 /* we feel free to scribble on configitems[] here */
13750 char *configitem = configitems[i];
13751 char *pos;
13752
13753 pos = strchr(configitem, '=');
13754 if (pos == NULL)
13755 continue;
13756 *pos++ = '\0';
13757 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13758
13759 /*
13760 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13761 * by flatten_set_variable_args() before they were put into the
13762 * proconfig array. However, because the quoting rules used there
13763 * aren't exactly like SQL's, we have to break the list value apart
13764 * and then quote the elements as string literals. (The elements may
13765 * be double-quoted as-is, but we can't just feed them to the SQL
13766 * parser; it would do the wrong thing with elements that are
13767 * zero-length or longer than NAMEDATALEN.) Also, we need a special
13768 * case for empty lists.
13769 *
13770 * Variables that are not so marked should just be emitted as simple
13771 * string literals. If the variable is not known to
13772 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13773 * to use GUC_LIST_QUOTE for extension variables.
13774 */
13775 if (variable_is_guc_list_quote(configitem))
13776 {
13777 char **namelist;
13778 char **nameptr;
13779
13780 /* Parse string into list of identifiers */
13781 /* this shouldn't fail really */
13782 if (SplitGUCList(pos, ',', &namelist))
13783 {
13784 /* Special case: represent an empty list as NULL */
13785 if (*namelist == NULL)
13786 appendPQExpBufferStr(q, "NULL");
13787 for (nameptr = namelist; *nameptr; nameptr++)
13788 {
13789 if (nameptr != namelist)
13790 appendPQExpBufferStr(q, ", ");
13791 appendStringLiteralAH(q, *nameptr, fout);
13792 }
13793 }
13794 pg_free(namelist);
13795 }
13796 else
13797 appendStringLiteralAH(q, pos, fout);
13798 }
13799
13800 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13801
13802 append_depends_on_extension(fout, q, &finfo->dobj,
13803 "pg_catalog.pg_proc", keyword,
13804 qual_funcsig);
13805
13806 if (dopt->binary_upgrade)
13808 keyword, funcsig,
13809 finfo->dobj.namespace->dobj.name);
13810
13811 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13812 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13813 ARCHIVE_OPTS(.tag = funcsig_tag,
13814 .namespace = finfo->dobj.namespace->dobj.name,
13815 .owner = finfo->rolname,
13816 .description = keyword,
13817 .section = finfo->postponed_def ?
13819 .createStmt = q->data,
13820 .dropStmt = delqry->data));
13821
13822 /* Dump Function Comments and Security Labels */
13823 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13824 dumpComment(fout, keyword, funcsig,
13825 finfo->dobj.namespace->dobj.name, finfo->rolname,
13826 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13827
13828 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13829 dumpSecLabel(fout, keyword, funcsig,
13830 finfo->dobj.namespace->dobj.name, finfo->rolname,
13831 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13832
13833 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13834 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13835 funcsig, NULL,
13836 finfo->dobj.namespace->dobj.name,
13837 NULL, finfo->rolname, &finfo->dacl);
13838
13839 PQclear(res);
13840
13841 destroyPQExpBuffer(query);
13843 destroyPQExpBuffer(delqry);
13844 destroyPQExpBuffer(asPart);
13845 free(funcsig);
13846 free(funcfullsig);
13847 free(funcsig_tag);
13848 free(qual_funcsig);
13849 free(configitems);
13850}
13851
13852
13853/*
13854 * Dump a user-defined cast
13855 */
13856static void
13857dumpCast(Archive *fout, const CastInfo *cast)
13858{
13859 DumpOptions *dopt = fout->dopt;
13860 PQExpBuffer defqry;
13861 PQExpBuffer delqry;
13862 PQExpBuffer labelq;
13863 PQExpBuffer castargs;
13864 FuncInfo *funcInfo = NULL;
13865 const char *sourceType;
13866 const char *targetType;
13867
13868 /* Do nothing if not dumping schema */
13869 if (!dopt->dumpSchema)
13870 return;
13871
13872 /* Cannot dump if we don't have the cast function's info */
13873 if (OidIsValid(cast->castfunc))
13874 {
13875 funcInfo = findFuncByOid(cast->castfunc);
13876 if (funcInfo == NULL)
13877 pg_fatal("could not find function definition for function with OID %u",
13878 cast->castfunc);
13879 }
13880
13881 defqry = createPQExpBuffer();
13882 delqry = createPQExpBuffer();
13883 labelq = createPQExpBuffer();
13884 castargs = createPQExpBuffer();
13885
13886 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13887 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13888 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13889 sourceType, targetType);
13890
13891 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13892 sourceType, targetType);
13893
13894 switch (cast->castmethod)
13895 {
13896 case COERCION_METHOD_BINARY:
13897 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13898 break;
13899 case COERCION_METHOD_INOUT:
13900 appendPQExpBufferStr(defqry, "WITH INOUT");
13901 break;
13902 case COERCION_METHOD_FUNCTION:
13903 if (funcInfo)
13904 {
13905 char *fsig = format_function_signature(fout, funcInfo, true);
13906
13907 /*
13908 * Always qualify the function name (format_function_signature
13909 * won't qualify it).
13910 */
13911 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13912 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13913 free(fsig);
13914 }
13915 else
13916 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13917 break;
13918 default:
13919 pg_log_warning("bogus value in pg_cast.castmethod field");
13920 }
13921
13922 if (cast->castcontext == 'a')
13923 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13924 else if (cast->castcontext == 'i')
13925 appendPQExpBufferStr(defqry, " AS IMPLICIT");
13926 appendPQExpBufferStr(defqry, ";\n");
13927
13928 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13929 sourceType, targetType);
13930
13931 appendPQExpBuffer(castargs, "(%s AS %s)",
13932 sourceType, targetType);
13933
13934 if (dopt->binary_upgrade)
13936 "CAST", castargs->data, NULL);
13937
13939 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13940 ARCHIVE_OPTS(.tag = labelq->data,
13941 .description = "CAST",
13942 .section = SECTION_PRE_DATA,
13943 .createStmt = defqry->data,
13944 .dropStmt = delqry->data));
13945
13946 /* Dump Cast Comments */
13947 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13948 dumpComment(fout, "CAST", castargs->data,
13949 NULL, "",
13950 cast->dobj.catId, 0, cast->dobj.dumpId);
13951
13952 destroyPQExpBuffer(defqry);
13953 destroyPQExpBuffer(delqry);
13954 destroyPQExpBuffer(labelq);
13955 destroyPQExpBuffer(castargs);
13956}
13957
13958/*
13959 * Dump a transform
13960 */
13961static void
13962dumpTransform(Archive *fout, const TransformInfo *transform)
13963{
13964 DumpOptions *dopt = fout->dopt;
13965 PQExpBuffer defqry;
13966 PQExpBuffer delqry;
13967 PQExpBuffer labelq;
13968 PQExpBuffer transformargs;
13969 FuncInfo *fromsqlFuncInfo = NULL;
13970 FuncInfo *tosqlFuncInfo = NULL;
13971 char *lanname;
13972 const char *transformType;
13973
13974 /* Do nothing if not dumping schema */
13975 if (!dopt->dumpSchema)
13976 return;
13977
13978 /* Cannot dump if we don't have the transform functions' info */
13979 if (OidIsValid(transform->trffromsql))
13980 {
13981 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13982 if (fromsqlFuncInfo == NULL)
13983 pg_fatal("could not find function definition for function with OID %u",
13984 transform->trffromsql);
13985 }
13986 if (OidIsValid(transform->trftosql))
13987 {
13988 tosqlFuncInfo = findFuncByOid(transform->trftosql);
13989 if (tosqlFuncInfo == NULL)
13990 pg_fatal("could not find function definition for function with OID %u",
13991 transform->trftosql);
13992 }
13993
13994 defqry = createPQExpBuffer();
13995 delqry = createPQExpBuffer();
13996 labelq = createPQExpBuffer();
13997 transformargs = createPQExpBuffer();
13998
13999 lanname = get_language_name(fout, transform->trflang);
14000 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14001
14002 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14003 transformType, lanname);
14004
14005 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14006 transformType, lanname);
14007
14008 if (!transform->trffromsql && !transform->trftosql)
14009 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14010
14011 if (transform->trffromsql)
14012 {
14013 if (fromsqlFuncInfo)
14014 {
14015 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14016
14017 /*
14018 * Always qualify the function name (format_function_signature
14019 * won't qualify it).
14020 */
14021 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14022 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14023 free(fsig);
14024 }
14025 else
14026 pg_log_warning("bogus value in pg_transform.trffromsql field");
14027 }
14028
14029 if (transform->trftosql)
14030 {
14031 if (transform->trffromsql)
14032 appendPQExpBufferStr(defqry, ", ");
14033
14034 if (tosqlFuncInfo)
14035 {
14036 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14037
14038 /*
14039 * Always qualify the function name (format_function_signature
14040 * won't qualify it).
14041 */
14042 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14043 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14044 free(fsig);
14045 }
14046 else
14047 pg_log_warning("bogus value in pg_transform.trftosql field");
14048 }
14049
14050 appendPQExpBufferStr(defqry, ");\n");
14051
14052 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14053 transformType, lanname);
14054
14055 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14056 transformType, lanname);
14057
14058 if (dopt->binary_upgrade)
14059 binary_upgrade_extension_member(defqry, &transform->dobj,
14060 "TRANSFORM", transformargs->data, NULL);
14061
14062 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14063 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14064 ARCHIVE_OPTS(.tag = labelq->data,
14065 .description = "TRANSFORM",
14066 .section = SECTION_PRE_DATA,
14067 .createStmt = defqry->data,
14068 .dropStmt = delqry->data,
14069 .deps = transform->dobj.dependencies,
14070 .nDeps = transform->dobj.nDeps));
14071
14072 /* Dump Transform Comments */
14073 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14074 dumpComment(fout, "TRANSFORM", transformargs->data,
14075 NULL, "",
14076 transform->dobj.catId, 0, transform->dobj.dumpId);
14077
14078 free(lanname);
14079 destroyPQExpBuffer(defqry);
14080 destroyPQExpBuffer(delqry);
14081 destroyPQExpBuffer(labelq);
14082 destroyPQExpBuffer(transformargs);
14083}
14084
14085
14086/*
14087 * dumpOpr
14088 * write out a single operator definition
14089 */
14090static void
14091dumpOpr(Archive *fout, const OprInfo *oprinfo)
14092{
14093 DumpOptions *dopt = fout->dopt;
14094 PQExpBuffer query;
14095 PQExpBuffer q;
14096 PQExpBuffer delq;
14098 PQExpBuffer details;
14099 PGresult *res;
14100 int i_oprkind;
14101 int i_oprcode;
14102 int i_oprleft;
14103 int i_oprright;
14104 int i_oprcom;
14105 int i_oprnegate;
14106 int i_oprrest;
14107 int i_oprjoin;
14108 int i_oprcanmerge;
14109 int i_oprcanhash;
14110 char *oprkind;
14111 char *oprcode;
14112 char *oprleft;
14113 char *oprright;
14114 char *oprcom;
14115 char *oprnegate;
14116 char *oprrest;
14117 char *oprjoin;
14118 char *oprcanmerge;
14119 char *oprcanhash;
14120 char *oprregproc;
14121 char *oprref;
14122
14123 /* Do nothing if not dumping schema */
14124 if (!dopt->dumpSchema)
14125 return;
14126
14127 /*
14128 * some operators are invalid because they were the result of user
14129 * defining operators before commutators exist
14130 */
14131 if (!OidIsValid(oprinfo->oprcode))
14132 return;
14133
14134 query = createPQExpBuffer();
14135 q = createPQExpBuffer();
14136 delq = createPQExpBuffer();
14138 details = createPQExpBuffer();
14139
14140 if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14141 {
14142 /* Set up query for operator-specific details */
14144 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14145 "SELECT oprkind, "
14146 "oprcode::pg_catalog.regprocedure, "
14147 "oprleft::pg_catalog.regtype, "
14148 "oprright::pg_catalog.regtype, "
14149 "oprcom, "
14150 "oprnegate, "
14151 "oprrest::pg_catalog.regprocedure, "
14152 "oprjoin::pg_catalog.regprocedure, "
14153 "oprcanmerge, oprcanhash "
14154 "FROM pg_catalog.pg_operator "
14155 "WHERE oid = $1");
14156
14157 ExecuteSqlStatement(fout, query->data);
14158
14159 fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14160 }
14161
14162 printfPQExpBuffer(query,
14163 "EXECUTE dumpOpr('%u')",
14164 oprinfo->dobj.catId.oid);
14165
14166 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14167
14168 i_oprkind = PQfnumber(res, "oprkind");
14169 i_oprcode = PQfnumber(res, "oprcode");
14170 i_oprleft = PQfnumber(res, "oprleft");
14171 i_oprright = PQfnumber(res, "oprright");
14172 i_oprcom = PQfnumber(res, "oprcom");
14173 i_oprnegate = PQfnumber(res, "oprnegate");
14174 i_oprrest = PQfnumber(res, "oprrest");
14175 i_oprjoin = PQfnumber(res, "oprjoin");
14176 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14177 i_oprcanhash = PQfnumber(res, "oprcanhash");
14178
14179 oprkind = PQgetvalue(res, 0, i_oprkind);
14180 oprcode = PQgetvalue(res, 0, i_oprcode);
14181 oprleft = PQgetvalue(res, 0, i_oprleft);
14182 oprright = PQgetvalue(res, 0, i_oprright);
14183 oprcom = PQgetvalue(res, 0, i_oprcom);
14184 oprnegate = PQgetvalue(res, 0, i_oprnegate);
14185 oprrest = PQgetvalue(res, 0, i_oprrest);
14186 oprjoin = PQgetvalue(res, 0, i_oprjoin);
14187 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14188 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14189
14190 /* In PG14 upwards postfix operator support does not exist anymore. */
14191 if (strcmp(oprkind, "r") == 0)
14192 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14193 oprcode);
14194
14195 oprregproc = convertRegProcReference(oprcode);
14196 if (oprregproc)
14197 {
14198 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14199 free(oprregproc);
14200 }
14201
14202 appendPQExpBuffer(oprid, "%s (",
14203 oprinfo->dobj.name);
14204
14205 /*
14206 * right unary means there's a left arg and left unary means there's a
14207 * right arg. (Although the "r" case is dead code for PG14 and later,
14208 * continue to support it in case we're dumping from an old server.)
14209 */
14210 if (strcmp(oprkind, "r") == 0 ||
14211 strcmp(oprkind, "b") == 0)
14212 {
14213 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14214 appendPQExpBufferStr(oprid, oprleft);
14215 }
14216 else
14217 appendPQExpBufferStr(oprid, "NONE");
14218
14219 if (strcmp(oprkind, "l") == 0 ||
14220 strcmp(oprkind, "b") == 0)
14221 {
14222 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14223 appendPQExpBuffer(oprid, ", %s)", oprright);
14224 }
14225 else
14226 appendPQExpBufferStr(oprid, ", NONE)");
14227
14228 oprref = getFormattedOperatorName(oprcom);
14229 if (oprref)
14230 {
14231 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14232 free(oprref);
14233 }
14234
14235 oprref = getFormattedOperatorName(oprnegate);
14236 if (oprref)
14237 {
14238 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14239 free(oprref);
14240 }
14241
14242 if (strcmp(oprcanmerge, "t") == 0)
14243 appendPQExpBufferStr(details, ",\n MERGES");
14244
14245 if (strcmp(oprcanhash, "t") == 0)
14246 appendPQExpBufferStr(details, ",\n HASHES");
14247
14248 oprregproc = convertRegProcReference(oprrest);
14249 if (oprregproc)
14250 {
14251 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14252 free(oprregproc);
14253 }
14254
14255 oprregproc = convertRegProcReference(oprjoin);
14256 if (oprregproc)
14257 {
14258 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14259 free(oprregproc);
14260 }
14261
14262 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14263 fmtId(oprinfo->dobj.namespace->dobj.name),
14264 oprid->data);
14265
14266 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14267 fmtId(oprinfo->dobj.namespace->dobj.name),
14268 oprinfo->dobj.name, details->data);
14269
14270 if (dopt->binary_upgrade)
14272 "OPERATOR", oprid->data,
14273 oprinfo->dobj.namespace->dobj.name);
14274
14275 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14276 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14277 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14278 .namespace = oprinfo->dobj.namespace->dobj.name,
14279 .owner = oprinfo->rolname,
14280 .description = "OPERATOR",
14281 .section = SECTION_PRE_DATA,
14282 .createStmt = q->data,
14283 .dropStmt = delq->data));
14284
14285 /* Dump Operator Comments */
14286 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14287 dumpComment(fout, "OPERATOR", oprid->data,
14288 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14289 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14290
14291 PQclear(res);
14292
14293 destroyPQExpBuffer(query);
14295 destroyPQExpBuffer(delq);
14297 destroyPQExpBuffer(details);
14298}
14299
14300/*
14301 * Convert a function reference obtained from pg_operator
14302 *
14303 * Returns allocated string of what to print, or NULL if function references
14304 * is InvalidOid. Returned string is expected to be free'd by the caller.
14305 *
14306 * The input is a REGPROCEDURE display; we have to strip the argument-types
14307 * part.
14308 */
14309static char *
14311{
14312 char *name;
14313 char *paren;
14314 bool inquote;
14315
14316 /* In all cases "-" means a null reference */
14317 if (strcmp(proc, "-") == 0)
14318 return NULL;
14319
14320 name = pg_strdup(proc);
14321 /* find non-double-quoted left paren */
14322 inquote = false;
14323 for (paren = name; *paren; paren++)
14324 {
14325 if (*paren == '(' && !inquote)
14326 {
14327 *paren = '\0';
14328 break;
14329 }
14330 if (*paren == '"')
14331 inquote = !inquote;
14332 }
14333 return name;
14334}
14335
14336/*
14337 * getFormattedOperatorName - retrieve the operator name for the
14338 * given operator OID (presented in string form).
14339 *
14340 * Returns an allocated string, or NULL if the given OID is invalid.
14341 * Caller is responsible for free'ing result string.
14342 *
14343 * What we produce has the format "OPERATOR(schema.oprname)". This is only
14344 * useful in commands where the operator's argument types can be inferred from
14345 * context. We always schema-qualify the name, though. The predecessor to
14346 * this code tried to skip the schema qualification if possible, but that led
14347 * to wrong results in corner cases, such as if an operator and its negator
14348 * are in different schemas.
14349 */
14350static char *
14351getFormattedOperatorName(const char *oproid)
14352{
14353 OprInfo *oprInfo;
14354
14355 /* In all cases "0" means a null reference */
14356 if (strcmp(oproid, "0") == 0)
14357 return NULL;
14358
14359 oprInfo = findOprByOid(atooid(oproid));
14360 if (oprInfo == NULL)
14361 {
14362 pg_log_warning("could not find operator with OID %s",
14363 oproid);
14364 return NULL;
14365 }
14366
14367 return psprintf("OPERATOR(%s.%s)",
14368 fmtId(oprInfo->dobj.namespace->dobj.name),
14369 oprInfo->dobj.name);
14370}
14371
14372/*
14373 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14374 *
14375 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14376 * argument lists of these functions are predetermined. Note that the
14377 * caller should ensure we are in the proper schema, because the results
14378 * are search path dependent!
14379 */
14380static char *
14382{
14383 char *result;
14384 char query[128];
14385 PGresult *res;
14386
14387 snprintf(query, sizeof(query),
14388 "SELECT '%u'::pg_catalog.regproc", funcOid);
14389 res = ExecuteSqlQueryForSingleRow(fout, query);
14390
14391 result = pg_strdup(PQgetvalue(res, 0, 0));
14392
14393 PQclear(res);
14394
14395 return result;
14396}
14397
14398/*
14399 * dumpAccessMethod
14400 * write out a single access method definition
14401 */
14402static void
14404{
14405 DumpOptions *dopt = fout->dopt;
14406 PQExpBuffer q;
14407 PQExpBuffer delq;
14408 char *qamname;
14409
14410 /* Do nothing if not dumping schema */
14411 if (!dopt->dumpSchema)
14412 return;
14413
14414 q = createPQExpBuffer();
14415 delq = createPQExpBuffer();
14416
14417 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14418
14419 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14420
14421 switch (aminfo->amtype)
14422 {
14423 case AMTYPE_INDEX:
14424 appendPQExpBufferStr(q, "TYPE INDEX ");
14425 break;
14426 case AMTYPE_TABLE:
14427 appendPQExpBufferStr(q, "TYPE TABLE ");
14428 break;
14429 default:
14430 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14431 aminfo->amtype, qamname);
14433 destroyPQExpBuffer(delq);
14434 free(qamname);
14435 return;
14436 }
14437
14438 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14439
14440 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14441 qamname);
14442
14443 if (dopt->binary_upgrade)
14445 "ACCESS METHOD", qamname, NULL);
14446
14447 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14448 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14449 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14450 .description = "ACCESS METHOD",
14451 .section = SECTION_PRE_DATA,
14452 .createStmt = q->data,
14453 .dropStmt = delq->data));
14454
14455 /* Dump Access Method Comments */
14456 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14457 dumpComment(fout, "ACCESS METHOD", qamname,
14458 NULL, "",
14459 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14460
14462 destroyPQExpBuffer(delq);
14463 free(qamname);
14464}
14465
14466/*
14467 * dumpOpclass
14468 * write out a single operator class definition
14469 */
14470static void
14471dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14472{
14473 DumpOptions *dopt = fout->dopt;
14474 PQExpBuffer query;
14475 PQExpBuffer q;
14476 PQExpBuffer delq;
14477 PQExpBuffer nameusing;
14478 PGresult *res;
14479 int ntups;
14480 int i_opcintype;
14481 int i_opckeytype;
14482 int i_opcdefault;
14483 int i_opcfamily;
14484 int i_opcfamilyname;
14485 int i_opcfamilynsp;
14486 int i_amname;
14487 int i_amopstrategy;
14488 int i_amopopr;
14489 int i_sortfamily;
14490 int i_sortfamilynsp;
14491 int i_amprocnum;
14492 int i_amproc;
14493 int i_amproclefttype;
14494 int i_amprocrighttype;
14495 char *opcintype;
14496 char *opckeytype;
14497 char *opcdefault;
14498 char *opcfamily;
14499 char *opcfamilyname;
14500 char *opcfamilynsp;
14501 char *amname;
14502 char *amopstrategy;
14503 char *amopopr;
14504 char *sortfamily;
14505 char *sortfamilynsp;
14506 char *amprocnum;
14507 char *amproc;
14508 char *amproclefttype;
14509 char *amprocrighttype;
14510 bool needComma;
14511 int i;
14512
14513 /* Do nothing if not dumping schema */
14514 if (!dopt->dumpSchema)
14515 return;
14516
14517 query = createPQExpBuffer();
14518 q = createPQExpBuffer();
14519 delq = createPQExpBuffer();
14520 nameusing = createPQExpBuffer();
14521
14522 /* Get additional fields from the pg_opclass row */
14523 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14524 "opckeytype::pg_catalog.regtype, "
14525 "opcdefault, opcfamily, "
14526 "opfname AS opcfamilyname, "
14527 "nspname AS opcfamilynsp, "
14528 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14529 "FROM pg_catalog.pg_opclass c "
14530 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14531 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14532 "WHERE c.oid = '%u'::pg_catalog.oid",
14533 opcinfo->dobj.catId.oid);
14534
14535 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14536
14537 i_opcintype = PQfnumber(res, "opcintype");
14538 i_opckeytype = PQfnumber(res, "opckeytype");
14539 i_opcdefault = PQfnumber(res, "opcdefault");
14540 i_opcfamily = PQfnumber(res, "opcfamily");
14541 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14542 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14543 i_amname = PQfnumber(res, "amname");
14544
14545 /* opcintype may still be needed after we PQclear res */
14546 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14547 opckeytype = PQgetvalue(res, 0, i_opckeytype);
14548 opcdefault = PQgetvalue(res, 0, i_opcdefault);
14549 /* opcfamily will still be needed after we PQclear res */
14550 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14551 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14552 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14553 /* amname will still be needed after we PQclear res */
14554 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14555
14556 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14557 fmtQualifiedDumpable(opcinfo));
14558 appendPQExpBuffer(delq, " USING %s;\n",
14559 fmtId(amname));
14560
14561 /* Build the fixed portion of the CREATE command */
14562 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14563 fmtQualifiedDumpable(opcinfo));
14564 if (strcmp(opcdefault, "t") == 0)
14565 appendPQExpBufferStr(q, "DEFAULT ");
14566 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14567 opcintype,
14568 fmtId(amname));
14569 if (strlen(opcfamilyname) > 0)
14570 {
14571 appendPQExpBufferStr(q, " FAMILY ");
14572 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14573 appendPQExpBufferStr(q, fmtId(opcfamilyname));
14574 }
14575 appendPQExpBufferStr(q, " AS\n ");
14576
14577 needComma = false;
14578
14579 if (strcmp(opckeytype, "-") != 0)
14580 {
14581 appendPQExpBuffer(q, "STORAGE %s",
14582 opckeytype);
14583 needComma = true;
14584 }
14585
14586 PQclear(res);
14587
14588 /*
14589 * Now fetch and print the OPERATOR entries (pg_amop rows).
14590 *
14591 * Print only those opfamily members that are tied to the opclass by
14592 * pg_depend entries.
14593 */
14594 resetPQExpBuffer(query);
14595 appendPQExpBuffer(query, "SELECT amopstrategy, "
14596 "amopopr::pg_catalog.regoperator, "
14597 "opfname AS sortfamily, "
14598 "nspname AS sortfamilynsp "
14599 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14600 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14601 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14602 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14603 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14604 "AND refobjid = '%u'::pg_catalog.oid "
14605 "AND amopfamily = '%s'::pg_catalog.oid "
14606 "ORDER BY amopstrategy",
14607 opcinfo->dobj.catId.oid,
14608 opcfamily);
14609
14610 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14611
14612 ntups = PQntuples(res);
14613
14614 i_amopstrategy = PQfnumber(res, "amopstrategy");
14615 i_amopopr = PQfnumber(res, "amopopr");
14616 i_sortfamily = PQfnumber(res, "sortfamily");
14617 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14618
14619 for (i = 0; i < ntups; i++)
14620 {
14621 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14622 amopopr = PQgetvalue(res, i, i_amopopr);
14623 sortfamily = PQgetvalue(res, i, i_sortfamily);
14624 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14625
14626 if (needComma)
14627 appendPQExpBufferStr(q, " ,\n ");
14628
14629 appendPQExpBuffer(q, "OPERATOR %s %s",
14630 amopstrategy, amopopr);
14631
14632 if (strlen(sortfamily) > 0)
14633 {
14634 appendPQExpBufferStr(q, " FOR ORDER BY ");
14635 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14636 appendPQExpBufferStr(q, fmtId(sortfamily));
14637 }
14638
14639 needComma = true;
14640 }
14641
14642 PQclear(res);
14643
14644 /*
14645 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14646 *
14647 * Print only those opfamily members that are tied to the opclass by
14648 * pg_depend entries.
14649 *
14650 * We print the amproclefttype/amprocrighttype even though in most cases
14651 * the backend could deduce the right values, because of the corner case
14652 * of a btree sort support function for a cross-type comparison.
14653 */
14654 resetPQExpBuffer(query);
14655
14656 appendPQExpBuffer(query, "SELECT amprocnum, "
14657 "amproc::pg_catalog.regprocedure, "
14658 "amproclefttype::pg_catalog.regtype, "
14659 "amprocrighttype::pg_catalog.regtype "
14660 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14661 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14662 "AND refobjid = '%u'::pg_catalog.oid "
14663 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14664 "AND objid = ap.oid "
14665 "ORDER BY amprocnum",
14666 opcinfo->dobj.catId.oid);
14667
14668 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14669
14670 ntups = PQntuples(res);
14671
14672 i_amprocnum = PQfnumber(res, "amprocnum");
14673 i_amproc = PQfnumber(res, "amproc");
14674 i_amproclefttype = PQfnumber(res, "amproclefttype");
14675 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14676
14677 for (i = 0; i < ntups; i++)
14678 {
14679 amprocnum = PQgetvalue(res, i, i_amprocnum);
14680 amproc = PQgetvalue(res, i, i_amproc);
14681 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14682 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14683
14684 if (needComma)
14685 appendPQExpBufferStr(q, " ,\n ");
14686
14687 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14688
14689 if (*amproclefttype && *amprocrighttype)
14690 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14691
14692 appendPQExpBuffer(q, " %s", amproc);
14693
14694 needComma = true;
14695 }
14696
14697 PQclear(res);
14698
14699 /*
14700 * If needComma is still false it means we haven't added anything after
14701 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14702 * clause with the same datatype. This isn't sanctioned by the
14703 * documentation, but actually DefineOpClass will treat it as a no-op.
14704 */
14705 if (!needComma)
14706 appendPQExpBuffer(q, "STORAGE %s", opcintype);
14707
14708 appendPQExpBufferStr(q, ";\n");
14709
14710 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14711 appendPQExpBuffer(nameusing, " USING %s",
14712 fmtId(amname));
14713
14714 if (dopt->binary_upgrade)
14716 "OPERATOR CLASS", nameusing->data,
14717 opcinfo->dobj.namespace->dobj.name);
14718
14719 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14720 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14721 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14722 .namespace = opcinfo->dobj.namespace->dobj.name,
14723 .owner = opcinfo->rolname,
14724 .description = "OPERATOR CLASS",
14725 .section = SECTION_PRE_DATA,
14726 .createStmt = q->data,
14727 .dropStmt = delq->data));
14728
14729 /* Dump Operator Class Comments */
14730 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14731 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14732 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14733 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14734
14735 free(opcintype);
14736 free(opcfamily);
14737 free(amname);
14738 destroyPQExpBuffer(query);
14740 destroyPQExpBuffer(delq);
14741 destroyPQExpBuffer(nameusing);
14742}
14743
14744/*
14745 * dumpOpfamily
14746 * write out a single operator family definition
14747 *
14748 * Note: this also dumps any "loose" operator members that aren't bound to a
14749 * specific opclass within the opfamily.
14750 */
14751static void
14752dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14753{
14754 DumpOptions *dopt = fout->dopt;
14755 PQExpBuffer query;
14756 PQExpBuffer q;
14757 PQExpBuffer delq;
14758 PQExpBuffer nameusing;
14759 PGresult *res;
14760 PGresult *res_ops;
14761 PGresult *res_procs;
14762 int ntups;
14763 int i_amname;
14764 int i_amopstrategy;
14765 int i_amopopr;
14766 int i_sortfamily;
14767 int i_sortfamilynsp;
14768 int i_amprocnum;
14769 int i_amproc;
14770 int i_amproclefttype;
14771 int i_amprocrighttype;
14772 char *amname;
14773 char *amopstrategy;
14774 char *amopopr;
14775 char *sortfamily;
14776 char *sortfamilynsp;
14777 char *amprocnum;
14778 char *amproc;
14779 char *amproclefttype;
14780 char *amprocrighttype;
14781 bool needComma;
14782 int i;
14783
14784 /* Do nothing if not dumping schema */
14785 if (!dopt->dumpSchema)
14786 return;
14787
14788 query = createPQExpBuffer();
14789 q = createPQExpBuffer();
14790 delq = createPQExpBuffer();
14791 nameusing = createPQExpBuffer();
14792
14793 /*
14794 * Fetch only those opfamily members that are tied directly to the
14795 * opfamily by pg_depend entries.
14796 */
14797 appendPQExpBuffer(query, "SELECT amopstrategy, "
14798 "amopopr::pg_catalog.regoperator, "
14799 "opfname AS sortfamily, "
14800 "nspname AS sortfamilynsp "
14801 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14802 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14803 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14804 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14805 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14806 "AND refobjid = '%u'::pg_catalog.oid "
14807 "AND amopfamily = '%u'::pg_catalog.oid "
14808 "ORDER BY amopstrategy",
14809 opfinfo->dobj.catId.oid,
14810 opfinfo->dobj.catId.oid);
14811
14812 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14813
14814 resetPQExpBuffer(query);
14815
14816 appendPQExpBuffer(query, "SELECT amprocnum, "
14817 "amproc::pg_catalog.regprocedure, "
14818 "amproclefttype::pg_catalog.regtype, "
14819 "amprocrighttype::pg_catalog.regtype "
14820 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14821 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14822 "AND refobjid = '%u'::pg_catalog.oid "
14823 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14824 "AND objid = ap.oid "
14825 "ORDER BY amprocnum",
14826 opfinfo->dobj.catId.oid);
14827
14828 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14829
14830 /* Get additional fields from the pg_opfamily row */
14831 resetPQExpBuffer(query);
14832
14833 appendPQExpBuffer(query, "SELECT "
14834 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14835 "FROM pg_catalog.pg_opfamily "
14836 "WHERE oid = '%u'::pg_catalog.oid",
14837 opfinfo->dobj.catId.oid);
14838
14839 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14840
14841 i_amname = PQfnumber(res, "amname");
14842
14843 /* amname will still be needed after we PQclear res */
14844 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14845
14846 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14847 fmtQualifiedDumpable(opfinfo));
14848 appendPQExpBuffer(delq, " USING %s;\n",
14849 fmtId(amname));
14850
14851 /* Build the fixed portion of the CREATE command */
14852 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14853 fmtQualifiedDumpable(opfinfo));
14854 appendPQExpBuffer(q, " USING %s;\n",
14855 fmtId(amname));
14856
14857 PQclear(res);
14858
14859 /* Do we need an ALTER to add loose members? */
14860 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14861 {
14862 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14863 fmtQualifiedDumpable(opfinfo));
14864 appendPQExpBuffer(q, " USING %s ADD\n ",
14865 fmtId(amname));
14866
14867 needComma = false;
14868
14869 /*
14870 * Now fetch and print the OPERATOR entries (pg_amop rows).
14871 */
14872 ntups = PQntuples(res_ops);
14873
14874 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14875 i_amopopr = PQfnumber(res_ops, "amopopr");
14876 i_sortfamily = PQfnumber(res_ops, "sortfamily");
14877 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14878
14879 for (i = 0; i < ntups; i++)
14880 {
14881 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14882 amopopr = PQgetvalue(res_ops, i, i_amopopr);
14883 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14884 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14885
14886 if (needComma)
14887 appendPQExpBufferStr(q, " ,\n ");
14888
14889 appendPQExpBuffer(q, "OPERATOR %s %s",
14890 amopstrategy, amopopr);
14891
14892 if (strlen(sortfamily) > 0)
14893 {
14894 appendPQExpBufferStr(q, " FOR ORDER BY ");
14895 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14896 appendPQExpBufferStr(q, fmtId(sortfamily));
14897 }
14898
14899 needComma = true;
14900 }
14901
14902 /*
14903 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14904 */
14905 ntups = PQntuples(res_procs);
14906
14907 i_amprocnum = PQfnumber(res_procs, "amprocnum");
14908 i_amproc = PQfnumber(res_procs, "amproc");
14909 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14910 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14911
14912 for (i = 0; i < ntups; i++)
14913 {
14914 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14915 amproc = PQgetvalue(res_procs, i, i_amproc);
14916 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14917 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14918
14919 if (needComma)
14920 appendPQExpBufferStr(q, " ,\n ");
14921
14922 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14923 amprocnum, amproclefttype, amprocrighttype,
14924 amproc);
14925
14926 needComma = true;
14927 }
14928
14929 appendPQExpBufferStr(q, ";\n");
14930 }
14931
14932 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14933 appendPQExpBuffer(nameusing, " USING %s",
14934 fmtId(amname));
14935
14936 if (dopt->binary_upgrade)
14938 "OPERATOR FAMILY", nameusing->data,
14939 opfinfo->dobj.namespace->dobj.name);
14940
14941 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14942 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14943 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14944 .namespace = opfinfo->dobj.namespace->dobj.name,
14945 .owner = opfinfo->rolname,
14946 .description = "OPERATOR FAMILY",
14947 .section = SECTION_PRE_DATA,
14948 .createStmt = q->data,
14949 .dropStmt = delq->data));
14950
14951 /* Dump Operator Family Comments */
14952 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14953 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14954 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14955 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14956
14957 free(amname);
14958 PQclear(res_ops);
14959 PQclear(res_procs);
14960 destroyPQExpBuffer(query);
14962 destroyPQExpBuffer(delq);
14963 destroyPQExpBuffer(nameusing);
14964}
14965
14966/*
14967 * dumpCollation
14968 * write out a single collation definition
14969 */
14970static void
14971dumpCollation(Archive *fout, const CollInfo *collinfo)
14972{
14973 DumpOptions *dopt = fout->dopt;
14974 PQExpBuffer query;
14975 PQExpBuffer q;
14976 PQExpBuffer delq;
14977 char *qcollname;
14978 PGresult *res;
14979 int i_collprovider;
14980 int i_collisdeterministic;
14981 int i_collcollate;
14982 int i_collctype;
14983 int i_colllocale;
14984 int i_collicurules;
14985 const char *collprovider;
14986 const char *collcollate;
14987 const char *collctype;
14988 const char *colllocale;
14989 const char *collicurules;
14990
14991 /* Do nothing if not dumping schema */
14992 if (!dopt->dumpSchema)
14993 return;
14994
14995 query = createPQExpBuffer();
14996 q = createPQExpBuffer();
14997 delq = createPQExpBuffer();
14998
14999 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15000
15001 /* Get collation-specific details */
15002 appendPQExpBufferStr(query, "SELECT ");
15003
15004 if (fout->remoteVersion >= 100000)
15006 "collprovider, "
15007 "collversion, ");
15008 else
15010 "'c' AS collprovider, "
15011 "NULL AS collversion, ");
15012
15013 if (fout->remoteVersion >= 120000)
15015 "collisdeterministic, ");
15016 else
15018 "true AS collisdeterministic, ");
15019
15020 if (fout->remoteVersion >= 170000)
15022 "colllocale, ");
15023 else if (fout->remoteVersion >= 150000)
15025 "colliculocale AS colllocale, ");
15026 else
15028 "NULL AS colllocale, ");
15029
15030 if (fout->remoteVersion >= 160000)
15032 "collicurules, ");
15033 else
15035 "NULL AS collicurules, ");
15036
15037 appendPQExpBuffer(query,
15038 "collcollate, "
15039 "collctype "
15040 "FROM pg_catalog.pg_collation c "
15041 "WHERE c.oid = '%u'::pg_catalog.oid",
15042 collinfo->dobj.catId.oid);
15043
15044 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15045
15046 i_collprovider = PQfnumber(res, "collprovider");
15047 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15048 i_collcollate = PQfnumber(res, "collcollate");
15049 i_collctype = PQfnumber(res, "collctype");
15050 i_colllocale = PQfnumber(res, "colllocale");
15051 i_collicurules = PQfnumber(res, "collicurules");
15052
15053 collprovider = PQgetvalue(res, 0, i_collprovider);
15054
15055 if (!PQgetisnull(res, 0, i_collcollate))
15056 collcollate = PQgetvalue(res, 0, i_collcollate);
15057 else
15058 collcollate = NULL;
15059
15060 if (!PQgetisnull(res, 0, i_collctype))
15061 collctype = PQgetvalue(res, 0, i_collctype);
15062 else
15063 collctype = NULL;
15064
15065 /*
15066 * Before version 15, collcollate and collctype were of type NAME and
15067 * non-nullable. Treat empty strings as NULL for consistency.
15068 */
15069 if (fout->remoteVersion < 150000)
15070 {
15071 if (collcollate[0] == '\0')
15072 collcollate = NULL;
15073 if (collctype[0] == '\0')
15074 collctype = NULL;
15075 }
15076
15077 if (!PQgetisnull(res, 0, i_colllocale))
15078 colllocale = PQgetvalue(res, 0, i_colllocale);
15079 else
15080 colllocale = NULL;
15081
15082 if (!PQgetisnull(res, 0, i_collicurules))
15083 collicurules = PQgetvalue(res, 0, i_collicurules);
15084 else
15085 collicurules = NULL;
15086
15087 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15088 fmtQualifiedDumpable(collinfo));
15089
15090 appendPQExpBuffer(q, "CREATE COLLATION %s (",
15091 fmtQualifiedDumpable(collinfo));
15092
15093 appendPQExpBufferStr(q, "provider = ");
15094 if (collprovider[0] == 'b')
15095 appendPQExpBufferStr(q, "builtin");
15096 else if (collprovider[0] == 'c')
15097 appendPQExpBufferStr(q, "libc");
15098 else if (collprovider[0] == 'i')
15099 appendPQExpBufferStr(q, "icu");
15100 else if (collprovider[0] == 'd')
15101 /* to allow dumping pg_catalog; not accepted on input */
15102 appendPQExpBufferStr(q, "default");
15103 else
15104 pg_fatal("unrecognized collation provider: %s",
15105 collprovider);
15106
15107 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15108 appendPQExpBufferStr(q, ", deterministic = false");
15109
15110 if (collprovider[0] == 'd')
15111 {
15112 if (collcollate || collctype || colllocale || collicurules)
15113 pg_log_warning("invalid collation \"%s\"", qcollname);
15114
15115 /* no locale -- the default collation cannot be reloaded anyway */
15116 }
15117 else if (collprovider[0] == 'b')
15118 {
15119 if (collcollate || collctype || !colllocale || collicurules)
15120 pg_log_warning("invalid collation \"%s\"", qcollname);
15121
15122 appendPQExpBufferStr(q, ", locale = ");
15123 appendStringLiteralAH(q, colllocale ? colllocale : "",
15124 fout);
15125 }
15126 else if (collprovider[0] == 'i')
15127 {
15128 if (fout->remoteVersion >= 150000)
15129 {
15130 if (collcollate || collctype || !colllocale)
15131 pg_log_warning("invalid collation \"%s\"", qcollname);
15132
15133 appendPQExpBufferStr(q, ", locale = ");
15134 appendStringLiteralAH(q, colllocale ? colllocale : "",
15135 fout);
15136 }
15137 else
15138 {
15139 if (!collcollate || !collctype || colllocale ||
15140 strcmp(collcollate, collctype) != 0)
15141 pg_log_warning("invalid collation \"%s\"", qcollname);
15142
15143 appendPQExpBufferStr(q, ", locale = ");
15144 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15145 }
15146
15147 if (collicurules)
15148 {
15149 appendPQExpBufferStr(q, ", rules = ");
15150 appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15151 }
15152 }
15153 else if (collprovider[0] == 'c')
15154 {
15155 if (colllocale || collicurules || !collcollate || !collctype)
15156 pg_log_warning("invalid collation \"%s\"", qcollname);
15157
15158 if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15159 {
15160 appendPQExpBufferStr(q, ", locale = ");
15161 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15162 }
15163 else
15164 {
15165 appendPQExpBufferStr(q, ", lc_collate = ");
15166 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15167 appendPQExpBufferStr(q, ", lc_ctype = ");
15168 appendStringLiteralAH(q, collctype ? collctype : "", fout);
15169 }
15170 }
15171 else
15172 pg_fatal("unrecognized collation provider: %s", collprovider);
15173
15174 /*
15175 * For binary upgrade, carry over the collation version. For normal
15176 * dump/restore, omit the version, so that it is computed upon restore.
15177 */
15178 if (dopt->binary_upgrade)
15179 {
15180 int i_collversion;
15181
15182 i_collversion = PQfnumber(res, "collversion");
15183 if (!PQgetisnull(res, 0, i_collversion))
15184 {
15185 appendPQExpBufferStr(q, ", version = ");
15187 PQgetvalue(res, 0, i_collversion),
15188 fout);
15189 }
15190 }
15191
15192 appendPQExpBufferStr(q, ");\n");
15193
15194 if (dopt->binary_upgrade)
15196 "COLLATION", qcollname,
15197 collinfo->dobj.namespace->dobj.name);
15198
15199 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15200 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15201 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15202 .namespace = collinfo->dobj.namespace->dobj.name,
15203 .owner = collinfo->rolname,
15204 .description = "COLLATION",
15205 .section = SECTION_PRE_DATA,
15206 .createStmt = q->data,
15207 .dropStmt = delq->data));
15208
15209 /* Dump Collation Comments */
15210 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15211 dumpComment(fout, "COLLATION", qcollname,
15212 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15213 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15214
15215 PQclear(res);
15216
15217 destroyPQExpBuffer(query);
15219 destroyPQExpBuffer(delq);
15220 free(qcollname);
15221}
15222
15223/*
15224 * dumpConversion
15225 * write out a single conversion definition
15226 */
15227static void
15228dumpConversion(Archive *fout, const ConvInfo *convinfo)
15229{
15230 DumpOptions *dopt = fout->dopt;
15231 PQExpBuffer query;
15232 PQExpBuffer q;
15233 PQExpBuffer delq;
15234 char *qconvname;
15235 PGresult *res;
15236 int i_conforencoding;
15237 int i_contoencoding;
15238 int i_conproc;
15239 int i_condefault;
15240 const char *conforencoding;
15241 const char *contoencoding;
15242 const char *conproc;
15243 bool condefault;
15244
15245 /* Do nothing if not dumping schema */
15246 if (!dopt->dumpSchema)
15247 return;
15248
15249 query = createPQExpBuffer();
15250 q = createPQExpBuffer();
15251 delq = createPQExpBuffer();
15252
15253 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15254
15255 /* Get conversion-specific details */
15256 appendPQExpBuffer(query, "SELECT "
15257 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15258 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15259 "conproc, condefault "
15260 "FROM pg_catalog.pg_conversion c "
15261 "WHERE c.oid = '%u'::pg_catalog.oid",
15262 convinfo->dobj.catId.oid);
15263
15264 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15265
15266 i_conforencoding = PQfnumber(res, "conforencoding");
15267 i_contoencoding = PQfnumber(res, "contoencoding");
15268 i_conproc = PQfnumber(res, "conproc");
15269 i_condefault = PQfnumber(res, "condefault");
15270
15271 conforencoding = PQgetvalue(res, 0, i_conforencoding);
15272 contoencoding = PQgetvalue(res, 0, i_contoencoding);
15273 conproc = PQgetvalue(res, 0, i_conproc);
15274 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15275
15276 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15277 fmtQualifiedDumpable(convinfo));
15278
15279 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15280 (condefault) ? "DEFAULT " : "",
15281 fmtQualifiedDumpable(convinfo));
15282 appendStringLiteralAH(q, conforencoding, fout);
15283 appendPQExpBufferStr(q, " TO ");
15284 appendStringLiteralAH(q, contoencoding, fout);
15285 /* regproc output is already sufficiently quoted */
15286 appendPQExpBuffer(q, " FROM %s;\n", conproc);
15287
15288 if (dopt->binary_upgrade)
15290 "CONVERSION", qconvname,
15291 convinfo->dobj.namespace->dobj.name);
15292
15293 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15294 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15295 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15296 .namespace = convinfo->dobj.namespace->dobj.name,
15297 .owner = convinfo->rolname,
15298 .description = "CONVERSION",
15299 .section = SECTION_PRE_DATA,
15300 .createStmt = q->data,
15301 .dropStmt = delq->data));
15302
15303 /* Dump Conversion Comments */
15304 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15305 dumpComment(fout, "CONVERSION", qconvname,
15306 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15307 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15308
15309 PQclear(res);
15310
15311 destroyPQExpBuffer(query);
15313 destroyPQExpBuffer(delq);
15314 free(qconvname);
15315}
15316
15317/*
15318 * format_aggregate_signature: generate aggregate name and argument list
15319 *
15320 * The argument type names are qualified if needed. The aggregate name
15321 * is never qualified.
15322 */
15323static char *
15324format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15325{
15327 int j;
15328
15330 if (honor_quotes)
15331 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15332 else
15333 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15334
15335 if (agginfo->aggfn.nargs == 0)
15336 appendPQExpBufferStr(&buf, "(*)");
15337 else
15338 {
15340 for (j = 0; j < agginfo->aggfn.nargs; j++)
15341 appendPQExpBuffer(&buf, "%s%s",
15342 (j > 0) ? ", " : "",
15344 agginfo->aggfn.argtypes[j],
15345 zeroIsError));
15347 }
15348 return buf.data;
15349}
15350
15351/*
15352 * dumpAgg
15353 * write out a single aggregate definition
15354 */
15355static void
15356dumpAgg(Archive *fout, const AggInfo *agginfo)
15357{
15358 DumpOptions *dopt = fout->dopt;
15359 PQExpBuffer query;
15360 PQExpBuffer q;
15361 PQExpBuffer delq;
15362 PQExpBuffer details;
15363 char *aggsig; /* identity signature */
15364 char *aggfullsig = NULL; /* full signature */
15365 char *aggsig_tag;
15366 PGresult *res;
15367 int i_agginitval;
15368 int i_aggminitval;
15369 const char *aggtransfn;
15370 const char *aggfinalfn;
15371 const char *aggcombinefn;
15372 const char *aggserialfn;
15373 const char *aggdeserialfn;
15374 const char *aggmtransfn;
15375 const char *aggminvtransfn;
15376 const char *aggmfinalfn;
15377 bool aggfinalextra;
15378 bool aggmfinalextra;
15379 char aggfinalmodify;
15380 char aggmfinalmodify;
15381 const char *aggsortop;
15382 char *aggsortconvop;
15383 char aggkind;
15384 const char *aggtranstype;
15385 const char *aggtransspace;
15386 const char *aggmtranstype;
15387 const char *aggmtransspace;
15388 const char *agginitval;
15389 const char *aggminitval;
15390 const char *proparallel;
15391 char defaultfinalmodify;
15392
15393 /* Do nothing if not dumping schema */
15394 if (!dopt->dumpSchema)
15395 return;
15396
15397 query = createPQExpBuffer();
15398 q = createPQExpBuffer();
15399 delq = createPQExpBuffer();
15400 details = createPQExpBuffer();
15401
15402 if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15403 {
15404 /* Set up query for aggregate-specific details */
15406 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15407
15409 "SELECT "
15410 "aggtransfn,\n"
15411 "aggfinalfn,\n"
15412 "aggtranstype::pg_catalog.regtype,\n"
15413 "agginitval,\n"
15414 "aggsortop,\n"
15415 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15416 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15417
15418 if (fout->remoteVersion >= 90400)
15420 "aggkind,\n"
15421 "aggmtransfn,\n"
15422 "aggminvtransfn,\n"
15423 "aggmfinalfn,\n"
15424 "aggmtranstype::pg_catalog.regtype,\n"
15425 "aggfinalextra,\n"
15426 "aggmfinalextra,\n"
15427 "aggtransspace,\n"
15428 "aggmtransspace,\n"
15429 "aggminitval,\n");
15430 else
15432 "'n' AS aggkind,\n"
15433 "'-' AS aggmtransfn,\n"
15434 "'-' AS aggminvtransfn,\n"
15435 "'-' AS aggmfinalfn,\n"
15436 "0 AS aggmtranstype,\n"
15437 "false AS aggfinalextra,\n"
15438 "false AS aggmfinalextra,\n"
15439 "0 AS aggtransspace,\n"
15440 "0 AS aggmtransspace,\n"
15441 "NULL AS aggminitval,\n");
15442
15443 if (fout->remoteVersion >= 90600)
15445 "aggcombinefn,\n"
15446 "aggserialfn,\n"
15447 "aggdeserialfn,\n"
15448 "proparallel,\n");
15449 else
15451 "'-' AS aggcombinefn,\n"
15452 "'-' AS aggserialfn,\n"
15453 "'-' AS aggdeserialfn,\n"
15454 "'u' AS proparallel,\n");
15455
15456 if (fout->remoteVersion >= 110000)
15458 "aggfinalmodify,\n"
15459 "aggmfinalmodify\n");
15460 else
15462 "'0' AS aggfinalmodify,\n"
15463 "'0' AS aggmfinalmodify\n");
15464
15466 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15467 "WHERE a.aggfnoid = p.oid "
15468 "AND p.oid = $1");
15469
15470 ExecuteSqlStatement(fout, query->data);
15471
15472 fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15473 }
15474
15475 printfPQExpBuffer(query,
15476 "EXECUTE dumpAgg('%u')",
15477 agginfo->aggfn.dobj.catId.oid);
15478
15479 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15480
15481 i_agginitval = PQfnumber(res, "agginitval");
15482 i_aggminitval = PQfnumber(res, "aggminitval");
15483
15484 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15485 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15486 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15487 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15488 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15489 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15490 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15491 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15492 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15493 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15494 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15495 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15496 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15497 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15498 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15499 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15500 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15501 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15502 agginitval = PQgetvalue(res, 0, i_agginitval);
15503 aggminitval = PQgetvalue(res, 0, i_aggminitval);
15504 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15505
15506 {
15507 char *funcargs;
15508 char *funciargs;
15509
15510 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15511 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15512 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15513 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15514 }
15515
15516 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15517
15518 /* identify default modify flag for aggkind (must match DefineAggregate) */
15519 defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15520 /* replace omitted flags for old versions */
15521 if (aggfinalmodify == '0')
15522 aggfinalmodify = defaultfinalmodify;
15523 if (aggmfinalmodify == '0')
15524 aggmfinalmodify = defaultfinalmodify;
15525
15526 /* regproc and regtype output is already sufficiently quoted */
15527 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15528 aggtransfn, aggtranstype);
15529
15530 if (strcmp(aggtransspace, "0") != 0)
15531 {
15532 appendPQExpBuffer(details, ",\n SSPACE = %s",
15533 aggtransspace);
15534 }
15535
15536 if (!PQgetisnull(res, 0, i_agginitval))
15537 {
15538 appendPQExpBufferStr(details, ",\n INITCOND = ");
15539 appendStringLiteralAH(details, agginitval, fout);
15540 }
15541
15542 if (strcmp(aggfinalfn, "-") != 0)
15543 {
15544 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15545 aggfinalfn);
15546 if (aggfinalextra)
15547 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15548 if (aggfinalmodify != defaultfinalmodify)
15549 {
15550 switch (aggfinalmodify)
15551 {
15552 case AGGMODIFY_READ_ONLY:
15553 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15554 break;
15555 case AGGMODIFY_SHAREABLE:
15556 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15557 break;
15558 case AGGMODIFY_READ_WRITE:
15559 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15560 break;
15561 default:
15562 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15563 agginfo->aggfn.dobj.name);
15564 break;
15565 }
15566 }
15567 }
15568
15569 if (strcmp(aggcombinefn, "-") != 0)
15570 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15571
15572 if (strcmp(aggserialfn, "-") != 0)
15573 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15574
15575 if (strcmp(aggdeserialfn, "-") != 0)
15576 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15577
15578 if (strcmp(aggmtransfn, "-") != 0)
15579 {
15580 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15581 aggmtransfn,
15582 aggminvtransfn,
15583 aggmtranstype);
15584 }
15585
15586 if (strcmp(aggmtransspace, "0") != 0)
15587 {
15588 appendPQExpBuffer(details, ",\n MSSPACE = %s",
15589 aggmtransspace);
15590 }
15591
15592 if (!PQgetisnull(res, 0, i_aggminitval))
15593 {
15594 appendPQExpBufferStr(details, ",\n MINITCOND = ");
15595 appendStringLiteralAH(details, aggminitval, fout);
15596 }
15597
15598 if (strcmp(aggmfinalfn, "-") != 0)
15599 {
15600 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15601 aggmfinalfn);
15602 if (aggmfinalextra)
15603 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15604 if (aggmfinalmodify != defaultfinalmodify)
15605 {
15606 switch (aggmfinalmodify)
15607 {
15608 case AGGMODIFY_READ_ONLY:
15609 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15610 break;
15611 case AGGMODIFY_SHAREABLE:
15612 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15613 break;
15614 case AGGMODIFY_READ_WRITE:
15615 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15616 break;
15617 default:
15618 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15619 agginfo->aggfn.dobj.name);
15620 break;
15621 }
15622 }
15623 }
15624
15625 aggsortconvop = getFormattedOperatorName(aggsortop);
15626 if (aggsortconvop)
15627 {
15628 appendPQExpBuffer(details, ",\n SORTOP = %s",
15629 aggsortconvop);
15630 free(aggsortconvop);
15631 }
15632
15633 if (aggkind == AGGKIND_HYPOTHETICAL)
15634 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15635
15636 if (proparallel[0] != PROPARALLEL_UNSAFE)
15637 {
15638 if (proparallel[0] == PROPARALLEL_SAFE)
15639 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15640 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15641 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15642 else if (proparallel[0] != PROPARALLEL_UNSAFE)
15643 pg_fatal("unrecognized proparallel value for function \"%s\"",
15644 agginfo->aggfn.dobj.name);
15645 }
15646
15647 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15648 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15649 aggsig);
15650
15651 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15652 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15653 aggfullsig ? aggfullsig : aggsig, details->data);
15654
15655 if (dopt->binary_upgrade)
15656 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15657 "AGGREGATE", aggsig,
15658 agginfo->aggfn.dobj.namespace->dobj.name);
15659
15660 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15661 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15662 agginfo->aggfn.dobj.dumpId,
15663 ARCHIVE_OPTS(.tag = aggsig_tag,
15664 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15665 .owner = agginfo->aggfn.rolname,
15666 .description = "AGGREGATE",
15667 .section = SECTION_PRE_DATA,
15668 .createStmt = q->data,
15669 .dropStmt = delq->data));
15670
15671 /* Dump Aggregate Comments */
15672 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15673 dumpComment(fout, "AGGREGATE", aggsig,
15674 agginfo->aggfn.dobj.namespace->dobj.name,
15675 agginfo->aggfn.rolname,
15676 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15677
15678 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15679 dumpSecLabel(fout, "AGGREGATE", aggsig,
15680 agginfo->aggfn.dobj.namespace->dobj.name,
15681 agginfo->aggfn.rolname,
15682 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15683
15684 /*
15685 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15686 * command look like a function's GRANT; in particular this affects the
15687 * syntax for zero-argument aggregates and ordered-set aggregates.
15688 */
15689 free(aggsig);
15690
15691 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15692
15693 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15694 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15695 "FUNCTION", aggsig, NULL,
15696 agginfo->aggfn.dobj.namespace->dobj.name,
15697 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15698
15699 free(aggsig);
15700 free(aggfullsig);
15701 free(aggsig_tag);
15702
15703 PQclear(res);
15704
15705 destroyPQExpBuffer(query);
15707 destroyPQExpBuffer(delq);
15708 destroyPQExpBuffer(details);
15709}
15710
15711/*
15712 * dumpTSParser
15713 * write out a single text search parser
15714 */
15715static void
15716dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15717{
15718 DumpOptions *dopt = fout->dopt;
15719 PQExpBuffer q;
15720 PQExpBuffer delq;
15721 char *qprsname;
15722
15723 /* Do nothing if not dumping schema */
15724 if (!dopt->dumpSchema)
15725 return;
15726
15727 q = createPQExpBuffer();
15728 delq = createPQExpBuffer();
15729
15730 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15731
15732 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15733 fmtQualifiedDumpable(prsinfo));
15734
15735 appendPQExpBuffer(q, " START = %s,\n",
15736 convertTSFunction(fout, prsinfo->prsstart));
15737 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15738 convertTSFunction(fout, prsinfo->prstoken));
15739 appendPQExpBuffer(q, " END = %s,\n",
15740 convertTSFunction(fout, prsinfo->prsend));
15741 if (prsinfo->prsheadline != InvalidOid)
15742 appendPQExpBuffer(q, " HEADLINE = %s,\n",
15743 convertTSFunction(fout, prsinfo->prsheadline));
15744 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15745 convertTSFunction(fout, prsinfo->prslextype));
15746
15747 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15748 fmtQualifiedDumpable(prsinfo));
15749
15750 if (dopt->binary_upgrade)
15752 "TEXT SEARCH PARSER", qprsname,
15753 prsinfo->dobj.namespace->dobj.name);
15754
15755 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15756 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15757 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15758 .namespace = prsinfo->dobj.namespace->dobj.name,
15759 .description = "TEXT SEARCH PARSER",
15760 .section = SECTION_PRE_DATA,
15761 .createStmt = q->data,
15762 .dropStmt = delq->data));
15763
15764 /* Dump Parser Comments */
15765 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15766 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15767 prsinfo->dobj.namespace->dobj.name, "",
15768 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15769
15771 destroyPQExpBuffer(delq);
15772 free(qprsname);
15773}
15774
15775/*
15776 * dumpTSDictionary
15777 * write out a single text search dictionary
15778 */
15779static void
15780dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15781{
15782 DumpOptions *dopt = fout->dopt;
15783 PQExpBuffer q;
15784 PQExpBuffer delq;
15785 PQExpBuffer query;
15786 char *qdictname;
15787 PGresult *res;
15788 char *nspname;
15789 char *tmplname;
15790
15791 /* Do nothing if not dumping schema */
15792 if (!dopt->dumpSchema)
15793 return;
15794
15795 q = createPQExpBuffer();
15796 delq = createPQExpBuffer();
15797 query = createPQExpBuffer();
15798
15799 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15800
15801 /* Fetch name and namespace of the dictionary's template */
15802 appendPQExpBuffer(query, "SELECT nspname, tmplname "
15803 "FROM pg_ts_template p, pg_namespace n "
15804 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15805 dictinfo->dicttemplate);
15806 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15807 nspname = PQgetvalue(res, 0, 0);
15808 tmplname = PQgetvalue(res, 0, 1);
15809
15810 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15811 fmtQualifiedDumpable(dictinfo));
15812
15813 appendPQExpBufferStr(q, " TEMPLATE = ");
15814 appendPQExpBuffer(q, "%s.", fmtId(nspname));
15815 appendPQExpBufferStr(q, fmtId(tmplname));
15816
15817 PQclear(res);
15818
15819 /* the dictinitoption can be dumped straight into the command */
15820 if (dictinfo->dictinitoption)
15821 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15822
15823 appendPQExpBufferStr(q, " );\n");
15824
15825 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15826 fmtQualifiedDumpable(dictinfo));
15827
15828 if (dopt->binary_upgrade)
15830 "TEXT SEARCH DICTIONARY", qdictname,
15831 dictinfo->dobj.namespace->dobj.name);
15832
15833 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15834 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15835 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15836 .namespace = dictinfo->dobj.namespace->dobj.name,
15837 .owner = dictinfo->rolname,
15838 .description = "TEXT SEARCH DICTIONARY",
15839 .section = SECTION_PRE_DATA,
15840 .createStmt = q->data,
15841 .dropStmt = delq->data));
15842
15843 /* Dump Dictionary Comments */
15844 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15845 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15846 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15847 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15848
15850 destroyPQExpBuffer(delq);
15851 destroyPQExpBuffer(query);
15852 free(qdictname);
15853}
15854
15855/*
15856 * dumpTSTemplate
15857 * write out a single text search template
15858 */
15859static void
15861{
15862 DumpOptions *dopt = fout->dopt;
15863 PQExpBuffer q;
15864 PQExpBuffer delq;
15865 char *qtmplname;
15866
15867 /* Do nothing if not dumping schema */
15868 if (!dopt->dumpSchema)
15869 return;
15870
15871 q = createPQExpBuffer();
15872 delq = createPQExpBuffer();
15873
15874 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15875
15876 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15877 fmtQualifiedDumpable(tmplinfo));
15878
15879 if (tmplinfo->tmplinit != InvalidOid)
15880 appendPQExpBuffer(q, " INIT = %s,\n",
15881 convertTSFunction(fout, tmplinfo->tmplinit));
15882 appendPQExpBuffer(q, " LEXIZE = %s );\n",
15883 convertTSFunction(fout, tmplinfo->tmpllexize));
15884
15885 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15886 fmtQualifiedDumpable(tmplinfo));
15887
15888 if (dopt->binary_upgrade)
15890 "TEXT SEARCH TEMPLATE", qtmplname,
15891 tmplinfo->dobj.namespace->dobj.name);
15892
15893 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15894 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15895 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15896 .namespace = tmplinfo->dobj.namespace->dobj.name,
15897 .description = "TEXT SEARCH TEMPLATE",
15898 .section = SECTION_PRE_DATA,
15899 .createStmt = q->data,
15900 .dropStmt = delq->data));
15901
15902 /* Dump Template Comments */
15903 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15904 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15905 tmplinfo->dobj.namespace->dobj.name, "",
15906 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15907
15909 destroyPQExpBuffer(delq);
15910 free(qtmplname);
15911}
15912
15913/*
15914 * dumpTSConfig
15915 * write out a single text search configuration
15916 */
15917static void
15918dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15919{
15920 DumpOptions *dopt = fout->dopt;
15921 PQExpBuffer q;
15922 PQExpBuffer delq;
15923 PQExpBuffer query;
15924 char *qcfgname;
15925 PGresult *res;
15926 char *nspname;
15927 char *prsname;
15928 int ntups,
15929 i;
15930 int i_tokenname;
15931 int i_dictname;
15932
15933 /* Do nothing if not dumping schema */
15934 if (!dopt->dumpSchema)
15935 return;
15936
15937 q = createPQExpBuffer();
15938 delq = createPQExpBuffer();
15939 query = createPQExpBuffer();
15940
15941 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15942
15943 /* Fetch name and namespace of the config's parser */
15944 appendPQExpBuffer(query, "SELECT nspname, prsname "
15945 "FROM pg_ts_parser p, pg_namespace n "
15946 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15947 cfginfo->cfgparser);
15948 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15949 nspname = PQgetvalue(res, 0, 0);
15950 prsname = PQgetvalue(res, 0, 1);
15951
15952 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15953 fmtQualifiedDumpable(cfginfo));
15954
15955 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15956 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15957
15958 PQclear(res);
15959
15960 resetPQExpBuffer(query);
15961 appendPQExpBuffer(query,
15962 "SELECT\n"
15963 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15964 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15965 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15966 "FROM pg_catalog.pg_ts_config_map AS m\n"
15967 "WHERE m.mapcfg = '%u'\n"
15968 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15969 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15970
15971 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15972 ntups = PQntuples(res);
15973
15974 i_tokenname = PQfnumber(res, "tokenname");
15975 i_dictname = PQfnumber(res, "dictname");
15976
15977 for (i = 0; i < ntups; i++)
15978 {
15979 char *tokenname = PQgetvalue(res, i, i_tokenname);
15980 char *dictname = PQgetvalue(res, i, i_dictname);
15981
15982 if (i == 0 ||
15983 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15984 {
15985 /* starting a new token type, so start a new command */
15986 if (i > 0)
15987 appendPQExpBufferStr(q, ";\n");
15988 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15989 fmtQualifiedDumpable(cfginfo));
15990 /* tokenname needs quoting, dictname does NOT */
15991 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15992 fmtId(tokenname), dictname);
15993 }
15994 else
15995 appendPQExpBuffer(q, ", %s", dictname);
15996 }
15997
15998 if (ntups > 0)
15999 appendPQExpBufferStr(q, ";\n");
16000
16001 PQclear(res);
16002
16003 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16004 fmtQualifiedDumpable(cfginfo));
16005
16006 if (dopt->binary_upgrade)
16008 "TEXT SEARCH CONFIGURATION", qcfgname,
16009 cfginfo->dobj.namespace->dobj.name);
16010
16011 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16012 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16013 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16014 .namespace = cfginfo->dobj.namespace->dobj.name,
16015 .owner = cfginfo->rolname,
16016 .description = "TEXT SEARCH CONFIGURATION",
16017 .section = SECTION_PRE_DATA,
16018 .createStmt = q->data,
16019 .dropStmt = delq->data));
16020
16021 /* Dump Configuration Comments */
16022 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16023 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16024 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16025 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16026
16028 destroyPQExpBuffer(delq);
16029 destroyPQExpBuffer(query);
16030 free(qcfgname);
16031}
16032
16033/*
16034 * dumpForeignDataWrapper
16035 * write out a single foreign-data wrapper definition
16036 */
16037static void
16039{
16040 DumpOptions *dopt = fout->dopt;
16041 PQExpBuffer q;
16042 PQExpBuffer delq;
16043 char *qfdwname;
16044
16045 /* Do nothing if not dumping schema */
16046 if (!dopt->dumpSchema)
16047 return;
16048
16049 q = createPQExpBuffer();
16050 delq = createPQExpBuffer();
16051
16052 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16053
16054 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16055 qfdwname);
16056
16057 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16058 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16059
16060 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16061 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16062
16063 if (strlen(fdwinfo->fdwoptions) > 0)
16064 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16065
16066 appendPQExpBufferStr(q, ";\n");
16067
16068 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16069 qfdwname);
16070
16071 if (dopt->binary_upgrade)
16073 "FOREIGN DATA WRAPPER", qfdwname,
16074 NULL);
16075
16076 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16077 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16078 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16079 .owner = fdwinfo->rolname,
16080 .description = "FOREIGN DATA WRAPPER",
16081 .section = SECTION_PRE_DATA,
16082 .createStmt = q->data,
16083 .dropStmt = delq->data));
16084
16085 /* Dump Foreign Data Wrapper Comments */
16086 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16087 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16088 NULL, fdwinfo->rolname,
16089 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16090
16091 /* Handle the ACL */
16092 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16093 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16094 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16095 NULL, fdwinfo->rolname, &fdwinfo->dacl);
16096
16097 free(qfdwname);
16098
16100 destroyPQExpBuffer(delq);
16101}
16102
16103/*
16104 * dumpForeignServer
16105 * write out a foreign server definition
16106 */
16107static void
16109{
16110 DumpOptions *dopt = fout->dopt;
16111 PQExpBuffer q;
16112 PQExpBuffer delq;
16113 PQExpBuffer query;
16114 PGresult *res;
16115 char *qsrvname;
16116 char *fdwname;
16117
16118 /* Do nothing if not dumping schema */
16119 if (!dopt->dumpSchema)
16120 return;
16121
16122 q = createPQExpBuffer();
16123 delq = createPQExpBuffer();
16124 query = createPQExpBuffer();
16125
16126 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16127
16128 /* look up the foreign-data wrapper */
16129 appendPQExpBuffer(query, "SELECT fdwname "
16130 "FROM pg_foreign_data_wrapper w "
16131 "WHERE w.oid = '%u'",
16132 srvinfo->srvfdw);
16133 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16134 fdwname = PQgetvalue(res, 0, 0);
16135
16136 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16137 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16138 {
16139 appendPQExpBufferStr(q, " TYPE ");
16140 appendStringLiteralAH(q, srvinfo->srvtype, fout);
16141 }
16142 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16143 {
16144 appendPQExpBufferStr(q, " VERSION ");
16145 appendStringLiteralAH(q, srvinfo->srvversion, fout);
16146 }
16147
16148 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16149 appendPQExpBufferStr(q, fmtId(fdwname));
16150
16151 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16152 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16153
16154 appendPQExpBufferStr(q, ";\n");
16155
16156 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16157 qsrvname);
16158
16159 if (dopt->binary_upgrade)
16161 "SERVER", qsrvname, NULL);
16162
16163 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16164 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16165 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16166 .owner = srvinfo->rolname,
16167 .description = "SERVER",
16168 .section = SECTION_PRE_DATA,
16169 .createStmt = q->data,
16170 .dropStmt = delq->data));
16171
16172 /* Dump Foreign Server Comments */
16173 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16174 dumpComment(fout, "SERVER", qsrvname,
16175 NULL, srvinfo->rolname,
16176 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16177
16178 /* Handle the ACL */
16179 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16180 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16181 "FOREIGN SERVER", qsrvname, NULL, NULL,
16182 NULL, srvinfo->rolname, &srvinfo->dacl);
16183
16184 /* Dump user mappings */
16185 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16186 dumpUserMappings(fout,
16187 srvinfo->dobj.name, NULL,
16188 srvinfo->rolname,
16189 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16190
16191 PQclear(res);
16192
16193 free(qsrvname);
16194
16196 destroyPQExpBuffer(delq);
16197 destroyPQExpBuffer(query);
16198}
16199
16200/*
16201 * dumpUserMappings
16202 *
16203 * This routine is used to dump any user mappings associated with the
16204 * server handed to this routine. Should be called after ArchiveEntry()
16205 * for the server.
16206 */
16207static void
16209 const char *servername, const char *namespace,
16210 const char *owner,
16211 CatalogId catalogId, DumpId dumpId)
16212{
16213 PQExpBuffer q;
16214 PQExpBuffer delq;
16215 PQExpBuffer query;
16216 PQExpBuffer tag;
16217 PGresult *res;
16218 int ntups;
16219 int i_usename;
16220 int i_umoptions;
16221 int i;
16222
16223 q = createPQExpBuffer();
16224 tag = createPQExpBuffer();
16225 delq = createPQExpBuffer();
16226 query = createPQExpBuffer();
16227
16228 /*
16229 * We read from the publicly accessible view pg_user_mappings, so as not
16230 * to fail if run by a non-superuser. Note that the view will show
16231 * umoptions as null if the user hasn't got privileges for the associated
16232 * server; this means that pg_dump will dump such a mapping, but with no
16233 * OPTIONS clause. A possible alternative is to skip such mappings
16234 * altogether, but it's not clear that that's an improvement.
16235 */
16236 appendPQExpBuffer(query,
16237 "SELECT usename, "
16238 "array_to_string(ARRAY("
16239 "SELECT quote_ident(option_name) || ' ' || "
16240 "quote_literal(option_value) "
16241 "FROM pg_options_to_table(umoptions) "
16242 "ORDER BY option_name"
16243 "), E',\n ') AS umoptions "
16244 "FROM pg_user_mappings "
16245 "WHERE srvid = '%u' "
16246 "ORDER BY usename",
16247 catalogId.oid);
16248
16249 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16250
16251 ntups = PQntuples(res);
16252 i_usename = PQfnumber(res, "usename");
16253 i_umoptions = PQfnumber(res, "umoptions");
16254
16255 for (i = 0; i < ntups; i++)
16256 {
16257 char *usename;
16258 char *umoptions;
16259
16260 usename = PQgetvalue(res, i, i_usename);
16261 umoptions = PQgetvalue(res, i, i_umoptions);
16262
16264 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16265 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16266
16267 if (umoptions && strlen(umoptions) > 0)
16268 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16269
16270 appendPQExpBufferStr(q, ";\n");
16271
16272 resetPQExpBuffer(delq);
16273 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16274 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16275
16276 resetPQExpBuffer(tag);
16277 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16278 usename, servername);
16279
16281 ARCHIVE_OPTS(.tag = tag->data,
16282 .namespace = namespace,
16283 .owner = owner,
16284 .description = "USER MAPPING",
16285 .section = SECTION_PRE_DATA,
16286 .createStmt = q->data,
16287 .dropStmt = delq->data));
16288 }
16289
16290 PQclear(res);
16291
16292 destroyPQExpBuffer(query);
16293 destroyPQExpBuffer(delq);
16294 destroyPQExpBuffer(tag);
16296}
16297
16298/*
16299 * Write out default privileges information
16300 */
16301static void
16303{
16304 DumpOptions *dopt = fout->dopt;
16305 PQExpBuffer q;
16306 PQExpBuffer tag;
16307 const char *type;
16308
16309 /* Do nothing if not dumping schema, or if we're skipping ACLs */
16310 if (!dopt->dumpSchema || dopt->aclsSkip)
16311 return;
16312
16313 q = createPQExpBuffer();
16314 tag = createPQExpBuffer();
16315
16316 switch (daclinfo->defaclobjtype)
16317 {
16318 case DEFACLOBJ_RELATION:
16319 type = "TABLES";
16320 break;
16321 case DEFACLOBJ_SEQUENCE:
16322 type = "SEQUENCES";
16323 break;
16324 case DEFACLOBJ_FUNCTION:
16325 type = "FUNCTIONS";
16326 break;
16327 case DEFACLOBJ_TYPE:
16328 type = "TYPES";
16329 break;
16330 case DEFACLOBJ_NAMESPACE:
16331 type = "SCHEMAS";
16332 break;
16333 case DEFACLOBJ_LARGEOBJECT:
16334 type = "LARGE OBJECTS";
16335 break;
16336 default:
16337 /* shouldn't get here */
16338 pg_fatal("unrecognized object type in default privileges: %d",
16339 (int) daclinfo->defaclobjtype);
16340 type = ""; /* keep compiler quiet */
16341 }
16342
16343 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16344
16345 /* build the actual command(s) for this tuple */
16347 daclinfo->dobj.namespace != NULL ?
16348 daclinfo->dobj.namespace->dobj.name : NULL,
16349 daclinfo->dacl.acl,
16350 daclinfo->dacl.acldefault,
16351 daclinfo->defaclrole,
16352 fout->remoteVersion,
16353 q))
16354 pg_fatal("could not parse default ACL list (%s)",
16355 daclinfo->dacl.acl);
16356
16357 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16358 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16359 ARCHIVE_OPTS(.tag = tag->data,
16360 .namespace = daclinfo->dobj.namespace ?
16361 daclinfo->dobj.namespace->dobj.name : NULL,
16362 .owner = daclinfo->defaclrole,
16363 .description = "DEFAULT ACL",
16364 .section = SECTION_POST_DATA,
16365 .createStmt = q->data));
16366
16367 destroyPQExpBuffer(tag);
16369}
16370
16371/*----------
16372 * Write out grant/revoke information
16373 *
16374 * 'objDumpId' is the dump ID of the underlying object.
16375 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16376 * or InvalidDumpId if there is no need for a second dependency.
16377 * 'type' must be one of
16378 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16379 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16380 * 'name' is the formatted name of the object. Must be quoted etc. already.
16381 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16382 * (Currently we assume that subname is only provided for table columns.)
16383 * 'nspname' is the namespace the object is in (NULL if none).
16384 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16385 * to use the default for the object type.
16386 * 'owner' is the owner, NULL if there is no owner (for languages).
16387 * 'dacl' is the DumpableAcl struct for the object.
16388 *
16389 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16390 * no ACL entry was created.
16391 *----------
16392 */
16393static DumpId
16394dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16395 const char *type, const char *name, const char *subname,
16396 const char *nspname, const char *tag, const char *owner,
16397 const DumpableAcl *dacl)
16398{
16399 DumpId aclDumpId = InvalidDumpId;
16400 DumpOptions *dopt = fout->dopt;
16401 const char *acls = dacl->acl;
16402 const char *acldefault = dacl->acldefault;
16403 char privtype = dacl->privtype;
16404 const char *initprivs = dacl->initprivs;
16405 const char *baseacls;
16406 PQExpBuffer sql;
16407
16408 /* Do nothing if ACL dump is not enabled */
16409 if (dopt->aclsSkip)
16410 return InvalidDumpId;
16411
16412 /* --data-only skips ACLs *except* large object ACLs */
16413 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16414 return InvalidDumpId;
16415
16416 sql = createPQExpBuffer();
16417
16418 /*
16419 * In binary upgrade mode, we don't run an extension's script but instead
16420 * dump out the objects independently and then recreate them. To preserve
16421 * any initial privileges which were set on extension objects, we need to
16422 * compute the set of GRANT and REVOKE commands necessary to get from the
16423 * default privileges of an object to its initial privileges as recorded
16424 * in pg_init_privs.
16425 *
16426 * At restore time, we apply these commands after having called
16427 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16428 * copy the results into pg_init_privs. This is how we preserve the
16429 * contents of that catalog across binary upgrades.
16430 */
16431 if (dopt->binary_upgrade && privtype == 'e' &&
16432 initprivs && *initprivs != '\0')
16433 {
16434 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16435 if (!buildACLCommands(name, subname, nspname, type,
16436 initprivs, acldefault, owner,
16437 "", fout->remoteVersion, sql))
16438 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16439 initprivs, acldefault, name, type);
16440 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16441 }
16442
16443 /*
16444 * Now figure the GRANT and REVOKE commands needed to get to the object's
16445 * actual current ACL, starting from the initprivs if given, else from the
16446 * object-type-specific default. Also, while buildACLCommands will assume
16447 * that a NULL/empty acls string means it needn't do anything, what that
16448 * actually represents is the object-type-specific default; so we need to
16449 * substitute the acldefault string to get the right results in that case.
16450 */
16451 if (initprivs && *initprivs != '\0')
16452 {
16453 baseacls = initprivs;
16454 if (acls == NULL || *acls == '\0')
16455 acls = acldefault;
16456 }
16457 else
16458 baseacls = acldefault;
16459
16460 if (!buildACLCommands(name, subname, nspname, type,
16461 acls, baseacls, owner,
16462 "", fout->remoteVersion, sql))
16463 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16464 acls, baseacls, name, type);
16465
16466 if (sql->len > 0)
16467 {
16468 PQExpBuffer tagbuf = createPQExpBuffer();
16469 DumpId aclDeps[2];
16470 int nDeps = 0;
16471
16472 if (tag)
16473 appendPQExpBufferStr(tagbuf, tag);
16474 else if (subname)
16475 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16476 else
16477 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16478
16479 aclDeps[nDeps++] = objDumpId;
16480 if (altDumpId != InvalidDumpId)
16481 aclDeps[nDeps++] = altDumpId;
16482
16483 aclDumpId = createDumpId();
16484
16485 ArchiveEntry(fout, nilCatalogId, aclDumpId,
16486 ARCHIVE_OPTS(.tag = tagbuf->data,
16487 .namespace = nspname,
16488 .owner = owner,
16489 .description = "ACL",
16490 .section = SECTION_NONE,
16491 .createStmt = sql->data,
16492 .deps = aclDeps,
16493 .nDeps = nDeps));
16494
16495 destroyPQExpBuffer(tagbuf);
16496 }
16497
16498 destroyPQExpBuffer(sql);
16499
16500 return aclDumpId;
16501}
16502
16503/*
16504 * dumpSecLabel
16505 *
16506 * This routine is used to dump any security labels associated with the
16507 * object handed to this routine. The routine takes the object type
16508 * and object name (ready to print, except for schema decoration), plus
16509 * the namespace and owner of the object (for labeling the ArchiveEntry),
16510 * plus catalog ID and subid which are the lookup key for pg_seclabel,
16511 * plus the dump ID for the object (for setting a dependency).
16512 * If a matching pg_seclabel entry is found, it is dumped.
16513 *
16514 * Note: although this routine takes a dumpId for dependency purposes,
16515 * that purpose is just to mark the dependency in the emitted dump file
16516 * for possible future use by pg_restore. We do NOT use it for determining
16517 * ordering of the label in the dump file, because this routine is called
16518 * after dependency sorting occurs. This routine should be called just after
16519 * calling ArchiveEntry() for the specified object.
16520 */
16521static void
16522dumpSecLabel(Archive *fout, const char *type, const char *name,
16523 const char *namespace, const char *owner,
16524 CatalogId catalogId, int subid, DumpId dumpId)
16525{
16526 DumpOptions *dopt = fout->dopt;
16527 SecLabelItem *labels;
16528 int nlabels;
16529 int i;
16530 PQExpBuffer query;
16531
16532 /* do nothing, if --no-security-labels is supplied */
16533 if (dopt->no_security_labels)
16534 return;
16535
16536 /*
16537 * Security labels are schema not data ... except large object labels are
16538 * data
16539 */
16540 if (strcmp(type, "LARGE OBJECT") != 0)
16541 {
16542 if (!dopt->dumpSchema)
16543 return;
16544 }
16545 else
16546 {
16547 /* We do dump large object security labels in binary-upgrade mode */
16548 if (!dopt->dumpData && !dopt->binary_upgrade)
16549 return;
16550 }
16551
16552 /* Search for security labels associated with catalogId, using table */
16553 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16554
16555 query = createPQExpBuffer();
16556
16557 for (i = 0; i < nlabels; i++)
16558 {
16559 /*
16560 * Ignore label entries for which the subid doesn't match.
16561 */
16562 if (labels[i].objsubid != subid)
16563 continue;
16564
16565 appendPQExpBuffer(query,
16566 "SECURITY LABEL FOR %s ON %s ",
16567 fmtId(labels[i].provider), type);
16568 if (namespace && *namespace)
16569 appendPQExpBuffer(query, "%s.", fmtId(namespace));
16570 appendPQExpBuffer(query, "%s IS ", name);
16571 appendStringLiteralAH(query, labels[i].label, fout);
16572 appendPQExpBufferStr(query, ";\n");
16573 }
16574
16575 if (query->len > 0)
16576 {
16578
16579 appendPQExpBuffer(tag, "%s %s", type, name);
16581 ARCHIVE_OPTS(.tag = tag->data,
16582 .namespace = namespace,
16583 .owner = owner,
16584 .description = "SECURITY LABEL",
16585 .section = SECTION_NONE,
16586 .createStmt = query->data,
16587 .deps = &dumpId,
16588 .nDeps = 1));
16589 destroyPQExpBuffer(tag);
16590 }
16591
16592 destroyPQExpBuffer(query);
16593}
16594
16595/*
16596 * dumpTableSecLabel
16597 *
16598 * As above, but dump security label for both the specified table (or view)
16599 * and its columns.
16600 */
16601static void
16602dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16603{
16604 DumpOptions *dopt = fout->dopt;
16605 SecLabelItem *labels;
16606 int nlabels;
16607 int i;
16608 PQExpBuffer query;
16609 PQExpBuffer target;
16610
16611 /* do nothing, if --no-security-labels is supplied */
16612 if (dopt->no_security_labels)
16613 return;
16614
16615 /* SecLabel are SCHEMA not data */
16616 if (!dopt->dumpSchema)
16617 return;
16618
16619 /* Search for comments associated with relation, using table */
16620 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16621 tbinfo->dobj.catId.oid,
16622 &labels);
16623
16624 /* If security labels exist, build SECURITY LABEL statements */
16625 if (nlabels <= 0)
16626 return;
16627
16628 query = createPQExpBuffer();
16629 target = createPQExpBuffer();
16630
16631 for (i = 0; i < nlabels; i++)
16632 {
16633 const char *colname;
16634 const char *provider = labels[i].provider;
16635 const char *label = labels[i].label;
16636 int objsubid = labels[i].objsubid;
16637
16638 resetPQExpBuffer(target);
16639 if (objsubid == 0)
16640 {
16641 appendPQExpBuffer(target, "%s %s", reltypename,
16642 fmtQualifiedDumpable(tbinfo));
16643 }
16644 else
16645 {
16646 colname = getAttrName(objsubid, tbinfo);
16647 /* first fmtXXX result must be consumed before calling again */
16648 appendPQExpBuffer(target, "COLUMN %s",
16649 fmtQualifiedDumpable(tbinfo));
16650 appendPQExpBuffer(target, ".%s", fmtId(colname));
16651 }
16652 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16653 fmtId(provider), target->data);
16654 appendStringLiteralAH(query, label, fout);
16655 appendPQExpBufferStr(query, ";\n");
16656 }
16657 if (query->len > 0)
16658 {
16659 resetPQExpBuffer(target);
16660 appendPQExpBuffer(target, "%s %s", reltypename,
16661 fmtId(tbinfo->dobj.name));
16663 ARCHIVE_OPTS(.tag = target->data,
16664 .namespace = tbinfo->dobj.namespace->dobj.name,
16665 .owner = tbinfo->rolname,
16666 .description = "SECURITY LABEL",
16667 .section = SECTION_NONE,
16668 .createStmt = query->data,
16669 .deps = &(tbinfo->dobj.dumpId),
16670 .nDeps = 1));
16671 }
16672 destroyPQExpBuffer(query);
16673 destroyPQExpBuffer(target);
16674}
16675
16676/*
16677 * findSecLabels
16678 *
16679 * Find the security label(s), if any, associated with the given object.
16680 * All the objsubid values associated with the given classoid/objoid are
16681 * found with one search.
16682 */
16683static int
16685{
16686 SecLabelItem *middle = NULL;
16687 SecLabelItem *low;
16688 SecLabelItem *high;
16689 int nmatch;
16690
16691 if (nseclabels <= 0) /* no labels, so no match is possible */
16692 {
16693 *items = NULL;
16694 return 0;
16695 }
16696
16697 /*
16698 * Do binary search to find some item matching the object.
16699 */
16700 low = &seclabels[0];
16701 high = &seclabels[nseclabels - 1];
16702 while (low <= high)
16703 {
16704 middle = low + (high - low) / 2;
16705
16706 if (classoid < middle->classoid)
16707 high = middle - 1;
16708 else if (classoid > middle->classoid)
16709 low = middle + 1;
16710 else if (objoid < middle->objoid)
16711 high = middle - 1;
16712 else if (objoid > middle->objoid)
16713 low = middle + 1;
16714 else
16715 break; /* found a match */
16716 }
16717
16718 if (low > high) /* no matches */
16719 {
16720 *items = NULL;
16721 return 0;
16722 }
16723
16724 /*
16725 * Now determine how many items match the object. The search loop
16726 * invariant still holds: only items between low and high inclusive could
16727 * match.
16728 */
16729 nmatch = 1;
16730 while (middle > low)
16731 {
16732 if (classoid != middle[-1].classoid ||
16733 objoid != middle[-1].objoid)
16734 break;
16735 middle--;
16736 nmatch++;
16737 }
16738
16739 *items = middle;
16740
16741 middle += nmatch;
16742 while (middle <= high)
16743 {
16744 if (classoid != middle->classoid ||
16745 objoid != middle->objoid)
16746 break;
16747 middle++;
16748 nmatch++;
16749 }
16750
16751 return nmatch;
16752}
16753
16754/*
16755 * collectSecLabels
16756 *
16757 * Construct a table of all security labels available for database objects;
16758 * also set the has-seclabel component flag for each relevant object.
16759 *
16760 * The table is sorted by classoid/objid/objsubid for speed in lookup.
16761 */
16762static void
16764{
16765 PGresult *res;
16766 PQExpBuffer query;
16767 int i_label;
16768 int i_provider;
16769 int i_classoid;
16770 int i_objoid;
16771 int i_objsubid;
16772 int ntups;
16773 int i;
16774 DumpableObject *dobj;
16775
16776 query = createPQExpBuffer();
16777
16779 "SELECT label, provider, classoid, objoid, objsubid "
16780 "FROM pg_catalog.pg_seclabels "
16781 "ORDER BY classoid, objoid, objsubid");
16782
16783 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16784
16785 /* Construct lookup table containing OIDs in numeric form */
16786 i_label = PQfnumber(res, "label");
16787 i_provider = PQfnumber(res, "provider");
16788 i_classoid = PQfnumber(res, "classoid");
16789 i_objoid = PQfnumber(res, "objoid");
16790 i_objsubid = PQfnumber(res, "objsubid");
16791
16792 ntups = PQntuples(res);
16793
16794 seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16795 nseclabels = 0;
16796 dobj = NULL;
16797
16798 for (i = 0; i < ntups; i++)
16799 {
16800 CatalogId objId;
16801 int subid;
16802
16803 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16804 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16805 subid = atoi(PQgetvalue(res, i, i_objsubid));
16806
16807 /* We needn't remember labels that don't match any dumpable object */
16808 if (dobj == NULL ||
16809 dobj->catId.tableoid != objId.tableoid ||
16810 dobj->catId.oid != objId.oid)
16811 dobj = findObjectByCatalogId(objId);
16812 if (dobj == NULL)
16813 continue;
16814
16815 /*
16816 * Labels on columns of composite types are linked to the type's
16817 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16818 * in the type's own DumpableObject.
16819 */
16820 if (subid != 0 && dobj->objType == DO_TABLE &&
16821 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16822 {
16823 TypeInfo *cTypeInfo;
16824
16825 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16826 if (cTypeInfo)
16828 }
16829 else
16830 dobj->components |= DUMP_COMPONENT_SECLABEL;
16831
16832 seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16833 seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16835 seclabels[nseclabels].objoid = objId.oid;
16836 seclabels[nseclabels].objsubid = subid;
16837 nseclabels++;
16838 }
16839
16840 PQclear(res);
16841 destroyPQExpBuffer(query);
16842}
16843
16844/*
16845 * dumpTable
16846 * write out to fout the declarations (not data) of a user-defined table
16847 */
16848static void
16849dumpTable(Archive *fout, const TableInfo *tbinfo)
16850{
16851 DumpOptions *dopt = fout->dopt;
16852 DumpId tableAclDumpId = InvalidDumpId;
16853 char *namecopy;
16854
16855 /* Do nothing if not dumping schema */
16856 if (!dopt->dumpSchema)
16857 return;
16858
16859 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16860 {
16861 if (tbinfo->relkind == RELKIND_SEQUENCE)
16862 dumpSequence(fout, tbinfo);
16863 else
16864 dumpTableSchema(fout, tbinfo);
16865 }
16866
16867 /* Handle the ACL here */
16868 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16869 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16870 {
16871 const char *objtype =
16872 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16873
16874 tableAclDumpId =
16875 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16876 objtype, namecopy, NULL,
16877 tbinfo->dobj.namespace->dobj.name,
16878 NULL, tbinfo->rolname, &tbinfo->dacl);
16879 }
16880
16881 /*
16882 * Handle column ACLs, if any. Note: we pull these with a separate query
16883 * rather than trying to fetch them during getTableAttrs, so that we won't
16884 * miss ACLs on system columns. Doing it this way also allows us to dump
16885 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16886 */
16887 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16888 {
16890 PGresult *res;
16891 int i;
16892
16894 {
16895 /* Set up query for column ACLs */
16897 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16898
16899 if (fout->remoteVersion >= 90600)
16900 {
16901 /*
16902 * In principle we should call acldefault('c', relowner) to
16903 * get the default ACL for a column. However, we don't
16904 * currently store the numeric OID of the relowner in
16905 * TableInfo. We could convert the owner name using regrole,
16906 * but that creates a risk of failure due to concurrent role
16907 * renames. Given that the default ACL for columns is empty
16908 * and is likely to stay that way, it's not worth extra cycles
16909 * and risk to avoid hard-wiring that knowledge here.
16910 */
16912 "SELECT at.attname, "
16913 "at.attacl, "
16914 "'{}' AS acldefault, "
16915 "pip.privtype, pip.initprivs "
16916 "FROM pg_catalog.pg_attribute at "
16917 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16918 "(at.attrelid = pip.objoid "
16919 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16920 "AND at.attnum = pip.objsubid) "
16921 "WHERE at.attrelid = $1 AND "
16922 "NOT at.attisdropped "
16923 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16924 "ORDER BY at.attnum");
16925 }
16926 else
16927 {
16929 "SELECT attname, attacl, '{}' AS acldefault, "
16930 "NULL AS privtype, NULL AS initprivs "
16931 "FROM pg_catalog.pg_attribute "
16932 "WHERE attrelid = $1 AND NOT attisdropped "
16933 "AND attacl IS NOT NULL "
16934 "ORDER BY attnum");
16935 }
16936
16937 ExecuteSqlStatement(fout, query->data);
16938
16940 }
16941
16942 printfPQExpBuffer(query,
16943 "EXECUTE getColumnACLs('%u')",
16944 tbinfo->dobj.catId.oid);
16945
16946 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16947
16948 for (i = 0; i < PQntuples(res); i++)
16949 {
16950 char *attname = PQgetvalue(res, i, 0);
16951 char *attacl = PQgetvalue(res, i, 1);
16952 char *acldefault = PQgetvalue(res, i, 2);
16953 char privtype = *(PQgetvalue(res, i, 3));
16954 char *initprivs = PQgetvalue(res, i, 4);
16955 DumpableAcl coldacl;
16956 char *attnamecopy;
16957
16958 coldacl.acl = attacl;
16959 coldacl.acldefault = acldefault;
16960 coldacl.privtype = privtype;
16961 coldacl.initprivs = initprivs;
16962 attnamecopy = pg_strdup(fmtId(attname));
16963
16964 /*
16965 * Column's GRANT type is always TABLE. Each column ACL depends
16966 * on the table-level ACL, since we can restore column ACLs in
16967 * parallel but the table-level ACL has to be done first.
16968 */
16969 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16970 "TABLE", namecopy, attnamecopy,
16971 tbinfo->dobj.namespace->dobj.name,
16972 NULL, tbinfo->rolname, &coldacl);
16973 free(attnamecopy);
16974 }
16975 PQclear(res);
16976 destroyPQExpBuffer(query);
16977 }
16978
16979 free(namecopy);
16980}
16981
16982/*
16983 * Create the AS clause for a view or materialized view. The semicolon is
16984 * stripped because a materialized view must add a WITH NO DATA clause.
16985 *
16986 * This returns a new buffer which must be freed by the caller.
16987 */
16988static PQExpBuffer
16990{
16992 PQExpBuffer result = createPQExpBuffer();
16993 PGresult *res;
16994 int len;
16995
16996 /* Fetch the view definition */
16997 appendPQExpBuffer(query,
16998 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16999 tbinfo->dobj.catId.oid);
17000
17001 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17002
17003 if (PQntuples(res) != 1)
17004 {
17005 if (PQntuples(res) < 1)
17006 pg_fatal("query to obtain definition of view \"%s\" returned no data",
17007 tbinfo->dobj.name);
17008 else
17009 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17010 tbinfo->dobj.name);
17011 }
17012
17013 len = PQgetlength(res, 0, 0);
17014
17015 if (len == 0)
17016 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17017 tbinfo->dobj.name);
17018
17019 /* Strip off the trailing semicolon so that other things may follow. */
17020 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17021 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17022
17023 PQclear(res);
17024 destroyPQExpBuffer(query);
17025
17026 return result;
17027}
17028
17029/*
17030 * Create a dummy AS clause for a view. This is used when the real view
17031 * definition has to be postponed because of circular dependencies.
17032 * We must duplicate the view's external properties -- column names and types
17033 * (including collation) -- so that it works for subsequent references.
17034 *
17035 * This returns a new buffer which must be freed by the caller.
17036 */
17037static PQExpBuffer
17039{
17040 PQExpBuffer result = createPQExpBuffer();
17041 int j;
17042
17043 appendPQExpBufferStr(result, "SELECT");
17044
17045 for (j = 0; j < tbinfo->numatts; j++)
17046 {
17047 if (j > 0)
17048 appendPQExpBufferChar(result, ',');
17049 appendPQExpBufferStr(result, "\n ");
17050
17051 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17052
17053 /*
17054 * Must add collation if not default for the type, because CREATE OR
17055 * REPLACE VIEW won't change it
17056 */
17057 if (OidIsValid(tbinfo->attcollation[j]))
17058 {
17059 CollInfo *coll;
17060
17061 coll = findCollationByOid(tbinfo->attcollation[j]);
17062 if (coll)
17063 appendPQExpBuffer(result, " COLLATE %s",
17064 fmtQualifiedDumpable(coll));
17065 }
17066
17067 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17068 }
17069
17070 return result;
17071}
17072
17073/*
17074 * dumpTableSchema
17075 * write the declaration (not data) of one user-defined table or view
17076 */
17077static void
17078dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17079{
17080 DumpOptions *dopt = fout->dopt;
17084 char *qrelname;
17085 char *qualrelname;
17086 int numParents;
17087 TableInfo **parents;
17088 int actual_atts; /* number of attrs in this CREATE statement */
17089 const char *reltypename;
17090 char *storage;
17091 int j,
17092 k;
17093
17094 /* We had better have loaded per-column details about this table */
17095 Assert(tbinfo->interesting);
17096
17097 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17098 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17099
17100 if (tbinfo->hasoids)
17101 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17102 qrelname);
17103
17104 if (dopt->binary_upgrade)
17105 binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17106
17107 /* Is it a table or a view? */
17108 if (tbinfo->relkind == RELKIND_VIEW)
17109 {
17110 PQExpBuffer result;
17111
17112 /*
17113 * Note: keep this code in sync with the is_view case in dumpRule()
17114 */
17115
17116 reltypename = "VIEW";
17117
17118 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17119
17120 if (dopt->binary_upgrade)
17122 tbinfo->dobj.catId.oid);
17123
17124 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17125
17126 if (tbinfo->dummy_view)
17127 result = createDummyViewAsClause(fout, tbinfo);
17128 else
17129 {
17130 if (nonemptyReloptions(tbinfo->reloptions))
17131 {
17132 appendPQExpBufferStr(q, " WITH (");
17133 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17134 appendPQExpBufferChar(q, ')');
17135 }
17136 result = createViewAsClause(fout, tbinfo);
17137 }
17138 appendPQExpBuffer(q, " AS\n%s", result->data);
17139 destroyPQExpBuffer(result);
17140
17141 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17142 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17143 appendPQExpBufferStr(q, ";\n");
17144 }
17145 else
17146 {
17147 char *partkeydef = NULL;
17148 char *ftoptions = NULL;
17149 char *srvname = NULL;
17150 const char *foreign = "";
17151
17152 /*
17153 * Set reltypename, and collect any relkind-specific data that we
17154 * didn't fetch during getTables().
17155 */
17156 switch (tbinfo->relkind)
17157 {
17158 case RELKIND_PARTITIONED_TABLE:
17159 {
17161 PGresult *res;
17162
17163 reltypename = "TABLE";
17164
17165 /* retrieve partition key definition */
17166 appendPQExpBuffer(query,
17167 "SELECT pg_get_partkeydef('%u')",
17168 tbinfo->dobj.catId.oid);
17169 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17170 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17171 PQclear(res);
17172 destroyPQExpBuffer(query);
17173 break;
17174 }
17175 case RELKIND_FOREIGN_TABLE:
17176 {
17178 PGresult *res;
17179 int i_srvname;
17180 int i_ftoptions;
17181
17182 reltypename = "FOREIGN TABLE";
17183
17184 /* retrieve name of foreign server and generic options */
17185 appendPQExpBuffer(query,
17186 "SELECT fs.srvname, "
17187 "pg_catalog.array_to_string(ARRAY("
17188 "SELECT pg_catalog.quote_ident(option_name) || "
17189 "' ' || pg_catalog.quote_literal(option_value) "
17190 "FROM pg_catalog.pg_options_to_table(ftoptions) "
17191 "ORDER BY option_name"
17192 "), E',\n ') AS ftoptions "
17193 "FROM pg_catalog.pg_foreign_table ft "
17194 "JOIN pg_catalog.pg_foreign_server fs "
17195 "ON (fs.oid = ft.ftserver) "
17196 "WHERE ft.ftrelid = '%u'",
17197 tbinfo->dobj.catId.oid);
17198 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17199 i_srvname = PQfnumber(res, "srvname");
17200 i_ftoptions = PQfnumber(res, "ftoptions");
17201 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17202 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17203 PQclear(res);
17204 destroyPQExpBuffer(query);
17205
17206 foreign = "FOREIGN ";
17207 break;
17208 }
17209 case RELKIND_MATVIEW:
17210 reltypename = "MATERIALIZED VIEW";
17211 break;
17212 default:
17213 reltypename = "TABLE";
17214 break;
17215 }
17216
17217 numParents = tbinfo->numParents;
17218 parents = tbinfo->parents;
17219
17220 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17221
17222 if (dopt->binary_upgrade)
17224 tbinfo->dobj.catId.oid);
17225
17226 /*
17227 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17228 * ignore it when dumping if it was set in this case.
17229 */
17230 appendPQExpBuffer(q, "CREATE %s%s %s",
17231 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17232 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17233 "UNLOGGED " : "",
17234 reltypename,
17235 qualrelname);
17236
17237 /*
17238 * Attach to type, if reloftype; except in case of a binary upgrade,
17239 * we dump the table normally and attach it to the type afterward.
17240 */
17241 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17242 appendPQExpBuffer(q, " OF %s",
17243 getFormattedTypeName(fout, tbinfo->reloftype,
17244 zeroIsError));
17245
17246 if (tbinfo->relkind != RELKIND_MATVIEW)
17247 {
17248 /* Dump the attributes */
17249 actual_atts = 0;
17250 for (j = 0; j < tbinfo->numatts; j++)
17251 {
17252 /*
17253 * Normally, dump if it's locally defined in this table, and
17254 * not dropped. But for binary upgrade, we'll dump all the
17255 * columns, and then fix up the dropped and nonlocal cases
17256 * below.
17257 */
17258 if (shouldPrintColumn(dopt, tbinfo, j))
17259 {
17260 bool print_default;
17261 bool print_notnull;
17262
17263 /*
17264 * Default value --- suppress if to be printed separately
17265 * or not at all.
17266 */
17267 print_default = (tbinfo->attrdefs[j] != NULL &&
17268 tbinfo->attrdefs[j]->dobj.dump &&
17269 !tbinfo->attrdefs[j]->separate);
17270
17271 /*
17272 * Not Null constraint --- print it if it is locally
17273 * defined, or if binary upgrade. (In the latter case, we
17274 * reset conislocal below.)
17275 */
17276 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17277 (tbinfo->notnull_islocal[j] ||
17278 dopt->binary_upgrade ||
17279 tbinfo->ispartition));
17280
17281 /*
17282 * Skip column if fully defined by reloftype, except in
17283 * binary upgrade
17284 */
17285 if (OidIsValid(tbinfo->reloftype) &&
17286 !print_default && !print_notnull &&
17287 !dopt->binary_upgrade)
17288 continue;
17289
17290 /* Format properly if not first attr */
17291 if (actual_atts == 0)
17292 appendPQExpBufferStr(q, " (");
17293 else
17294 appendPQExpBufferChar(q, ',');
17295 appendPQExpBufferStr(q, "\n ");
17296 actual_atts++;
17297
17298 /* Attribute name */
17299 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17300
17301 if (tbinfo->attisdropped[j])
17302 {
17303 /*
17304 * ALTER TABLE DROP COLUMN clears
17305 * pg_attribute.atttypid, so we will not have gotten a
17306 * valid type name; insert INTEGER as a stopgap. We'll
17307 * clean things up later.
17308 */
17309 appendPQExpBufferStr(q, " INTEGER /* dummy */");
17310 /* and skip to the next column */
17311 continue;
17312 }
17313
17314 /*
17315 * Attribute type; print it except when creating a typed
17316 * table ('OF type_name'), but in binary-upgrade mode,
17317 * print it in that case too.
17318 */
17319 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17320 {
17321 appendPQExpBuffer(q, " %s",
17322 tbinfo->atttypnames[j]);
17323 }
17324
17325 if (print_default)
17326 {
17327 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17328 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17329 tbinfo->attrdefs[j]->adef_expr);
17330 else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17331 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17332 tbinfo->attrdefs[j]->adef_expr);
17333 else
17334 appendPQExpBuffer(q, " DEFAULT %s",
17335 tbinfo->attrdefs[j]->adef_expr);
17336 }
17337
17338 if (print_notnull)
17339 {
17340 if (tbinfo->notnull_constrs[j][0] == '\0')
17341 appendPQExpBufferStr(q, " NOT NULL");
17342 else
17343 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17344 fmtId(tbinfo->notnull_constrs[j]));
17345
17346 if (tbinfo->notnull_noinh[j])
17347 appendPQExpBufferStr(q, " NO INHERIT");
17348 }
17349
17350 /* Add collation if not default for the type */
17351 if (OidIsValid(tbinfo->attcollation[j]))
17352 {
17353 CollInfo *coll;
17354
17355 coll = findCollationByOid(tbinfo->attcollation[j]);
17356 if (coll)
17357 appendPQExpBuffer(q, " COLLATE %s",
17358 fmtQualifiedDumpable(coll));
17359 }
17360 }
17361
17362 /*
17363 * On the other hand, if we choose not to print a column
17364 * (likely because it is created by inheritance), but the
17365 * column has a locally-defined not-null constraint, we need
17366 * to dump the constraint as a standalone object.
17367 *
17368 * This syntax isn't SQL-conforming, but if you wanted
17369 * standard output you wouldn't be creating non-standard
17370 * objects to begin with.
17371 */
17372 if (!shouldPrintColumn(dopt, tbinfo, j) &&
17373 !tbinfo->attisdropped[j] &&
17374 tbinfo->notnull_constrs[j] != NULL &&
17375 tbinfo->notnull_islocal[j])
17376 {
17377 /* Format properly if not first attr */
17378 if (actual_atts == 0)
17379 appendPQExpBufferStr(q, " (");
17380 else
17381 appendPQExpBufferChar(q, ',');
17382 appendPQExpBufferStr(q, "\n ");
17383 actual_atts++;
17384
17385 if (tbinfo->notnull_constrs[j][0] == '\0')
17386 appendPQExpBuffer(q, "NOT NULL %s",
17387 fmtId(tbinfo->attnames[j]));
17388 else
17389 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17390 tbinfo->notnull_constrs[j],
17391 fmtId(tbinfo->attnames[j]));
17392 }
17393 }
17394
17395 /*
17396 * Add non-inherited CHECK constraints, if any.
17397 *
17398 * For partitions, we need to include check constraints even if
17399 * they're not defined locally, because the ALTER TABLE ATTACH
17400 * PARTITION that we'll emit later expects the constraint to be
17401 * there. (No need to fix conislocal: ATTACH PARTITION does that)
17402 */
17403 for (j = 0; j < tbinfo->ncheck; j++)
17404 {
17405 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17406
17407 if (constr->separate ||
17408 (!constr->conislocal && !tbinfo->ispartition))
17409 continue;
17410
17411 if (actual_atts == 0)
17412 appendPQExpBufferStr(q, " (\n ");
17413 else
17414 appendPQExpBufferStr(q, ",\n ");
17415
17416 appendPQExpBuffer(q, "CONSTRAINT %s ",
17417 fmtId(constr->dobj.name));
17418 appendPQExpBufferStr(q, constr->condef);
17419
17420 actual_atts++;
17421 }
17422
17423 if (actual_atts)
17424 appendPQExpBufferStr(q, "\n)");
17425 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17426 {
17427 /*
17428 * No attributes? we must have a parenthesized attribute list,
17429 * even though empty, when not using the OF TYPE syntax.
17430 */
17431 appendPQExpBufferStr(q, " (\n)");
17432 }
17433
17434 /*
17435 * Emit the INHERITS clause (not for partitions), except in
17436 * binary-upgrade mode.
17437 */
17438 if (numParents > 0 && !tbinfo->ispartition &&
17439 !dopt->binary_upgrade)
17440 {
17441 appendPQExpBufferStr(q, "\nINHERITS (");
17442 for (k = 0; k < numParents; k++)
17443 {
17444 TableInfo *parentRel = parents[k];
17445
17446 if (k > 0)
17447 appendPQExpBufferStr(q, ", ");
17449 }
17450 appendPQExpBufferChar(q, ')');
17451 }
17452
17453 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17454 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17455
17456 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17457 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17458 }
17459
17460 if (nonemptyReloptions(tbinfo->reloptions) ||
17462 {
17463 bool addcomma = false;
17464
17465 appendPQExpBufferStr(q, "\nWITH (");
17466 if (nonemptyReloptions(tbinfo->reloptions))
17467 {
17468 addcomma = true;
17469 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17470 }
17472 {
17473 if (addcomma)
17474 appendPQExpBufferStr(q, ", ");
17475 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17476 fout);
17477 }
17478 appendPQExpBufferChar(q, ')');
17479 }
17480
17481 /* Dump generic options if any */
17482 if (ftoptions && ftoptions[0])
17483 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17484
17485 /*
17486 * For materialized views, create the AS clause just like a view. At
17487 * this point, we always mark the view as not populated.
17488 */
17489 if (tbinfo->relkind == RELKIND_MATVIEW)
17490 {
17491 PQExpBuffer result;
17492
17493 result = createViewAsClause(fout, tbinfo);
17494 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17495 result->data);
17496 destroyPQExpBuffer(result);
17497 }
17498 else
17499 appendPQExpBufferStr(q, ";\n");
17500
17501 /* Materialized views can depend on extensions */
17502 if (tbinfo->relkind == RELKIND_MATVIEW)
17503 append_depends_on_extension(fout, q, &tbinfo->dobj,
17504 "pg_catalog.pg_class",
17505 "MATERIALIZED VIEW",
17506 qualrelname);
17507
17508 /*
17509 * in binary upgrade mode, update the catalog with any missing values
17510 * that might be present.
17511 */
17512 if (dopt->binary_upgrade)
17513 {
17514 for (j = 0; j < tbinfo->numatts; j++)
17515 {
17516 if (tbinfo->attmissingval[j][0] != '\0')
17517 {
17518 appendPQExpBufferStr(q, "\n-- set missing value.\n");
17520 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17521 appendStringLiteralAH(q, qualrelname, fout);
17522 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17523 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17524 appendPQExpBufferChar(q, ',');
17525 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17526 appendPQExpBufferStr(q, ");\n\n");
17527 }
17528 }
17529 }
17530
17531 /*
17532 * To create binary-compatible heap files, we have to ensure the same
17533 * physical column order, including dropped columns, as in the
17534 * original. Therefore, we create dropped columns above and drop them
17535 * here, also updating their attlen/attalign values so that the
17536 * dropped column can be skipped properly. (We do not bother with
17537 * restoring the original attbyval setting.) Also, inheritance
17538 * relationships are set up by doing ALTER TABLE INHERIT rather than
17539 * using an INHERITS clause --- the latter would possibly mess up the
17540 * column order. That also means we have to take care about setting
17541 * attislocal correctly, plus fix up any inherited CHECK constraints.
17542 * Analogously, we set up typed tables using ALTER TABLE / OF here.
17543 *
17544 * We process foreign and partitioned tables here, even though they
17545 * lack heap storage, because they can participate in inheritance
17546 * relationships and we want this stuff to be consistent across the
17547 * inheritance tree. We can exclude indexes, toast tables, sequences
17548 * and matviews, even though they have storage, because we don't
17549 * support altering or dropping columns in them, nor can they be part
17550 * of inheritance trees.
17551 */
17552 if (dopt->binary_upgrade &&
17553 (tbinfo->relkind == RELKIND_RELATION ||
17554 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17555 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17556 {
17557 bool firstitem;
17558 bool firstitem_extra;
17559
17560 /*
17561 * Drop any dropped columns. Merge the pg_attribute manipulations
17562 * into a single SQL command, so that we don't cause repeated
17563 * relcache flushes on the target table. Otherwise we risk O(N^2)
17564 * relcache bloat while dropping N columns.
17565 */
17566 resetPQExpBuffer(extra);
17567 firstitem = true;
17568 for (j = 0; j < tbinfo->numatts; j++)
17569 {
17570 if (tbinfo->attisdropped[j])
17571 {
17572 if (firstitem)
17573 {
17574 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17575 "UPDATE pg_catalog.pg_attribute\n"
17576 "SET attlen = v.dlen, "
17577 "attalign = v.dalign, "
17578 "attbyval = false\n"
17579 "FROM (VALUES ");
17580 firstitem = false;
17581 }
17582 else
17583 appendPQExpBufferStr(q, ",\n ");
17584 appendPQExpBufferChar(q, '(');
17585 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17586 appendPQExpBuffer(q, ", %d, '%c')",
17587 tbinfo->attlen[j],
17588 tbinfo->attalign[j]);
17589 /* The ALTER ... DROP COLUMN commands must come after */
17590 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17591 foreign, qualrelname);
17592 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17593 fmtId(tbinfo->attnames[j]));
17594 }
17595 }
17596 if (!firstitem)
17597 {
17598 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17599 "WHERE attrelid = ");
17600 appendStringLiteralAH(q, qualrelname, fout);
17601 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17602 " AND attname = v.dname;\n");
17603 /* Now we can issue the actual DROP COLUMN commands */
17604 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17605 }
17606
17607 /*
17608 * Fix up inherited columns. As above, do the pg_attribute
17609 * manipulations in a single SQL command.
17610 */
17611 firstitem = true;
17612 for (j = 0; j < tbinfo->numatts; j++)
17613 {
17614 if (!tbinfo->attisdropped[j] &&
17615 !tbinfo->attislocal[j])
17616 {
17617 if (firstitem)
17618 {
17619 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17620 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17621 "SET attislocal = false\n"
17622 "WHERE attrelid = ");
17623 appendStringLiteralAH(q, qualrelname, fout);
17624 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17625 " AND attname IN (");
17626 firstitem = false;
17627 }
17628 else
17629 appendPQExpBufferStr(q, ", ");
17630 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17631 }
17632 }
17633 if (!firstitem)
17634 appendPQExpBufferStr(q, ");\n");
17635
17636 /*
17637 * Fix up not-null constraints that come from inheritance. As
17638 * above, do the pg_constraint manipulations in a single SQL
17639 * command. (Actually, two in special cases, if we're doing an
17640 * upgrade from < 18).
17641 */
17642 firstitem = true;
17643 firstitem_extra = true;
17644 resetPQExpBuffer(extra);
17645 for (j = 0; j < tbinfo->numatts; j++)
17646 {
17647 /*
17648 * If a not-null constraint comes from inheritance, reset
17649 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17650 * below. Special hack: in versions < 18, columns with no
17651 * local definition need their constraint to be matched by
17652 * column number in conkeys instead of by constraint name,
17653 * because the latter is not available. (We distinguish the
17654 * case because the constraint name is the empty string.)
17655 */
17656 if (tbinfo->notnull_constrs[j] != NULL &&
17657 !tbinfo->notnull_islocal[j])
17658 {
17659 if (tbinfo->notnull_constrs[j][0] != '\0')
17660 {
17661 if (firstitem)
17662 {
17663 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17664 "SET conislocal = false\n"
17665 "WHERE contype = 'n' AND conrelid = ");
17666 appendStringLiteralAH(q, qualrelname, fout);
17667 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17668 "conname IN (");
17669 firstitem = false;
17670 }
17671 else
17672 appendPQExpBufferStr(q, ", ");
17673 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17674 }
17675 else
17676 {
17677 if (firstitem_extra)
17678 {
17679 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17680 "SET conislocal = false\n"
17681 "WHERE contype = 'n' AND conrelid = ");
17682 appendStringLiteralAH(extra, qualrelname, fout);
17683 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17684 "conkey IN (");
17685 firstitem_extra = false;
17686 }
17687 else
17688 appendPQExpBufferStr(extra, ", ");
17689 appendPQExpBuffer(extra, "'{%d}'", j + 1);
17690 }
17691 }
17692 }
17693 if (!firstitem)
17694 appendPQExpBufferStr(q, ");\n");
17695 if (!firstitem_extra)
17696 appendPQExpBufferStr(extra, ");\n");
17697
17698 if (extra->len > 0)
17699 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17700
17701 /*
17702 * Add inherited CHECK constraints, if any.
17703 *
17704 * For partitions, they were already dumped, and conislocal
17705 * doesn't need fixing.
17706 *
17707 * As above, issue only one direct manipulation of pg_constraint.
17708 * Although it is tempting to merge the ALTER ADD CONSTRAINT
17709 * commands into one as well, refrain for now due to concern about
17710 * possible backend memory bloat if there are many such
17711 * constraints.
17712 */
17713 resetPQExpBuffer(extra);
17714 firstitem = true;
17715 for (k = 0; k < tbinfo->ncheck; k++)
17716 {
17717 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17718
17719 if (constr->separate || constr->conislocal || tbinfo->ispartition)
17720 continue;
17721
17722 if (firstitem)
17723 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17724 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17725 foreign, qualrelname,
17726 fmtId(constr->dobj.name),
17727 constr->condef);
17728 /* Update pg_constraint after all the ALTER TABLEs */
17729 if (firstitem)
17730 {
17731 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17732 "SET conislocal = false\n"
17733 "WHERE contype = 'c' AND conrelid = ");
17734 appendStringLiteralAH(extra, qualrelname, fout);
17735 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17736 appendPQExpBufferStr(extra, " AND conname IN (");
17737 firstitem = false;
17738 }
17739 else
17740 appendPQExpBufferStr(extra, ", ");
17741 appendStringLiteralAH(extra, constr->dobj.name, fout);
17742 }
17743 if (!firstitem)
17744 {
17745 appendPQExpBufferStr(extra, ");\n");
17746 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17747 }
17748
17749 if (numParents > 0 && !tbinfo->ispartition)
17750 {
17751 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17752 for (k = 0; k < numParents; k++)
17753 {
17754 TableInfo *parentRel = parents[k];
17755
17756 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17757 qualrelname,
17758 fmtQualifiedDumpable(parentRel));
17759 }
17760 }
17761
17762 if (OidIsValid(tbinfo->reloftype))
17763 {
17764 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17765 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17766 qualrelname,
17767 getFormattedTypeName(fout, tbinfo->reloftype,
17768 zeroIsError));
17769 }
17770 }
17771
17772 /*
17773 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17774 * relminmxid of all vacuumable relations. (While vacuum.c processes
17775 * TOAST tables semi-independently, here we see them only as children
17776 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17777 * child toast table is handled below.)
17778 */
17779 if (dopt->binary_upgrade &&
17780 (tbinfo->relkind == RELKIND_RELATION ||
17781 tbinfo->relkind == RELKIND_MATVIEW))
17782 {
17783 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17784 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17785 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17786 "WHERE oid = ",
17787 tbinfo->frozenxid, tbinfo->minmxid);
17788 appendStringLiteralAH(q, qualrelname, fout);
17789 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17790
17791 if (tbinfo->toast_oid)
17792 {
17793 /*
17794 * The toast table will have the same OID at restore, so we
17795 * can safely target it by OID.
17796 */
17797 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17798 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17799 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17800 "WHERE oid = '%u';\n",
17801 tbinfo->toast_frozenxid,
17802 tbinfo->toast_minmxid, tbinfo->toast_oid);
17803 }
17804 }
17805
17806 /*
17807 * In binary_upgrade mode, restore matviews' populated status by
17808 * poking pg_class directly. This is pretty ugly, but we can't use
17809 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17810 * matview is not populated even though this matview is; in any case,
17811 * we want to transfer the matview's heap storage, not run REFRESH.
17812 */
17813 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17814 tbinfo->relispopulated)
17815 {
17816 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17817 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17818 "SET relispopulated = 't'\n"
17819 "WHERE oid = ");
17820 appendStringLiteralAH(q, qualrelname, fout);
17821 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17822 }
17823
17824 /*
17825 * Dump additional per-column properties that we can't handle in the
17826 * main CREATE TABLE command.
17827 */
17828 for (j = 0; j < tbinfo->numatts; j++)
17829 {
17830 /* None of this applies to dropped columns */
17831 if (tbinfo->attisdropped[j])
17832 continue;
17833
17834 /*
17835 * Dump per-column statistics information. We only issue an ALTER
17836 * TABLE statement if the attstattarget entry for this column is
17837 * not the default value.
17838 */
17839 if (tbinfo->attstattarget[j] >= 0)
17840 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17841 foreign, qualrelname,
17842 fmtId(tbinfo->attnames[j]),
17843 tbinfo->attstattarget[j]);
17844
17845 /*
17846 * Dump per-column storage information. The statement is only
17847 * dumped if the storage has been changed from the type's default.
17848 */
17849 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17850 {
17851 switch (tbinfo->attstorage[j])
17852 {
17853 case TYPSTORAGE_PLAIN:
17854 storage = "PLAIN";
17855 break;
17856 case TYPSTORAGE_EXTERNAL:
17857 storage = "EXTERNAL";
17858 break;
17859 case TYPSTORAGE_EXTENDED:
17860 storage = "EXTENDED";
17861 break;
17862 case TYPSTORAGE_MAIN:
17863 storage = "MAIN";
17864 break;
17865 default:
17866 storage = NULL;
17867 }
17868
17869 /*
17870 * Only dump the statement if it's a storage type we recognize
17871 */
17872 if (storage != NULL)
17873 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17874 foreign, qualrelname,
17875 fmtId(tbinfo->attnames[j]),
17876 storage);
17877 }
17878
17879 /*
17880 * Dump per-column compression, if it's been set.
17881 */
17882 if (!dopt->no_toast_compression)
17883 {
17884 const char *cmname;
17885
17886 switch (tbinfo->attcompression[j])
17887 {
17888 case 'p':
17889 cmname = "pglz";
17890 break;
17891 case 'l':
17892 cmname = "lz4";
17893 break;
17894 default:
17895 cmname = NULL;
17896 break;
17897 }
17898
17899 if (cmname != NULL)
17900 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17901 foreign, qualrelname,
17902 fmtId(tbinfo->attnames[j]),
17903 cmname);
17904 }
17905
17906 /*
17907 * Dump per-column attributes.
17908 */
17909 if (tbinfo->attoptions[j][0] != '\0')
17910 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17911 foreign, qualrelname,
17912 fmtId(tbinfo->attnames[j]),
17913 tbinfo->attoptions[j]);
17914
17915 /*
17916 * Dump per-column fdw options.
17917 */
17918 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17919 tbinfo->attfdwoptions[j][0] != '\0')
17921 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17922 " %s\n"
17923 ");\n",
17924 qualrelname,
17925 fmtId(tbinfo->attnames[j]),
17926 tbinfo->attfdwoptions[j]);
17927 } /* end loop over columns */
17928
17929 free(partkeydef);
17930 free(ftoptions);
17931 free(srvname);
17932 }
17933
17934 /*
17935 * dump properties we only have ALTER TABLE syntax for
17936 */
17937 if ((tbinfo->relkind == RELKIND_RELATION ||
17938 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17939 tbinfo->relkind == RELKIND_MATVIEW) &&
17940 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17941 {
17942 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17943 {
17944 /* nothing to do, will be set when the index is dumped */
17945 }
17946 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17947 {
17948 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17949 qualrelname);
17950 }
17951 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17952 {
17953 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17954 qualrelname);
17955 }
17956 }
17957
17958 if (tbinfo->forcerowsec)
17959 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17960 qualrelname);
17961
17962 if (dopt->binary_upgrade)
17964 reltypename, qrelname,
17965 tbinfo->dobj.namespace->dobj.name);
17966
17967 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17968 {
17969 char *tablespace = NULL;
17970 char *tableam = NULL;
17971
17972 /*
17973 * _selectTablespace() relies on tablespace-enabled objects in the
17974 * default tablespace to have a tablespace of "" (empty string) versus
17975 * non-tablespace-enabled objects to have a tablespace of NULL.
17976 * getTables() sets tbinfo->reltablespace to "" for the default
17977 * tablespace (not NULL).
17978 */
17979 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17980 tablespace = tbinfo->reltablespace;
17981
17982 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17983 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17984 tableam = tbinfo->amname;
17985
17986 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17987 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17988 .namespace = tbinfo->dobj.namespace->dobj.name,
17989 .tablespace = tablespace,
17990 .tableam = tableam,
17991 .relkind = tbinfo->relkind,
17992 .owner = tbinfo->rolname,
17993 .description = reltypename,
17994 .section = tbinfo->postponed_def ?
17996 .createStmt = q->data,
17997 .dropStmt = delq->data));
17998 }
17999
18000 /* Dump Table Comments */
18001 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18002 dumpTableComment(fout, tbinfo, reltypename);
18003
18004 /* Dump Table Security Labels */
18005 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18006 dumpTableSecLabel(fout, tbinfo, reltypename);
18007
18008 /*
18009 * Dump comments for not-null constraints that aren't to be dumped
18010 * separately (those are processed by collectComments/dumpComment).
18011 */
18012 if (!fout->dopt->no_comments && dopt->dumpSchema &&
18013 fout->remoteVersion >= 180000)
18014 {
18015 PQExpBuffer comment = NULL;
18016 PQExpBuffer tag = NULL;
18017
18018 for (j = 0; j < tbinfo->numatts; j++)
18019 {
18020 if (tbinfo->notnull_constrs[j] != NULL &&
18021 tbinfo->notnull_comment[j] != NULL)
18022 {
18023 if (comment == NULL)
18024 {
18026 tag = createPQExpBuffer();
18027 }
18028 else
18029 {
18031 resetPQExpBuffer(tag);
18032 }
18033
18034 appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18035 fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18038
18039 appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18040 fmtId(tbinfo->notnull_constrs[j]), qrelname);
18041
18043 ARCHIVE_OPTS(.tag = tag->data,
18044 .namespace = tbinfo->dobj.namespace->dobj.name,
18045 .owner = tbinfo->rolname,
18046 .description = "COMMENT",
18047 .section = SECTION_NONE,
18048 .createStmt = comment->data,
18049 .deps = &(tbinfo->dobj.dumpId),
18050 .nDeps = 1));
18051 }
18052 }
18053
18055 destroyPQExpBuffer(tag);
18056 }
18057
18058 /* Dump comments on inlined table constraints */
18059 for (j = 0; j < tbinfo->ncheck; j++)
18060 {
18061 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18062
18063 if (constr->separate || !constr->conislocal)
18064 continue;
18065
18066 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18067 dumpTableConstraintComment(fout, constr);
18068 }
18069
18071 destroyPQExpBuffer(delq);
18072 destroyPQExpBuffer(extra);
18073 free(qrelname);
18074 free(qualrelname);
18075}
18076
18077/*
18078 * dumpTableAttach
18079 * write to fout the commands to attach a child partition
18080 *
18081 * Child partitions are always made by creating them separately
18082 * and then using ATTACH PARTITION, rather than using
18083 * CREATE TABLE ... PARTITION OF. This is important for preserving
18084 * any possible discrepancy in column layout, to allow assigning the
18085 * correct tablespace if different, and so that it's possible to restore
18086 * a partition without restoring its parent. (You'll get an error from
18087 * the ATTACH PARTITION command, but that can be ignored, or skipped
18088 * using "pg_restore -L" if you prefer.) The last point motivates
18089 * treating ATTACH PARTITION as a completely separate ArchiveEntry
18090 * rather than emitting it within the child partition's ArchiveEntry.
18091 */
18092static void
18093dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18094{
18095 DumpOptions *dopt = fout->dopt;
18096 PQExpBuffer q;
18097 PGresult *res;
18098 char *partbound;
18099
18100 /* Do nothing if not dumping schema */
18101 if (!dopt->dumpSchema)
18102 return;
18103
18104 q = createPQExpBuffer();
18105
18107 {
18108 /* Set up query for partbound details */
18110 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18111
18113 "SELECT pg_get_expr(c.relpartbound, c.oid) "
18114 "FROM pg_class c "
18115 "WHERE c.oid = $1");
18116
18117 ExecuteSqlStatement(fout, q->data);
18118
18120 }
18121
18123 "EXECUTE dumpTableAttach('%u')",
18124 attachinfo->partitionTbl->dobj.catId.oid);
18125
18126 res = ExecuteSqlQueryForSingleRow(fout, q->data);
18127 partbound = PQgetvalue(res, 0, 0);
18128
18129 /* Perform ALTER TABLE on the parent */
18131 "ALTER TABLE ONLY %s ",
18132 fmtQualifiedDumpable(attachinfo->parentTbl));
18134 "ATTACH PARTITION %s %s;\n",
18136 partbound);
18137
18138 /*
18139 * There is no point in creating a drop query as the drop is done by table
18140 * drop. (If you think to change this, see also _printTocEntry().)
18141 * Although this object doesn't really have ownership as such, set the
18142 * owner field anyway to ensure that the command is run by the correct
18143 * role at restore time.
18144 */
18145 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18146 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18147 .namespace = attachinfo->dobj.namespace->dobj.name,
18148 .owner = attachinfo->partitionTbl->rolname,
18149 .description = "TABLE ATTACH",
18150 .section = SECTION_PRE_DATA,
18151 .createStmt = q->data));
18152
18153 PQclear(res);
18155}
18156
18157/*
18158 * dumpAttrDef --- dump an attribute's default-value declaration
18159 */
18160static void
18161dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18162{
18163 DumpOptions *dopt = fout->dopt;
18164 TableInfo *tbinfo = adinfo->adtable;
18165 int adnum = adinfo->adnum;
18166 PQExpBuffer q;
18167 PQExpBuffer delq;
18168 char *qualrelname;
18169 char *tag;
18170 char *foreign;
18171
18172 /* Do nothing if not dumping schema */
18173 if (!dopt->dumpSchema)
18174 return;
18175
18176 /* Skip if not "separate"; it was dumped in the table's definition */
18177 if (!adinfo->separate)
18178 return;
18179
18180 q = createPQExpBuffer();
18181 delq = createPQExpBuffer();
18182
18183 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18184
18185 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18186
18188 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18189 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18190 adinfo->adef_expr);
18191
18192 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18193 foreign, qualrelname,
18194 fmtId(tbinfo->attnames[adnum - 1]));
18195
18196 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18197
18198 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18199 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18200 ARCHIVE_OPTS(.tag = tag,
18201 .namespace = tbinfo->dobj.namespace->dobj.name,
18202 .owner = tbinfo->rolname,
18203 .description = "DEFAULT",
18204 .section = SECTION_PRE_DATA,
18205 .createStmt = q->data,
18206 .dropStmt = delq->data));
18207
18208 free(tag);
18210 destroyPQExpBuffer(delq);
18211 free(qualrelname);
18212}
18213
18214/*
18215 * getAttrName: extract the correct name for an attribute
18216 *
18217 * The array tblInfo->attnames[] only provides names of user attributes;
18218 * if a system attribute number is supplied, we have to fake it.
18219 * We also do a little bit of bounds checking for safety's sake.
18220 */
18221static const char *
18222getAttrName(int attrnum, const TableInfo *tblInfo)
18223{
18224 if (attrnum > 0 && attrnum <= tblInfo->numatts)
18225 return tblInfo->attnames[attrnum - 1];
18226 switch (attrnum)
18227 {
18229 return "ctid";
18231 return "xmin";
18233 return "cmin";
18235 return "xmax";
18237 return "cmax";
18239 return "tableoid";
18240 }
18241 pg_fatal("invalid column number %d for table \"%s\"",
18242 attrnum, tblInfo->dobj.name);
18243 return NULL; /* keep compiler quiet */
18244}
18245
18246/*
18247 * dumpIndex
18248 * write out to fout a user-defined index
18249 */
18250static void
18251dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18252{
18253 DumpOptions *dopt = fout->dopt;
18254 TableInfo *tbinfo = indxinfo->indextable;
18255 bool is_constraint = (indxinfo->indexconstraint != 0);
18256 PQExpBuffer q;
18257 PQExpBuffer delq;
18258 char *qindxname;
18259 char *qqindxname;
18260
18261 /* Do nothing if not dumping schema */
18262 if (!dopt->dumpSchema)
18263 return;
18264
18265 q = createPQExpBuffer();
18266 delq = createPQExpBuffer();
18267
18268 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18269 qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18270
18271 /*
18272 * If there's an associated constraint, don't dump the index per se, but
18273 * do dump any comment for it. (This is safe because dependency ordering
18274 * will have ensured the constraint is emitted first.) Note that the
18275 * emitted comment has to be shown as depending on the constraint, not the
18276 * index, in such cases.
18277 */
18278 if (!is_constraint)
18279 {
18280 char *indstatcols = indxinfo->indstatcols;
18281 char *indstatvals = indxinfo->indstatvals;
18282 char **indstatcolsarray = NULL;
18283 char **indstatvalsarray = NULL;
18284 int nstatcols = 0;
18285 int nstatvals = 0;
18286
18287 if (dopt->binary_upgrade)
18289 indxinfo->dobj.catId.oid);
18290
18291 /* Plain secondary index */
18292 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18293
18294 /*
18295 * Append ALTER TABLE commands as needed to set properties that we
18296 * only have ALTER TABLE syntax for. Keep this in sync with the
18297 * similar code in dumpConstraint!
18298 */
18299
18300 /* If the index is clustered, we need to record that. */
18301 if (indxinfo->indisclustered)
18302 {
18303 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18304 fmtQualifiedDumpable(tbinfo));
18305 /* index name is not qualified in this syntax */
18306 appendPQExpBuffer(q, " ON %s;\n",
18307 qindxname);
18308 }
18309
18310 /*
18311 * If the index has any statistics on some of its columns, generate
18312 * the associated ALTER INDEX queries.
18313 */
18314 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18315 {
18316 int j;
18317
18318 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18319 pg_fatal("could not parse index statistic columns");
18320 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18321 pg_fatal("could not parse index statistic values");
18322 if (nstatcols != nstatvals)
18323 pg_fatal("mismatched number of columns and values for index statistics");
18324
18325 for (j = 0; j < nstatcols; j++)
18326 {
18327 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18328
18329 /*
18330 * Note that this is a column number, so no quotes should be
18331 * used.
18332 */
18333 appendPQExpBuffer(q, "ALTER COLUMN %s ",
18334 indstatcolsarray[j]);
18335 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18336 indstatvalsarray[j]);
18337 }
18338 }
18339
18340 /* Indexes can depend on extensions */
18341 append_depends_on_extension(fout, q, &indxinfo->dobj,
18342 "pg_catalog.pg_class",
18343 "INDEX", qqindxname);
18344
18345 /* If the index defines identity, we need to record that. */
18346 if (indxinfo->indisreplident)
18347 {
18348 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18349 fmtQualifiedDumpable(tbinfo));
18350 /* index name is not qualified in this syntax */
18351 appendPQExpBuffer(q, " INDEX %s;\n",
18352 qindxname);
18353 }
18354
18355 /*
18356 * If this index is a member of a partitioned index, the backend will
18357 * not allow us to drop it separately, so don't try. It will go away
18358 * automatically when we drop either the index's table or the
18359 * partitioned index. (If, in a selective restore with --clean, we
18360 * drop neither of those, then this index will not be dropped either.
18361 * But that's fine, and even if you think it's not, the backend won't
18362 * let us do differently.)
18363 */
18364 if (indxinfo->parentidx == 0)
18365 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18366
18367 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18368 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18369 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18370 .namespace = tbinfo->dobj.namespace->dobj.name,
18371 .tablespace = indxinfo->tablespace,
18372 .owner = tbinfo->rolname,
18373 .description = "INDEX",
18374 .section = SECTION_POST_DATA,
18375 .createStmt = q->data,
18376 .dropStmt = delq->data));
18377
18378 free(indstatcolsarray);
18379 free(indstatvalsarray);
18380 }
18381
18382 /* Dump Index Comments */
18383 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18384 dumpComment(fout, "INDEX", qindxname,
18385 tbinfo->dobj.namespace->dobj.name,
18386 tbinfo->rolname,
18387 indxinfo->dobj.catId, 0,
18388 is_constraint ? indxinfo->indexconstraint :
18389 indxinfo->dobj.dumpId);
18390
18392 destroyPQExpBuffer(delq);
18393 free(qindxname);
18394 free(qqindxname);
18395}
18396
18397/*
18398 * dumpIndexAttach
18399 * write out to fout a partitioned-index attachment clause
18400 */
18401static void
18402dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18403{
18404 /* Do nothing if not dumping schema */
18405 if (!fout->dopt->dumpSchema)
18406 return;
18407
18409 {
18411
18412 appendPQExpBuffer(q, "ALTER INDEX %s ",
18413 fmtQualifiedDumpable(attachinfo->parentIdx));
18414 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18415 fmtQualifiedDumpable(attachinfo->partitionIdx));
18416
18417 /*
18418 * There is no need for a dropStmt since the drop is done implicitly
18419 * when we drop either the index's table or the partitioned index.
18420 * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18421 * there's no way to do it anyway. (If you think to change this,
18422 * consider also what to do with --if-exists.)
18423 *
18424 * Although this object doesn't really have ownership as such, set the
18425 * owner field anyway to ensure that the command is run by the correct
18426 * role at restore time.
18427 */
18428 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18429 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18430 .namespace = attachinfo->dobj.namespace->dobj.name,
18431 .owner = attachinfo->parentIdx->indextable->rolname,
18432 .description = "INDEX ATTACH",
18433 .section = SECTION_POST_DATA,
18434 .createStmt = q->data));
18435
18437 }
18438}
18439
18440/*
18441 * dumpStatisticsExt
18442 * write out to fout an extended statistics object
18443 */
18444static void
18445dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18446{
18447 DumpOptions *dopt = fout->dopt;
18448 PQExpBuffer q;
18449 PQExpBuffer delq;
18450 PQExpBuffer query;
18451 char *qstatsextname;
18452 PGresult *res;
18453 char *stxdef;
18454
18455 /* Do nothing if not dumping schema */
18456 if (!dopt->dumpSchema)
18457 return;
18458
18459 q = createPQExpBuffer();
18460 delq = createPQExpBuffer();
18461 query = createPQExpBuffer();
18462
18463 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18464
18465 appendPQExpBuffer(query, "SELECT "
18466 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18467 statsextinfo->dobj.catId.oid);
18468
18469 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18470
18471 stxdef = PQgetvalue(res, 0, 0);
18472
18473 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18474 appendPQExpBuffer(q, "%s;\n", stxdef);
18475
18476 /*
18477 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18478 * for this statistics object is not the default value.
18479 */
18480 if (statsextinfo->stattarget >= 0)
18481 {
18482 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18483 fmtQualifiedDumpable(statsextinfo));
18484 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18485 statsextinfo->stattarget);
18486 }
18487
18488 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18489 fmtQualifiedDumpable(statsextinfo));
18490
18491 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18492 ArchiveEntry(fout, statsextinfo->dobj.catId,
18493 statsextinfo->dobj.dumpId,
18494 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18495 .namespace = statsextinfo->dobj.namespace->dobj.name,
18496 .owner = statsextinfo->rolname,
18497 .description = "STATISTICS",
18498 .section = SECTION_POST_DATA,
18499 .createStmt = q->data,
18500 .dropStmt = delq->data));
18501
18502 /* Dump Statistics Comments */
18503 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18504 dumpComment(fout, "STATISTICS", qstatsextname,
18505 statsextinfo->dobj.namespace->dobj.name,
18506 statsextinfo->rolname,
18507 statsextinfo->dobj.catId, 0,
18508 statsextinfo->dobj.dumpId);
18509
18510 PQclear(res);
18512 destroyPQExpBuffer(delq);
18513 destroyPQExpBuffer(query);
18514 free(qstatsextname);
18515}
18516
18517/*
18518 * dumpConstraint
18519 * write out to fout a user-defined constraint
18520 */
18521static void
18523{
18524 DumpOptions *dopt = fout->dopt;
18525 TableInfo *tbinfo = coninfo->contable;
18526 PQExpBuffer q;
18527 PQExpBuffer delq;
18528 char *tag = NULL;
18529 char *foreign;
18530
18531 /* Do nothing if not dumping schema */
18532 if (!dopt->dumpSchema)
18533 return;
18534
18535 q = createPQExpBuffer();
18536 delq = createPQExpBuffer();
18537
18538 foreign = tbinfo &&
18539 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18540
18541 if (coninfo->contype == 'p' ||
18542 coninfo->contype == 'u' ||
18543 coninfo->contype == 'x')
18544 {
18545 /* Index-related constraint */
18546 IndxInfo *indxinfo;
18547 int k;
18548
18549 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18550
18551 if (indxinfo == NULL)
18552 pg_fatal("missing index for constraint \"%s\"",
18553 coninfo->dobj.name);
18554
18555 if (dopt->binary_upgrade)
18557 indxinfo->dobj.catId.oid);
18558
18559 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18560 fmtQualifiedDumpable(tbinfo));
18561 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18562 fmtId(coninfo->dobj.name));
18563
18564 if (coninfo->condef)
18565 {
18566 /* pg_get_constraintdef should have provided everything */
18567 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18568 }
18569 else
18570 {
18572 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18573
18574 /*
18575 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18576 * indexes. Being able to create this was fixed, but we need to
18577 * make the index distinct in order to be able to restore the
18578 * dump.
18579 */
18580 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18581 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18582 appendPQExpBufferStr(q, " (");
18583 for (k = 0; k < indxinfo->indnkeyattrs; k++)
18584 {
18585 int indkey = (int) indxinfo->indkeys[k];
18586 const char *attname;
18587
18588 if (indkey == InvalidAttrNumber)
18589 break;
18590 attname = getAttrName(indkey, tbinfo);
18591
18592 appendPQExpBuffer(q, "%s%s",
18593 (k == 0) ? "" : ", ",
18594 fmtId(attname));
18595 }
18596 if (coninfo->conperiod)
18597 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18598
18599 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18600 appendPQExpBufferStr(q, ") INCLUDE (");
18601
18602 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18603 {
18604 int indkey = (int) indxinfo->indkeys[k];
18605 const char *attname;
18606
18607 if (indkey == InvalidAttrNumber)
18608 break;
18609 attname = getAttrName(indkey, tbinfo);
18610
18611 appendPQExpBuffer(q, "%s%s",
18612 (k == indxinfo->indnkeyattrs) ? "" : ", ",
18613 fmtId(attname));
18614 }
18615
18616 appendPQExpBufferChar(q, ')');
18617
18618 if (nonemptyReloptions(indxinfo->indreloptions))
18619 {
18620 appendPQExpBufferStr(q, " WITH (");
18621 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18622 appendPQExpBufferChar(q, ')');
18623 }
18624
18625 if (coninfo->condeferrable)
18626 {
18627 appendPQExpBufferStr(q, " DEFERRABLE");
18628 if (coninfo->condeferred)
18629 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18630 }
18631
18632 appendPQExpBufferStr(q, ";\n");
18633 }
18634
18635 /*
18636 * Append ALTER TABLE commands as needed to set properties that we
18637 * only have ALTER TABLE syntax for. Keep this in sync with the
18638 * similar code in dumpIndex!
18639 */
18640
18641 /* If the index is clustered, we need to record that. */
18642 if (indxinfo->indisclustered)
18643 {
18644 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18645 fmtQualifiedDumpable(tbinfo));
18646 /* index name is not qualified in this syntax */
18647 appendPQExpBuffer(q, " ON %s;\n",
18648 fmtId(indxinfo->dobj.name));
18649 }
18650
18651 /* If the index defines identity, we need to record that. */
18652 if (indxinfo->indisreplident)
18653 {
18654 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18655 fmtQualifiedDumpable(tbinfo));
18656 /* index name is not qualified in this syntax */
18657 appendPQExpBuffer(q, " INDEX %s;\n",
18658 fmtId(indxinfo->dobj.name));
18659 }
18660
18661 /* Indexes can depend on extensions */
18662 append_depends_on_extension(fout, q, &indxinfo->dobj,
18663 "pg_catalog.pg_class", "INDEX",
18664 fmtQualifiedDumpable(indxinfo));
18665
18666 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18667 fmtQualifiedDumpable(tbinfo));
18668 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18669 fmtId(coninfo->dobj.name));
18670
18671 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18672
18673 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18674 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18675 ARCHIVE_OPTS(.tag = tag,
18676 .namespace = tbinfo->dobj.namespace->dobj.name,
18677 .tablespace = indxinfo->tablespace,
18678 .owner = tbinfo->rolname,
18679 .description = "CONSTRAINT",
18680 .section = SECTION_POST_DATA,
18681 .createStmt = q->data,
18682 .dropStmt = delq->data));
18683 }
18684 else if (coninfo->contype == 'f')
18685 {
18686 char *only;
18687
18688 /*
18689 * Foreign keys on partitioned tables are always declared as
18690 * inheriting to partitions; for all other cases, emit them as
18691 * applying ONLY directly to the named table, because that's how they
18692 * work for regular inherited tables.
18693 */
18694 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18695
18696 /*
18697 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18698 * current table data is not processed
18699 */
18700 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18701 only, fmtQualifiedDumpable(tbinfo));
18702 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18703 fmtId(coninfo->dobj.name),
18704 coninfo->condef);
18705
18706 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18707 only, fmtQualifiedDumpable(tbinfo));
18708 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18709 fmtId(coninfo->dobj.name));
18710
18711 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18712
18713 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18714 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18715 ARCHIVE_OPTS(.tag = tag,
18716 .namespace = tbinfo->dobj.namespace->dobj.name,
18717 .owner = tbinfo->rolname,
18718 .description = "FK CONSTRAINT",
18719 .section = SECTION_POST_DATA,
18720 .createStmt = q->data,
18721 .dropStmt = delq->data));
18722 }
18723 else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18724 {
18725 /* CHECK or invalid not-null constraint on a table */
18726
18727 /* Ignore if not to be dumped separately, or if it was inherited */
18728 if (coninfo->separate && coninfo->conislocal)
18729 {
18730 const char *keyword;
18731
18732 if (coninfo->contype == 'c')
18733 keyword = "CHECK CONSTRAINT";
18734 else
18735 keyword = "CONSTRAINT";
18736
18737 /* not ONLY since we want it to propagate to children */
18738 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18739 fmtQualifiedDumpable(tbinfo));
18740 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18741 fmtId(coninfo->dobj.name),
18742 coninfo->condef);
18743
18744 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18745 fmtQualifiedDumpable(tbinfo));
18746 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18747 fmtId(coninfo->dobj.name));
18748
18749 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18750
18751 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18752 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18753 ARCHIVE_OPTS(.tag = tag,
18754 .namespace = tbinfo->dobj.namespace->dobj.name,
18755 .owner = tbinfo->rolname,
18756 .description = keyword,
18757 .section = SECTION_POST_DATA,
18758 .createStmt = q->data,
18759 .dropStmt = delq->data));
18760 }
18761 }
18762 else if (tbinfo == NULL)
18763 {
18764 /* CHECK, NOT NULL constraint on a domain */
18765 TypeInfo *tyinfo = coninfo->condomain;
18766
18767 Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18768
18769 /* Ignore if not to be dumped separately */
18770 if (coninfo->separate)
18771 {
18772 const char *keyword;
18773
18774 if (coninfo->contype == 'c')
18775 keyword = "CHECK CONSTRAINT";
18776 else
18777 keyword = "CONSTRAINT";
18778
18779 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18780 fmtQualifiedDumpable(tyinfo));
18781 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18782 fmtId(coninfo->dobj.name),
18783 coninfo->condef);
18784
18785 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18786 fmtQualifiedDumpable(tyinfo));
18787 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18788 fmtId(coninfo->dobj.name));
18789
18790 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18791
18792 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18793 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18794 ARCHIVE_OPTS(.tag = tag,
18795 .namespace = tyinfo->dobj.namespace->dobj.name,
18796 .owner = tyinfo->rolname,
18797 .description = keyword,
18798 .section = SECTION_POST_DATA,
18799 .createStmt = q->data,
18800 .dropStmt = delq->data));
18801
18802 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18803 {
18804 PQExpBuffer conprefix = createPQExpBuffer();
18805 char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18806
18807 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18808 fmtId(coninfo->dobj.name));
18809
18810 dumpComment(fout, conprefix->data, qtypname,
18811 tyinfo->dobj.namespace->dobj.name,
18812 tyinfo->rolname,
18813 coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18814 destroyPQExpBuffer(conprefix);
18815 free(qtypname);
18816 }
18817 }
18818 }
18819 else
18820 {
18821 pg_fatal("unrecognized constraint type: %c",
18822 coninfo->contype);
18823 }
18824
18825 /* Dump Constraint Comments --- only works for table constraints */
18826 if (tbinfo && coninfo->separate &&
18827 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18828 dumpTableConstraintComment(fout, coninfo);
18829
18830 free(tag);
18832 destroyPQExpBuffer(delq);
18833}
18834
18835/*
18836 * dumpTableConstraintComment --- dump a constraint's comment if any
18837 *
18838 * This is split out because we need the function in two different places
18839 * depending on whether the constraint is dumped as part of CREATE TABLE
18840 * or as a separate ALTER command.
18841 */
18842static void
18844{
18845 TableInfo *tbinfo = coninfo->contable;
18846 PQExpBuffer conprefix = createPQExpBuffer();
18847 char *qtabname;
18848
18849 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18850
18851 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18852 fmtId(coninfo->dobj.name));
18853
18854 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18855 dumpComment(fout, conprefix->data, qtabname,
18856 tbinfo->dobj.namespace->dobj.name,
18857 tbinfo->rolname,
18858 coninfo->dobj.catId, 0,
18859 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18860
18861 destroyPQExpBuffer(conprefix);
18862 free(qtabname);
18863}
18864
18865static inline SeqType
18867{
18868 for (int i = 0; i < lengthof(SeqTypeNames); i++)
18869 {
18870 if (strcmp(SeqTypeNames[i], name) == 0)
18871 return (SeqType) i;
18872 }
18873
18874 pg_fatal("unrecognized sequence type: %s", name);
18875 return (SeqType) 0; /* keep compiler quiet */
18876}
18877
18878/*
18879 * bsearch() comparator for SequenceItem
18880 */
18881static int
18882SequenceItemCmp(const void *p1, const void *p2)
18883{
18884 SequenceItem v1 = *((const SequenceItem *) p1);
18885 SequenceItem v2 = *((const SequenceItem *) p2);
18886
18887 return pg_cmp_u32(v1.oid, v2.oid);
18888}
18889
18890/*
18891 * collectSequences
18892 *
18893 * Construct a table of sequence information. This table is sorted by OID for
18894 * speed in lookup.
18895 */
18896static void
18898{
18899 PGresult *res;
18900 const char *query;
18901
18902 /*
18903 * Before Postgres 10, sequence metadata is in the sequence itself. With
18904 * some extra effort, we might be able to use the sorted table for those
18905 * versions, but for now it seems unlikely to be worth it.
18906 *
18907 * Since version 18, we can gather the sequence data in this query with
18908 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18909 */
18910 if (fout->remoteVersion < 100000)
18911 return;
18912 else if (fout->remoteVersion < 180000 ||
18913 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18914 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18915 "seqstart, seqincrement, "
18916 "seqmax, seqmin, "
18917 "seqcache, seqcycle, "
18918 "NULL, 'f' "
18919 "FROM pg_catalog.pg_sequence "
18920 "ORDER BY seqrelid";
18921 else
18922 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18923 "seqstart, seqincrement, "
18924 "seqmax, seqmin, "
18925 "seqcache, seqcycle, "
18926 "last_value, is_called "
18927 "FROM pg_catalog.pg_sequence, "
18928 "pg_get_sequence_data(seqrelid) "
18929 "ORDER BY seqrelid;";
18930
18931 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18932
18933 nsequences = PQntuples(res);
18935
18936 for (int i = 0; i < nsequences; i++)
18937 {
18938 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18940 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18941 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18942 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18943 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18944 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18945 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18946 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18947 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18948 }
18949
18950 PQclear(res);
18951}
18952
18953/*
18954 * dumpSequence
18955 * write the declaration (not data) of one user-defined sequence
18956 */
18957static void
18958dumpSequence(Archive *fout, const TableInfo *tbinfo)
18959{
18960 DumpOptions *dopt = fout->dopt;
18961 SequenceItem *seq;
18962 bool is_ascending;
18963 int64 default_minv,
18964 default_maxv;
18966 PQExpBuffer delqry = createPQExpBuffer();
18967 char *qseqname;
18968 TableInfo *owning_tab = NULL;
18969
18970 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18971
18972 /*
18973 * For versions >= 10, the sequence information is gathered in a sorted
18974 * table before any calls to dumpSequence(). See collectSequences() for
18975 * more information.
18976 */
18977 if (fout->remoteVersion >= 100000)
18978 {
18979 SequenceItem key = {0};
18980
18982
18983 key.oid = tbinfo->dobj.catId.oid;
18984 seq = bsearch(&key, sequences, nsequences,
18985 sizeof(SequenceItem), SequenceItemCmp);
18986 }
18987 else
18988 {
18989 PGresult *res;
18990
18991 /*
18992 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18993 *
18994 * Note: it might seem that 'bigint' potentially needs to be
18995 * schema-qualified, but actually that's a keyword.
18996 */
18997 appendPQExpBuffer(query,
18998 "SELECT 'bigint' AS sequence_type, "
18999 "start_value, increment_by, max_value, min_value, "
19000 "cache_value, is_cycled FROM %s",
19001 fmtQualifiedDumpable(tbinfo));
19002
19003 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19004
19005 if (PQntuples(res) != 1)
19006 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19007 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19008 PQntuples(res)),
19009 tbinfo->dobj.name, PQntuples(res));
19010
19011 seq = pg_malloc0(sizeof(SequenceItem));
19012 seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19013 seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19014 seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19015 seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19016 seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19017 seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19018 seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19019
19020 PQclear(res);
19021 }
19022
19023 /* Calculate default limits for a sequence of this type */
19024 is_ascending = (seq->incby >= 0);
19025 if (seq->seqtype == SEQTYPE_SMALLINT)
19026 {
19027 default_minv = is_ascending ? 1 : PG_INT16_MIN;
19028 default_maxv = is_ascending ? PG_INT16_MAX : -1;
19029 }
19030 else if (seq->seqtype == SEQTYPE_INTEGER)
19031 {
19032 default_minv = is_ascending ? 1 : PG_INT32_MIN;
19033 default_maxv = is_ascending ? PG_INT32_MAX : -1;
19034 }
19035 else if (seq->seqtype == SEQTYPE_BIGINT)
19036 {
19037 default_minv = is_ascending ? 1 : PG_INT64_MIN;
19038 default_maxv = is_ascending ? PG_INT64_MAX : -1;
19039 }
19040 else
19041 {
19042 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19043 default_minv = default_maxv = 0; /* keep compiler quiet */
19044 }
19045
19046 /*
19047 * Identity sequences are not to be dropped separately.
19048 */
19049 if (!tbinfo->is_identity_sequence)
19050 {
19051 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19052 fmtQualifiedDumpable(tbinfo));
19053 }
19054
19055 resetPQExpBuffer(query);
19056
19057 if (dopt->binary_upgrade)
19058 {
19060 tbinfo->dobj.catId.oid);
19061
19062 /*
19063 * In older PG versions a sequence will have a pg_type entry, but v14
19064 * and up don't use that, so don't attempt to preserve the type OID.
19065 */
19066 }
19067
19068 if (tbinfo->is_identity_sequence)
19069 {
19070 owning_tab = findTableByOid(tbinfo->owning_tab);
19071
19072 appendPQExpBuffer(query,
19073 "ALTER TABLE %s ",
19074 fmtQualifiedDumpable(owning_tab));
19075 appendPQExpBuffer(query,
19076 "ALTER COLUMN %s ADD GENERATED ",
19077 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19078 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19079 appendPQExpBufferStr(query, "ALWAYS");
19080 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19081 appendPQExpBufferStr(query, "BY DEFAULT");
19082 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19083 fmtQualifiedDumpable(tbinfo));
19084
19085 /*
19086 * Emit persistence option only if it's different from the owning
19087 * table's. This avoids using this new syntax unnecessarily.
19088 */
19089 if (tbinfo->relpersistence != owning_tab->relpersistence)
19090 appendPQExpBuffer(query, " %s\n",
19091 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19092 "UNLOGGED" : "LOGGED");
19093 }
19094 else
19095 {
19096 appendPQExpBuffer(query,
19097 "CREATE %sSEQUENCE %s\n",
19098 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19099 "UNLOGGED " : "",
19100 fmtQualifiedDumpable(tbinfo));
19101
19102 if (seq->seqtype != SEQTYPE_BIGINT)
19103 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19104 }
19105
19106 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19107
19108 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19109
19110 if (seq->minv != default_minv)
19111 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19112 else
19113 appendPQExpBufferStr(query, " NO MINVALUE\n");
19114
19115 if (seq->maxv != default_maxv)
19116 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19117 else
19118 appendPQExpBufferStr(query, " NO MAXVALUE\n");
19119
19120 appendPQExpBuffer(query,
19121 " CACHE " INT64_FORMAT "%s",
19122 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19123
19124 if (tbinfo->is_identity_sequence)
19125 appendPQExpBufferStr(query, "\n);\n");
19126 else
19127 appendPQExpBufferStr(query, ";\n");
19128
19129 /* binary_upgrade: no need to clear TOAST table oid */
19130
19131 if (dopt->binary_upgrade)
19132 binary_upgrade_extension_member(query, &tbinfo->dobj,
19133 "SEQUENCE", qseqname,
19134 tbinfo->dobj.namespace->dobj.name);
19135
19136 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19137 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19138 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19139 .namespace = tbinfo->dobj.namespace->dobj.name,
19140 .owner = tbinfo->rolname,
19141 .description = "SEQUENCE",
19142 .section = SECTION_PRE_DATA,
19143 .createStmt = query->data,
19144 .dropStmt = delqry->data));
19145
19146 /*
19147 * If the sequence is owned by a table column, emit the ALTER for it as a
19148 * separate TOC entry immediately following the sequence's own entry. It's
19149 * OK to do this rather than using full sorting logic, because the
19150 * dependency that tells us it's owned will have forced the table to be
19151 * created first. We can't just include the ALTER in the TOC entry
19152 * because it will fail if we haven't reassigned the sequence owner to
19153 * match the table's owner.
19154 *
19155 * We need not schema-qualify the table reference because both sequence
19156 * and table must be in the same schema.
19157 */
19158 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19159 {
19160 owning_tab = findTableByOid(tbinfo->owning_tab);
19161
19162 if (owning_tab == NULL)
19163 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19164 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19165
19166 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19167 {
19168 resetPQExpBuffer(query);
19169 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19170 fmtQualifiedDumpable(tbinfo));
19171 appendPQExpBuffer(query, " OWNED BY %s",
19172 fmtQualifiedDumpable(owning_tab));
19173 appendPQExpBuffer(query, ".%s;\n",
19174 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19175
19176 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19178 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19179 .namespace = tbinfo->dobj.namespace->dobj.name,
19180 .owner = tbinfo->rolname,
19181 .description = "SEQUENCE OWNED BY",
19182 .section = SECTION_PRE_DATA,
19183 .createStmt = query->data,
19184 .deps = &(tbinfo->dobj.dumpId),
19185 .nDeps = 1));
19186 }
19187 }
19188
19189 /* Dump Sequence Comments and Security Labels */
19190 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19191 dumpComment(fout, "SEQUENCE", qseqname,
19192 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19193 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19194
19195 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19196 dumpSecLabel(fout, "SEQUENCE", qseqname,
19197 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19198 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19199
19200 if (fout->remoteVersion < 100000)
19201 pg_free(seq);
19202 destroyPQExpBuffer(query);
19203 destroyPQExpBuffer(delqry);
19204 free(qseqname);
19205}
19206
19207/*
19208 * dumpSequenceData
19209 * write the data of one user-defined sequence
19210 */
19211static void
19213{
19214 TableInfo *tbinfo = tdinfo->tdtable;
19215 int64 last;
19216 bool called;
19218
19219 /*
19220 * For versions >= 18, the sequence information is gathered in the sorted
19221 * array before any calls to dumpSequenceData(). See collectSequences()
19222 * for more information.
19223 *
19224 * For older versions, we have to query the sequence relations
19225 * individually.
19226 */
19227 if (fout->remoteVersion < 180000)
19228 {
19229 PGresult *res;
19230
19231 appendPQExpBuffer(query,
19232 "SELECT last_value, is_called FROM %s",
19233 fmtQualifiedDumpable(tbinfo));
19234
19235 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19236
19237 if (PQntuples(res) != 1)
19238 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19239 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19240 PQntuples(res)),
19241 tbinfo->dobj.name, PQntuples(res));
19242
19243 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19244 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19245
19246 PQclear(res);
19247 }
19248 else
19249 {
19250 SequenceItem key = {0};
19251 SequenceItem *entry;
19252
19254 Assert(tbinfo->dobj.catId.oid);
19255
19256 key.oid = tbinfo->dobj.catId.oid;
19257 entry = bsearch(&key, sequences, nsequences,
19258 sizeof(SequenceItem), SequenceItemCmp);
19259
19260 last = entry->last_value;
19261 called = entry->is_called;
19262 }
19263
19264 resetPQExpBuffer(query);
19265 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19266 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19267 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19268 last, (called ? "true" : "false"));
19269
19270 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19272 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19273 .namespace = tbinfo->dobj.namespace->dobj.name,
19274 .owner = tbinfo->rolname,
19275 .description = "SEQUENCE SET",
19276 .section = SECTION_DATA,
19277 .createStmt = query->data,
19278 .deps = &(tbinfo->dobj.dumpId),
19279 .nDeps = 1));
19280
19281 destroyPQExpBuffer(query);
19282}
19283
19284/*
19285 * dumpTrigger
19286 * write the declaration of one user-defined table trigger
19287 */
19288static void
19289dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19290{
19291 DumpOptions *dopt = fout->dopt;
19292 TableInfo *tbinfo = tginfo->tgtable;
19293 PQExpBuffer query;
19294 PQExpBuffer delqry;
19295 PQExpBuffer trigprefix;
19296 PQExpBuffer trigidentity;
19297 char *qtabname;
19298 char *tag;
19299
19300 /* Do nothing if not dumping schema */
19301 if (!dopt->dumpSchema)
19302 return;
19303
19304 query = createPQExpBuffer();
19305 delqry = createPQExpBuffer();
19306 trigprefix = createPQExpBuffer();
19307 trigidentity = createPQExpBuffer();
19308
19309 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19310
19311 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19312 appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19313
19314 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19315 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19316
19317 /* Triggers can depend on extensions */
19318 append_depends_on_extension(fout, query, &tginfo->dobj,
19319 "pg_catalog.pg_trigger", "TRIGGER",
19320 trigidentity->data);
19321
19322 if (tginfo->tgispartition)
19323 {
19324 Assert(tbinfo->ispartition);
19325
19326 /*
19327 * Partition triggers only appear here because their 'tgenabled' flag
19328 * differs from its parent's. The trigger is created already, so
19329 * remove the CREATE and replace it with an ALTER. (Clear out the
19330 * DROP query too, so that pg_dump --create does not cause errors.)
19331 */
19332 resetPQExpBuffer(query);
19333 resetPQExpBuffer(delqry);
19334 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19335 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19336 fmtQualifiedDumpable(tbinfo));
19337 switch (tginfo->tgenabled)
19338 {
19339 case 'f':
19340 case 'D':
19341 appendPQExpBufferStr(query, "DISABLE");
19342 break;
19343 case 't':
19344 case 'O':
19345 appendPQExpBufferStr(query, "ENABLE");
19346 break;
19347 case 'R':
19348 appendPQExpBufferStr(query, "ENABLE REPLICA");
19349 break;
19350 case 'A':
19351 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19352 break;
19353 }
19354 appendPQExpBuffer(query, " TRIGGER %s;\n",
19355 fmtId(tginfo->dobj.name));
19356 }
19357 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19358 {
19359 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19360 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19361 fmtQualifiedDumpable(tbinfo));
19362 switch (tginfo->tgenabled)
19363 {
19364 case 'D':
19365 case 'f':
19366 appendPQExpBufferStr(query, "DISABLE");
19367 break;
19368 case 'A':
19369 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19370 break;
19371 case 'R':
19372 appendPQExpBufferStr(query, "ENABLE REPLICA");
19373 break;
19374 default:
19375 appendPQExpBufferStr(query, "ENABLE");
19376 break;
19377 }
19378 appendPQExpBuffer(query, " TRIGGER %s;\n",
19379 fmtId(tginfo->dobj.name));
19380 }
19381
19382 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19383 fmtId(tginfo->dobj.name));
19384
19385 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19386
19387 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19388 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19389 ARCHIVE_OPTS(.tag = tag,
19390 .namespace = tbinfo->dobj.namespace->dobj.name,
19391 .owner = tbinfo->rolname,
19392 .description = "TRIGGER",
19393 .section = SECTION_POST_DATA,
19394 .createStmt = query->data,
19395 .dropStmt = delqry->data));
19396
19397 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19398 dumpComment(fout, trigprefix->data, qtabname,
19399 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19400 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19401
19402 free(tag);
19403 destroyPQExpBuffer(query);
19404 destroyPQExpBuffer(delqry);
19405 destroyPQExpBuffer(trigprefix);
19406 destroyPQExpBuffer(trigidentity);
19407 free(qtabname);
19408}
19409
19410/*
19411 * dumpEventTrigger
19412 * write the declaration of one user-defined event trigger
19413 */
19414static void
19416{
19417 DumpOptions *dopt = fout->dopt;
19418 PQExpBuffer query;
19419 PQExpBuffer delqry;
19420 char *qevtname;
19421
19422 /* Do nothing if not dumping schema */
19423 if (!dopt->dumpSchema)
19424 return;
19425
19426 query = createPQExpBuffer();
19427 delqry = createPQExpBuffer();
19428
19429 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19430
19431 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19432 appendPQExpBufferStr(query, qevtname);
19433 appendPQExpBufferStr(query, " ON ");
19434 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19435
19436 if (strcmp("", evtinfo->evttags) != 0)
19437 {
19438 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19439 appendPQExpBufferStr(query, evtinfo->evttags);
19440 appendPQExpBufferChar(query, ')');
19441 }
19442
19443 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19444 appendPQExpBufferStr(query, evtinfo->evtfname);
19445 appendPQExpBufferStr(query, "();\n");
19446
19447 if (evtinfo->evtenabled != 'O')
19448 {
19449 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19450 qevtname);
19451 switch (evtinfo->evtenabled)
19452 {
19453 case 'D':
19454 appendPQExpBufferStr(query, "DISABLE");
19455 break;
19456 case 'A':
19457 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19458 break;
19459 case 'R':
19460 appendPQExpBufferStr(query, "ENABLE REPLICA");
19461 break;
19462 default:
19463 appendPQExpBufferStr(query, "ENABLE");
19464 break;
19465 }
19466 appendPQExpBufferStr(query, ";\n");
19467 }
19468
19469 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19470 qevtname);
19471
19472 if (dopt->binary_upgrade)
19473 binary_upgrade_extension_member(query, &evtinfo->dobj,
19474 "EVENT TRIGGER", qevtname, NULL);
19475
19476 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19477 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19478 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19479 .owner = evtinfo->evtowner,
19480 .description = "EVENT TRIGGER",
19481 .section = SECTION_POST_DATA,
19482 .createStmt = query->data,
19483 .dropStmt = delqry->data));
19484
19485 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19486 dumpComment(fout, "EVENT TRIGGER", qevtname,
19487 NULL, evtinfo->evtowner,
19488 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19489
19490 if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19491 dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19492 NULL, evtinfo->evtowner,
19493 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19494
19495 destroyPQExpBuffer(query);
19496 destroyPQExpBuffer(delqry);
19497 free(qevtname);
19498}
19499
19500/*
19501 * dumpRule
19502 * Dump a rule
19503 */
19504static void
19505dumpRule(Archive *fout, const RuleInfo *rinfo)
19506{
19507 DumpOptions *dopt = fout->dopt;
19508 TableInfo *tbinfo = rinfo->ruletable;
19509 bool is_view;
19510 PQExpBuffer query;
19511 PQExpBuffer cmd;
19512 PQExpBuffer delcmd;
19513 PQExpBuffer ruleprefix;
19514 char *qtabname;
19515 PGresult *res;
19516 char *tag;
19517
19518 /* Do nothing if not dumping schema */
19519 if (!dopt->dumpSchema)
19520 return;
19521
19522 /*
19523 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19524 * we do not want to dump it as a separate object.
19525 */
19526 if (!rinfo->separate)
19527 return;
19528
19529 /*
19530 * If it's an ON SELECT rule, we want to print it as a view definition,
19531 * instead of a rule.
19532 */
19533 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19534
19535 query = createPQExpBuffer();
19536 cmd = createPQExpBuffer();
19537 delcmd = createPQExpBuffer();
19538 ruleprefix = createPQExpBuffer();
19539
19540 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19541
19542 if (is_view)
19543 {
19544 PQExpBuffer result;
19545
19546 /*
19547 * We need OR REPLACE here because we'll be replacing a dummy view.
19548 * Otherwise this should look largely like the regular view dump code.
19549 */
19550 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19551 fmtQualifiedDumpable(tbinfo));
19552 if (nonemptyReloptions(tbinfo->reloptions))
19553 {
19554 appendPQExpBufferStr(cmd, " WITH (");
19555 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19556 appendPQExpBufferChar(cmd, ')');
19557 }
19558 result = createViewAsClause(fout, tbinfo);
19559 appendPQExpBuffer(cmd, " AS\n%s", result->data);
19560 destroyPQExpBuffer(result);
19561 if (tbinfo->checkoption != NULL)
19562 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19563 tbinfo->checkoption);
19564 appendPQExpBufferStr(cmd, ";\n");
19565 }
19566 else
19567 {
19568 /* In the rule case, just print pg_get_ruledef's result verbatim */
19569 appendPQExpBuffer(query,
19570 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19571 rinfo->dobj.catId.oid);
19572
19573 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19574
19575 if (PQntuples(res) != 1)
19576 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19577 rinfo->dobj.name, tbinfo->dobj.name);
19578
19579 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19580
19581 PQclear(res);
19582 }
19583
19584 /*
19585 * Add the command to alter the rules replication firing semantics if it
19586 * differs from the default.
19587 */
19588 if (rinfo->ev_enabled != 'O')
19589 {
19590 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19591 switch (rinfo->ev_enabled)
19592 {
19593 case 'A':
19594 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19595 fmtId(rinfo->dobj.name));
19596 break;
19597 case 'R':
19598 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19599 fmtId(rinfo->dobj.name));
19600 break;
19601 case 'D':
19602 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19603 fmtId(rinfo->dobj.name));
19604 break;
19605 }
19606 }
19607
19608 if (is_view)
19609 {
19610 /*
19611 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19612 * REPLACE VIEW to replace the rule with something with minimal
19613 * dependencies.
19614 */
19615 PQExpBuffer result;
19616
19617 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19618 fmtQualifiedDumpable(tbinfo));
19619 result = createDummyViewAsClause(fout, tbinfo);
19620 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19621 destroyPQExpBuffer(result);
19622 }
19623 else
19624 {
19625 appendPQExpBuffer(delcmd, "DROP RULE %s ",
19626 fmtId(rinfo->dobj.name));
19627 appendPQExpBuffer(delcmd, "ON %s;\n",
19628 fmtQualifiedDumpable(tbinfo));
19629 }
19630
19631 appendPQExpBuffer(ruleprefix, "RULE %s ON",
19632 fmtId(rinfo->dobj.name));
19633
19634 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19635
19636 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19637 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19638 ARCHIVE_OPTS(.tag = tag,
19639 .namespace = tbinfo->dobj.namespace->dobj.name,
19640 .owner = tbinfo->rolname,
19641 .description = "RULE",
19642 .section = SECTION_POST_DATA,
19643 .createStmt = cmd->data,
19644 .dropStmt = delcmd->data));
19645
19646 /* Dump rule comments */
19647 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19648 dumpComment(fout, ruleprefix->data, qtabname,
19649 tbinfo->dobj.namespace->dobj.name,
19650 tbinfo->rolname,
19651 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19652
19653 free(tag);
19654 destroyPQExpBuffer(query);
19655 destroyPQExpBuffer(cmd);
19656 destroyPQExpBuffer(delcmd);
19657 destroyPQExpBuffer(ruleprefix);
19658 free(qtabname);
19659}
19660
19661/*
19662 * getExtensionMembership --- obtain extension membership data
19663 *
19664 * We need to identify objects that are extension members as soon as they're
19665 * loaded, so that we can correctly determine whether they need to be dumped.
19666 * Generally speaking, extension member objects will get marked as *not* to
19667 * be dumped, as they will be recreated by the single CREATE EXTENSION
19668 * command. However, in binary upgrade mode we still need to dump the members
19669 * individually.
19670 */
19671void
19673 int numExtensions)
19674{
19675 PQExpBuffer query;
19676 PGresult *res;
19677 int ntups,
19678 i;
19679 int i_classid,
19680 i_objid,
19681 i_refobjid;
19682 ExtensionInfo *ext;
19683
19684 /* Nothing to do if no extensions */
19685 if (numExtensions == 0)
19686 return;
19687
19688 query = createPQExpBuffer();
19689
19690 /* refclassid constraint is redundant but may speed the search */
19691 appendPQExpBufferStr(query, "SELECT "
19692 "classid, objid, refobjid "
19693 "FROM pg_depend "
19694 "WHERE refclassid = 'pg_extension'::regclass "
19695 "AND deptype = 'e' "
19696 "ORDER BY 3");
19697
19698 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19699
19700 ntups = PQntuples(res);
19701
19702 i_classid = PQfnumber(res, "classid");
19703 i_objid = PQfnumber(res, "objid");
19704 i_refobjid = PQfnumber(res, "refobjid");
19705
19706 /*
19707 * Since we ordered the SELECT by referenced ID, we can expect that
19708 * multiple entries for the same extension will appear together; this
19709 * saves on searches.
19710 */
19711 ext = NULL;
19712
19713 for (i = 0; i < ntups; i++)
19714 {
19715 CatalogId objId;
19716 Oid extId;
19717
19718 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19719 objId.oid = atooid(PQgetvalue(res, i, i_objid));
19720 extId = atooid(PQgetvalue(res, i, i_refobjid));
19721
19722 if (ext == NULL ||
19723 ext->dobj.catId.oid != extId)
19724 ext = findExtensionByOid(extId);
19725
19726 if (ext == NULL)
19727 {
19728 /* shouldn't happen */
19729 pg_log_warning("could not find referenced extension %u", extId);
19730 continue;
19731 }
19732
19733 recordExtensionMembership(objId, ext);
19734 }
19735
19736 PQclear(res);
19737
19738 destroyPQExpBuffer(query);
19739}
19740
19741/*
19742 * processExtensionTables --- deal with extension configuration tables
19743 *
19744 * There are two parts to this process:
19745 *
19746 * 1. Identify and create dump records for extension configuration tables.
19747 *
19748 * Extensions can mark tables as "configuration", which means that the user
19749 * is able and expected to modify those tables after the extension has been
19750 * loaded. For these tables, we dump out only the data- the structure is
19751 * expected to be handled at CREATE EXTENSION time, including any indexes or
19752 * foreign keys, which brings us to-
19753 *
19754 * 2. Record FK dependencies between configuration tables.
19755 *
19756 * Due to the FKs being created at CREATE EXTENSION time and therefore before
19757 * the data is loaded, we have to work out what the best order for reloading
19758 * the data is, to avoid FK violations when the tables are restored. This is
19759 * not perfect- we can't handle circular dependencies and if any exist they
19760 * will cause an invalid dump to be produced (though at least all of the data
19761 * is included for a user to manually restore). This is currently documented
19762 * but perhaps we can provide a better solution in the future.
19763 */
19764void
19766 int numExtensions)
19767{
19768 DumpOptions *dopt = fout->dopt;
19769 PQExpBuffer query;
19770 PGresult *res;
19771 int ntups,
19772 i;
19773 int i_conrelid,
19774 i_confrelid;
19775
19776 /* Nothing to do if no extensions */
19777 if (numExtensions == 0)
19778 return;
19779
19780 /*
19781 * Identify extension configuration tables and create TableDataInfo
19782 * objects for them, ensuring their data will be dumped even though the
19783 * tables themselves won't be.
19784 *
19785 * Note that we create TableDataInfo objects even in schema-only mode, ie,
19786 * user data in a configuration table is treated like schema data. This
19787 * seems appropriate since system data in a config table would get
19788 * reloaded by CREATE EXTENSION. If the extension is not listed in the
19789 * list of extensions to be included, none of its data is dumped.
19790 */
19791 for (i = 0; i < numExtensions; i++)
19792 {
19793 ExtensionInfo *curext = &(extinfo[i]);
19794 char *extconfig = curext->extconfig;
19795 char *extcondition = curext->extcondition;
19796 char **extconfigarray = NULL;
19797 char **extconditionarray = NULL;
19798 int nconfigitems = 0;
19799 int nconditionitems = 0;
19800
19801 /*
19802 * Check if this extension is listed as to include in the dump. If
19803 * not, any table data associated with it is discarded.
19804 */
19805 if (extension_include_oids.head != NULL &&
19807 curext->dobj.catId.oid))
19808 continue;
19809
19810 /*
19811 * Check if this extension is listed as to exclude in the dump. If
19812 * yes, any table data associated with it is discarded.
19813 */
19814 if (extension_exclude_oids.head != NULL &&
19816 curext->dobj.catId.oid))
19817 continue;
19818
19819 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19820 {
19821 int j;
19822
19823 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19824 pg_fatal("could not parse %s array", "extconfig");
19825 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19826 pg_fatal("could not parse %s array", "extcondition");
19827 if (nconfigitems != nconditionitems)
19828 pg_fatal("mismatched number of configurations and conditions for extension");
19829
19830 for (j = 0; j < nconfigitems; j++)
19831 {
19832 TableInfo *configtbl;
19833 Oid configtbloid = atooid(extconfigarray[j]);
19834 bool dumpobj =
19836
19837 configtbl = findTableByOid(configtbloid);
19838 if (configtbl == NULL)
19839 continue;
19840
19841 /*
19842 * Tables of not-to-be-dumped extensions shouldn't be dumped
19843 * unless the table or its schema is explicitly included
19844 */
19845 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19846 {
19847 /* check table explicitly requested */
19848 if (table_include_oids.head != NULL &&
19850 configtbloid))
19851 dumpobj = true;
19852
19853 /* check table's schema explicitly requested */
19854 if (configtbl->dobj.namespace->dobj.dump &
19856 dumpobj = true;
19857 }
19858
19859 /* check table excluded by an exclusion switch */
19860 if (table_exclude_oids.head != NULL &&
19862 configtbloid))
19863 dumpobj = false;
19864
19865 /* check schema excluded by an exclusion switch */
19867 configtbl->dobj.namespace->dobj.catId.oid))
19868 dumpobj = false;
19869
19870 if (dumpobj)
19871 {
19872 makeTableDataInfo(dopt, configtbl);
19873 if (configtbl->dataObj != NULL)
19874 {
19875 if (strlen(extconditionarray[j]) > 0)
19876 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19877 }
19878 }
19879 }
19880 }
19881 if (extconfigarray)
19882 free(extconfigarray);
19883 if (extconditionarray)
19884 free(extconditionarray);
19885 }
19886
19887 /*
19888 * Now that all the TableDataInfo objects have been created for all the
19889 * extensions, check their FK dependencies and register them to try and
19890 * dump the data out in an order that they can be restored in.
19891 *
19892 * Note that this is not a problem for user tables as their FKs are
19893 * recreated after the data has been loaded.
19894 */
19895
19896 query = createPQExpBuffer();
19897
19898 printfPQExpBuffer(query,
19899 "SELECT conrelid, confrelid "
19900 "FROM pg_constraint "
19901 "JOIN pg_depend ON (objid = confrelid) "
19902 "WHERE contype = 'f' "
19903 "AND refclassid = 'pg_extension'::regclass "
19904 "AND classid = 'pg_class'::regclass;");
19905
19906 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19907 ntups = PQntuples(res);
19908
19909 i_conrelid = PQfnumber(res, "conrelid");
19910 i_confrelid = PQfnumber(res, "confrelid");
19911
19912 /* Now get the dependencies and register them */
19913 for (i = 0; i < ntups; i++)
19914 {
19915 Oid conrelid,
19916 confrelid;
19917 TableInfo *reftable,
19918 *contable;
19919
19920 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19921 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19922 contable = findTableByOid(conrelid);
19923 reftable = findTableByOid(confrelid);
19924
19925 if (reftable == NULL ||
19926 reftable->dataObj == NULL ||
19927 contable == NULL ||
19928 contable->dataObj == NULL)
19929 continue;
19930
19931 /*
19932 * Make referencing TABLE_DATA object depend on the referenced table's
19933 * TABLE_DATA object.
19934 */
19935 addObjectDependency(&contable->dataObj->dobj,
19936 reftable->dataObj->dobj.dumpId);
19937 }
19938 PQclear(res);
19939 destroyPQExpBuffer(query);
19940}
19941
19942/*
19943 * getDependencies --- obtain available dependency data
19944 */
19945static void
19947{
19948 PQExpBuffer query;
19949 PGresult *res;
19950 int ntups,
19951 i;
19952 int i_classid,
19953 i_objid,
19954 i_refclassid,
19955 i_refobjid,
19956 i_deptype;
19957 DumpableObject *dobj,
19958 *refdobj;
19959
19960 pg_log_info("reading dependency data");
19961
19962 query = createPQExpBuffer();
19963
19964 /*
19965 * Messy query to collect the dependency data we need. Note that we
19966 * ignore the sub-object column, so that dependencies of or on a column
19967 * look the same as dependencies of or on a whole table.
19968 *
19969 * PIN dependencies aren't interesting, and EXTENSION dependencies were
19970 * already processed by getExtensionMembership.
19971 */
19972 appendPQExpBufferStr(query, "SELECT "
19973 "classid, objid, refclassid, refobjid, deptype "
19974 "FROM pg_depend "
19975 "WHERE deptype != 'p' AND deptype != 'e'\n");
19976
19977 /*
19978 * Since we don't treat pg_amop entries as separate DumpableObjects, we
19979 * have to translate their dependencies into dependencies of their parent
19980 * opfamily. Ignore internal dependencies though, as those will point to
19981 * their parent opclass, which we needn't consider here (and if we did,
19982 * it'd just result in circular dependencies). Also, "loose" opfamily
19983 * entries will have dependencies on their parent opfamily, which we
19984 * should drop since they'd likewise become useless self-dependencies.
19985 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19986 */
19987 appendPQExpBufferStr(query, "UNION ALL\n"
19988 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19989 "FROM pg_depend d, pg_amop o "
19990 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19991 "classid = 'pg_amop'::regclass AND objid = o.oid "
19992 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19993
19994 /* Likewise for pg_amproc entries */
19995 appendPQExpBufferStr(query, "UNION ALL\n"
19996 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19997 "FROM pg_depend d, pg_amproc p "
19998 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19999 "classid = 'pg_amproc'::regclass AND objid = p.oid "
20000 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20001
20002 /* Sort the output for efficiency below */
20003 appendPQExpBufferStr(query, "ORDER BY 1,2");
20004
20005 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20006
20007 ntups = PQntuples(res);
20008
20009 i_classid = PQfnumber(res, "classid");
20010 i_objid = PQfnumber(res, "objid");
20011 i_refclassid = PQfnumber(res, "refclassid");
20012 i_refobjid = PQfnumber(res, "refobjid");
20013 i_deptype = PQfnumber(res, "deptype");
20014
20015 /*
20016 * Since we ordered the SELECT by referencing ID, we can expect that
20017 * multiple entries for the same object will appear together; this saves
20018 * on searches.
20019 */
20020 dobj = NULL;
20021
20022 for (i = 0; i < ntups; i++)
20023 {
20024 CatalogId objId;
20025 CatalogId refobjId;
20026 char deptype;
20027
20028 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20029 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20030 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20031 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20032 deptype = *(PQgetvalue(res, i, i_deptype));
20033
20034 if (dobj == NULL ||
20035 dobj->catId.tableoid != objId.tableoid ||
20036 dobj->catId.oid != objId.oid)
20037 dobj = findObjectByCatalogId(objId);
20038
20039 /*
20040 * Failure to find objects mentioned in pg_depend is not unexpected,
20041 * since for example we don't collect info about TOAST tables.
20042 */
20043 if (dobj == NULL)
20044 {
20045#ifdef NOT_USED
20046 pg_log_warning("no referencing object %u %u",
20047 objId.tableoid, objId.oid);
20048#endif
20049 continue;
20050 }
20051
20052 refdobj = findObjectByCatalogId(refobjId);
20053
20054 if (refdobj == NULL)
20055 {
20056#ifdef NOT_USED
20057 pg_log_warning("no referenced object %u %u",
20058 refobjId.tableoid, refobjId.oid);
20059#endif
20060 continue;
20061 }
20062
20063 /*
20064 * For 'x' dependencies, mark the object for later; we still add the
20065 * normal dependency, for possible ordering purposes. Currently
20066 * pg_dump_sort.c knows to put extensions ahead of all object types
20067 * that could possibly depend on them, but this is safer.
20068 */
20069 if (deptype == 'x')
20070 dobj->depends_on_ext = true;
20071
20072 /*
20073 * Ordinarily, table rowtypes have implicit dependencies on their
20074 * tables. However, for a composite type the implicit dependency goes
20075 * the other way in pg_depend; which is the right thing for DROP but
20076 * it doesn't produce the dependency ordering we need. So in that one
20077 * case, we reverse the direction of the dependency.
20078 */
20079 if (deptype == 'i' &&
20080 dobj->objType == DO_TABLE &&
20081 refdobj->objType == DO_TYPE)
20082 addObjectDependency(refdobj, dobj->dumpId);
20083 else
20084 /* normal case */
20085 addObjectDependency(dobj, refdobj->dumpId);
20086 }
20087
20088 PQclear(res);
20089
20090 destroyPQExpBuffer(query);
20091}
20092
20093
20094/*
20095 * createBoundaryObjects - create dummy DumpableObjects to represent
20096 * dump section boundaries.
20097 */
20098static DumpableObject *
20100{
20101 DumpableObject *dobjs;
20102
20103 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20104
20105 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20106 dobjs[0].catId = nilCatalogId;
20107 AssignDumpId(dobjs + 0);
20108 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20109
20110 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20111 dobjs[1].catId = nilCatalogId;
20112 AssignDumpId(dobjs + 1);
20113 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20114
20115 return dobjs;
20116}
20117
20118/*
20119 * addBoundaryDependencies - add dependencies as needed to enforce the dump
20120 * section boundaries.
20121 */
20122static void
20124 DumpableObject *boundaryObjs)
20125{
20126 DumpableObject *preDataBound = boundaryObjs + 0;
20127 DumpableObject *postDataBound = boundaryObjs + 1;
20128 int i;
20129
20130 for (i = 0; i < numObjs; i++)
20131 {
20132 DumpableObject *dobj = dobjs[i];
20133
20134 /*
20135 * The classification of object types here must match the SECTION_xxx
20136 * values assigned during subsequent ArchiveEntry calls!
20137 */
20138 switch (dobj->objType)
20139 {
20140 case DO_NAMESPACE:
20141 case DO_EXTENSION:
20142 case DO_TYPE:
20143 case DO_SHELL_TYPE:
20144 case DO_FUNC:
20145 case DO_AGG:
20146 case DO_OPERATOR:
20147 case DO_ACCESS_METHOD:
20148 case DO_OPCLASS:
20149 case DO_OPFAMILY:
20150 case DO_COLLATION:
20151 case DO_CONVERSION:
20152 case DO_TABLE:
20153 case DO_TABLE_ATTACH:
20154 case DO_ATTRDEF:
20155 case DO_PROCLANG:
20156 case DO_CAST:
20157 case DO_DUMMY_TYPE:
20158 case DO_TSPARSER:
20159 case DO_TSDICT:
20160 case DO_TSTEMPLATE:
20161 case DO_TSCONFIG:
20162 case DO_FDW:
20163 case DO_FOREIGN_SERVER:
20164 case DO_TRANSFORM:
20165 /* Pre-data objects: must come before the pre-data boundary */
20166 addObjectDependency(preDataBound, dobj->dumpId);
20167 break;
20168 case DO_TABLE_DATA:
20169 case DO_SEQUENCE_SET:
20170 case DO_LARGE_OBJECT:
20172 /* Data objects: must come between the boundaries */
20173 addObjectDependency(dobj, preDataBound->dumpId);
20174 addObjectDependency(postDataBound, dobj->dumpId);
20175 break;
20176 case DO_INDEX:
20177 case DO_INDEX_ATTACH:
20178 case DO_STATSEXT:
20179 case DO_REFRESH_MATVIEW:
20180 case DO_TRIGGER:
20181 case DO_EVENT_TRIGGER:
20182 case DO_DEFAULT_ACL:
20183 case DO_POLICY:
20184 case DO_PUBLICATION:
20185 case DO_PUBLICATION_REL:
20187 case DO_SUBSCRIPTION:
20189 /* Post-data objects: must come after the post-data boundary */
20190 addObjectDependency(dobj, postDataBound->dumpId);
20191 break;
20192 case DO_RULE:
20193 /* Rules are post-data, but only if dumped separately */
20194 if (((RuleInfo *) dobj)->separate)
20195 addObjectDependency(dobj, postDataBound->dumpId);
20196 break;
20197 case DO_CONSTRAINT:
20198 case DO_FK_CONSTRAINT:
20199 /* Constraints are post-data, but only if dumped separately */
20200 if (((ConstraintInfo *) dobj)->separate)
20201 addObjectDependency(dobj, postDataBound->dumpId);
20202 break;
20204 /* nothing to do */
20205 break;
20207 /* must come after the pre-data boundary */
20208 addObjectDependency(dobj, preDataBound->dumpId);
20209 break;
20210 case DO_REL_STATS:
20211 /* stats section varies by parent object type, DATA or POST */
20212 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20213 {
20214 addObjectDependency(dobj, preDataBound->dumpId);
20215 addObjectDependency(postDataBound, dobj->dumpId);
20216 }
20217 else
20218 addObjectDependency(dobj, postDataBound->dumpId);
20219 break;
20220 }
20221 }
20222}
20223
20224
20225/*
20226 * BuildArchiveDependencies - create dependency data for archive TOC entries
20227 *
20228 * The raw dependency data obtained by getDependencies() is not terribly
20229 * useful in an archive dump, because in many cases there are dependency
20230 * chains linking through objects that don't appear explicitly in the dump.
20231 * For example, a view will depend on its _RETURN rule while the _RETURN rule
20232 * will depend on other objects --- but the rule will not appear as a separate
20233 * object in the dump. We need to adjust the view's dependencies to include
20234 * whatever the rule depends on that is included in the dump.
20235 *
20236 * Just to make things more complicated, there are also "special" dependencies
20237 * such as the dependency of a TABLE DATA item on its TABLE, which we must
20238 * not rearrange because pg_restore knows that TABLE DATA only depends on
20239 * its table. In these cases we must leave the dependencies strictly as-is
20240 * even if they refer to not-to-be-dumped objects.
20241 *
20242 * To handle this, the convention is that "special" dependencies are created
20243 * during ArchiveEntry calls, and an archive TOC item that has any such
20244 * entries will not be touched here. Otherwise, we recursively search the
20245 * DumpableObject data structures to build the correct dependencies for each
20246 * archive TOC item.
20247 */
20248static void
20250{
20251 ArchiveHandle *AH = (ArchiveHandle *) fout;
20252 TocEntry *te;
20253
20254 /* Scan all TOC entries in the archive */
20255 for (te = AH->toc->next; te != AH->toc; te = te->next)
20256 {
20257 DumpableObject *dobj;
20258 DumpId *dependencies;
20259 int nDeps;
20260 int allocDeps;
20261
20262 /* No need to process entries that will not be dumped */
20263 if (te->reqs == 0)
20264 continue;
20265 /* Ignore entries that already have "special" dependencies */
20266 if (te->nDeps > 0)
20267 continue;
20268 /* Otherwise, look up the item's original DumpableObject, if any */
20269 dobj = findObjectByDumpId(te->dumpId);
20270 if (dobj == NULL)
20271 continue;
20272 /* No work if it has no dependencies */
20273 if (dobj->nDeps <= 0)
20274 continue;
20275 /* Set up work array */
20276 allocDeps = 64;
20277 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20278 nDeps = 0;
20279 /* Recursively find all dumpable dependencies */
20280 findDumpableDependencies(AH, dobj,
20281 &dependencies, &nDeps, &allocDeps);
20282 /* And save 'em ... */
20283 if (nDeps > 0)
20284 {
20285 dependencies = (DumpId *) pg_realloc(dependencies,
20286 nDeps * sizeof(DumpId));
20287 te->dependencies = dependencies;
20288 te->nDeps = nDeps;
20289 }
20290 else
20291 free(dependencies);
20292 }
20293}
20294
20295/* Recursive search subroutine for BuildArchiveDependencies */
20296static void
20298 DumpId **dependencies, int *nDeps, int *allocDeps)
20299{
20300 int i;
20301
20302 /*
20303 * Ignore section boundary objects: if we search through them, we'll
20304 * report lots of bogus dependencies.
20305 */
20306 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20308 return;
20309
20310 for (i = 0; i < dobj->nDeps; i++)
20311 {
20312 DumpId depid = dobj->dependencies[i];
20313
20314 if (TocIDRequired(AH, depid) != 0)
20315 {
20316 /* Object will be dumped, so just reference it as a dependency */
20317 if (*nDeps >= *allocDeps)
20318 {
20319 *allocDeps *= 2;
20320 *dependencies = (DumpId *) pg_realloc(*dependencies,
20321 *allocDeps * sizeof(DumpId));
20322 }
20323 (*dependencies)[*nDeps] = depid;
20324 (*nDeps)++;
20325 }
20326 else
20327 {
20328 /*
20329 * Object will not be dumped, so recursively consider its deps. We
20330 * rely on the assumption that sortDumpableObjects already broke
20331 * any dependency loops, else we might recurse infinitely.
20332 */
20333 DumpableObject *otherdobj = findObjectByDumpId(depid);
20334
20335 if (otherdobj)
20336 findDumpableDependencies(AH, otherdobj,
20337 dependencies, nDeps, allocDeps);
20338 }
20339 }
20340}
20341
20342
20343/*
20344 * getFormattedTypeName - retrieve a nicely-formatted type name for the
20345 * given type OID.
20346 *
20347 * This does not guarantee to schema-qualify the output, so it should not
20348 * be used to create the target object name for CREATE or ALTER commands.
20349 *
20350 * Note that the result is cached and must not be freed by the caller.
20351 */
20352static const char *
20354{
20355 TypeInfo *typeInfo;
20356 char *result;
20357 PQExpBuffer query;
20358 PGresult *res;
20359
20360 if (oid == 0)
20361 {
20362 if ((opts & zeroAsStar) != 0)
20363 return "*";
20364 else if ((opts & zeroAsNone) != 0)
20365 return "NONE";
20366 }
20367
20368 /* see if we have the result cached in the type's TypeInfo record */
20369 typeInfo = findTypeByOid(oid);
20370 if (typeInfo && typeInfo->ftypname)
20371 return typeInfo->ftypname;
20372
20373 query = createPQExpBuffer();
20374 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20375 oid);
20376
20377 res = ExecuteSqlQueryForSingleRow(fout, query->data);
20378
20379 /* result of format_type is already quoted */
20380 result = pg_strdup(PQgetvalue(res, 0, 0));
20381
20382 PQclear(res);
20383 destroyPQExpBuffer(query);
20384
20385 /*
20386 * Cache the result for re-use in later requests, if possible. If we
20387 * don't have a TypeInfo for the type, the string will be leaked once the
20388 * caller is done with it ... but that case really should not happen, so
20389 * leaking if it does seems acceptable.
20390 */
20391 if (typeInfo)
20392 typeInfo->ftypname = result;
20393
20394 return result;
20395}
20396
20397/*
20398 * Return a column list clause for the given relation.
20399 *
20400 * Special case: if there are no undropped columns in the relation, return
20401 * "", not an invalid "()" column list.
20402 */
20403static const char *
20405{
20406 int numatts = ti->numatts;
20407 char **attnames = ti->attnames;
20408 bool *attisdropped = ti->attisdropped;
20409 char *attgenerated = ti->attgenerated;
20410 bool needComma;
20411 int i;
20412
20413 appendPQExpBufferChar(buffer, '(');
20414 needComma = false;
20415 for (i = 0; i < numatts; i++)
20416 {
20417 if (attisdropped[i])
20418 continue;
20419 if (attgenerated[i])
20420 continue;
20421 if (needComma)
20422 appendPQExpBufferStr(buffer, ", ");
20423 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20424 needComma = true;
20425 }
20426
20427 if (!needComma)
20428 return ""; /* no undropped columns */
20429
20430 appendPQExpBufferChar(buffer, ')');
20431 return buffer->data;
20432}
20433
20434/*
20435 * Check if a reloptions array is nonempty.
20436 */
20437static bool
20438nonemptyReloptions(const char *reloptions)
20439{
20440 /* Don't want to print it if it's just "{}" */
20441 return (reloptions != NULL && strlen(reloptions) > 2);
20442}
20443
20444/*
20445 * Format a reloptions array and append it to the given buffer.
20446 *
20447 * "prefix" is prepended to the option names; typically it's "" or "toast.".
20448 */
20449static void
20450appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20451 const char *prefix, Archive *fout)
20452{
20453 bool res;
20454
20455 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20456 fout->std_strings);
20457 if (!res)
20458 pg_log_warning("could not parse %s array", "reloptions");
20459}
20460
20461/*
20462 * read_dump_filters - retrieve object identifier patterns from file
20463 *
20464 * Parse the specified filter file for include and exclude patterns, and add
20465 * them to the relevant lists. If the filename is "-" then filters will be
20466 * read from STDIN rather than a file.
20467 */
20468static void
20470{
20471 FilterStateData fstate;
20472 char *objname;
20473 FilterCommandType comtype;
20474 FilterObjectType objtype;
20475
20476 filter_init(&fstate, filename, exit_nicely);
20477
20478 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20479 {
20480 if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20481 {
20482 switch (objtype)
20483 {
20485 break;
20492 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20493 "include",
20494 filter_object_type_name(objtype));
20495 exit_nicely(1);
20496 break; /* unreachable */
20497
20500 break;
20503 break;
20506 dopt->include_everything = false;
20507 break;
20510 dopt->include_everything = false;
20511 break;
20514 objname);
20515 dopt->include_everything = false;
20516 break;
20517 }
20518 }
20519 else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20520 {
20521 switch (objtype)
20522 {
20524 break;
20530 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20531 "exclude",
20532 filter_object_type_name(objtype));
20533 exit_nicely(1);
20534 break;
20535
20538 break;
20541 objname);
20542 break;
20545 objname);
20546 break;
20549 break;
20552 break;
20555 objname);
20556 break;
20557 }
20558 }
20559 else
20560 {
20561 Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20562 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20563 }
20564
20565 if (objname)
20566 free(objname);
20567 }
20568
20569 filter_free(&fstate);
20570}
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:598
#define ngettext(s, p, n)
Definition: c.h:1184
#define INT64_FORMAT
Definition: c.h:560
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1217
int64_t int64
Definition: c.h:539
#define PG_INT16_MIN
Definition: c.h:594
#define CppAsString2(x)
Definition: c.h:422
int32_t int32
Definition: c.h:538
#define PG_INT64_MAX
Definition: c.h:601
#define PG_INT64_MIN
Definition: c.h:600
uint32_t uint32
Definition: c.h:542
#define lengthof(array)
Definition: c.h:791
#define PG_INT32_MIN
Definition: c.h:597
#define PG_INT16_MAX
Definition: c.h:595
#define OidIsValid(objectId)
Definition: c.h:778
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:7523
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
Definition: fe-connect.c:7644
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7779
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7689
int PQsetClientEncoding(PGconn *conn, const char *encoding)
Definition: fe-connect.c:7787
void PQfreemem(void *ptr)
Definition: fe-exec.c:4043
Oid PQftype(const PGresult *res, int field_num)
Definition: fe-exec.c:3730
int PQfnumber(const PGresult *res, const char *field_name)
Definition: fe-exec.c:3600
int PQgetCopyData(PGconn *conn, char **buffer, int async)
Definition: fe-exec.c:2827
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:652
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:1649
static const CatalogId nilCatalogId
Definition: pg_dump.c:189
static void dumpEncoding(Archive *AH)
Definition: pg_dump.c:3821
void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:8277
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:16394
static SimpleStringList schema_include_patterns
Definition: pg_dump.c:165
static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
Definition: pg_dump.c:18161
ExtensionInfo * getExtensions(Archive *fout, int *numExtensions)
Definition: pg_dump.c:6109
static void selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
Definition: pg_dump.c:2201
static void collectBinaryUpgradeClassOids(Archive *fout)
Definition: pg_dump.c:5809
static PQExpBuffer createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17038
static void dumpUserMappings(Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
Definition: pg_dump.c:16208
static void dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
Definition: pg_dump.c:4949
static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
Definition: pg_dump.c:20123
void getPublicationNamespaces(Archive *fout)
Definition: pg_dump.c:4742
static void dumpSearchPath(Archive *AH)
Definition: pg_dump.c:3870
static int ncomments
Definition: pg_dump.c:201
static void selectDumpableTable(TableInfo *tbinfo, Archive *fout)
Definition: pg_dump.c:2070
static DumpableObject * createBoundaryObjects(void)
Definition: pg_dump.c:20099
static char * convertTSFunction(Archive *fout, Oid funcOid)
Definition: pg_dump.c:14381
static void dumpDatabase(Archive *fout)
Definition: pg_dump.c:3268
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:5622
static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
Definition: pg_dump.c:5667
static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
Definition: pg_dump.c:11843
static bool forcePartitionRootLoad(const TableInfo *tbinfo)
Definition: pg_dump.c:2828
static void dumpCast(Archive *fout, const CastInfo *cast)
Definition: pg_dump.c:13857
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:18251
void getPartitioningInfo(Archive *fout)
Definition: pg_dump.c:7769
static int nbinaryUpgradeClassOids
Definition: pg_dump.c:209
static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12436
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:11088
static SimpleOidList extension_include_oids
Definition: pg_dump.c:184
static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
Definition: pg_dump.c:15780
static void dumpAgg(Archive *fout, const AggInfo *agginfo)
Definition: pg_dump.c:15356
static int extra_float_digits
Definition: pg_dump.c:193
static int SequenceItemCmp(const void *p1, const void *p2)
Definition: pg_dump.c:18882
static void dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
Definition: pg_dump.c:11361
static void dumpTableComment(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition: pg_dump.c:11387
static SimpleStringList extension_include_patterns
Definition: pg_dump.c:183
static void selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
Definition: pg_dump.c:1984
InhInfo * getInherits(Archive *fout, int *numInherits)
Definition: pg_dump.c:7713
void getForeignDataWrappers(Archive *fout)
Definition: pg_dump.c:10397
static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
Definition: pg_dump.c:19289
static void binary_upgrade_set_type_oids_by_rel(Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
Definition: pg_dump.c:5778
static void dumpTable(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:16849
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:16989
static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
Definition: pg_dump.c:18445
void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:4233
static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12214
void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition: pg_dump.c:19672
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:10966
static char * getFormattedOperatorName(const char *oproid)
Definition: pg_dump.c:14351
static char * format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
Definition: pg_dump.c:13406
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:3846
static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
Definition: pg_dump.c:18522
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12043
static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
Definition: pg_dump.c:18093
static void help(const char *progname)
Definition: pg_dump.c:1304
void getTypes(Archive *fout)
Definition: pg_dump.c:6184
static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
Definition: pg_dump.c:14403
int main(int argc, char **argv)
Definition: pg_dump.c:421
static void dumpOpr(Archive *fout, const OprInfo *oprinfo)
Definition: pg_dump.c:14091
static void selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
Definition: pg_dump.c:2326
static void selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:2308
static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
Definition: pg_dump.c:19212
static void dumpFunc(Archive *fout, const FuncInfo *finfo)
Definition: pg_dump.c:13435
static void selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
Definition: pg_dump.c:2154
static void BuildArchiveDependencies(Archive *fout)
Definition: pg_dump.c:20249
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:7094
static const char *const SeqTypeNames[]
Definition: pg_dump.c:118
void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:7648
static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
Definition: pg_dump.c:3025
static int nsequences
Definition: pg_dump.c:213
static const char * getAttrName(int attrnum, const TableInfo *tblInfo)
Definition: pg_dump.c:18222
static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
Definition: pg_dump.c:16108
static RoleNameItem * rolenames
Definition: pg_dump.c:196
static void collectRoleNames(Archive *fout)
Definition: pg_dump.c:10702
static PGresult * fetchAttributeStats(Archive *fout)
Definition: pg_dump.c:11000
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
Definition: pg_dump.c:20450
void getOpclasses(Archive *fout)
Definition: pg_dump.c:6630
void getForeignServers(Archive *fout)
Definition: pg_dump.c:10481
void getFuncs(Archive *fout)
Definition: pg_dump.c:6899
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
Definition: pg_dump.c:2856
static void prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
Definition: pg_dump.c:1909
static bool dosync
Definition: pg_dump.c:150
static int dumpTableData_copy(Archive *fout, const void *dcontext)
Definition: pg_dump.c:2366
#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:20353
static void getDependencies(Archive *fout)
Definition: pg_dump.c:19946
static void buildMatViewRefreshDependencies(Archive *fout)
Definition: pg_dump.c:3112
void getTSDictionaries(Archive *fout)
Definition: pg_dump.c:10213
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:5698
#define DUMP_DEFAULT_ROWS_PER_INSERT
Definition: pg_dump.c:228
void getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:4822
static SeqType parse_sequence_type(const char *name)
Definition: pg_dump.c:18866
static const char * getRoleName(const char *roleoid_str)
Definition: pg_dump.c:10666
static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
Definition: pg_dump.c:13205
static SequenceItem * sequences
Definition: pg_dump.c:212
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
Definition: pg_dump.c:2971
static int findComments(Oid classoid, Oid objoid, CommentItem **items)
Definition: pg_dump.c:11485
static SimpleStringList foreign_servers_include_patterns
Definition: pg_dump.c:180
static void selectDumpableCast(CastInfo *cast, Archive *fout)
Definition: pg_dump.c:2176
void getCasts(Archive *fout)
Definition: pg_dump.c:9026
static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
Definition: pg_dump.c:4639
static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
Definition: pg_dump.c:4405
void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:7829
static void setupDumpWorker(Archive *AH)
Definition: pg_dump.c:1582
static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
Definition: pg_dump.c:8436
void getTSConfigurations(Archive *fout)
Definition: pg_dump.c:10338
static int nrolenames
Definition: pg_dump.c:197
static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
Definition: pg_dump.c:16684
static SimpleStringList table_include_patterns_and_children
Definition: pg_dump.c:171
static char * convertRegProcReference(const char *proc)
Definition: pg_dump.c:14310
static void getAdditionalACLs(Archive *fout)
Definition: pg_dump.c:10737
StaticAssertDecl(lengthof(SeqTypeNames)==(SEQTYPE_BIGINT+1), "array length mismatch")
static bool is_superuser(Archive *fout)
Definition: pg_dump.c:5053
static void getTableDataFKConstraints(void)
Definition: pg_dump.c:3227
static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
Definition: pg_dump.c:3006
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:6556
void getConversions(Archive *fout)
Definition: pg_dump.c:6494
void getRules(Archive *fout)
Definition: pg_dump.c:8571
static void dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12685
void getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
Definition: pg_dump.c:9220
static void collectComments(Archive *fout)
Definition: pg_dump.c:11562
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
Definition: pg_dump.c:8459
static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
Definition: pg_dump.c:14752
static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
Definition: pg_dump.c:16302
static void selectDumpableObject(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:2344
static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
Definition: pg_dump.c:15860
static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
Definition: pg_dump.c:18402
static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
Definition: pg_dump.c:5399
void getCollations(Archive *fout)
Definition: pg_dump.c:6428
static char * format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
Definition: pg_dump.c:13383
static int strict_names
Definition: pg_dump.c:155
static void dumpTransform(Archive *fout, const TransformInfo *transform)
Definition: pg_dump.c:13962
void getAggregates(Archive *fout)
Definition: pg_dump.c:6758
static void dumpLO(Archive *fout, const LoInfo *loinfo)
Definition: pg_dump.c:4097
void getNamespaces(Archive *fout)
Definition: pg_dump.c:5977
static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
Definition: pg_dump.c:4992
void getPublications(Archive *fout)
Definition: pg_dump.c:4523
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:5933
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
Definition: pg_dump.c:11647
static void getLOs(Archive *fout)
Definition: pg_dump.c:3932
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, const char *dbname, Oid dboid)
Definition: pg_dump.c:3777
void getTSParsers(Archive *fout)
Definition: pg_dump.c:10139
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:1412
static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
Definition: pg_dump.c:18843
static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12372
static void selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
Definition: pg_dump.c:2234
static const char * fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
Definition: pg_dump.c:20404
static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names, bool with_child_tables)
Definition: pg_dump.c:1813
static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, DumpId **dependencies, int *nDeps, int *allocDeps)
Definition: pg_dump.c:20297
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:9997
static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17078
static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
Definition: pg_dump.c:15716
static void expand_foreign_server_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids)
Definition: pg_dump.c:1761
TableInfo * getTables(Archive *fout, int *numTables)
Definition: pg_dump.c:7171
static void dumpRule(Archive *fout, const RuleInfo *rinfo)
Definition: pg_dump.c:19505
static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12910
static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12074
static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
Definition: pg_dump.c:11920
#define fmtQualifiedDumpable(obj)
Definition: pg_dump.c:240
static bool nonemptyReloptions(const char *reloptions)
Definition: pg_dump.c:20438
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:8198
static NamespaceInfo * findNamespace(Oid nsoid)
Definition: pg_dump.c:6091
static char * get_synchronized_snapshot(Archive *fout)
Definition: pg_dump.c:1597
static int dumpLOs(Archive *fout, const void *arg)
Definition: pg_dump.c:4187
static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
Definition: pg_dump.c:5468
static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
Definition: pg_dump.c:10982
void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition: pg_dump.c:19765
static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
Definition: pg_dump.c:19415
static int BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
Definition: pg_dump.c:5793
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:10866
void getDefaultACLs(Archive *fout)
Definition: pg_dump.c:10569
static SimpleStringList tabledata_exclude_patterns
Definition: pg_dump.c:176
static void dumpConversion(Archive *fout, const ConvInfo *convinfo)
Definition: pg_dump.c:15228
static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
Definition: pg_dump.c:16038
static void dumpProcLang(Archive *fout, const ProcLangInfo *plang)
Definition: pg_dump.c:13251
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:16522
void getSubscriptions(Archive *fout)
Definition: pg_dump.c:5093
static void collectSecLabels(Archive *fout)
Definition: pg_dump.c:16763
static void selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
Definition: pg_dump.c:2269
static void collectSequences(Archive *fout)
Definition: pg_dump.c:18897
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:8668
void getTransforms(Archive *fout)
Definition: pg_dump.c:9136
void getEventTriggers(Archive *fout)
Definition: pg_dump.c:8864
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode)
Definition: pg_dump.c:1611
static void read_dump_filters(const char *filename, DumpOptions *dopt)
Definition: pg_dump.c:20469
static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
Definition: pg_dump.c:15918
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:9115
static bool checkExtensionMembership(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:1934
static CommentItem * comments
Definition: pg_dump.c:200
static int dumpTableData_insert(Archive *fout, const void *dcontext)
Definition: pg_dump.c:2534
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:5843
void getTSTemplates(Archive *fout)
Definition: pg_dump.c:10279
static void set_restrict_relation_kind(Archive *AH, const char *value)
Definition: pg_dump.c:5072
static char * format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
Definition: pg_dump.c:15324
void getProcLangs(Archive *fout)
Definition: pg_dump.c:8942
static void dumpSequence(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:18958
bool shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
Definition: pg_dump.c:10124
static TableInfo * getRootTableInfo(const TableInfo *tbinfo)
Definition: pg_dump.c:2803
void getSubscriptionRelations(Archive *fout)
Definition: pg_dump.c:5313
void getOperators(Archive *fout)
Definition: pg_dump.c:6352
static SimpleOidList foreign_servers_include_oids
Definition: pg_dump.c:181
static void dumpCollation(Archive *fout, const CollInfo *collinfo)
Definition: pg_dump.c:14971
static void dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition: pg_dump.c:16602
static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res)
Definition: pg_dump.c:13116
static void expand_extension_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition: pg_dump.c:1708
void getOpfamilies(Archive *fout)
Definition: pg_dump.c:6693
static void selectDumpableType(TypeInfo *tyinfo, Archive *fout)
Definition: pg_dump.c:2109
static SimpleOidList schema_include_oids
Definition: pg_dump.c:166
static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
Definition: pg_dump.c:14471
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
Definition: pg_test_fsync.c:72
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:36
#define snprintf
Definition: port.h:239
const char * get_progname(const char *argv0)
Definition: path.c:652
#define printf(...)
Definition: port.h:245
#define pgoff_t
Definition: port.h:401
#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:2992
const char * description
const char * type
const char * name
ArchiveMode
Definition: xlog.h:64