Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

PostgreSQL Source Code git master
pg_dump.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_dump.c
4 * pg_dump is a utility for dumping out a postgres database
5 * into a script file.
6 *
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * pg_dump will read the system catalogs in a database and dump out a
11 * script that reproduces the schema in terms of SQL that is understood
12 * by PostgreSQL
13 *
14 * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 * so it sees a consistent snapshot of the database including system
16 * catalogs. However, it relies in part on various specialized backend
17 * functions like pg_get_indexdef(), and those things tend to look at
18 * the currently committed state. So it is possible to get 'cache
19 * lookup failed' error if someone performs DDL changes while a dump is
20 * happening. The window for this sort of thing is from the acquisition
21 * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 * AccessShareLock on every table it intends to dump). It isn't very large,
23 * but it can happen.
24 *
25 * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 *
27 * IDENTIFICATION
28 * src/bin/pg_dump/pg_dump.c
29 *
30 *-------------------------------------------------------------------------
31 */
32#include "postgres_fe.h"
33
34#include <unistd.h>
35#include <ctype.h>
36#include <limits.h>
37#ifdef HAVE_TERMIOS_H
38#include <termios.h>
39#endif
40
41#include "access/attnum.h"
42#include "access/sysattr.h"
43#include "access/transam.h"
44#include "catalog/pg_aggregate_d.h"
45#include "catalog/pg_am_d.h"
46#include "catalog/pg_attribute_d.h"
47#include "catalog/pg_authid_d.h"
48#include "catalog/pg_cast_d.h"
49#include "catalog/pg_class_d.h"
50#include "catalog/pg_constraint_d.h"
51#include "catalog/pg_default_acl_d.h"
52#include "catalog/pg_largeobject_d.h"
53#include "catalog/pg_largeobject_metadata_d.h"
54#include "catalog/pg_proc_d.h"
55#include "catalog/pg_publication_d.h"
56#include "catalog/pg_shdepend_d.h"
57#include "catalog/pg_subscription_d.h"
58#include "catalog/pg_type_d.h"
59#include "common/connect.h"
60#include "common/int.h"
61#include "common/relpath.h"
62#include "common/shortest_dec.h"
63#include "compress_io.h"
64#include "dumputils.h"
67#include "filter.h"
68#include "getopt_long.h"
69#include "libpq/libpq-fs.h"
70#include "parallel.h"
71#include "pg_backup_db.h"
72#include "pg_backup_utils.h"
73#include "pg_dump.h"
74#include "storage/block.h"
75
76typedef struct
77{
78 Oid roleoid; /* role's OID */
79 const char *rolename; /* role's name */
81
82typedef struct
83{
84 const char *descr; /* comment for an object */
85 Oid classoid; /* object class (catalog OID) */
86 Oid objoid; /* object OID */
87 int objsubid; /* subobject (table column #) */
89
90typedef struct
91{
92 const char *provider; /* label provider of this security label */
93 const char *label; /* security label for an object */
94 Oid classoid; /* object class (catalog OID) */
95 Oid objoid; /* object OID */
96 int objsubid; /* subobject (table column #) */
98
99typedef struct
100{
101 Oid oid; /* object OID */
102 char relkind; /* object kind */
103 RelFileNumber relfilenumber; /* object filenode */
104 Oid toast_oid; /* toast table OID */
105 RelFileNumber toast_relfilenumber; /* toast table filenode */
106 Oid toast_index_oid; /* toast table index OID */
107 RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
109
110/* sequence types */
111typedef enum SeqType
112{
117
118static const char *const SeqTypeNames[] =
119{
120 [SEQTYPE_SMALLINT] = "smallint",
121 [SEQTYPE_INTEGER] = "integer",
122 [SEQTYPE_BIGINT] = "bigint",
123};
124
126 "array length mismatch");
127
128typedef struct
129{
130 Oid oid; /* sequence OID */
131 SeqType seqtype; /* data type of sequence */
132 bool cycled; /* whether sequence cycles */
133 int64 minv; /* minimum value */
134 int64 maxv; /* maximum value */
135 int64 startv; /* start value */
136 int64 incby; /* increment value */
137 int64 cache; /* cache size */
138 int64 last_value; /* last value of sequence */
139 bool is_called; /* whether nextval advances before returning */
141
142typedef enum OidOptions
143{
148
149/* global decls */
150static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
151
152static Oid g_last_builtin_oid; /* value of the last builtin oid */
153
154/* The specified names/patterns should to match at least one entity */
155static int strict_names = 0;
156
158
159/*
160 * Object inclusion/exclusion lists
161 *
162 * The string lists record the patterns given by command-line switches,
163 * which we then convert to lists of OIDs of matching objects.
164 */
166static SimpleOidList schema_include_oids = {NULL, NULL};
168static SimpleOidList schema_exclude_oids = {NULL, NULL};
169
172static SimpleOidList table_include_oids = {NULL, NULL};
175static SimpleOidList table_exclude_oids = {NULL, NULL};
179
182
185
188
189static const CatalogId nilCatalogId = {0, 0};
190
191/* override for standard extra_float_digits setting */
192static bool have_extra_float_digits = false;
194
195/* sorted table of role names */
196static RoleNameItem *rolenames = NULL;
197static int nrolenames = 0;
198
199/* sorted table of comments */
200static CommentItem *comments = NULL;
201static int ncomments = 0;
202
203/* sorted table of security labels */
204static SecLabelItem *seclabels = NULL;
205static int nseclabels = 0;
206
207/* sorted table of pg_class information for binary upgrade */
210
211/* sorted table of sequences */
212static SequenceItem *sequences = NULL;
213static int nsequences = 0;
214
215/*
216 * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use
217 * as a dependency for pg_shdepend and any large object comments/seclabels.
218 */
220
221/* Maximum number of relations to fetch in a fetchAttributeStats() call. */
222#define MAX_ATTR_STATS_RELS 64
223
224/*
225 * The default number of rows per INSERT when
226 * --inserts is specified without --rows-per-insert
227 */
228#define DUMP_DEFAULT_ROWS_PER_INSERT 1
229
230/*
231 * Maximum number of large objects to group into a single ArchiveEntry.
232 * At some point we might want to make this user-controllable, but for now
233 * a hard-wired setting will suffice.
234 */
235#define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
236
237/*
238 * Macro for producing quoted, schema-qualified name of a dumpable object.
239 */
240#define fmtQualifiedDumpable(obj) \
241 fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
242 (obj)->dobj.name)
243
244static void help(const char *progname);
245static void setup_connection(Archive *AH,
246 const char *dumpencoding, const char *dumpsnapshot,
247 char *use_role);
249static void expand_schema_name_patterns(Archive *fout,
250 SimpleStringList *patterns,
251 SimpleOidList *oids,
252 bool strict_names);
254 SimpleStringList *patterns,
255 SimpleOidList *oids,
256 bool strict_names);
258 SimpleStringList *patterns,
259 SimpleOidList *oids);
260static void expand_table_name_patterns(Archive *fout,
261 SimpleStringList *patterns,
262 SimpleOidList *oids,
263 bool strict_names,
264 bool with_child_tables);
265static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
266 const char *pattern);
267
268static NamespaceInfo *findNamespace(Oid nsoid);
269static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
270static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
271static const char *getRoleName(const char *roleoid_str);
272static void collectRoleNames(Archive *fout);
273static void getAdditionalACLs(Archive *fout);
274static void dumpCommentExtended(Archive *fout, const char *type,
275 const char *name, const char *namespace,
276 const char *owner, CatalogId catalogId,
277 int subid, DumpId dumpId,
278 const char *initdb_comment);
279static inline void dumpComment(Archive *fout, const char *type,
280 const char *name, const char *namespace,
281 const char *owner, CatalogId catalogId,
282 int subid, DumpId dumpId);
283static int findComments(Oid classoid, Oid objoid, CommentItem **items);
284static void collectComments(Archive *fout);
285static void dumpSecLabel(Archive *fout, const char *type, const char *name,
286 const char *namespace, const char *owner,
287 CatalogId catalogId, int subid, DumpId dumpId);
288static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
289static void collectSecLabels(Archive *fout);
290static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
291static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
292static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
293static void dumpType(Archive *fout, const TypeInfo *tyinfo);
294static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
295static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
296static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
297static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
298static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
299static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
300static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
301 PGresult *res);
302static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
303static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
304static void dumpFunc(Archive *fout, const FuncInfo *finfo);
305static void dumpCast(Archive *fout, const CastInfo *cast);
306static void dumpTransform(Archive *fout, const TransformInfo *transform);
307static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
308static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
309static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
310static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
311static void dumpCollation(Archive *fout, const CollInfo *collinfo);
312static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
313static void dumpRule(Archive *fout, const RuleInfo *rinfo);
314static void dumpAgg(Archive *fout, const AggInfo *agginfo);
315static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
316static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
317static void dumpTable(Archive *fout, const TableInfo *tbinfo);
318static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
319static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
320static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
321static void collectSequences(Archive *fout);
322static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
323static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
324static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
325static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
326static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
327static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
328static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
329static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
330static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
331static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
332static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
333static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
334static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
335static void dumpUserMappings(Archive *fout,
336 const char *servername, const char *namespace,
337 const char *owner, CatalogId catalogId, DumpId dumpId);
338static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
339
340static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
341 const char *type, const char *name, const char *subname,
342 const char *nspname, const char *tag, const char *owner,
343 const DumpableAcl *dacl);
344
345static void getDependencies(Archive *fout);
346static void BuildArchiveDependencies(Archive *fout);
347static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
348 DumpId **dependencies, int *nDeps, int *allocDeps);
349
351static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
352 DumpableObject *boundaryObjs);
353
354static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
355static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
356static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
357static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
359static void getTableDataFKConstraints(void);
360static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
361 TableInfo *tbinfo, int j,
362 int i_notnull_name,
363 int i_notnull_comment,
364 int i_notnull_invalidoid,
365 int i_notnull_noinherit,
366 int i_notnull_islocal,
367 PQExpBuffer *invalidnotnulloids);
368static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
369 bool is_agg);
370static char *format_function_signature(Archive *fout,
371 const FuncInfo *finfo, bool honor_quotes);
372static char *convertRegProcReference(const char *proc);
373static char *getFormattedOperatorName(const char *oproid);
374static char *convertTSFunction(Archive *fout, Oid funcOid);
375static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
376static void getLOs(Archive *fout);
377static void dumpLO(Archive *fout, const LoInfo *loinfo);
378static int dumpLOs(Archive *fout, const void *arg);
379static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
380static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
381static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
382static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
383static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
384static void dumpDatabase(Archive *fout);
385static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
386 const char *dbname, Oid dboid);
387static void dumpEncoding(Archive *AH);
388static void dumpStdStrings(Archive *AH);
389static void dumpSearchPath(Archive *AH);
391 PQExpBuffer upgrade_buffer,
392 Oid pg_type_oid,
393 bool force_array_type,
394 bool include_multirange_type);
396 PQExpBuffer upgrade_buffer,
397 const TableInfo *tbinfo);
398static void collectBinaryUpgradeClassOids(Archive *fout);
400 PQExpBuffer upgrade_buffer,
401 Oid pg_class_oid);
402static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
403 const DumpableObject *dobj,
404 const char *objtype,
405 const char *objname,
406 const char *objnamespace);
407static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
408static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
409static bool nonemptyReloptions(const char *reloptions);
410static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
411 const char *prefix, Archive *fout);
412static char *get_synchronized_snapshot(Archive *fout);
413static void set_restrict_relation_kind(Archive *AH, const char *value);
414static void setupDumpWorker(Archive *AH);
415static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
416static bool forcePartitionRootLoad(const TableInfo *tbinfo);
417static void read_dump_filters(const char *filename, DumpOptions *dopt);
418
419
420int
421main(int argc, char **argv)
422{
423 int c;
424 const char *filename = NULL;
425 const char *format = "p";
426 TableInfo *tblinfo;
427 int numTables;
428 DumpableObject **dobjs;
429 int numObjs;
430 DumpableObject *boundaryObjs;
431 int i;
432 int optindex;
433 RestoreOptions *ropt;
434 Archive *fout; /* the script file */
435 bool g_verbose = false;
436 const char *dumpencoding = NULL;
437 const char *dumpsnapshot = NULL;
438 char *use_role = NULL;
439 int numWorkers = 1;
440 int plainText = 0;
441 ArchiveFormat archiveFormat = archUnknown;
442 ArchiveMode archiveMode;
443 pg_compress_specification compression_spec = {0};
444 char *compression_detail = NULL;
445 char *compression_algorithm_str = "none";
446 char *error_detail = NULL;
447 bool user_compression_defined = false;
449 bool data_only = false;
450 bool schema_only = false;
451 bool statistics_only = false;
452 bool with_statistics = false;
453 bool no_data = false;
454 bool no_schema = false;
455 bool no_statistics = false;
456
457 static DumpOptions dopt;
458
459 static struct option long_options[] = {
460 {"data-only", no_argument, NULL, 'a'},
461 {"blobs", no_argument, NULL, 'b'},
462 {"large-objects", no_argument, NULL, 'b'},
463 {"no-blobs", no_argument, NULL, 'B'},
464 {"no-large-objects", no_argument, NULL, 'B'},
465 {"clean", no_argument, NULL, 'c'},
466 {"create", no_argument, NULL, 'C'},
467 {"dbname", required_argument, NULL, 'd'},
468 {"extension", required_argument, NULL, 'e'},
469 {"file", required_argument, NULL, 'f'},
470 {"format", required_argument, NULL, 'F'},
471 {"host", required_argument, NULL, 'h'},
472 {"jobs", 1, NULL, 'j'},
473 {"no-reconnect", no_argument, NULL, 'R'},
474 {"no-owner", no_argument, NULL, 'O'},
475 {"port", required_argument, NULL, 'p'},
476 {"schema", required_argument, NULL, 'n'},
477 {"exclude-schema", required_argument, NULL, 'N'},
478 {"schema-only", no_argument, NULL, 's'},
479 {"superuser", required_argument, NULL, 'S'},
480 {"table", required_argument, NULL, 't'},
481 {"exclude-table", required_argument, NULL, 'T'},
482 {"no-password", no_argument, NULL, 'w'},
483 {"password", no_argument, NULL, 'W'},
484 {"username", required_argument, NULL, 'U'},
485 {"verbose", no_argument, NULL, 'v'},
486 {"no-privileges", no_argument, NULL, 'x'},
487 {"no-acl", no_argument, NULL, 'x'},
488 {"compress", required_argument, NULL, 'Z'},
489 {"encoding", required_argument, NULL, 'E'},
490 {"help", no_argument, NULL, '?'},
491 {"version", no_argument, NULL, 'V'},
492
493 /*
494 * the following options don't have an equivalent short option letter
495 */
496 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
497 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
498 {"column-inserts", no_argument, &dopt.column_inserts, 1},
499 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
500 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
501 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
502 {"exclude-table-data", required_argument, NULL, 4},
503 {"extra-float-digits", required_argument, NULL, 8},
504 {"if-exists", no_argument, &dopt.if_exists, 1},
505 {"inserts", no_argument, NULL, 9},
506 {"lock-wait-timeout", required_argument, NULL, 2},
507 {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
508 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
509 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
510 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
511 {"role", required_argument, NULL, 3},
512 {"section", required_argument, NULL, 5},
513 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
514 {"snapshot", required_argument, NULL, 6},
515 {"statistics", no_argument, NULL, 22},
516 {"statistics-only", no_argument, NULL, 18},
517 {"strict-names", no_argument, &strict_names, 1},
518 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
519 {"no-comments", no_argument, &dopt.no_comments, 1},
520 {"no-data", no_argument, NULL, 19},
521 {"no-policies", no_argument, &dopt.no_policies, 1},
522 {"no-publications", no_argument, &dopt.no_publications, 1},
523 {"no-schema", no_argument, NULL, 20},
524 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
525 {"no-statistics", no_argument, NULL, 21},
526 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
527 {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
528 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
529 {"no-sync", no_argument, NULL, 7},
530 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
531 {"rows-per-insert", required_argument, NULL, 10},
532 {"include-foreign-data", required_argument, NULL, 11},
533 {"table-and-children", required_argument, NULL, 12},
534 {"exclude-table-and-children", required_argument, NULL, 13},
535 {"exclude-table-data-and-children", required_argument, NULL, 14},
536 {"sync-method", required_argument, NULL, 15},
537 {"filter", required_argument, NULL, 16},
538 {"exclude-extension", required_argument, NULL, 17},
539 {"sequence-data", no_argument, &dopt.sequence_data, 1},
540 {"restrict-key", required_argument, NULL, 25},
541
542 {NULL, 0, NULL, 0}
543 };
544
545 pg_logging_init(argv[0]);
547 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
548
549 /*
550 * Initialize what we need for parallel execution, especially for thread
551 * support on Windows.
552 */
554
555 progname = get_progname(argv[0]);
556
557 if (argc > 1)
558 {
559 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
560 {
561 help(progname);
562 exit_nicely(0);
563 }
564 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
565 {
566 puts("pg_dump (PostgreSQL) " PG_VERSION);
567 exit_nicely(0);
568 }
569 }
570
571 InitDumpOptions(&dopt);
572
573 while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
574 long_options, &optindex)) != -1)
575 {
576 switch (c)
577 {
578 case 'a': /* Dump data only */
579 data_only = true;
580 break;
581
582 case 'b': /* Dump LOs */
583 dopt.outputLOs = true;
584 break;
585
586 case 'B': /* Don't dump LOs */
587 dopt.dontOutputLOs = true;
588 break;
589
590 case 'c': /* clean (i.e., drop) schema prior to create */
591 dopt.outputClean = 1;
592 break;
593
594 case 'C': /* Create DB */
595 dopt.outputCreateDB = 1;
596 break;
597
598 case 'd': /* database name */
600 break;
601
602 case 'e': /* include extension(s) */
604 dopt.include_everything = false;
605 break;
606
607 case 'E': /* Dump encoding */
608 dumpencoding = pg_strdup(optarg);
609 break;
610
611 case 'f':
613 break;
614
615 case 'F':
617 break;
618
619 case 'h': /* server host */
621 break;
622
623 case 'j': /* number of dump jobs */
624 if (!option_parse_int(optarg, "-j/--jobs", 1,
626 &numWorkers))
627 exit_nicely(1);
628 break;
629
630 case 'n': /* include schema(s) */
632 dopt.include_everything = false;
633 break;
634
635 case 'N': /* exclude schema(s) */
637 break;
638
639 case 'O': /* Don't reconnect to match owner */
640 dopt.outputNoOwner = 1;
641 break;
642
643 case 'p': /* server port */
645 break;
646
647 case 'R':
648 /* no-op, still accepted for backwards compatibility */
649 break;
650
651 case 's': /* dump schema only */
652 schema_only = true;
653 break;
654
655 case 'S': /* Username for superuser in plain text output */
657 break;
658
659 case 't': /* include table(s) */
661 dopt.include_everything = false;
662 break;
663
664 case 'T': /* exclude table(s) */
666 break;
667
668 case 'U':
670 break;
671
672 case 'v': /* verbose */
673 g_verbose = true;
675 break;
676
677 case 'w':
679 break;
680
681 case 'W':
683 break;
684
685 case 'x': /* skip ACL dump */
686 dopt.aclsSkip = true;
687 break;
688
689 case 'Z': /* Compression */
690 parse_compress_options(optarg, &compression_algorithm_str,
691 &compression_detail);
692 user_compression_defined = true;
693 break;
694
695 case 0:
696 /* This covers the long options. */
697 break;
698
699 case 2: /* lock-wait-timeout */
701 break;
702
703 case 3: /* SET ROLE */
704 use_role = pg_strdup(optarg);
705 break;
706
707 case 4: /* exclude table(s) data */
709 break;
710
711 case 5: /* section */
713 break;
714
715 case 6: /* snapshot */
716 dumpsnapshot = pg_strdup(optarg);
717 break;
718
719 case 7: /* no-sync */
720 dosync = false;
721 break;
722
723 case 8:
725 if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
727 exit_nicely(1);
728 break;
729
730 case 9: /* inserts */
731
732 /*
733 * dump_inserts also stores --rows-per-insert, careful not to
734 * overwrite that.
735 */
736 if (dopt.dump_inserts == 0)
738 break;
739
740 case 10: /* rows per insert */
741 if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
742 &dopt.dump_inserts))
743 exit_nicely(1);
744 break;
745
746 case 11: /* include foreign data */
748 optarg);
749 break;
750
751 case 12: /* include table(s) and their children */
753 optarg);
754 dopt.include_everything = false;
755 break;
756
757 case 13: /* exclude table(s) and their children */
759 optarg);
760 break;
761
762 case 14: /* exclude data of table(s) and children */
764 optarg);
765 break;
766
767 case 15:
769 exit_nicely(1);
770 break;
771
772 case 16: /* read object filters from file */
774 break;
775
776 case 17: /* exclude extension(s) */
778 optarg);
779 break;
780
781 case 18:
782 statistics_only = true;
783 break;
784
785 case 19:
786 no_data = true;
787 break;
788
789 case 20:
790 no_schema = true;
791 break;
792
793 case 21:
794 no_statistics = true;
795 break;
796
797 case 22:
798 with_statistics = true;
799 break;
800
801 case 25:
803 break;
804
805 default:
806 /* getopt_long already emitted a complaint */
807 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
808 exit_nicely(1);
809 }
810 }
811
812 /*
813 * Non-option argument specifies database name as long as it wasn't
814 * already specified with -d / --dbname
815 */
816 if (optind < argc && dopt.cparams.dbname == NULL)
817 dopt.cparams.dbname = argv[optind++];
818
819 /* Complain if any arguments remain */
820 if (optind < argc)
821 {
822 pg_log_error("too many command-line arguments (first is \"%s\")",
823 argv[optind]);
824 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
825 exit_nicely(1);
826 }
827
828 /* --column-inserts implies --inserts */
829 if (dopt.column_inserts && dopt.dump_inserts == 0)
831
832 /* reject conflicting "-only" options */
833 if (data_only && schema_only)
834 pg_fatal("options -s/--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_pubinsert;
4535 int i_pubupdate;
4536 int i_pubdelete;
4537 int i_pubtruncate;
4538 int i_pubviaroot;
4539 int i_pubgencols;
4540 int i,
4541 ntups;
4542
4543 if (dopt->no_publications || fout->remoteVersion < 100000)
4544 return;
4545
4546 query = createPQExpBuffer();
4547
4548 /* Get the publications. */
4549 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4550 "p.pubowner, p.puballtables, p.pubinsert, "
4551 "p.pubupdate, p.pubdelete, ");
4552
4553 if (fout->remoteVersion >= 110000)
4554 appendPQExpBufferStr(query, "p.pubtruncate, ");
4555 else
4556 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4557
4558 if (fout->remoteVersion >= 130000)
4559 appendPQExpBufferStr(query, "p.pubviaroot, ");
4560 else
4561 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4562
4563 if (fout->remoteVersion >= 180000)
4564 appendPQExpBufferStr(query, "p.pubgencols ");
4565 else
4566 appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4567
4568 appendPQExpBufferStr(query, "FROM pg_publication p");
4569
4570 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4571
4572 ntups = PQntuples(res);
4573
4574 if (ntups == 0)
4575 goto cleanup;
4576
4577 i_tableoid = PQfnumber(res, "tableoid");
4578 i_oid = PQfnumber(res, "oid");
4579 i_pubname = PQfnumber(res, "pubname");
4580 i_pubowner = PQfnumber(res, "pubowner");
4581 i_puballtables = PQfnumber(res, "puballtables");
4582 i_pubinsert = PQfnumber(res, "pubinsert");
4583 i_pubupdate = PQfnumber(res, "pubupdate");
4584 i_pubdelete = PQfnumber(res, "pubdelete");
4585 i_pubtruncate = PQfnumber(res, "pubtruncate");
4586 i_pubviaroot = PQfnumber(res, "pubviaroot");
4587 i_pubgencols = PQfnumber(res, "pubgencols");
4588
4589 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4590
4591 for (i = 0; i < ntups; i++)
4592 {
4593 pubinfo[i].dobj.objType = DO_PUBLICATION;
4594 pubinfo[i].dobj.catId.tableoid =
4595 atooid(PQgetvalue(res, i, i_tableoid));
4596 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4597 AssignDumpId(&pubinfo[i].dobj);
4598 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4599 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4600 pubinfo[i].puballtables =
4601 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4602 pubinfo[i].pubinsert =
4603 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4604 pubinfo[i].pubupdate =
4605 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4606 pubinfo[i].pubdelete =
4607 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4608 pubinfo[i].pubtruncate =
4609 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4610 pubinfo[i].pubviaroot =
4611 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4612 pubinfo[i].pubgencols_type =
4613 *(PQgetvalue(res, i, i_pubgencols));
4614
4615 /* Decide whether we want to dump it */
4616 selectDumpableObject(&(pubinfo[i].dobj), fout);
4617 }
4618
4619cleanup:
4620 PQclear(res);
4621
4622 destroyPQExpBuffer(query);
4623}
4624
4625/*
4626 * dumpPublication
4627 * dump the definition of the given publication
4628 */
4629static void
4631{
4632 DumpOptions *dopt = fout->dopt;
4633 PQExpBuffer delq;
4634 PQExpBuffer query;
4635 char *qpubname;
4636 bool first = true;
4637
4638 /* Do nothing if not dumping schema */
4639 if (!dopt->dumpSchema)
4640 return;
4641
4642 delq = createPQExpBuffer();
4643 query = createPQExpBuffer();
4644
4645 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4646
4647 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4648 qpubname);
4649
4650 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4651 qpubname);
4652
4653 if (pubinfo->puballtables)
4654 appendPQExpBufferStr(query, " FOR ALL TABLES");
4655
4656 appendPQExpBufferStr(query, " WITH (publish = '");
4657 if (pubinfo->pubinsert)
4658 {
4659 appendPQExpBufferStr(query, "insert");
4660 first = false;
4661 }
4662
4663 if (pubinfo->pubupdate)
4664 {
4665 if (!first)
4666 appendPQExpBufferStr(query, ", ");
4667
4668 appendPQExpBufferStr(query, "update");
4669 first = false;
4670 }
4671
4672 if (pubinfo->pubdelete)
4673 {
4674 if (!first)
4675 appendPQExpBufferStr(query, ", ");
4676
4677 appendPQExpBufferStr(query, "delete");
4678 first = false;
4679 }
4680
4681 if (pubinfo->pubtruncate)
4682 {
4683 if (!first)
4684 appendPQExpBufferStr(query, ", ");
4685
4686 appendPQExpBufferStr(query, "truncate");
4687 first = false;
4688 }
4689
4690 appendPQExpBufferChar(query, '\'');
4691
4692 if (pubinfo->pubviaroot)
4693 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4694
4695 if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4696 appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4697
4698 appendPQExpBufferStr(query, ");\n");
4699
4700 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4701 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4702 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4703 .owner = pubinfo->rolname,
4704 .description = "PUBLICATION",
4705 .section = SECTION_POST_DATA,
4706 .createStmt = query->data,
4707 .dropStmt = delq->data));
4708
4709 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4710 dumpComment(fout, "PUBLICATION", qpubname,
4711 NULL, pubinfo->rolname,
4712 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4713
4714 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4715 dumpSecLabel(fout, "PUBLICATION", qpubname,
4716 NULL, pubinfo->rolname,
4717 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4718
4719 destroyPQExpBuffer(delq);
4720 destroyPQExpBuffer(query);
4721 free(qpubname);
4722}
4723
4724/*
4725 * getPublicationNamespaces
4726 * get information about publication membership for dumpable schemas.
4727 */
4728void
4730{
4731 PQExpBuffer query;
4732 PGresult *res;
4733 PublicationSchemaInfo *pubsinfo;
4734 DumpOptions *dopt = fout->dopt;
4735 int i_tableoid;
4736 int i_oid;
4737 int i_pnpubid;
4738 int i_pnnspid;
4739 int i,
4740 j,
4741 ntups;
4742
4743 if (dopt->no_publications || fout->remoteVersion < 150000)
4744 return;
4745
4746 query = createPQExpBuffer();
4747
4748 /* Collect all publication membership info. */
4750 "SELECT tableoid, oid, pnpubid, pnnspid "
4751 "FROM pg_catalog.pg_publication_namespace");
4752 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4753
4754 ntups = PQntuples(res);
4755
4756 i_tableoid = PQfnumber(res, "tableoid");
4757 i_oid = PQfnumber(res, "oid");
4758 i_pnpubid = PQfnumber(res, "pnpubid");
4759 i_pnnspid = PQfnumber(res, "pnnspid");
4760
4761 /* this allocation may be more than we need */
4762 pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4763 j = 0;
4764
4765 for (i = 0; i < ntups; i++)
4766 {
4767 Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4768 Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4769 PublicationInfo *pubinfo;
4770 NamespaceInfo *nspinfo;
4771
4772 /*
4773 * Ignore any entries for which we aren't interested in either the
4774 * publication or the rel.
4775 */
4776 pubinfo = findPublicationByOid(pnpubid);
4777 if (pubinfo == NULL)
4778 continue;
4779 nspinfo = findNamespaceByOid(pnnspid);
4780 if (nspinfo == NULL)
4781 continue;
4782
4783 /* OK, make a DumpableObject for this relationship */
4785 pubsinfo[j].dobj.catId.tableoid =
4786 atooid(PQgetvalue(res, i, i_tableoid));
4787 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4788 AssignDumpId(&pubsinfo[j].dobj);
4789 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4790 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4791 pubsinfo[j].publication = pubinfo;
4792 pubsinfo[j].pubschema = nspinfo;
4793
4794 /* Decide whether we want to dump it */
4795 selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4796
4797 j++;
4798 }
4799
4800 PQclear(res);
4801 destroyPQExpBuffer(query);
4802}
4803
4804/*
4805 * getPublicationTables
4806 * get information about publication membership for dumpable tables.
4807 */
4808void
4809getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4810{
4811 PQExpBuffer query;
4812 PGresult *res;
4813 PublicationRelInfo *pubrinfo;
4814 DumpOptions *dopt = fout->dopt;
4815 int i_tableoid;
4816 int i_oid;
4817 int i_prpubid;
4818 int i_prrelid;
4819 int i_prrelqual;
4820 int i_prattrs;
4821 int i,
4822 j,
4823 ntups;
4824
4825 if (dopt->no_publications || fout->remoteVersion < 100000)
4826 return;
4827
4828 query = createPQExpBuffer();
4829
4830 /* Collect all publication membership info. */
4831 if (fout->remoteVersion >= 150000)
4833 "SELECT tableoid, oid, prpubid, prrelid, "
4834 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4835 "(CASE\n"
4836 " WHEN pr.prattrs IS NOT NULL THEN\n"
4837 " (SELECT array_agg(attname)\n"
4838 " FROM\n"
4839 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4840 " pg_catalog.pg_attribute\n"
4841 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4842 " ELSE NULL END) prattrs "
4843 "FROM pg_catalog.pg_publication_rel pr");
4844 else
4846 "SELECT tableoid, oid, prpubid, prrelid, "
4847 "NULL AS prrelqual, NULL AS prattrs "
4848 "FROM pg_catalog.pg_publication_rel");
4849 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4850
4851 ntups = PQntuples(res);
4852
4853 i_tableoid = PQfnumber(res, "tableoid");
4854 i_oid = PQfnumber(res, "oid");
4855 i_prpubid = PQfnumber(res, "prpubid");
4856 i_prrelid = PQfnumber(res, "prrelid");
4857 i_prrelqual = PQfnumber(res, "prrelqual");
4858 i_prattrs = PQfnumber(res, "prattrs");
4859
4860 /* this allocation may be more than we need */
4861 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4862 j = 0;
4863
4864 for (i = 0; i < ntups; i++)
4865 {
4866 Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4867 Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4868 PublicationInfo *pubinfo;
4869 TableInfo *tbinfo;
4870
4871 /*
4872 * Ignore any entries for which we aren't interested in either the
4873 * publication or the rel.
4874 */
4875 pubinfo = findPublicationByOid(prpubid);
4876 if (pubinfo == NULL)
4877 continue;
4878 tbinfo = findTableByOid(prrelid);
4879 if (tbinfo == NULL)
4880 continue;
4881
4882 /* OK, make a DumpableObject for this relationship */
4883 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4884 pubrinfo[j].dobj.catId.tableoid =
4885 atooid(PQgetvalue(res, i, i_tableoid));
4886 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4887 AssignDumpId(&pubrinfo[j].dobj);
4888 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4889 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4890 pubrinfo[j].publication = pubinfo;
4891 pubrinfo[j].pubtable = tbinfo;
4892 if (PQgetisnull(res, i, i_prrelqual))
4893 pubrinfo[j].pubrelqual = NULL;
4894 else
4895 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4896
4897 if (!PQgetisnull(res, i, i_prattrs))
4898 {
4899 char **attnames;
4900 int nattnames;
4901 PQExpBuffer attribs;
4902
4903 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4904 &attnames, &nattnames))
4905 pg_fatal("could not parse %s array", "prattrs");
4906 attribs = createPQExpBuffer();
4907 for (int k = 0; k < nattnames; k++)
4908 {
4909 if (k > 0)
4910 appendPQExpBufferStr(attribs, ", ");
4911
4912 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4913 }
4914 pubrinfo[j].pubrattrs = attribs->data;
4915 free(attribs); /* but not attribs->data */
4916 free(attnames);
4917 }
4918 else
4919 pubrinfo[j].pubrattrs = NULL;
4920
4921 /* Decide whether we want to dump it */
4922 selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4923
4924 j++;
4925 }
4926
4927 PQclear(res);
4928 destroyPQExpBuffer(query);
4929}
4930
4931/*
4932 * dumpPublicationNamespace
4933 * dump the definition of the given publication schema mapping.
4934 */
4935static void
4937{
4938 DumpOptions *dopt = fout->dopt;
4939 NamespaceInfo *schemainfo = pubsinfo->pubschema;
4940 PublicationInfo *pubinfo = pubsinfo->publication;
4941 PQExpBuffer query;
4942 char *tag;
4943
4944 /* Do nothing if not dumping schema */
4945 if (!dopt->dumpSchema)
4946 return;
4947
4948 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4949
4950 query = createPQExpBuffer();
4951
4952 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4953 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4954
4955 /*
4956 * There is no point in creating drop query as the drop is done by schema
4957 * drop.
4958 */
4959 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4960 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4961 ARCHIVE_OPTS(.tag = tag,
4962 .namespace = schemainfo->dobj.name,
4963 .owner = pubinfo->rolname,
4964 .description = "PUBLICATION TABLES IN SCHEMA",
4965 .section = SECTION_POST_DATA,
4966 .createStmt = query->data));
4967
4968 /* These objects can't currently have comments or seclabels */
4969
4970 free(tag);
4971 destroyPQExpBuffer(query);
4972}
4973
4974/*
4975 * dumpPublicationTable
4976 * dump the definition of the given publication table mapping
4977 */
4978static void
4980{
4981 DumpOptions *dopt = fout->dopt;
4982 PublicationInfo *pubinfo = pubrinfo->publication;
4983 TableInfo *tbinfo = pubrinfo->pubtable;
4984 PQExpBuffer query;
4985 char *tag;
4986
4987 /* Do nothing if not dumping schema */
4988 if (!dopt->dumpSchema)
4989 return;
4990
4991 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4992
4993 query = createPQExpBuffer();
4994
4995 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4996 fmtId(pubinfo->dobj.name));
4997 appendPQExpBuffer(query, " %s",
4998 fmtQualifiedDumpable(tbinfo));
4999
5000 if (pubrinfo->pubrattrs)
5001 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5002
5003 if (pubrinfo->pubrelqual)
5004 {
5005 /*
5006 * It's necessary to add parentheses around the expression because
5007 * pg_get_expr won't supply the parentheses for things like WHERE
5008 * TRUE.
5009 */
5010 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5011 }
5012 appendPQExpBufferStr(query, ";\n");
5013
5014 /*
5015 * There is no point in creating a drop query as the drop is done by table
5016 * drop. (If you think to change this, see also _printTocEntry().)
5017 * Although this object doesn't really have ownership as such, set the
5018 * owner field anyway to ensure that the command is run by the correct
5019 * role at restore time.
5020 */
5021 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5022 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5023 ARCHIVE_OPTS(.tag = tag,
5024 .namespace = tbinfo->dobj.namespace->dobj.name,
5025 .owner = pubinfo->rolname,
5026 .description = "PUBLICATION TABLE",
5027 .section = SECTION_POST_DATA,
5028 .createStmt = query->data));
5029
5030 /* These objects can't currently have comments or seclabels */
5031
5032 free(tag);
5033 destroyPQExpBuffer(query);
5034}
5035
5036/*
5037 * Is the currently connected user a superuser?
5038 */
5039static bool
5041{
5042 ArchiveHandle *AH = (ArchiveHandle *) fout;
5043 const char *val;
5044
5045 val = PQparameterStatus(AH->connection, "is_superuser");
5046
5047 if (val && strcmp(val, "on") == 0)
5048 return true;
5049
5050 return false;
5051}
5052
5053/*
5054 * Set the given value to restrict_nonsystem_relation_kind value. Since
5055 * restrict_nonsystem_relation_kind is introduced in minor version releases,
5056 * the setting query is effective only where available.
5057 */
5058static void
5060{
5062 PGresult *res;
5063
5064 appendPQExpBuffer(query,
5065 "SELECT set_config(name, '%s', false) "
5066 "FROM pg_settings "
5067 "WHERE name = 'restrict_nonsystem_relation_kind'",
5068 value);
5069 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5070
5071 PQclear(res);
5072 destroyPQExpBuffer(query);
5073}
5074
5075/*
5076 * getSubscriptions
5077 * get information about subscriptions
5078 */
5079void
5081{
5082 DumpOptions *dopt = fout->dopt;
5083 PQExpBuffer query;
5084 PGresult *res;
5085 SubscriptionInfo *subinfo;
5086 int i_tableoid;
5087 int i_oid;
5088 int i_subname;
5089 int i_subowner;
5090 int i_subbinary;
5091 int i_substream;
5092 int i_subtwophasestate;
5093 int i_subdisableonerr;
5094 int i_subpasswordrequired;
5095 int i_subrunasowner;
5096 int i_subconninfo;
5097 int i_subslotname;
5098 int i_subsynccommit;
5099 int i_subpublications;
5100 int i_suborigin;
5101 int i_suboriginremotelsn;
5102 int i_subenabled;
5103 int i_subfailover;
5104 int i_subretaindeadtuples;
5105 int i_submaxretention;
5106 int i,
5107 ntups;
5108
5109 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5110 return;
5111
5112 if (!is_superuser(fout))
5113 {
5114 int n;
5115
5116 res = ExecuteSqlQuery(fout,
5117 "SELECT count(*) FROM pg_subscription "
5118 "WHERE subdbid = (SELECT oid FROM pg_database"
5119 " WHERE datname = current_database())",
5121 n = atoi(PQgetvalue(res, 0, 0));
5122 if (n > 0)
5123 pg_log_warning("subscriptions not dumped because current user is not a superuser");
5124 PQclear(res);
5125 return;
5126 }
5127
5128 query = createPQExpBuffer();
5129
5130 /* Get the subscriptions in current database. */
5132 "SELECT s.tableoid, s.oid, s.subname,\n"
5133 " s.subowner,\n"
5134 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5135 " s.subpublications,\n");
5136
5137 if (fout->remoteVersion >= 140000)
5138 appendPQExpBufferStr(query, " s.subbinary,\n");
5139 else
5140 appendPQExpBufferStr(query, " false AS subbinary,\n");
5141
5142 if (fout->remoteVersion >= 140000)
5143 appendPQExpBufferStr(query, " s.substream,\n");
5144 else
5145 appendPQExpBufferStr(query, " 'f' AS substream,\n");
5146
5147 if (fout->remoteVersion >= 150000)
5149 " s.subtwophasestate,\n"
5150 " s.subdisableonerr,\n");
5151 else
5152 appendPQExpBuffer(query,
5153 " '%c' AS subtwophasestate,\n"
5154 " false AS subdisableonerr,\n",
5155 LOGICALREP_TWOPHASE_STATE_DISABLED);
5156
5157 if (fout->remoteVersion >= 160000)
5159 " s.subpasswordrequired,\n"
5160 " s.subrunasowner,\n"
5161 " s.suborigin,\n");
5162 else
5163 appendPQExpBuffer(query,
5164 " 't' AS subpasswordrequired,\n"
5165 " 't' AS subrunasowner,\n"
5166 " '%s' AS suborigin,\n",
5167 LOGICALREP_ORIGIN_ANY);
5168
5169 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5170 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5171 " s.subenabled,\n");
5172 else
5173 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5174 " false AS subenabled,\n");
5175
5176 if (fout->remoteVersion >= 170000)
5178 " s.subfailover,\n");
5179 else
5181 " false AS subfailover,\n");
5182
5183 if (fout->remoteVersion >= 190000)
5185 " s.subretaindeadtuples,\n");
5186 else
5188 " false AS subretaindeadtuples,\n");
5189
5190 if (fout->remoteVersion >= 190000)
5192 " s.submaxretention\n");
5193 else
5194 appendPQExpBuffer(query,
5195 " 0 AS submaxretention\n");
5196
5198 "FROM pg_subscription s\n");
5199
5200 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5202 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5203 " ON o.external_id = 'pg_' || s.oid::text \n");
5204
5206 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5207 " WHERE datname = current_database())");
5208
5209 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5210
5211 ntups = PQntuples(res);
5212
5213 /*
5214 * Get subscription fields. We don't include subskiplsn in the dump as
5215 * after restoring the dump this value may no longer be relevant.
5216 */
5217 i_tableoid = PQfnumber(res, "tableoid");
5218 i_oid = PQfnumber(res, "oid");
5219 i_subname = PQfnumber(res, "subname");
5220 i_subowner = PQfnumber(res, "subowner");
5221 i_subenabled = PQfnumber(res, "subenabled");
5222 i_subbinary = PQfnumber(res, "subbinary");
5223 i_substream = PQfnumber(res, "substream");
5224 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5225 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5226 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5227 i_subrunasowner = PQfnumber(res, "subrunasowner");
5228 i_subfailover = PQfnumber(res, "subfailover");
5229 i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5230 i_submaxretention = PQfnumber(res, "submaxretention");
5231 i_subconninfo = PQfnumber(res, "subconninfo");
5232 i_subslotname = PQfnumber(res, "subslotname");
5233 i_subsynccommit = PQfnumber(res, "subsynccommit");
5234 i_subpublications = PQfnumber(res, "subpublications");
5235 i_suborigin = PQfnumber(res, "suborigin");
5236 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5237
5238 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5239
5240 for (i = 0; i < ntups; i++)
5241 {
5242 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5243 subinfo[i].dobj.catId.tableoid =
5244 atooid(PQgetvalue(res, i, i_tableoid));
5245 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5246 AssignDumpId(&subinfo[i].dobj);
5247 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5248 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5249
5250 subinfo[i].subenabled =
5251 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5252 subinfo[i].subbinary =
5253 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5254 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5255 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5256 subinfo[i].subdisableonerr =
5257 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5258 subinfo[i].subpasswordrequired =
5259 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5260 subinfo[i].subrunasowner =
5261 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5262 subinfo[i].subfailover =
5263 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5264 subinfo[i].subretaindeadtuples =
5265 (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5266 subinfo[i].submaxretention =
5267 atoi(PQgetvalue(res, i, i_submaxretention));
5268 subinfo[i].subconninfo =
5269 pg_strdup(PQgetvalue(res, i, i_subconninfo));
5270 if (PQgetisnull(res, i, i_subslotname))
5271 subinfo[i].subslotname = NULL;
5272 else
5273 subinfo[i].subslotname =
5274 pg_strdup(PQgetvalue(res, i, i_subslotname));
5275 subinfo[i].subsynccommit =
5276 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5277 subinfo[i].subpublications =
5278 pg_strdup(PQgetvalue(res, i, i_subpublications));
5279 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5280 if (PQgetisnull(res, i, i_suboriginremotelsn))
5281 subinfo[i].suboriginremotelsn = NULL;
5282 else
5283 subinfo[i].suboriginremotelsn =
5284 pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5285
5286 /* Decide whether we want to dump it */
5287 selectDumpableObject(&(subinfo[i].dobj), fout);
5288 }
5289 PQclear(res);
5290
5291 destroyPQExpBuffer(query);
5292}
5293
5294/*
5295 * getSubscriptionTables
5296 * Get information about subscription membership for dumpable tables. This
5297 * will be used only in binary-upgrade mode for PG17 or later versions.
5298 */
5299void
5301{
5302 DumpOptions *dopt = fout->dopt;
5303 SubscriptionInfo *subinfo = NULL;
5304 SubRelInfo *subrinfo;
5305 PGresult *res;
5306 int i_srsubid;
5307 int i_srrelid;
5308 int i_srsubstate;
5309 int i_srsublsn;
5310 int ntups;
5311 Oid last_srsubid = InvalidOid;
5312
5313 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5314 fout->remoteVersion < 170000)
5315 return;
5316
5317 res = ExecuteSqlQuery(fout,
5318 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5319 "FROM pg_catalog.pg_subscription_rel "
5320 "ORDER BY srsubid",
5322 ntups = PQntuples(res);
5323 if (ntups == 0)
5324 goto cleanup;
5325
5326 /* Get pg_subscription_rel attributes */
5327 i_srsubid = PQfnumber(res, "srsubid");
5328 i_srrelid = PQfnumber(res, "srrelid");
5329 i_srsubstate = PQfnumber(res, "srsubstate");
5330 i_srsublsn = PQfnumber(res, "srsublsn");
5331
5332 subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5333 for (int i = 0; i < ntups; i++)
5334 {
5335 Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5336 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5337 TableInfo *tblinfo;
5338
5339 /*
5340 * If we switched to a new subscription, check if the subscription
5341 * exists.
5342 */
5343 if (cur_srsubid != last_srsubid)
5344 {
5345 subinfo = findSubscriptionByOid(cur_srsubid);
5346 if (subinfo == NULL)
5347 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5348
5349 last_srsubid = cur_srsubid;
5350 }
5351
5352 tblinfo = findTableByOid(relid);
5353 if (tblinfo == NULL)
5354 pg_fatal("failed sanity check, table with OID %u not found",
5355 relid);
5356
5357 /* OK, make a DumpableObject for this relationship */
5358 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5359 subrinfo[i].dobj.catId.tableoid = relid;
5360 subrinfo[i].dobj.catId.oid = cur_srsubid;
5361 AssignDumpId(&subrinfo[i].dobj);
5362 subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5363 subrinfo[i].tblinfo = tblinfo;
5364 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5365 if (PQgetisnull(res, i, i_srsublsn))
5366 subrinfo[i].srsublsn = NULL;
5367 else
5368 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5369
5370 subrinfo[i].subinfo = subinfo;
5371
5372 /* Decide whether we want to dump it */
5373 selectDumpableObject(&(subrinfo[i].dobj), fout);
5374 }
5375
5376cleanup:
5377 PQclear(res);
5378}
5379
5380/*
5381 * dumpSubscriptionTable
5382 * Dump the definition of the given subscription table mapping. This will be
5383 * used only in binary-upgrade mode for PG17 or later versions.
5384 */
5385static void
5387{
5388 DumpOptions *dopt = fout->dopt;
5389 SubscriptionInfo *subinfo = subrinfo->subinfo;
5390 PQExpBuffer query;
5391 char *tag;
5392
5393 /* Do nothing if not dumping schema */
5394 if (!dopt->dumpSchema)
5395 return;
5396
5397 Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5398
5399 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5400
5401 query = createPQExpBuffer();
5402
5403 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5404 {
5405 /*
5406 * binary_upgrade_add_sub_rel_state will add the subscription relation
5407 * to pg_subscription_rel table. This will be used only in
5408 * binary-upgrade mode.
5409 */
5411 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5413 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5414 appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5415 appendPQExpBuffer(query,
5416 ", %u, '%c'",
5417 subrinfo->tblinfo->dobj.catId.oid,
5418 subrinfo->srsubstate);
5419
5420 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5421 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5422 else
5423 appendPQExpBufferStr(query, ", NULL");
5424
5425 appendPQExpBufferStr(query, ");\n");
5426 }
5427
5428 /*
5429 * There is no point in creating a drop query as the drop is done by table
5430 * drop. (If you think to change this, see also _printTocEntry().)
5431 * Although this object doesn't really have ownership as such, set the
5432 * owner field anyway to ensure that the command is run by the correct
5433 * role at restore time.
5434 */
5435 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5436 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5437 ARCHIVE_OPTS(.tag = tag,
5438 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5439 .owner = subinfo->rolname,
5440 .description = "SUBSCRIPTION TABLE",
5441 .section = SECTION_POST_DATA,
5442 .createStmt = query->data));
5443
5444 /* These objects can't currently have comments or seclabels */
5445
5446 free(tag);
5447 destroyPQExpBuffer(query);
5448}
5449
5450/*
5451 * dumpSubscription
5452 * dump the definition of the given subscription
5453 */
5454static void
5456{
5457 DumpOptions *dopt = fout->dopt;
5458 PQExpBuffer delq;
5459 PQExpBuffer query;
5460 PQExpBuffer publications;
5461 char *qsubname;
5462 char **pubnames = NULL;
5463 int npubnames = 0;
5464 int i;
5465
5466 /* Do nothing if not dumping schema */
5467 if (!dopt->dumpSchema)
5468 return;
5469
5470 delq = createPQExpBuffer();
5471 query = createPQExpBuffer();
5472
5473 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5474
5475 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5476 qsubname);
5477
5478 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5479 qsubname);
5480 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5481
5482 /* Build list of quoted publications and append them to query. */
5483 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5484 pg_fatal("could not parse %s array", "subpublications");
5485
5486 publications = createPQExpBuffer();
5487 for (i = 0; i < npubnames; i++)
5488 {
5489 if (i > 0)
5490 appendPQExpBufferStr(publications, ", ");
5491
5492 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5493 }
5494
5495 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5496 if (subinfo->subslotname)
5497 appendStringLiteralAH(query, subinfo->subslotname, fout);
5498 else
5499 appendPQExpBufferStr(query, "NONE");
5500
5501 if (subinfo->subbinary)
5502 appendPQExpBufferStr(query, ", binary = true");
5503
5504 if (subinfo->substream == LOGICALREP_STREAM_ON)
5505 appendPQExpBufferStr(query, ", streaming = on");
5506 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5507 appendPQExpBufferStr(query, ", streaming = parallel");
5508 else
5509 appendPQExpBufferStr(query, ", streaming = off");
5510
5511 if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5512 appendPQExpBufferStr(query, ", two_phase = on");
5513
5514 if (subinfo->subdisableonerr)
5515 appendPQExpBufferStr(query, ", disable_on_error = true");
5516
5517 if (!subinfo->subpasswordrequired)
5518 appendPQExpBufferStr(query, ", password_required = false");
5519
5520 if (subinfo->subrunasowner)
5521 appendPQExpBufferStr(query, ", run_as_owner = true");
5522
5523 if (subinfo->subfailover)
5524 appendPQExpBufferStr(query, ", failover = true");
5525
5526 if (subinfo->subretaindeadtuples)
5527 appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5528
5529 if (subinfo->submaxretention)
5530 appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5531
5532 if (strcmp(subinfo->subsynccommit, "off") != 0)
5533 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5534
5535 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5536 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5537
5538 appendPQExpBufferStr(query, ");\n");
5539
5540 /*
5541 * In binary-upgrade mode, we allow the replication to continue after the
5542 * upgrade.
5543 */
5544 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5545 {
5546 if (subinfo->suboriginremotelsn)
5547 {
5548 /*
5549 * Preserve the remote_lsn for the subscriber's replication
5550 * origin. This value is required to start the replication from
5551 * the position before the upgrade. This value will be stale if
5552 * the publisher gets upgraded before the subscriber node.
5553 * However, this shouldn't be a problem as the upgrade of the
5554 * publisher ensures that all the transactions were replicated
5555 * before upgrading it.
5556 */
5558 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5560 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5561 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5562 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5563 }
5564
5565 if (subinfo->subenabled)
5566 {
5567 /*
5568 * Enable the subscription to allow the replication to continue
5569 * after the upgrade.
5570 */
5572 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5573 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5574 }
5575 }
5576
5577 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5578 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5579 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5580 .owner = subinfo->rolname,
5581 .description = "SUBSCRIPTION",
5582 .section = SECTION_POST_DATA,
5583 .createStmt = query->data,
5584 .dropStmt = delq->data));
5585
5586 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5587 dumpComment(fout, "SUBSCRIPTION", qsubname,
5588 NULL, subinfo->rolname,
5589 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5590
5591 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5592 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5593 NULL, subinfo->rolname,
5594 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5595
5596 destroyPQExpBuffer(publications);
5597 free(pubnames);
5598
5599 destroyPQExpBuffer(delq);
5600 destroyPQExpBuffer(query);
5601 free(qsubname);
5602}
5603
5604/*
5605 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5606 * the object needs.
5607 */
5608static void
5610 PQExpBuffer create,
5611 const DumpableObject *dobj,
5612 const char *catalog,
5613 const char *keyword,
5614 const char *objname)
5615{
5616 if (dobj->depends_on_ext)
5617 {
5618 char *nm;
5619 PGresult *res;
5620 PQExpBuffer query;
5621 int ntups;
5622 int i_extname;
5623 int i;
5624
5625 /* dodge fmtId() non-reentrancy */
5626 nm = pg_strdup(objname);
5627
5628 query = createPQExpBuffer();
5629 appendPQExpBuffer(query,
5630 "SELECT e.extname "
5631 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5632 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5633 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5634 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5635 catalog,
5636 dobj->catId.oid);
5637 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5638 ntups = PQntuples(res);
5639 i_extname = PQfnumber(res, "extname");
5640 for (i = 0; i < ntups; i++)
5641 {
5642 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5643 keyword, nm,
5644 fmtId(PQgetvalue(res, i, i_extname)));
5645 }
5646
5647 PQclear(res);
5648 destroyPQExpBuffer(query);
5649 pg_free(nm);
5650 }
5651}
5652
5653static Oid
5655{
5656 /*
5657 * If the old version didn't assign an array type, but the new version
5658 * does, we must select an unused type OID to assign. This currently only
5659 * happens for domains, when upgrading pre-v11 to v11 and up.
5660 *
5661 * Note: local state here is kind of ugly, but we must have some, since we
5662 * mustn't choose the same unused OID more than once.
5663 */
5664 static Oid next_possible_free_oid = FirstNormalObjectId;
5665 PGresult *res;
5666 bool is_dup;
5667
5668 do
5669 {
5670 ++next_possible_free_oid;
5671 printfPQExpBuffer(upgrade_query,
5672 "SELECT EXISTS(SELECT 1 "
5673 "FROM pg_catalog.pg_type "
5674 "WHERE oid = '%u'::pg_catalog.oid);",
5675 next_possible_free_oid);
5676 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5677 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5678 PQclear(res);
5679 } while (is_dup);
5680
5681 return next_possible_free_oid;
5682}
5683
5684static void
5686 PQExpBuffer upgrade_buffer,
5687 Oid pg_type_oid,
5688 bool force_array_type,
5689 bool include_multirange_type)
5690{
5691 PQExpBuffer upgrade_query = createPQExpBuffer();
5692 PGresult *res;
5693 Oid pg_type_array_oid;
5694 Oid pg_type_multirange_oid;
5695 Oid pg_type_multirange_array_oid;
5696 TypeInfo *tinfo;
5697
5698 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5699 appendPQExpBuffer(upgrade_buffer,
5700 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5701 pg_type_oid);
5702
5703 tinfo = findTypeByOid(pg_type_oid);
5704 if (tinfo)
5705 pg_type_array_oid = tinfo->typarray;
5706 else
5707 pg_type_array_oid = InvalidOid;
5708
5709 if (!OidIsValid(pg_type_array_oid) && force_array_type)
5710 pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5711
5712 if (OidIsValid(pg_type_array_oid))
5713 {
5714 appendPQExpBufferStr(upgrade_buffer,
5715 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5716 appendPQExpBuffer(upgrade_buffer,
5717 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5718 pg_type_array_oid);
5719 }
5720
5721 /*
5722 * Pre-set the multirange type oid and its own array type oid.
5723 */
5724 if (include_multirange_type)
5725 {
5726 if (fout->remoteVersion >= 140000)
5727 {
5728 printfPQExpBuffer(upgrade_query,
5729 "SELECT t.oid, t.typarray "
5730 "FROM pg_catalog.pg_type t "
5731 "JOIN pg_catalog.pg_range r "
5732 "ON t.oid = r.rngmultitypid "
5733 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5734 pg_type_oid);
5735
5736 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5737
5738 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5739 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5740
5741 PQclear(res);
5742 }
5743 else
5744 {
5745 pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5746 pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5747 }
5748
5749 appendPQExpBufferStr(upgrade_buffer,
5750 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5751 appendPQExpBuffer(upgrade_buffer,
5752 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5753 pg_type_multirange_oid);
5754 appendPQExpBufferStr(upgrade_buffer,
5755 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5756 appendPQExpBuffer(upgrade_buffer,
5757 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5758 pg_type_multirange_array_oid);
5759 }
5760
5761 destroyPQExpBuffer(upgrade_query);
5762}
5763
5764static void
5766 PQExpBuffer upgrade_buffer,
5767 const TableInfo *tbinfo)
5768{
5769 Oid pg_type_oid = tbinfo->reltype;
5770
5771 if (OidIsValid(pg_type_oid))
5772 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5773 pg_type_oid, false, false);
5774}
5775
5776/*
5777 * bsearch() comparator for BinaryUpgradeClassOidItem
5778 */
5779static int
5780BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5781{
5784
5785 return pg_cmp_u32(v1.oid, v2.oid);
5786}
5787
5788/*
5789 * collectBinaryUpgradeClassOids
5790 *
5791 * Construct a table of pg_class information required for
5792 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5793 * lookup.
5794 */
5795static void
5797{
5798 PGresult *res;
5799 const char *query;
5800
5801 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5802 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5803 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5804 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5805 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5806 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5807 "ORDER BY c.oid;";
5808
5809 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5810
5814
5815 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5816 {
5824 }
5825
5826 PQclear(res);
5827}
5828
5829static void
5831 PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5832{
5835
5837
5838 /*
5839 * Preserve the OID and relfilenumber of the table, table's index, table's
5840 * toast table and toast table's index if any.
5841 *
5842 * One complexity is that the current table definition might not require
5843 * the creation of a TOAST table, but the old database might have a TOAST
5844 * table that was created earlier, before some wide columns were dropped.
5845 * By setting the TOAST oid we force creation of the TOAST heap and index
5846 * by the new backend, so we can copy the files during binary upgrade
5847 * without worrying about this case.
5848 */
5849 key.oid = pg_class_oid;
5853
5854 appendPQExpBufferStr(upgrade_buffer,
5855 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5856
5857 if (entry->relkind != RELKIND_INDEX &&
5858 entry->relkind != RELKIND_PARTITIONED_INDEX)
5859 {
5860 appendPQExpBuffer(upgrade_buffer,
5861 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5862 pg_class_oid);
5863
5864 /*
5865 * Not every relation has storage. Also, in a pre-v12 database,
5866 * partitioned tables have a relfilenumber, which should not be
5867 * preserved when upgrading.
5868 */
5869 if (RelFileNumberIsValid(entry->relfilenumber) &&
5870 entry->relkind != RELKIND_PARTITIONED_TABLE)
5871 appendPQExpBuffer(upgrade_buffer,
5872 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5873 entry->relfilenumber);
5874
5875 /*
5876 * In a pre-v12 database, partitioned tables might be marked as having
5877 * toast tables, but we should ignore them if so.
5878 */
5879 if (OidIsValid(entry->toast_oid) &&
5880 entry->relkind != RELKIND_PARTITIONED_TABLE)
5881 {
5882 appendPQExpBuffer(upgrade_buffer,
5883 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5884 entry->toast_oid);
5885 appendPQExpBuffer(upgrade_buffer,
5886 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5887 entry->toast_relfilenumber);
5888
5889 /* every toast table has an index */
5890 appendPQExpBuffer(upgrade_buffer,
5891 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5892 entry->toast_index_oid);
5893 appendPQExpBuffer(upgrade_buffer,
5894 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5896 }
5897 }
5898 else
5899 {
5900 /* Preserve the OID and relfilenumber of the index */
5901 appendPQExpBuffer(upgrade_buffer,
5902 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5903 pg_class_oid);
5904 appendPQExpBuffer(upgrade_buffer,
5905 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5906 entry->relfilenumber);
5907 }
5908
5909 appendPQExpBufferChar(upgrade_buffer, '\n');
5910}
5911
5912/*
5913 * If the DumpableObject is a member of an extension, add a suitable
5914 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5915 *
5916 * For somewhat historical reasons, objname should already be quoted,
5917 * but not objnamespace (if any).
5918 */
5919static void
5921 const DumpableObject *dobj,
5922 const char *objtype,
5923 const char *objname,
5924 const char *objnamespace)
5925{
5926 DumpableObject *extobj = NULL;
5927 int i;
5928
5929 if (!dobj->ext_member)
5930 return;
5931
5932 /*
5933 * Find the parent extension. We could avoid this search if we wanted to
5934 * add a link field to DumpableObject, but the space costs of that would
5935 * be considerable. We assume that member objects could only have a
5936 * direct dependency on their own extension, not any others.
5937 */
5938 for (i = 0; i < dobj->nDeps; i++)
5939 {
5940 extobj = findObjectByDumpId(dobj->dependencies[i]);
5941 if (extobj && extobj->objType == DO_EXTENSION)
5942 break;
5943 extobj = NULL;
5944 }
5945 if (extobj == NULL)
5946 pg_fatal("could not find parent extension for %s %s",
5947 objtype, objname);
5948
5949 appendPQExpBufferStr(upgrade_buffer,
5950 "\n-- For binary upgrade, handle extension membership the hard way\n");
5951 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5952 fmtId(extobj->name),
5953 objtype);
5954 if (objnamespace && *objnamespace)
5955 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5956 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5957}
5958
5959/*
5960 * getNamespaces:
5961 * get information about all namespaces in the system catalogs
5962 */
5963void
5965{
5966 PGresult *res;
5967 int ntups;
5968 int i;
5969 PQExpBuffer query;
5970 NamespaceInfo *nsinfo;
5971 int i_tableoid;
5972 int i_oid;
5973 int i_nspname;
5974 int i_nspowner;
5975 int i_nspacl;
5976 int i_acldefault;
5977
5978 query = createPQExpBuffer();
5979
5980 /*
5981 * we fetch all namespaces including system ones, so that every object we
5982 * read in can be linked to a containing namespace.
5983 */
5984 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5985 "n.nspowner, "
5986 "n.nspacl, "
5987 "acldefault('n', n.nspowner) AS acldefault "
5988 "FROM pg_namespace n");
5989
5990 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5991
5992 ntups = PQntuples(res);
5993
5994 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5995
5996 i_tableoid = PQfnumber(res, "tableoid");
5997 i_oid = PQfnumber(res, "oid");
5998 i_nspname = PQfnumber(res, "nspname");
5999 i_nspowner = PQfnumber(res, "nspowner");
6000 i_nspacl = PQfnumber(res, "nspacl");
6001 i_acldefault = PQfnumber(res, "acldefault");
6002
6003 for (i = 0; i < ntups; i++)
6004 {
6005 const char *nspowner;
6006
6007 nsinfo[i].dobj.objType = DO_NAMESPACE;
6008 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6009 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6010 AssignDumpId(&nsinfo[i].dobj);
6011 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6012 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6013 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6014 nsinfo[i].dacl.privtype = 0;
6015 nsinfo[i].dacl.initprivs = NULL;
6016 nspowner = PQgetvalue(res, i, i_nspowner);
6017 nsinfo[i].nspowner = atooid(nspowner);
6018 nsinfo[i].rolname = getRoleName(nspowner);
6019
6020 /* Decide whether to dump this namespace */
6021 selectDumpableNamespace(&nsinfo[i], fout);
6022
6023 /* Mark whether namespace has an ACL */
6024 if (!PQgetisnull(res, i, i_nspacl))
6026
6027 /*
6028 * We ignore any pg_init_privs.initprivs entry for the public schema
6029 * and assume a predetermined default, for several reasons. First,
6030 * dropping and recreating the schema removes its pg_init_privs entry,
6031 * but an empty destination database starts with this ACL nonetheless.
6032 * Second, we support dump/reload of public schema ownership changes.
6033 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6034 * initprivs continues to reflect the initial owner. Hence,
6035 * synthesize the value that nspacl will have after the restore's
6036 * ALTER SCHEMA OWNER. Third, this makes the destination database
6037 * match the source's ACL, even if the latter was an initdb-default
6038 * ACL, which changed in v15. An upgrade pulls in changes to most
6039 * system object ACLs that the DBA had not customized. We've made the
6040 * public schema depart from that, because changing its ACL so easily
6041 * breaks applications.
6042 */
6043 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6044 {
6045 PQExpBuffer aclarray = createPQExpBuffer();
6046 PQExpBuffer aclitem = createPQExpBuffer();
6047
6048 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6049 appendPQExpBufferChar(aclarray, '{');
6050 quoteAclUserName(aclitem, nsinfo[i].rolname);
6051 appendPQExpBufferStr(aclitem, "=UC/");
6052 quoteAclUserName(aclitem, nsinfo[i].rolname);
6053 appendPGArray(aclarray, aclitem->data);
6054 resetPQExpBuffer(aclitem);
6055 appendPQExpBufferStr(aclitem, "=U/");
6056 quoteAclUserName(aclitem, nsinfo[i].rolname);
6057 appendPGArray(aclarray, aclitem->data);
6058 appendPQExpBufferChar(aclarray, '}');
6059
6060 nsinfo[i].dacl.privtype = 'i';
6061 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6063
6064 destroyPQExpBuffer(aclarray);
6065 destroyPQExpBuffer(aclitem);
6066 }
6067 }
6068
6069 PQclear(res);
6070 destroyPQExpBuffer(query);
6071}
6072
6073/*
6074 * findNamespace:
6075 * given a namespace OID, look up the info read by getNamespaces
6076 */
6077static NamespaceInfo *
6079{
6080 NamespaceInfo *nsinfo;
6081
6082 nsinfo = findNamespaceByOid(nsoid);
6083 if (nsinfo == NULL)
6084 pg_fatal("schema with OID %u does not exist", nsoid);
6085 return nsinfo;
6086}
6087
6088/*
6089 * getExtensions:
6090 * read all extensions in the system catalogs and return them in the
6091 * ExtensionInfo* structure
6092 *
6093 * numExtensions is set to the number of extensions read in
6094 */
6096getExtensions(Archive *fout, int *numExtensions)
6097{
6098 DumpOptions *dopt = fout->dopt;
6099 PGresult *res;
6100 int ntups;
6101 int i;
6102 PQExpBuffer query;
6103 ExtensionInfo *extinfo = NULL;
6104 int i_tableoid;
6105 int i_oid;
6106 int i_extname;
6107 int i_nspname;
6108 int i_extrelocatable;
6109 int i_extversion;
6110 int i_extconfig;
6111 int i_extcondition;
6112
6113 query = createPQExpBuffer();
6114
6115 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6116 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6117 "FROM pg_extension x "
6118 "JOIN pg_namespace n ON n.oid = x.extnamespace");
6119
6120 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6121
6122 ntups = PQntuples(res);
6123 if (ntups == 0)
6124 goto cleanup;
6125
6126 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6127
6128 i_tableoid = PQfnumber(res, "tableoid");
6129 i_oid = PQfnumber(res, "oid");
6130 i_extname = PQfnumber(res, "extname");
6131 i_nspname = PQfnumber(res, "nspname");
6132 i_extrelocatable = PQfnumber(res, "extrelocatable");
6133 i_extversion = PQfnumber(res, "extversion");
6134 i_extconfig = PQfnumber(res, "extconfig");
6135 i_extcondition = PQfnumber(res, "extcondition");
6136
6137 for (i = 0; i < ntups; i++)
6138 {
6139 extinfo[i].dobj.objType = DO_EXTENSION;
6140 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6141 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6142 AssignDumpId(&extinfo[i].dobj);
6143 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6144 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6145 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6146 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6147 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6148 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6149
6150 /* Decide whether we want to dump it */
6151 selectDumpableExtension(&(extinfo[i]), dopt);
6152 }
6153
6154cleanup:
6155 PQclear(res);
6156 destroyPQExpBuffer(query);
6157
6158 *numExtensions = ntups;
6159
6160 return extinfo;
6161}
6162
6163/*
6164 * getTypes:
6165 * get information about all types in the system catalogs
6166 *
6167 * NB: this must run after getFuncs() because we assume we can do
6168 * findFuncByOid().
6169 */
6170void
6172{
6173 PGresult *res;
6174 int ntups;
6175 int i;
6177 TypeInfo *tyinfo;
6178 ShellTypeInfo *stinfo;
6179 int i_tableoid;
6180 int i_oid;
6181 int i_typname;
6182 int i_typnamespace;
6183 int i_typacl;
6184 int i_acldefault;
6185 int i_typowner;
6186 int i_typelem;
6187 int i_typrelid;
6188 int i_typrelkind;
6189 int i_typtype;
6190 int i_typisdefined;
6191 int i_isarray;
6192 int i_typarray;
6193
6194 /*
6195 * we include even the built-in types because those may be used as array
6196 * elements by user-defined types
6197 *
6198 * we filter out the built-in types when we dump out the types
6199 *
6200 * same approach for undefined (shell) types and array types
6201 *
6202 * Note: as of 8.3 we can reliably detect whether a type is an
6203 * auto-generated array type by checking the element type's typarray.
6204 * (Before that the test is capable of generating false positives.) We
6205 * still check for name beginning with '_', though, so as to avoid the
6206 * cost of the subselect probe for all standard types. This would have to
6207 * be revisited if the backend ever allows renaming of array types.
6208 */
6209 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6210 "typnamespace, typacl, "
6211 "acldefault('T', typowner) AS acldefault, "
6212 "typowner, "
6213 "typelem, typrelid, typarray, "
6214 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6215 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6216 "typtype, typisdefined, "
6217 "typname[0] = '_' AND typelem != 0 AND "
6218 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6219 "FROM pg_type");
6220
6221 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6222
6223 ntups = PQntuples(res);
6224
6225 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6226
6227 i_tableoid = PQfnumber(res, "tableoid");
6228 i_oid = PQfnumber(res, "oid");
6229 i_typname = PQfnumber(res, "typname");
6230 i_typnamespace = PQfnumber(res, "typnamespace");
6231 i_typacl = PQfnumber(res, "typacl");
6232 i_acldefault = PQfnumber(res, "acldefault");
6233 i_typowner = PQfnumber(res, "typowner");
6234 i_typelem = PQfnumber(res, "typelem");
6235 i_typrelid = PQfnumber(res, "typrelid");
6236 i_typrelkind = PQfnumber(res, "typrelkind");
6237 i_typtype = PQfnumber(res, "typtype");
6238 i_typisdefined = PQfnumber(res, "typisdefined");
6239 i_isarray = PQfnumber(res, "isarray");
6240 i_typarray = PQfnumber(res, "typarray");
6241
6242 for (i = 0; i < ntups; i++)
6243 {
6244 tyinfo[i].dobj.objType = DO_TYPE;
6245 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6246 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6247 AssignDumpId(&tyinfo[i].dobj);
6248 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6249 tyinfo[i].dobj.namespace =
6250 findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6251 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6252 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6253 tyinfo[i].dacl.privtype = 0;
6254 tyinfo[i].dacl.initprivs = NULL;
6255 tyinfo[i].ftypname = NULL; /* may get filled later */
6256 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6257 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6258 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6259 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6260 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6261 tyinfo[i].shellType = NULL;
6262
6263 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6264 tyinfo[i].isDefined = true;
6265 else
6266 tyinfo[i].isDefined = false;
6267
6268 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6269 tyinfo[i].isArray = true;
6270 else
6271 tyinfo[i].isArray = false;
6272
6273 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6274
6275 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6276 tyinfo[i].isMultirange = true;
6277 else
6278 tyinfo[i].isMultirange = false;
6279
6280 /* Decide whether we want to dump it */
6281 selectDumpableType(&tyinfo[i], fout);
6282
6283 /* Mark whether type has an ACL */
6284 if (!PQgetisnull(res, i, i_typacl))
6286
6287 /*
6288 * If it's a domain, fetch info about its constraints, if any
6289 */
6290 tyinfo[i].nDomChecks = 0;
6291 tyinfo[i].domChecks = NULL;
6292 tyinfo[i].notnull = NULL;
6293 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6294 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6295 getDomainConstraints(fout, &(tyinfo[i]));
6296
6297 /*
6298 * If it's a base type, make a DumpableObject representing a shell
6299 * definition of the type. We will need to dump that ahead of the I/O
6300 * functions for the type. Similarly, range types need a shell
6301 * definition in case they have a canonicalize function.
6302 *
6303 * Note: the shell type doesn't have a catId. You might think it
6304 * should copy the base type's catId, but then it might capture the
6305 * pg_depend entries for the type, which we don't want.
6306 */
6307 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6308 (tyinfo[i].typtype == TYPTYPE_BASE ||
6309 tyinfo[i].typtype == TYPTYPE_RANGE))
6310 {
6311 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6312 stinfo->dobj.objType = DO_SHELL_TYPE;
6313 stinfo->dobj.catId = nilCatalogId;
6314 AssignDumpId(&stinfo->dobj);
6315 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6316 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6317 stinfo->baseType = &(tyinfo[i]);
6318 tyinfo[i].shellType = stinfo;
6319
6320 /*
6321 * Initially mark the shell type as not to be dumped. We'll only
6322 * dump it if the I/O or canonicalize functions need to be dumped;
6323 * this is taken care of while sorting dependencies.
6324 */
6325 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6326 }
6327 }
6328
6329 PQclear(res);
6330
6331 destroyPQExpBuffer(query);
6332}
6333
6334/*
6335 * getOperators:
6336 * get information about all operators in the system catalogs
6337 */
6338void
6340{
6341 PGresult *res;
6342 int ntups;
6343 int i;
6345 OprInfo *oprinfo;
6346 int i_tableoid;
6347 int i_oid;
6348 int i_oprname;
6349 int i_oprnamespace;
6350 int i_oprowner;
6351 int i_oprkind;
6352 int i_oprleft;
6353 int i_oprright;
6354 int i_oprcode;
6355
6356 /*
6357 * find all operators, including builtin operators; we filter out
6358 * system-defined operators at dump-out time.
6359 */
6360
6361 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6362 "oprnamespace, "
6363 "oprowner, "
6364 "oprkind, "
6365 "oprleft, "
6366 "oprright, "
6367 "oprcode::oid AS oprcode "
6368 "FROM pg_operator");
6369
6370 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6371
6372 ntups = PQntuples(res);
6373
6374 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6375
6376 i_tableoid = PQfnumber(res, "tableoid");
6377 i_oid = PQfnumber(res, "oid");
6378 i_oprname = PQfnumber(res, "oprname");
6379 i_oprnamespace = PQfnumber(res, "oprnamespace");
6380 i_oprowner = PQfnumber(res, "oprowner");
6381 i_oprkind = PQfnumber(res, "oprkind");
6382 i_oprleft = PQfnumber(res, "oprleft");
6383 i_oprright = PQfnumber(res, "oprright");
6384 i_oprcode = PQfnumber(res, "oprcode");
6385
6386 for (i = 0; i < ntups; i++)
6387 {
6388 oprinfo[i].dobj.objType = DO_OPERATOR;
6389 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6390 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6391 AssignDumpId(&oprinfo[i].dobj);
6392 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6393 oprinfo[i].dobj.namespace =
6394 findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6395 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6396 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6397 oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6398 oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6399 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6400
6401 /* Decide whether we want to dump it */
6402 selectDumpableObject(&(oprinfo[i].dobj), fout);
6403 }
6404
6405 PQclear(res);
6406
6407 destroyPQExpBuffer(query);
6408}
6409
6410/*
6411 * getCollations:
6412 * get information about all collations in the system catalogs
6413 */
6414void
6416{
6417 PGresult *res;
6418 int ntups;
6419 int i;
6420 PQExpBuffer query;
6421 CollInfo *collinfo;
6422 int i_tableoid;
6423 int i_oid;
6424 int i_collname;
6425 int i_collnamespace;
6426 int i_collowner;
6427 int i_collencoding;
6428
6429 query = createPQExpBuffer();
6430
6431 /*
6432 * find all collations, including builtin collations; we filter out
6433 * system-defined collations at dump-out time.
6434 */
6435
6436 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6437 "collnamespace, "
6438 "collowner, "
6439 "collencoding "
6440 "FROM pg_collation");
6441
6442 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6443
6444 ntups = PQntuples(res);
6445
6446 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6447
6448 i_tableoid = PQfnumber(res, "tableoid");
6449 i_oid = PQfnumber(res, "oid");
6450 i_collname = PQfnumber(res, "collname");
6451 i_collnamespace = PQfnumber(res, "collnamespace");
6452 i_collowner = PQfnumber(res, "collowner");
6453 i_collencoding = PQfnumber(res, "collencoding");
6454
6455 for (i = 0; i < ntups; i++)
6456 {
6457 collinfo[i].dobj.objType = DO_COLLATION;
6458 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6459 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6460 AssignDumpId(&collinfo[i].dobj);
6461 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6462 collinfo[i].dobj.namespace =
6463 findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6464 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6465 collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6466
6467 /* Decide whether we want to dump it */
6468 selectDumpableObject(&(collinfo[i].dobj), fout);
6469 }
6470
6471 PQclear(res);
6472
6473 destroyPQExpBuffer(query);
6474}
6475
6476/*
6477 * getConversions:
6478 * get information about all conversions in the system catalogs
6479 */
6480void
6482{
6483 PGresult *res;
6484 int ntups;
6485 int i;
6486 PQExpBuffer query;
6487 ConvInfo *convinfo;
6488 int i_tableoid;
6489 int i_oid;
6490 int i_conname;
6491 int i_connamespace;
6492 int i_conowner;
6493
6494 query = createPQExpBuffer();
6495
6496 /*
6497 * find all conversions, including builtin conversions; we filter out
6498 * system-defined conversions at dump-out time.
6499 */
6500
6501 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6502 "connamespace, "
6503 "conowner "
6504 "FROM pg_conversion");
6505
6506 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6507
6508 ntups = PQntuples(res);
6509
6510 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6511
6512 i_tableoid = PQfnumber(res, "tableoid");
6513 i_oid = PQfnumber(res, "oid");
6514 i_conname = PQfnumber(res, "conname");
6515 i_connamespace = PQfnumber(res, "connamespace");
6516 i_conowner = PQfnumber(res, "conowner");
6517
6518 for (i = 0; i < ntups; i++)
6519 {
6520 convinfo[i].dobj.objType = DO_CONVERSION;
6521 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6522 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6523 AssignDumpId(&convinfo[i].dobj);
6524 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6525 convinfo[i].dobj.namespace =
6526 findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6527 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6528
6529 /* Decide whether we want to dump it */
6530 selectDumpableObject(&(convinfo[i].dobj), fout);
6531 }
6532
6533 PQclear(res);
6534
6535 destroyPQExpBuffer(query);
6536}
6537
6538/*
6539 * getAccessMethods:
6540 * get information about all user-defined access methods
6541 */
6542void
6544{
6545 PGresult *res;
6546 int ntups;
6547 int i;
6548 PQExpBuffer query;
6549 AccessMethodInfo *aminfo;
6550 int i_tableoid;
6551 int i_oid;
6552 int i_amname;
6553 int i_amhandler;
6554 int i_amtype;
6555
6556 query = createPQExpBuffer();
6557
6558 /*
6559 * Select all access methods from pg_am table. v9.6 introduced CREATE
6560 * ACCESS METHOD, so earlier versions usually have only built-in access
6561 * methods. v9.6 also changed the access method API, replacing dozens of
6562 * pg_am columns with amhandler. Even if a user created an access method
6563 * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6564 * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6565 * pg_am just to facilitate findAccessMethodByOid() providing the
6566 * OID-to-name mapping.
6567 */
6568 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6569 if (fout->remoteVersion >= 90600)
6571 "amtype, "
6572 "amhandler::pg_catalog.regproc AS amhandler ");
6573 else
6575 "'i'::pg_catalog.\"char\" AS amtype, "
6576 "'-'::pg_catalog.regproc AS amhandler ");
6577 appendPQExpBufferStr(query, "FROM pg_am");
6578
6579 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6580
6581 ntups = PQntuples(res);
6582
6583 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6584
6585 i_tableoid = PQfnumber(res, "tableoid");
6586 i_oid = PQfnumber(res, "oid");
6587 i_amname = PQfnumber(res, "amname");
6588 i_amhandler = PQfnumber(res, "amhandler");
6589 i_amtype = PQfnumber(res, "amtype");
6590
6591 for (i = 0; i < ntups; i++)
6592 {
6593 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6594 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6595 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6596 AssignDumpId(&aminfo[i].dobj);
6597 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6598 aminfo[i].dobj.namespace = NULL;
6599 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6600 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6601
6602 /* Decide whether we want to dump it */
6603 selectDumpableAccessMethod(&(aminfo[i]), fout);
6604 }
6605
6606 PQclear(res);
6607
6608 destroyPQExpBuffer(query);
6609}
6610
6611
6612/*
6613 * getOpclasses:
6614 * get information about all opclasses in the system catalogs
6615 */
6616void
6618{
6619 PGresult *res;
6620 int ntups;
6621 int i;
6623 OpclassInfo *opcinfo;
6624 int i_tableoid;
6625 int i_oid;
6626 int i_opcmethod;
6627 int i_opcname;
6628 int i_opcnamespace;
6629 int i_opcowner;
6630
6631 /*
6632 * find all opclasses, including builtin opclasses; we filter out
6633 * system-defined opclasses at dump-out time.
6634 */
6635
6636 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6637 "opcnamespace, "
6638 "opcowner "
6639 "FROM pg_opclass");
6640
6641 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6642
6643 ntups = PQntuples(res);
6644
6645 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6646
6647 i_tableoid = PQfnumber(res, "tableoid");
6648 i_oid = PQfnumber(res, "oid");
6649 i_opcmethod = PQfnumber(res, "opcmethod");
6650 i_opcname = PQfnumber(res, "opcname");
6651 i_opcnamespace = PQfnumber(res, "opcnamespace");
6652 i_opcowner = PQfnumber(res, "opcowner");
6653
6654 for (i = 0; i < ntups; i++)
6655 {
6656 opcinfo[i].dobj.objType = DO_OPCLASS;
6657 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6658 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6659 AssignDumpId(&opcinfo[i].dobj);
6660 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6661 opcinfo[i].dobj.namespace =
6662 findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6663 opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6664 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6665
6666 /* Decide whether we want to dump it */
6667 selectDumpableObject(&(opcinfo[i].dobj), fout);
6668 }
6669
6670 PQclear(res);
6671
6672 destroyPQExpBuffer(query);
6673}
6674
6675/*
6676 * getOpfamilies:
6677 * get information about all opfamilies in the system catalogs
6678 */
6679void
6681{
6682 PGresult *res;
6683 int ntups;
6684 int i;
6685 PQExpBuffer query;
6686 OpfamilyInfo *opfinfo;
6687 int i_tableoid;
6688 int i_oid;
6689 int i_opfmethod;
6690 int i_opfname;
6691 int i_opfnamespace;
6692 int i_opfowner;
6693
6694 query = createPQExpBuffer();
6695
6696 /*
6697 * find all opfamilies, including builtin opfamilies; we filter out
6698 * system-defined opfamilies at dump-out time.
6699 */
6700
6701 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6702 "opfnamespace, "
6703 "opfowner "
6704 "FROM pg_opfamily");
6705
6706 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6707
6708 ntups = PQntuples(res);
6709
6710 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6711
6712 i_tableoid = PQfnumber(res, "tableoid");
6713 i_oid = PQfnumber(res, "oid");
6714 i_opfname = PQfnumber(res, "opfname");
6715 i_opfmethod = PQfnumber(res, "opfmethod");
6716 i_opfnamespace = PQfnumber(res, "opfnamespace");
6717 i_opfowner = PQfnumber(res, "opfowner");
6718
6719 for (i = 0; i < ntups; i++)
6720 {
6721 opfinfo[i].dobj.objType = DO_OPFAMILY;
6722 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6723 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6724 AssignDumpId(&opfinfo[i].dobj);
6725 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6726 opfinfo[i].dobj.namespace =
6727 findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6728 opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6729 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6730
6731 /* Decide whether we want to dump it */
6732 selectDumpableObject(&(opfinfo[i].dobj), fout);
6733 }
6734
6735 PQclear(res);
6736
6737 destroyPQExpBuffer(query);
6738}
6739
6740/*
6741 * getAggregates:
6742 * get information about all user-defined aggregates in the system catalogs
6743 */
6744void
6746{
6747 DumpOptions *dopt = fout->dopt;
6748 PGresult *res;
6749 int ntups;
6750 int i;
6752 AggInfo *agginfo;
6753 int i_tableoid;
6754 int i_oid;
6755 int i_aggname;
6756 int i_aggnamespace;
6757 int i_pronargs;
6758 int i_proargtypes;
6759 int i_proowner;
6760 int i_aggacl;
6761 int i_acldefault;
6762
6763 /*
6764 * Find all interesting aggregates. See comment in getFuncs() for the
6765 * rationale behind the filtering logic.
6766 */
6767 if (fout->remoteVersion >= 90600)
6768 {
6769 const char *agg_check;
6770
6771 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6772 : "p.proisagg");
6773
6774 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6775 "p.proname AS aggname, "
6776 "p.pronamespace AS aggnamespace, "
6777 "p.pronargs, p.proargtypes, "
6778 "p.proowner, "
6779 "p.proacl AS aggacl, "
6780 "acldefault('f', p.proowner) AS acldefault "
6781 "FROM pg_proc p "
6782 "LEFT JOIN pg_init_privs pip ON "
6783 "(p.oid = pip.objoid "
6784 "AND pip.classoid = 'pg_proc'::regclass "
6785 "AND pip.objsubid = 0) "
6786 "WHERE %s AND ("
6787 "p.pronamespace != "
6788 "(SELECT oid FROM pg_namespace "
6789 "WHERE nspname = 'pg_catalog') OR "
6790 "p.proacl IS DISTINCT FROM pip.initprivs",
6791 agg_check);
6792 if (dopt->binary_upgrade)
6794 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6795 "classid = 'pg_proc'::regclass AND "
6796 "objid = p.oid AND "
6797 "refclassid = 'pg_extension'::regclass AND "
6798 "deptype = 'e')");
6799 appendPQExpBufferChar(query, ')');
6800 }
6801 else
6802 {
6803 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6804 "pronamespace AS aggnamespace, "
6805 "pronargs, proargtypes, "
6806 "proowner, "
6807 "proacl AS aggacl, "
6808 "acldefault('f', proowner) AS acldefault "
6809 "FROM pg_proc p "
6810 "WHERE proisagg AND ("
6811 "pronamespace != "
6812 "(SELECT oid FROM pg_namespace "
6813 "WHERE nspname = 'pg_catalog')");
6814 if (dopt->binary_upgrade)
6816 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6817 "classid = 'pg_proc'::regclass AND "
6818 "objid = p.oid AND "
6819 "refclassid = 'pg_extension'::regclass AND "
6820 "deptype = 'e')");
6821 appendPQExpBufferChar(query, ')');
6822 }
6823
6824 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6825
6826 ntups = PQntuples(res);
6827
6828 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6829
6830 i_tableoid = PQfnumber(res, "tableoid");
6831 i_oid = PQfnumber(res, "oid");
6832 i_aggname = PQfnumber(res, "aggname");
6833 i_aggnamespace = PQfnumber(res, "aggnamespace");
6834 i_pronargs = PQfnumber(res, "pronargs");
6835 i_proargtypes = PQfnumber(res, "proargtypes");
6836 i_proowner = PQfnumber(res, "proowner");
6837 i_aggacl = PQfnumber(res, "aggacl");
6838 i_acldefault = PQfnumber(res, "acldefault");
6839
6840 for (i = 0; i < ntups; i++)
6841 {
6842 agginfo[i].aggfn.dobj.objType = DO_AGG;
6843 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6844 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6845 AssignDumpId(&agginfo[i].aggfn.dobj);
6846 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6847 agginfo[i].aggfn.dobj.namespace =
6848 findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6849 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6850 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6851 agginfo[i].aggfn.dacl.privtype = 0;
6852 agginfo[i].aggfn.dacl.initprivs = NULL;
6853 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6854 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6855 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6856 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6857 if (agginfo[i].aggfn.nargs == 0)
6858 agginfo[i].aggfn.argtypes = NULL;
6859 else
6860 {
6861 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6862 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6863 agginfo[i].aggfn.argtypes,
6864 agginfo[i].aggfn.nargs);
6865 }
6866 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6867
6868 /* Decide whether we want to dump it */
6869 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6870
6871 /* Mark whether aggregate has an ACL */
6872 if (!PQgetisnull(res, i, i_aggacl))
6873 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6874 }
6875
6876 PQclear(res);
6877
6878 destroyPQExpBuffer(query);
6879}
6880
6881/*
6882 * getFuncs:
6883 * get information about all user-defined functions in the system catalogs
6884 */
6885void
6887{
6888 DumpOptions *dopt = fout->dopt;
6889 PGresult *res;
6890 int ntups;
6891 int i;
6893 FuncInfo *finfo;
6894 int i_tableoid;
6895 int i_oid;
6896 int i_proname;
6897 int i_pronamespace;
6898 int i_proowner;
6899 int i_prolang;
6900 int i_pronargs;
6901 int i_proargtypes;
6902 int i_prorettype;
6903 int i_proacl;
6904 int i_acldefault;
6905
6906 /*
6907 * Find all interesting functions. This is a bit complicated:
6908 *
6909 * 1. Always exclude aggregates; those are handled elsewhere.
6910 *
6911 * 2. Always exclude functions that are internally dependent on something
6912 * else, since presumably those will be created as a result of creating
6913 * the something else. This currently acts only to suppress constructor
6914 * functions for range types. Note this is OK only because the
6915 * constructors don't have any dependencies the range type doesn't have;
6916 * otherwise we might not get creation ordering correct.
6917 *
6918 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6919 * they're members of extensions and we are in binary-upgrade mode then
6920 * include them, since we want to dump extension members individually in
6921 * that mode. Also, if they are used by casts or transforms then we need
6922 * to gather the information about them, though they won't be dumped if
6923 * they are built-in. Also, in 9.6 and up, include functions in
6924 * pg_catalog if they have an ACL different from what's shown in
6925 * pg_init_privs (so we have to join to pg_init_privs; annoying).
6926 */
6927 if (fout->remoteVersion >= 90600)
6928 {
6929 const char *not_agg_check;
6930
6931 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6932 : "NOT p.proisagg");
6933
6934 appendPQExpBuffer(query,
6935 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6936 "p.pronargs, p.proargtypes, p.prorettype, "
6937 "p.proacl, "
6938 "acldefault('f', p.proowner) AS acldefault, "
6939 "p.pronamespace, "
6940 "p.proowner "
6941 "FROM pg_proc p "
6942 "LEFT JOIN pg_init_privs pip ON "
6943 "(p.oid = pip.objoid "
6944 "AND pip.classoid = 'pg_proc'::regclass "
6945 "AND pip.objsubid = 0) "
6946 "WHERE %s"
6947 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6948 "WHERE classid = 'pg_proc'::regclass AND "
6949 "objid = p.oid AND deptype = 'i')"
6950 "\n AND ("
6951 "\n pronamespace != "
6952 "(SELECT oid FROM pg_namespace "
6953 "WHERE nspname = 'pg_catalog')"
6954 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6955 "\n WHERE pg_cast.oid > %u "
6956 "\n AND p.oid = pg_cast.castfunc)"
6957 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6958 "\n WHERE pg_transform.oid > %u AND "
6959 "\n (p.oid = pg_transform.trffromsql"
6960 "\n OR p.oid = pg_transform.trftosql))",
6961 not_agg_check,
6964 if (dopt->binary_upgrade)
6966 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6967 "classid = 'pg_proc'::regclass AND "
6968 "objid = p.oid AND "
6969 "refclassid = 'pg_extension'::regclass AND "
6970 "deptype = 'e')");
6972 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6973 appendPQExpBufferChar(query, ')');
6974 }
6975 else
6976 {
6977 appendPQExpBuffer(query,
6978 "SELECT tableoid, oid, proname, prolang, "
6979 "pronargs, proargtypes, prorettype, proacl, "
6980 "acldefault('f', proowner) AS acldefault, "
6981 "pronamespace, "
6982 "proowner "
6983 "FROM pg_proc p "
6984 "WHERE NOT proisagg"
6985 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6986 "WHERE classid = 'pg_proc'::regclass AND "
6987 "objid = p.oid AND deptype = 'i')"
6988 "\n AND ("
6989 "\n pronamespace != "
6990 "(SELECT oid FROM pg_namespace "
6991 "WHERE nspname = 'pg_catalog')"
6992 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6993 "\n WHERE pg_cast.oid > '%u'::oid"
6994 "\n AND p.oid = pg_cast.castfunc)",
6996
6997 if (fout->remoteVersion >= 90500)
6998 appendPQExpBuffer(query,
6999 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7000 "\n WHERE pg_transform.oid > '%u'::oid"
7001 "\n AND (p.oid = pg_transform.trffromsql"
7002 "\n OR p.oid = pg_transform.trftosql))",
7004
7005 if (dopt->binary_upgrade)
7007 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7008 "classid = 'pg_proc'::regclass AND "
7009 "objid = p.oid AND "
7010 "refclassid = 'pg_extension'::regclass AND "
7011 "deptype = 'e')");
7012 appendPQExpBufferChar(query, ')');
7013 }
7014
7015 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7016
7017 ntups = PQntuples(res);
7018
7019 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7020
7021 i_tableoid = PQfnumber(res, "tableoid");
7022 i_oid = PQfnumber(res, "oid");
7023 i_proname = PQfnumber(res, "proname");
7024 i_pronamespace = PQfnumber(res, "pronamespace");
7025 i_proowner = PQfnumber(res, "proowner");
7026 i_prolang = PQfnumber(res, "prolang");
7027 i_pronargs = PQfnumber(res, "pronargs");
7028 i_proargtypes = PQfnumber(res, "proargtypes");
7029 i_prorettype = PQfnumber(res, "prorettype");
7030 i_proacl = PQfnumber(res, "proacl");
7031 i_acldefault = PQfnumber(res, "acldefault");
7032
7033 for (i = 0; i < ntups; i++)
7034 {
7035 finfo[i].dobj.objType = DO_FUNC;
7036 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7037 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7038 AssignDumpId(&finfo[i].dobj);
7039 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7040 finfo[i].dobj.namespace =
7041 findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7042 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7043 finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7044 finfo[i].dacl.privtype = 0;
7045 finfo[i].dacl.initprivs = NULL;
7046 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7047 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7048 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7049 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7050 if (finfo[i].nargs == 0)
7051 finfo[i].argtypes = NULL;
7052 else
7053 {
7054 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7055 parseOidArray(PQgetvalue(res, i, i_proargtypes),
7056 finfo[i].argtypes, finfo[i].nargs);
7057 }
7058 finfo[i].postponed_def = false; /* might get set during sort */
7059
7060 /* Decide whether we want to dump it */
7061 selectDumpableObject(&(finfo[i].dobj), fout);
7062
7063 /* Mark whether function has an ACL */
7064 if (!PQgetisnull(res, i, i_proacl))
7066 }
7067
7068 PQclear(res);
7069
7070 destroyPQExpBuffer(query);
7071}
7072
7073/*
7074 * getRelationStatistics
7075 * register the statistics object as a dependent of the relation.
7076 *
7077 * reltuples is passed as a string to avoid complexities in converting from/to
7078 * floating point.
7079 */
7080static RelStatsInfo *
7082 char *reltuples, int32 relallvisible,
7083 int32 relallfrozen, char relkind,
7084 char **indAttNames, int nindAttNames)
7085{
7086 if (!fout->dopt->dumpStatistics)
7087 return NULL;
7088
7089 if ((relkind == RELKIND_RELATION) ||
7090 (relkind == RELKIND_PARTITIONED_TABLE) ||
7091 (relkind == RELKIND_INDEX) ||
7092 (relkind == RELKIND_PARTITIONED_INDEX) ||
7093 (relkind == RELKIND_MATVIEW ||
7094 relkind == RELKIND_FOREIGN_TABLE))
7095 {
7096 RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7097 DumpableObject *dobj = &info->dobj;
7098
7099 dobj->objType = DO_REL_STATS;
7100 dobj->catId.tableoid = 0;
7101 dobj->catId.oid = 0;
7102 AssignDumpId(dobj);
7103 dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7104 dobj->dependencies[0] = rel->dumpId;
7105 dobj->nDeps = 1;
7106 dobj->allocDeps = 1;
7108 dobj->name = pg_strdup(rel->name);
7109 dobj->namespace = rel->namespace;
7110 info->relpages = relpages;
7111 info->reltuples = pstrdup(reltuples);
7112 info->relallvisible = relallvisible;
7113 info->relallfrozen = relallfrozen;
7114 info->relkind = relkind;
7115 info->indAttNames = indAttNames;
7116 info->nindAttNames = nindAttNames;
7117
7118 /*
7119 * Ordinarily, stats go in SECTION_DATA for tables and
7120 * SECTION_POST_DATA for indexes.
7121 *
7122 * However, the section may be updated later for materialized view
7123 * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7124 * the stats, so the stats must be restored after the data. Also, the
7125 * materialized view definition may be postponed to SECTION_POST_DATA
7126 * (see repairMatViewBoundaryMultiLoop()).
7127 */
7128 switch (info->relkind)
7129 {
7130 case RELKIND_RELATION:
7131 case RELKIND_PARTITIONED_TABLE:
7132 case RELKIND_MATVIEW:
7133 case RELKIND_FOREIGN_TABLE:
7134 info->section = SECTION_DATA;
7135 break;
7136 case RELKIND_INDEX:
7137 case RELKIND_PARTITIONED_INDEX:
7138 info->section = SECTION_POST_DATA;
7139 break;
7140 default:
7141 pg_fatal("cannot dump statistics for relation kind \"%c\"",
7142 info->relkind);
7143 }
7144
7145 return info;
7146 }
7147 return NULL;
7148}
7149
7150/*
7151 * getTables
7152 * read all the tables (no indexes) in the system catalogs,
7153 * and return them as an array of TableInfo structures
7154 *
7155 * *numTables is set to the number of tables read in
7156 */
7157TableInfo *
7158getTables(Archive *fout, int *numTables)
7159{
7160 DumpOptions *dopt = fout->dopt;
7161 PGresult *res;
7162 int ntups;
7163 int i;
7165 TableInfo *tblinfo;
7166 int i_reltableoid;
7167 int i_reloid;
7168 int i_relname;
7169 int i_relnamespace;
7170 int i_relkind;
7171 int i_reltype;
7172 int i_relowner;
7173 int i_relchecks;
7174 int i_relhasindex;
7175 int i_relhasrules;
7176 int i_relpages;
7177 int i_reltuples;
7178 int i_relallvisible;
7179 int i_relallfrozen;
7180 int i_toastpages;
7181 int i_owning_tab;
7182 int i_owning_col;
7183 int i_reltablespace;
7184 int i_relhasoids;
7185 int i_relhastriggers;
7186 int i_relpersistence;
7187 int i_relispopulated;
7188 int i_relreplident;
7189 int i_relrowsec;
7190 int i_relforcerowsec;
7191 int i_relfrozenxid;
7192 int i_toastfrozenxid;
7193 int i_toastoid;
7194 int i_relminmxid;
7195 int i_toastminmxid;
7196 int i_reloptions;
7197 int i_checkoption;
7198 int i_toastreloptions;
7199 int i_reloftype;
7200 int i_foreignserver;
7201 int i_amname;
7202 int i_is_identity_sequence;
7203 int i_relacl;
7204 int i_acldefault;
7205 int i_ispartition;
7206
7207 /*
7208 * Find all the tables and table-like objects.
7209 *
7210 * We must fetch all tables in this phase because otherwise we cannot
7211 * correctly identify inherited columns, owned sequences, etc.
7212 *
7213 * We include system catalogs, so that we can work if a user table is
7214 * defined to inherit from a system catalog (pretty weird, but...)
7215 *
7216 * Note: in this phase we should collect only a minimal amount of
7217 * information about each table, basically just enough to decide if it is
7218 * interesting. In particular, since we do not yet have lock on any user
7219 * table, we MUST NOT invoke any server-side data collection functions
7220 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7221 * wrong answers if any concurrent DDL is happening.
7222 */
7223
7225 "SELECT c.tableoid, c.oid, c.relname, "
7226 "c.relnamespace, c.relkind, c.reltype, "
7227 "c.relowner, "
7228 "c.relchecks, "
7229 "c.relhasindex, c.relhasrules, c.relpages, "
7230 "c.reltuples, c.relallvisible, ");
7231
7232 if (fout->remoteVersion >= 180000)
7233 appendPQExpBufferStr(query, "c.relallfrozen, ");
7234 else
7235 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7236
7238 "c.relhastriggers, c.relpersistence, "
7239 "c.reloftype, "
7240 "c.relacl, "
7241 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7242 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7243 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7244 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7245 "ELSE 0 END AS foreignserver, "
7246 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7247 "tc.oid AS toid, "
7248 "tc.relpages AS toastpages, "
7249 "tc.reloptions AS toast_reloptions, "
7250 "d.refobjid AS owning_tab, "
7251 "d.refobjsubid AS owning_col, "
7252 "tsp.spcname AS reltablespace, ");
7253
7254 if (fout->remoteVersion >= 120000)
7256 "false AS relhasoids, ");
7257 else
7259 "c.relhasoids, ");
7260
7261 if (fout->remoteVersion >= 90300)
7263 "c.relispopulated, ");
7264 else
7266 "'t' as relispopulated, ");
7267
7268 if (fout->remoteVersion >= 90400)
7270 "c.relreplident, ");
7271 else
7273 "'d' AS relreplident, ");
7274
7275 if (fout->remoteVersion >= 90500)
7277 "c.relrowsecurity, c.relforcerowsecurity, ");
7278 else
7280 "false AS relrowsecurity, "
7281 "false AS relforcerowsecurity, ");
7282
7283 if (fout->remoteVersion >= 90300)
7285 "c.relminmxid, tc.relminmxid AS tminmxid, ");
7286 else
7288 "0 AS relminmxid, 0 AS tminmxid, ");
7289
7290 if (fout->remoteVersion >= 90300)
7292 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7293 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7294 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7295 else
7297 "c.reloptions, NULL AS checkoption, ");
7298
7299 if (fout->remoteVersion >= 90600)
7301 "am.amname, ");
7302 else
7304 "NULL AS amname, ");
7305
7306 if (fout->remoteVersion >= 90600)
7308 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7309 else
7311 "false AS is_identity_sequence, ");
7312
7313 if (fout->remoteVersion >= 100000)
7315 "c.relispartition AS ispartition ");
7316 else
7318 "false AS ispartition ");
7319
7320 /*
7321 * Left join to pg_depend to pick up dependency info linking sequences to
7322 * their owning column, if any (note this dependency is AUTO except for
7323 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7324 * collect the spcname.
7325 */
7327 "\nFROM pg_class c\n"
7328 "LEFT JOIN pg_depend d ON "
7329 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7330 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7331 "d.objsubid = 0 AND "
7332 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7333 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7334
7335 /*
7336 * In 9.6 and up, left join to pg_am to pick up the amname.
7337 */
7338 if (fout->remoteVersion >= 90600)
7340 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7341
7342 /*
7343 * We purposefully ignore toast OIDs for partitioned tables; the reason is
7344 * that versions 10 and 11 have them, but later versions do not, so
7345 * emitting them causes the upgrade to fail.
7346 */
7348 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7349 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7350 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7351
7352 /*
7353 * Restrict to interesting relkinds (in particular, not indexes). Not all
7354 * relkinds are possible in older servers, but it's not worth the trouble
7355 * to emit a version-dependent list.
7356 *
7357 * Composite-type table entries won't be dumped as such, but we have to
7358 * make a DumpableObject for them so that we can track dependencies of the
7359 * composite type (pg_depend entries for columns of the composite type
7360 * link to the pg_class entry not the pg_type entry).
7361 */
7363 "WHERE c.relkind IN ("
7364 CppAsString2(RELKIND_RELATION) ", "
7365 CppAsString2(RELKIND_SEQUENCE) ", "
7366 CppAsString2(RELKIND_VIEW) ", "
7367 CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7368 CppAsString2(RELKIND_MATVIEW) ", "
7369 CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7370 CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7371 "ORDER BY c.oid");
7372
7373 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7374
7375 ntups = PQntuples(res);
7376
7377 *numTables = ntups;
7378
7379 /*
7380 * Extract data from result and lock dumpable tables. We do the locking
7381 * before anything else, to minimize the window wherein a table could
7382 * disappear under us.
7383 *
7384 * Note that we have to save info about all tables here, even when dumping
7385 * only one, because we don't yet know which tables might be inheritance
7386 * ancestors of the target table.
7387 */
7388 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7389
7390 i_reltableoid = PQfnumber(res, "tableoid");
7391 i_reloid = PQfnumber(res, "oid");
7392 i_relname = PQfnumber(res, "relname");
7393 i_relnamespace = PQfnumber(res, "relnamespace");
7394 i_relkind = PQfnumber(res, "relkind");
7395 i_reltype = PQfnumber(res, "reltype");
7396 i_relowner = PQfnumber(res, "relowner");
7397 i_relchecks = PQfnumber(res, "relchecks");
7398 i_relhasindex = PQfnumber(res, "relhasindex");
7399 i_relhasrules = PQfnumber(res, "relhasrules");
7400 i_relpages = PQfnumber(res, "relpages");
7401 i_reltuples = PQfnumber(res, "reltuples");
7402 i_relallvisible = PQfnumber(res, "relallvisible");
7403 i_relallfrozen = PQfnumber(res, "relallfrozen");
7404 i_toastpages = PQfnumber(res, "toastpages");
7405 i_owning_tab = PQfnumber(res, "owning_tab");
7406 i_owning_col = PQfnumber(res, "owning_col");
7407 i_reltablespace = PQfnumber(res, "reltablespace");
7408 i_relhasoids = PQfnumber(res, "relhasoids");
7409 i_relhastriggers = PQfnumber(res, "relhastriggers");
7410 i_relpersistence = PQfnumber(res, "relpersistence");
7411 i_relispopulated = PQfnumber(res, "relispopulated");
7412 i_relreplident = PQfnumber(res, "relreplident");
7413 i_relrowsec = PQfnumber(res, "relrowsecurity");
7414 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7415 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7416 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7417 i_toastoid = PQfnumber(res, "toid");
7418 i_relminmxid = PQfnumber(res, "relminmxid");
7419 i_toastminmxid = PQfnumber(res, "tminmxid");
7420 i_reloptions = PQfnumber(res, "reloptions");
7421 i_checkoption = PQfnumber(res, "checkoption");
7422 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7423 i_reloftype = PQfnumber(res, "reloftype");
7424 i_foreignserver = PQfnumber(res, "foreignserver");
7425 i_amname = PQfnumber(res, "amname");
7426 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7427 i_relacl = PQfnumber(res, "relacl");
7428 i_acldefault = PQfnumber(res, "acldefault");
7429 i_ispartition = PQfnumber(res, "ispartition");
7430
7431 if (dopt->lockWaitTimeout)
7432 {
7433 /*
7434 * Arrange to fail instead of waiting forever for a table lock.
7435 *
7436 * NB: this coding assumes that the only queries issued within the
7437 * following loop are LOCK TABLEs; else the timeout may be undesirably
7438 * applied to other things too.
7439 */
7440 resetPQExpBuffer(query);
7441 appendPQExpBufferStr(query, "SET statement_timeout = ");
7443 ExecuteSqlStatement(fout, query->data);
7444 }
7445
7446 resetPQExpBuffer(query);
7447
7448 for (i = 0; i < ntups; i++)
7449 {
7450 int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7451 int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7452
7453 tblinfo[i].dobj.objType = DO_TABLE;
7454 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7455 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7456 AssignDumpId(&tblinfo[i].dobj);
7457 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7458 tblinfo[i].dobj.namespace =
7459 findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7460 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7461 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7462 tblinfo[i].dacl.privtype = 0;
7463 tblinfo[i].dacl.initprivs = NULL;
7464 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7465 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7466 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7467 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7468 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7469 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7470 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7471 if (PQgetisnull(res, i, i_toastpages))
7472 tblinfo[i].toastpages = 0;
7473 else
7474 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7475 if (PQgetisnull(res, i, i_owning_tab))
7476 {
7477 tblinfo[i].owning_tab = InvalidOid;
7478 tblinfo[i].owning_col = 0;
7479 }
7480 else
7481 {
7482 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7483 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7484 }
7485 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7486 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7487 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7488 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7489 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7490 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7491 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7492 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7493 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7494 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7495 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7496 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7497 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7498 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7499 if (PQgetisnull(res, i, i_checkoption))
7500 tblinfo[i].checkoption = NULL;
7501 else
7502 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7503 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7504 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7505 tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7506 if (PQgetisnull(res, i, i_amname))
7507 tblinfo[i].amname = NULL;
7508 else
7509 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7510 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7511 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7512
7513 /* other fields were zeroed above */
7514
7515 /*
7516 * Decide whether we want to dump this table.
7517 */
7518 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7519 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7520 else
7521 selectDumpableTable(&tblinfo[i], fout);
7522
7523 /*
7524 * Now, consider the table "interesting" if we need to dump its
7525 * definition, data or its statistics. Later on, we'll skip a lot of
7526 * data collection for uninteresting tables.
7527 *
7528 * Note: the "interesting" flag will also be set by flagInhTables for
7529 * parents of interesting tables, so that we collect necessary
7530 * inheritance info even when the parents are not themselves being
7531 * dumped. This is the main reason why we need an "interesting" flag
7532 * that's separate from the components-to-dump bitmask.
7533 */
7534 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7538
7539 tblinfo[i].dummy_view = false; /* might get set during sort */
7540 tblinfo[i].postponed_def = false; /* might get set during sort */
7541
7542 /* Tables have data */
7544
7545 /* Mark whether table has an ACL */
7546 if (!PQgetisnull(res, i, i_relacl))
7547 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7548 tblinfo[i].hascolumnACLs = false; /* may get set later */
7549
7550 /* Add statistics */
7551 if (tblinfo[i].interesting)
7552 {
7553 RelStatsInfo *stats;
7554
7555 stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7556 tblinfo[i].relpages,
7557 PQgetvalue(res, i, i_reltuples),
7558 relallvisible, relallfrozen,
7559 tblinfo[i].relkind, NULL, 0);
7560 if (tblinfo[i].relkind == RELKIND_MATVIEW)
7561 tblinfo[i].stats = stats;
7562 }
7563
7564 /*
7565 * Read-lock target tables to make sure they aren't DROPPED or altered
7566 * in schema before we get around to dumping them.
7567 *
7568 * Note that we don't explicitly lock parents of the target tables; we
7569 * assume our lock on the child is enough to prevent schema
7570 * alterations to parent tables.
7571 *
7572 * NOTE: it'd be kinda nice to lock other relations too, not only
7573 * plain or partitioned tables, but the backend doesn't presently
7574 * allow that.
7575 *
7576 * We only need to lock the table for certain components; see
7577 * pg_dump.h
7578 */
7579 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7580 (tblinfo[i].relkind == RELKIND_RELATION ||
7581 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7582 {
7583 /*
7584 * Tables are locked in batches. When dumping from a remote
7585 * server this can save a significant amount of time by reducing
7586 * the number of round trips.
7587 */
7588 if (query->len == 0)
7589 appendPQExpBuffer(query, "LOCK TABLE %s",
7590 fmtQualifiedDumpable(&tblinfo[i]));
7591 else
7592 {
7593 appendPQExpBuffer(query, ", %s",
7594 fmtQualifiedDumpable(&tblinfo[i]));
7595
7596 /* Arbitrarily end a batch when query length reaches 100K. */
7597 if (query->len >= 100000)
7598 {
7599 /* Lock another batch of tables. */
7600 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7601 ExecuteSqlStatement(fout, query->data);
7602 resetPQExpBuffer(query);
7603 }
7604 }
7605 }
7606 }
7607
7608 if (query->len != 0)
7609 {
7610 /* Lock the tables in the last batch. */
7611 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7612 ExecuteSqlStatement(fout, query->data);
7613 }
7614
7615 if (dopt->lockWaitTimeout)
7616 {
7617 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7618 }
7619
7620 PQclear(res);
7621
7622 destroyPQExpBuffer(query);
7623
7624 return tblinfo;
7625}
7626
7627/*
7628 * getOwnedSeqs
7629 * identify owned sequences and mark them as dumpable if owning table is
7630 *
7631 * We used to do this in getTables(), but it's better to do it after the
7632 * index used by findTableByOid() has been set up.
7633 */
7634void
7635getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7636{
7637 int i;
7638
7639 /*
7640 * Force sequences that are "owned" by table columns to be dumped whenever
7641 * their owning table is being dumped.
7642 */
7643 for (i = 0; i < numTables; i++)
7644 {
7645 TableInfo *seqinfo = &tblinfo[i];
7646 TableInfo *owning_tab;
7647
7648 if (!OidIsValid(seqinfo->owning_tab))
7649 continue; /* not an owned sequence */
7650
7651 owning_tab = findTableByOid(seqinfo->owning_tab);
7652 if (owning_tab == NULL)
7653 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7654 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7655
7656 /*
7657 * For an identity sequence, dump exactly the same components for the
7658 * sequence as for the owning table. This is important because we
7659 * treat the identity sequence as an integral part of the table. For
7660 * example, there is not any DDL command that allows creation of such
7661 * a sequence independently of the table.
7662 *
7663 * For other owned sequences such as serial sequences, we need to dump
7664 * the components that are being dumped for the table and any
7665 * components that the sequence is explicitly marked with.
7666 *
7667 * We can't simply use the set of components which are being dumped
7668 * for the table as the table might be in an extension (and only the
7669 * non-extension components, eg: ACLs if changed, security labels, and
7670 * policies, are being dumped) while the sequence is not (and
7671 * therefore the definition and other components should also be
7672 * dumped).
7673 *
7674 * If the sequence is part of the extension then it should be properly
7675 * marked by checkExtensionMembership() and this will be a no-op as
7676 * the table will be equivalently marked.
7677 */
7678 if (seqinfo->is_identity_sequence)
7679 seqinfo->dobj.dump = owning_tab->dobj.dump;
7680 else
7681 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7682
7683 /* Make sure that necessary data is available if we're dumping it */
7684 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7685 {
7686 seqinfo->interesting = true;
7687 owning_tab->interesting = true;
7688 }
7689 }
7690}
7691
7692/*
7693 * getInherits
7694 * read all the inheritance information
7695 * from the system catalogs return them in the InhInfo* structure
7696 *
7697 * numInherits is set to the number of pairs read in
7698 */
7699InhInfo *
7700getInherits(Archive *fout, int *numInherits)
7701{
7702 PGresult *res;
7703 int ntups;
7704 int i;
7706 InhInfo *inhinfo;
7707
7708 int i_inhrelid;
7709 int i_inhparent;
7710
7711 /* find all the inheritance information */
7712 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7713
7714 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7715
7716 ntups = PQntuples(res);
7717
7718 *numInherits = ntups;
7719
7720 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7721
7722 i_inhrelid = PQfnumber(res, "inhrelid");
7723 i_inhparent = PQfnumber(res, "inhparent");
7724
7725 for (i = 0; i < ntups; i++)
7726 {
7727 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7728 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7729 }
7730
7731 PQclear(res);
7732
7733 destroyPQExpBuffer(query);
7734
7735 return inhinfo;
7736}
7737
7738/*
7739 * getPartitioningInfo
7740 * get information about partitioning
7741 *
7742 * For the most part, we only collect partitioning info about tables we
7743 * intend to dump. However, this function has to consider all partitioned
7744 * tables in the database, because we need to know about parents of partitions
7745 * we are going to dump even if the parents themselves won't be dumped.
7746 *
7747 * Specifically, what we need to know is whether each partitioned table
7748 * has an "unsafe" partitioning scheme that requires us to force
7749 * load-via-partition-root mode for its children. Currently the only case
7750 * for which we force that is hash partitioning on enum columns, since the
7751 * hash codes depend on enum value OIDs which won't be replicated across
7752 * dump-and-reload. There are other cases in which load-via-partition-root
7753 * might be necessary, but we expect users to cope with them.
7754 */
7755void
7757{
7758 PQExpBuffer query;
7759 PGresult *res;
7760 int ntups;
7761
7762 /* hash partitioning didn't exist before v11 */
7763 if (fout->remoteVersion < 110000)
7764 return;
7765 /* needn't bother if not dumping data */
7766 if (!fout->dopt->dumpData)
7767 return;
7768
7769 query = createPQExpBuffer();
7770
7771 /*
7772 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7773 * appears among the partition opclasses. We needn't check partstrat.
7774 *
7775 * Note that this query may well retrieve info about tables we aren't
7776 * going to dump and hence have no lock on. That's okay since we need not
7777 * invoke any unsafe server-side functions.
7778 */
7780 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7781 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7782 "ON c.opcmethod = a.oid\n"
7783 "WHERE opcname = 'enum_ops' "
7784 "AND opcnamespace = 'pg_catalog'::regnamespace "
7785 "AND amname = 'hash') = ANY(partclass)");
7786
7787 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7788
7789 ntups = PQntuples(res);
7790
7791 for (int i = 0; i < ntups; i++)
7792 {
7793 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7794 TableInfo *tbinfo;
7795
7796 tbinfo = findTableByOid(tabrelid);
7797 if (tbinfo == NULL)
7798 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7799 tabrelid);
7800 tbinfo->unsafe_partitions = true;
7801 }
7802
7803 PQclear(res);
7804
7805 destroyPQExpBuffer(query);
7806}
7807
7808/*
7809 * getIndexes
7810 * get information about every index on a dumpable table
7811 *
7812 * Note: index data is not returned directly to the caller, but it
7813 * does get entered into the DumpableObject tables.
7814 */
7815void
7816getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7817{
7819 PQExpBuffer tbloids = createPQExpBuffer();
7820 PGresult *res;
7821 int ntups;
7822 int curtblindx;
7823 IndxInfo *indxinfo;
7824 int i_tableoid,
7825 i_oid,
7826 i_indrelid,
7827 i_indexname,
7828 i_relpages,
7829 i_reltuples,
7830 i_relallvisible,
7831 i_relallfrozen,
7832 i_parentidx,
7833 i_indexdef,
7834 i_indnkeyatts,
7835 i_indnatts,
7836 i_indkey,
7837 i_indisclustered,
7838 i_indisreplident,
7839 i_indnullsnotdistinct,
7840 i_contype,
7841 i_conname,
7842 i_condeferrable,
7843 i_condeferred,
7844 i_conperiod,
7845 i_contableoid,
7846 i_conoid,
7847 i_condef,
7848 i_indattnames,
7849 i_tablespace,
7850 i_indreloptions,
7851 i_indstatcols,
7852 i_indstatvals;
7853
7854 /*
7855 * We want to perform just one query against pg_index. However, we
7856 * mustn't try to select every row of the catalog and then sort it out on
7857 * the client side, because some of the server-side functions we need
7858 * would be unsafe to apply to tables we don't have lock on. Hence, we
7859 * build an array of the OIDs of tables we care about (and now have lock
7860 * on!), and use a WHERE clause to constrain which rows are selected.
7861 */
7862 appendPQExpBufferChar(tbloids, '{');
7863 for (int i = 0; i < numTables; i++)
7864 {
7865 TableInfo *tbinfo = &tblinfo[i];
7866
7867 if (!tbinfo->hasindex)
7868 continue;
7869
7870 /*
7871 * We can ignore indexes of uninteresting tables.
7872 */
7873 if (!tbinfo->interesting)
7874 continue;
7875
7876 /* OK, we need info for this table */
7877 if (tbloids->len > 1) /* do we have more than the '{'? */
7878 appendPQExpBufferChar(tbloids, ',');
7879 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7880 }
7881 appendPQExpBufferChar(tbloids, '}');
7882
7884 "SELECT t.tableoid, t.oid, i.indrelid, "
7885 "t.relname AS indexname, "
7886 "t.relpages, t.reltuples, t.relallvisible, ");
7887
7888 if (fout->remoteVersion >= 180000)
7889 appendPQExpBufferStr(query, "t.relallfrozen, ");
7890 else
7891 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7892
7894 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7895 "i.indkey, i.indisclustered, "
7896 "c.contype, c.conname, "
7897 "c.condeferrable, c.condeferred, "
7898 "c.tableoid AS contableoid, "
7899 "c.oid AS conoid, "
7900 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7901 "CASE WHEN i.indexprs IS NOT NULL THEN "
7902 "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7903 " FROM pg_catalog.pg_attribute "
7904 " WHERE attrelid = i.indexrelid) "
7905 "ELSE NULL END AS indattnames, "
7906 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7907 "t.reloptions AS indreloptions, ");
7908
7909
7910 if (fout->remoteVersion >= 90400)
7912 "i.indisreplident, ");
7913 else
7915 "false AS indisreplident, ");
7916
7917 if (fout->remoteVersion >= 110000)
7919 "inh.inhparent AS parentidx, "
7920 "i.indnkeyatts AS indnkeyatts, "
7921 "i.indnatts AS indnatts, "
7922 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7923 " FROM pg_catalog.pg_attribute "
7924 " WHERE attrelid = i.indexrelid AND "
7925 " attstattarget >= 0) AS indstatcols, "
7926 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7927 " FROM pg_catalog.pg_attribute "
7928 " WHERE attrelid = i.indexrelid AND "
7929 " attstattarget >= 0) AS indstatvals, ");
7930 else
7932 "0 AS parentidx, "
7933 "i.indnatts AS indnkeyatts, "
7934 "i.indnatts AS indnatts, "
7935 "'' AS indstatcols, "
7936 "'' AS indstatvals, ");
7937
7938 if (fout->remoteVersion >= 150000)
7940 "i.indnullsnotdistinct, ");
7941 else
7943 "false AS indnullsnotdistinct, ");
7944
7945 if (fout->remoteVersion >= 180000)
7947 "c.conperiod ");
7948 else
7950 "NULL AS conperiod ");
7951
7952 /*
7953 * The point of the messy-looking outer join is to find a constraint that
7954 * is related by an internal dependency link to the index. If we find one,
7955 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7956 * index won't have more than one internal dependency.
7957 *
7958 * Note: the check on conrelid is redundant, but useful because that
7959 * column is indexed while conindid is not.
7960 */
7961 if (fout->remoteVersion >= 110000)
7962 {
7963 appendPQExpBuffer(query,
7964 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7965 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7966 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7967 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7968 "LEFT JOIN pg_catalog.pg_constraint c "
7969 "ON (i.indrelid = c.conrelid AND "
7970 "i.indexrelid = c.conindid AND "
7971 "c.contype IN ('p','u','x')) "
7972 "LEFT JOIN pg_catalog.pg_inherits inh "
7973 "ON (inh.inhrelid = indexrelid) "
7974 "WHERE (i.indisvalid OR t2.relkind = 'p') "
7975 "AND i.indisready "
7976 "ORDER BY i.indrelid, indexname",
7977 tbloids->data);
7978 }
7979 else
7980 {
7981 /*
7982 * the test on indisready is necessary in 9.2, and harmless in
7983 * earlier/later versions
7984 */
7985 appendPQExpBuffer(query,
7986 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7987 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7988 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7989 "LEFT JOIN pg_catalog.pg_constraint c "
7990 "ON (i.indrelid = c.conrelid AND "
7991 "i.indexrelid = c.conindid AND "
7992 "c.contype IN ('p','u','x')) "
7993 "WHERE i.indisvalid AND i.indisready "
7994 "ORDER BY i.indrelid, indexname",
7995 tbloids->data);
7996 }
7997
7998 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7999
8000 ntups = PQntuples(res);
8001
8002 i_tableoid = PQfnumber(res, "tableoid");
8003 i_oid = PQfnumber(res, "oid");
8004 i_indrelid = PQfnumber(res, "indrelid");
8005 i_indexname = PQfnumber(res, "indexname");
8006 i_relpages = PQfnumber(res, "relpages");
8007 i_reltuples = PQfnumber(res, "reltuples");
8008 i_relallvisible = PQfnumber(res, "relallvisible");
8009 i_relallfrozen = PQfnumber(res, "relallfrozen");
8010 i_parentidx = PQfnumber(res, "parentidx");
8011 i_indexdef = PQfnumber(res, "indexdef");
8012 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8013 i_indnatts = PQfnumber(res, "indnatts");
8014 i_indkey = PQfnumber(res, "indkey");
8015 i_indisclustered = PQfnumber(res, "indisclustered");
8016 i_indisreplident = PQfnumber(res, "indisreplident");
8017 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8018 i_contype = PQfnumber(res, "contype");
8019 i_conname = PQfnumber(res, "conname");
8020 i_condeferrable = PQfnumber(res, "condeferrable");
8021 i_condeferred = PQfnumber(res, "condeferred");
8022 i_conperiod = PQfnumber(res, "conperiod");
8023 i_contableoid = PQfnumber(res, "contableoid");
8024 i_conoid = PQfnumber(res, "conoid");
8025 i_condef = PQfnumber(res, "condef");
8026 i_indattnames = PQfnumber(res, "indattnames");
8027 i_tablespace = PQfnumber(res, "tablespace");
8028 i_indreloptions = PQfnumber(res, "indreloptions");
8029 i_indstatcols = PQfnumber(res, "indstatcols");
8030 i_indstatvals = PQfnumber(res, "indstatvals");
8031
8032 indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8033
8034 /*
8035 * Outer loop iterates once per table, not once per row. Incrementing of
8036 * j is handled by the inner loop.
8037 */
8038 curtblindx = -1;
8039 for (int j = 0; j < ntups;)
8040 {
8041 Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8042 TableInfo *tbinfo = NULL;
8043 char **indAttNames = NULL;
8044 int nindAttNames = 0;
8045 int numinds;
8046
8047 /* Count rows for this table */
8048 for (numinds = 1; numinds < ntups - j; numinds++)
8049 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8050 break;
8051
8052 /*
8053 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8054 * order.
8055 */
8056 while (++curtblindx < numTables)
8057 {
8058 tbinfo = &tblinfo[curtblindx];
8059 if (tbinfo->dobj.catId.oid == indrelid)
8060 break;
8061 }
8062 if (curtblindx >= numTables)
8063 pg_fatal("unrecognized table OID %u", indrelid);
8064 /* cross-check that we only got requested tables */
8065 if (!tbinfo->hasindex ||
8066 !tbinfo->interesting)
8067 pg_fatal("unexpected index data for table \"%s\"",
8068 tbinfo->dobj.name);
8069
8070 /* Save data for this table */
8071 tbinfo->indexes = indxinfo + j;
8072 tbinfo->numIndexes = numinds;
8073
8074 for (int c = 0; c < numinds; c++, j++)
8075 {
8076 char contype;
8077 char indexkind;
8078 RelStatsInfo *relstats;
8079 int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8080 int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8081 int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8082
8083 indxinfo[j].dobj.objType = DO_INDEX;
8084 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8085 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8086 AssignDumpId(&indxinfo[j].dobj);
8087 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8088 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8089 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8090 indxinfo[j].indextable = tbinfo;
8091 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8092 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8093 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8094 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8095 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8096 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8097 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8098 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8099 parseOidArray(PQgetvalue(res, j, i_indkey),
8100 indxinfo[j].indkeys, indxinfo[j].indnattrs);
8101 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8102 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8103 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8104 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8105 indxinfo[j].partattaches = (SimplePtrList)
8106 {
8107 NULL, NULL
8108 };
8109
8110 if (indxinfo[j].parentidx == 0)
8111 indexkind = RELKIND_INDEX;
8112 else
8113 indexkind = RELKIND_PARTITIONED_INDEX;
8114
8115 if (!PQgetisnull(res, j, i_indattnames))
8116 {
8117 if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8118 &indAttNames, &nindAttNames))
8119 pg_fatal("could not parse %s array", "indattnames");
8120 }
8121
8122 relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8123 PQgetvalue(res, j, i_reltuples),
8124 relallvisible, relallfrozen, indexkind,
8125 indAttNames, nindAttNames);
8126
8127 contype = *(PQgetvalue(res, j, i_contype));
8128 if (contype == 'p' || contype == 'u' || contype == 'x')
8129 {
8130 /*
8131 * If we found a constraint matching the index, create an
8132 * entry for it.
8133 */
8134 ConstraintInfo *constrinfo;
8135
8136 constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8137 constrinfo->dobj.objType = DO_CONSTRAINT;
8138 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8139 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8140 AssignDumpId(&constrinfo->dobj);
8141 constrinfo->dobj.dump = tbinfo->dobj.dump;
8142 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8143 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8144 constrinfo->contable = tbinfo;
8145 constrinfo->condomain = NULL;
8146 constrinfo->contype = contype;
8147 if (contype == 'x')
8148 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8149 else
8150 constrinfo->condef = NULL;
8151 constrinfo->confrelid = InvalidOid;
8152 constrinfo->conindex = indxinfo[j].dobj.dumpId;
8153 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8154 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8155 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8156 constrinfo->conislocal = true;
8157 constrinfo->separate = true;
8158
8159 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8160 if (relstats != NULL)
8161 addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8162 }
8163 else
8164 {
8165 /* Plain secondary index */
8166 indxinfo[j].indexconstraint = 0;
8167 }
8168 }
8169 }
8170
8171 PQclear(res);
8172
8173 destroyPQExpBuffer(query);
8174 destroyPQExpBuffer(tbloids);
8175}
8176
8177/*
8178 * getExtendedStatistics
8179 * get information about extended-statistics objects.
8180 *
8181 * Note: extended statistics data is not returned directly to the caller, but
8182 * it does get entered into the DumpableObject tables.
8183 */
8184void
8186{
8187 PQExpBuffer query;
8188 PGresult *res;
8189 StatsExtInfo *statsextinfo;
8190 int ntups;
8191 int i_tableoid;
8192 int i_oid;
8193 int i_stxname;
8194 int i_stxnamespace;
8195 int i_stxowner;
8196 int i_stxrelid;
8197 int i_stattarget;
8198 int i;
8199
8200 /* Extended statistics were new in v10 */
8201 if (fout->remoteVersion < 100000)
8202 return;
8203
8204 query = createPQExpBuffer();
8205
8206 if (fout->remoteVersion < 130000)
8207 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8208 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8209 "FROM pg_catalog.pg_statistic_ext");
8210 else
8211 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8212 "stxnamespace, stxowner, stxrelid, stxstattarget "
8213 "FROM pg_catalog.pg_statistic_ext");
8214
8215 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8216
8217 ntups = PQntuples(res);
8218
8219 i_tableoid = PQfnumber(res, "tableoid");
8220 i_oid = PQfnumber(res, "oid");
8221 i_stxname = PQfnumber(res, "stxname");
8222 i_stxnamespace = PQfnumber(res, "stxnamespace");
8223 i_stxowner = PQfnumber(res, "stxowner");
8224 i_stxrelid = PQfnumber(res, "stxrelid");
8225 i_stattarget = PQfnumber(res, "stxstattarget");
8226
8227 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8228
8229 for (i = 0; i < ntups; i++)
8230 {
8231 statsextinfo[i].dobj.objType = DO_STATSEXT;
8232 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8233 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8234 AssignDumpId(&statsextinfo[i].dobj);
8235 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8236 statsextinfo[i].dobj.namespace =
8237 findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8238 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8239 statsextinfo[i].stattable =
8240 findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8241 if (PQgetisnull(res, i, i_stattarget))
8242 statsextinfo[i].stattarget = -1;
8243 else
8244 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8245
8246 /* Decide whether we want to dump it */
8247 selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8248 }
8249
8250 PQclear(res);
8251 destroyPQExpBuffer(query);
8252}
8253
8254/*
8255 * getConstraints
8256 *
8257 * Get info about constraints on dumpable tables.
8258 *
8259 * Currently handles foreign keys only.
8260 * Unique and primary key constraints are handled with indexes,
8261 * while check constraints are processed in getTableAttrs().
8262 */
8263void
8264getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8265{
8267 PQExpBuffer tbloids = createPQExpBuffer();
8268 PGresult *res;
8269 int ntups;
8270 int curtblindx;
8271 TableInfo *tbinfo = NULL;
8272 ConstraintInfo *constrinfo;
8273 int i_contableoid,
8274 i_conoid,
8275 i_conrelid,
8276 i_conname,
8277 i_confrelid,
8278 i_conindid,
8279 i_condef;
8280
8281 /*
8282 * We want to perform just one query against pg_constraint. However, we
8283 * mustn't try to select every row of the catalog and then sort it out on
8284 * the client side, because some of the server-side functions we need
8285 * would be unsafe to apply to tables we don't have lock on. Hence, we
8286 * build an array of the OIDs of tables we care about (and now have lock
8287 * on!), and use a WHERE clause to constrain which rows are selected.
8288 */
8289 appendPQExpBufferChar(tbloids, '{');
8290 for (int i = 0; i < numTables; i++)
8291 {
8292 TableInfo *tinfo = &tblinfo[i];
8293
8294 if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8295 continue;
8296
8297 /* OK, we need info for this table */
8298 if (tbloids->len > 1) /* do we have more than the '{'? */
8299 appendPQExpBufferChar(tbloids, ',');
8300 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8301 }
8302 appendPQExpBufferChar(tbloids, '}');
8303
8305 "SELECT c.tableoid, c.oid, "
8306 "conrelid, conname, confrelid, ");
8307 if (fout->remoteVersion >= 110000)
8308 appendPQExpBufferStr(query, "conindid, ");
8309 else
8310 appendPQExpBufferStr(query, "0 AS conindid, ");
8311 appendPQExpBuffer(query,
8312 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8313 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8314 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8315 "WHERE contype = 'f' ",
8316 tbloids->data);
8317 if (fout->remoteVersion >= 110000)
8319 "AND conparentid = 0 ");
8321 "ORDER BY conrelid, conname");
8322
8323 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8324
8325 ntups = PQntuples(res);
8326
8327 i_contableoid = PQfnumber(res, "tableoid");
8328 i_conoid = PQfnumber(res, "oid");
8329 i_conrelid = PQfnumber(res, "conrelid");
8330 i_conname = PQfnumber(res, "conname");
8331 i_confrelid = PQfnumber(res, "confrelid");
8332 i_conindid = PQfnumber(res, "conindid");
8333 i_condef = PQfnumber(res, "condef");
8334
8335 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8336
8337 curtblindx = -1;
8338 for (int j = 0; j < ntups; j++)
8339 {
8340 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8341 TableInfo *reftable;
8342
8343 /*
8344 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8345 * order.
8346 */
8347 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8348 {
8349 while (++curtblindx < numTables)
8350 {
8351 tbinfo = &tblinfo[curtblindx];
8352 if (tbinfo->dobj.catId.oid == conrelid)
8353 break;
8354 }
8355 if (curtblindx >= numTables)
8356 pg_fatal("unrecognized table OID %u", conrelid);
8357 }
8358
8359 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8360 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8361 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8362 AssignDumpId(&constrinfo[j].dobj);
8363 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8364 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8365 constrinfo[j].contable = tbinfo;
8366 constrinfo[j].condomain = NULL;
8367 constrinfo[j].contype = 'f';
8368 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8369 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8370 constrinfo[j].conindex = 0;
8371 constrinfo[j].condeferrable = false;
8372 constrinfo[j].condeferred = false;
8373 constrinfo[j].conislocal = true;
8374 constrinfo[j].separate = true;
8375
8376 /*
8377 * Restoring an FK that points to a partitioned table requires that
8378 * all partition indexes have been attached beforehand. Ensure that
8379 * happens by making the constraint depend on each index partition
8380 * attach object.
8381 */
8382 reftable = findTableByOid(constrinfo[j].confrelid);
8383 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8384 {
8385 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8386
8387 if (indexOid != InvalidOid)
8388 {
8389 for (int k = 0; k < reftable->numIndexes; k++)
8390 {
8391 IndxInfo *refidx;
8392
8393 /* not our index? */
8394 if (reftable->indexes[k].dobj.catId.oid != indexOid)
8395 continue;
8396
8397 refidx = &reftable->indexes[k];
8398 addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8399 break;
8400 }
8401 }
8402 }
8403 }
8404
8405 PQclear(res);
8406
8407 destroyPQExpBuffer(query);
8408 destroyPQExpBuffer(tbloids);
8409}
8410
8411/*
8412 * addConstrChildIdxDeps
8413 *
8414 * Recursive subroutine for getConstraints
8415 *
8416 * Given an object representing a foreign key constraint and an index on the
8417 * partitioned table it references, mark the constraint object as dependent
8418 * on the DO_INDEX_ATTACH object of each index partition, recursively
8419 * drilling down to their partitions if any. This ensures that the FK is not
8420 * restored until the index is fully marked valid.
8421 */
8422static void
8424{
8425 SimplePtrListCell *cell;
8426
8428
8429 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8430 {
8431 IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8432
8433 addObjectDependency(dobj, attach->dobj.dumpId);
8434
8435 if (attach->partitionIdx->partattaches.head != NULL)
8436 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8437 }
8438}
8439
8440/*
8441 * getDomainConstraints
8442 *
8443 * Get info about constraints on a domain.
8444 */
8445static void
8447{
8448 ConstraintInfo *constrinfo;
8450 PGresult *res;
8451 int i_tableoid,
8452 i_oid,
8453 i_conname,
8454 i_consrc,
8455 i_convalidated,
8456 i_contype;
8457 int ntups;
8458
8460 {
8461 /*
8462 * Set up query for constraint-specific details. For servers 17 and
8463 * up, domains have constraints of type 'n' as well as 'c', otherwise
8464 * just the latter.
8465 */
8466 appendPQExpBuffer(query,
8467 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8468 "SELECT tableoid, oid, conname, "
8469 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8470 "convalidated, contype "
8471 "FROM pg_catalog.pg_constraint "
8472 "WHERE contypid = $1 AND contype IN (%s) "
8473 "ORDER BY conname",
8474 fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8475
8476 ExecuteSqlStatement(fout, query->data);
8477
8479 }
8480
8481 printfPQExpBuffer(query,
8482 "EXECUTE getDomainConstraints('%u')",
8483 tyinfo->dobj.catId.oid);
8484
8485 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8486
8487 ntups = PQntuples(res);
8488
8489 i_tableoid = PQfnumber(res, "tableoid");
8490 i_oid = PQfnumber(res, "oid");
8491 i_conname = PQfnumber(res, "conname");
8492 i_consrc = PQfnumber(res, "consrc");
8493 i_convalidated = PQfnumber(res, "convalidated");
8494 i_contype = PQfnumber(res, "contype");
8495
8496 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8497 tyinfo->domChecks = constrinfo;
8498
8499 /* 'i' tracks result rows; 'j' counts CHECK constraints */
8500 for (int i = 0, j = 0; i < ntups; i++)
8501 {
8502 bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8503 char contype = (PQgetvalue(res, i, i_contype))[0];
8504 ConstraintInfo *constraint;
8505
8506 if (contype == CONSTRAINT_CHECK)
8507 {
8508 constraint = &constrinfo[j++];
8509 tyinfo->nDomChecks++;
8510 }
8511 else
8512 {
8513 Assert(contype == CONSTRAINT_NOTNULL);
8514 Assert(tyinfo->notnull == NULL);
8515 /* use last item in array for the not-null constraint */
8516 tyinfo->notnull = &(constrinfo[ntups - 1]);
8517 constraint = tyinfo->notnull;
8518 }
8519
8520 constraint->dobj.objType = DO_CONSTRAINT;
8521 constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8522 constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8523 AssignDumpId(&(constraint->dobj));
8524 constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8525 constraint->dobj.namespace = tyinfo->dobj.namespace;
8526 constraint->contable = NULL;
8527 constraint->condomain = tyinfo;
8528 constraint->contype = contype;
8529 constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8530 constraint->confrelid = InvalidOid;
8531 constraint->conindex = 0;
8532 constraint->condeferrable = false;
8533 constraint->condeferred = false;
8534 constraint->conislocal = true;
8535
8536 constraint->separate = !validated;
8537
8538 /*
8539 * Make the domain depend on the constraint, ensuring it won't be
8540 * output till any constraint dependencies are OK. If the constraint
8541 * has not been validated, it's going to be dumped after the domain
8542 * anyway, so this doesn't matter.
8543 */
8544 if (validated)
8545 addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8546 }
8547
8548 PQclear(res);
8549
8550 destroyPQExpBuffer(query);
8551}
8552
8553/*
8554 * getRules
8555 * get basic information about every rule in the system
8556 */
8557void
8559{
8560 PGresult *res;
8561 int ntups;
8562 int i;
8564 RuleInfo *ruleinfo;
8565 int i_tableoid;
8566 int i_oid;
8567 int i_rulename;
8568 int i_ruletable;
8569 int i_ev_type;
8570 int i_is_instead;
8571 int i_ev_enabled;
8572
8573 appendPQExpBufferStr(query, "SELECT "
8574 "tableoid, oid, rulename, "
8575 "ev_class AS ruletable, ev_type, is_instead, "
8576 "ev_enabled "
8577 "FROM pg_rewrite "
8578 "ORDER BY oid");
8579
8580 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8581
8582 ntups = PQntuples(res);
8583
8584 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8585
8586 i_tableoid = PQfnumber(res, "tableoid");
8587 i_oid = PQfnumber(res, "oid");
8588 i_rulename = PQfnumber(res, "rulename");
8589 i_ruletable = PQfnumber(res, "ruletable");
8590 i_ev_type = PQfnumber(res, "ev_type");
8591 i_is_instead = PQfnumber(res, "is_instead");
8592 i_ev_enabled = PQfnumber(res, "ev_enabled");
8593
8594 for (i = 0; i < ntups; i++)
8595 {
8596 Oid ruletableoid;
8597
8598 ruleinfo[i].dobj.objType = DO_RULE;
8599 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8600 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8601 AssignDumpId(&ruleinfo[i].dobj);
8602 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8603 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8604 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8605 if (ruleinfo[i].ruletable == NULL)
8606 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8607 ruletableoid, ruleinfo[i].dobj.catId.oid);
8608 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8609 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8610 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8611 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8612 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8613 if (ruleinfo[i].ruletable)
8614 {
8615 /*
8616 * If the table is a view or materialized view, force its ON
8617 * SELECT rule to be sorted before the view itself --- this
8618 * ensures that any dependencies for the rule affect the table's
8619 * positioning. Other rules are forced to appear after their
8620 * table.
8621 */
8622 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8623 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8624 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8625 {
8626 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8627 ruleinfo[i].dobj.dumpId);
8628 /* We'll merge the rule into CREATE VIEW, if possible */
8629 ruleinfo[i].separate = false;
8630 }
8631 else
8632 {
8633 addObjectDependency(&ruleinfo[i].dobj,
8634 ruleinfo[i].ruletable->dobj.dumpId);
8635 ruleinfo[i].separate = true;
8636 }
8637 }
8638 else
8639 ruleinfo[i].separate = true;
8640 }
8641
8642 PQclear(res);
8643
8644 destroyPQExpBuffer(query);
8645}
8646
8647/*
8648 * getTriggers
8649 * get information about every trigger on a dumpable table
8650 *
8651 * Note: trigger data is not returned directly to the caller, but it
8652 * does get entered into the DumpableObject tables.
8653 */
8654void
8655getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8656{
8658 PQExpBuffer tbloids = createPQExpBuffer();
8659 PGresult *res;
8660 int ntups;
8661 int curtblindx;
8662 TriggerInfo *tginfo;
8663 int i_tableoid,
8664 i_oid,
8665 i_tgrelid,
8666 i_tgname,
8667 i_tgenabled,
8668 i_tgispartition,
8669 i_tgdef;
8670
8671 /*
8672 * We want to perform just one query against pg_trigger. However, we
8673 * mustn't try to select every row of the catalog and then sort it out on
8674 * the client side, because some of the server-side functions we need
8675 * would be unsafe to apply to tables we don't have lock on. Hence, we
8676 * build an array of the OIDs of tables we care about (and now have lock
8677 * on!), and use a WHERE clause to constrain which rows are selected.
8678 */
8679 appendPQExpBufferChar(tbloids, '{');
8680 for (int i = 0; i < numTables; i++)
8681 {
8682 TableInfo *tbinfo = &tblinfo[i];
8683
8684 if (!tbinfo->hastriggers ||
8685 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8686 continue;
8687
8688 /* OK, we need info for this table */
8689 if (tbloids->len > 1) /* do we have more than the '{'? */
8690 appendPQExpBufferChar(tbloids, ',');
8691 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8692 }
8693 appendPQExpBufferChar(tbloids, '}');
8694
8695 if (fout->remoteVersion >= 150000)
8696 {
8697 /*
8698 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8699 * result in non-forward-compatible dumps of WHEN clauses due to
8700 * under-parenthesization.
8701 *
8702 * NB: We need to see partition triggers in case the tgenabled flag
8703 * has been changed from the parent.
8704 */
8705 appendPQExpBuffer(query,
8706 "SELECT t.tgrelid, t.tgname, "
8707 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8708 "t.tgenabled, t.tableoid, t.oid, "
8709 "t.tgparentid <> 0 AS tgispartition\n"
8710 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8711 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8712 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8713 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8714 "OR t.tgenabled != u.tgenabled) "
8715 "ORDER BY t.tgrelid, t.tgname",
8716 tbloids->data);
8717 }
8718 else if (fout->remoteVersion >= 130000)
8719 {
8720 /*
8721 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8722 * result in non-forward-compatible dumps of WHEN clauses due to
8723 * under-parenthesization.
8724 *
8725 * NB: We need to see tgisinternal triggers in partitions, in case the
8726 * tgenabled flag has been changed from the parent.
8727 */
8728 appendPQExpBuffer(query,
8729 "SELECT t.tgrelid, t.tgname, "
8730 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8731 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8732 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8733 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8734 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8735 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8736 "ORDER BY t.tgrelid, t.tgname",
8737 tbloids->data);
8738 }
8739 else if (fout->remoteVersion >= 110000)
8740 {
8741 /*
8742 * NB: We need to see tgisinternal triggers in partitions, in case the
8743 * tgenabled flag has been changed from the parent. No tgparentid in
8744 * version 11-12, so we have to match them via pg_depend.
8745 *
8746 * See above about pretty=true in pg_get_triggerdef.
8747 */
8748 appendPQExpBuffer(query,
8749 "SELECT t.tgrelid, t.tgname, "
8750 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8751 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8752 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8753 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8754 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8755 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8756 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8757 " d.objid = t.oid "
8758 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8759 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8760 "ORDER BY t.tgrelid, t.tgname",
8761 tbloids->data);
8762 }
8763 else
8764 {
8765 /* See above about pretty=true in pg_get_triggerdef */
8766 appendPQExpBuffer(query,
8767 "SELECT t.tgrelid, t.tgname, "
8768 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8769 "t.tgenabled, false as tgispartition, "
8770 "t.tableoid, t.oid "
8771 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8772 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8773 "WHERE NOT tgisinternal "
8774 "ORDER BY t.tgrelid, t.tgname",
8775 tbloids->data);
8776 }
8777
8778 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8779
8780 ntups = PQntuples(res);
8781
8782 i_tableoid = PQfnumber(res, "tableoid");
8783 i_oid = PQfnumber(res, "oid");
8784 i_tgrelid = PQfnumber(res, "tgrelid");
8785 i_tgname = PQfnumber(res, "tgname");
8786 i_tgenabled = PQfnumber(res, "tgenabled");
8787 i_tgispartition = PQfnumber(res, "tgispartition");
8788 i_tgdef = PQfnumber(res, "tgdef");
8789
8790 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8791
8792 /*
8793 * Outer loop iterates once per table, not once per row. Incrementing of
8794 * j is handled by the inner loop.
8795 */
8796 curtblindx = -1;
8797 for (int j = 0; j < ntups;)
8798 {
8799 Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8800 TableInfo *tbinfo = NULL;
8801 int numtrigs;
8802
8803 /* Count rows for this table */
8804 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8805 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8806 break;
8807
8808 /*
8809 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8810 * order.
8811 */
8812 while (++curtblindx < numTables)
8813 {
8814 tbinfo = &tblinfo[curtblindx];
8815 if (tbinfo->dobj.catId.oid == tgrelid)
8816 break;
8817 }
8818 if (curtblindx >= numTables)
8819 pg_fatal("unrecognized table OID %u", tgrelid);
8820
8821 /* Save data for this table */
8822 tbinfo->triggers = tginfo + j;
8823 tbinfo->numTriggers = numtrigs;
8824
8825 for (int c = 0; c < numtrigs; c++, j++)
8826 {
8827 tginfo[j].dobj.objType = DO_TRIGGER;
8828 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8829 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8830 AssignDumpId(&tginfo[j].dobj);
8831 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8832 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8833 tginfo[j].tgtable = tbinfo;
8834 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8835 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8836 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8837 }
8838 }
8839
8840 PQclear(res);
8841
8842 destroyPQExpBuffer(query);
8843 destroyPQExpBuffer(tbloids);
8844}
8845
8846/*
8847 * getEventTriggers
8848 * get information about event triggers
8849 */
8850void
8852{
8853 int i;
8854 PQExpBuffer query;
8855 PGresult *res;
8856 EventTriggerInfo *evtinfo;
8857 int i_tableoid,
8858 i_oid,
8859 i_evtname,
8860 i_evtevent,
8861 i_evtowner,
8862 i_evttags,
8863 i_evtfname,
8864 i_evtenabled;
8865 int ntups;
8866
8867 /* Before 9.3, there are no event triggers */
8868 if (fout->remoteVersion < 90300)
8869 return;
8870
8871 query = createPQExpBuffer();
8872
8874 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8875 "evtevent, evtowner, "
8876 "array_to_string(array("
8877 "select quote_literal(x) "
8878 " from unnest(evttags) as t(x)), ', ') as evttags, "
8879 "e.evtfoid::regproc as evtfname "
8880 "FROM pg_event_trigger e "
8881 "ORDER BY e.oid");
8882
8883 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8884
8885 ntups = PQntuples(res);
8886
8887 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8888
8889 i_tableoid = PQfnumber(res, "tableoid");
8890 i_oid = PQfnumber(res, "oid");
8891 i_evtname = PQfnumber(res, "evtname");
8892 i_evtevent = PQfnumber(res, "evtevent");
8893 i_evtowner = PQfnumber(res, "evtowner");
8894 i_evttags = PQfnumber(res, "evttags");
8895 i_evtfname = PQfnumber(res, "evtfname");
8896 i_evtenabled = PQfnumber(res, "evtenabled");
8897
8898 for (i = 0; i < ntups; i++)
8899 {
8900 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8901 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8902 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8903 AssignDumpId(&evtinfo[i].dobj);
8904 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8905 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8906 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8907 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8908 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8909 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8910 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8911
8912 /* Decide whether we want to dump it */
8913 selectDumpableObject(&(evtinfo[i].dobj), fout);
8914 }
8915
8916 PQclear(res);
8917
8918 destroyPQExpBuffer(query);
8919}
8920
8921/*
8922 * getProcLangs
8923 * get basic information about every procedural language in the system
8924 *
8925 * NB: this must run after getFuncs() because we assume we can do
8926 * findFuncByOid().
8927 */
8928void
8930{
8931 PGresult *res;
8932 int ntups;
8933 int i;
8935 ProcLangInfo *planginfo;
8936 int i_tableoid;
8937 int i_oid;
8938 int i_lanname;
8939 int i_lanpltrusted;
8940 int i_lanplcallfoid;
8941 int i_laninline;
8942 int i_lanvalidator;
8943 int i_lanacl;
8944 int i_acldefault;
8945 int i_lanowner;
8946
8947 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8948 "lanname, lanpltrusted, lanplcallfoid, "
8949 "laninline, lanvalidator, "
8950 "lanacl, "
8951 "acldefault('l', lanowner) AS acldefault, "
8952 "lanowner "
8953 "FROM pg_language "
8954 "WHERE lanispl "
8955 "ORDER BY oid");
8956
8957 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8958
8959 ntups = PQntuples(res);
8960
8961 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8962
8963 i_tableoid = PQfnumber(res, "tableoid");
8964 i_oid = PQfnumber(res, "oid");
8965 i_lanname = PQfnumber(res, "lanname");
8966 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8967 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8968 i_laninline = PQfnumber(res, "laninline");
8969 i_lanvalidator = PQfnumber(res, "lanvalidator");
8970 i_lanacl = PQfnumber(res, "lanacl");
8971 i_acldefault = PQfnumber(res, "acldefault");
8972 i_lanowner = PQfnumber(res, "lanowner");
8973
8974 for (i = 0; i < ntups; i++)
8975 {
8976 planginfo[i].dobj.objType = DO_PROCLANG;
8977 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8978 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8979 AssignDumpId(&planginfo[i].dobj);
8980
8981 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8982 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8983 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8984 planginfo[i].dacl.privtype = 0;
8985 planginfo[i].dacl.initprivs = NULL;
8986 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8987 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8988 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8989 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8990 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8991
8992 /* Decide whether we want to dump it */
8993 selectDumpableProcLang(&(planginfo[i]), fout);
8994
8995 /* Mark whether language has an ACL */
8996 if (!PQgetisnull(res, i, i_lanacl))
8997 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8998 }
8999
9000 PQclear(res);
9001
9002 destroyPQExpBuffer(query);
9003}
9004
9005/*
9006 * getCasts
9007 * get basic information about most casts in the system
9008 *
9009 * Skip casts from a range to its multirange, since we'll create those
9010 * automatically.
9011 */
9012void
9014{
9015 PGresult *res;
9016 int ntups;
9017 int i;
9019 CastInfo *castinfo;
9020 int i_tableoid;
9021 int i_oid;
9022 int i_castsource;
9023 int i_casttarget;
9024 int i_castfunc;
9025 int i_castcontext;
9026 int i_castmethod;
9027
9028 if (fout->remoteVersion >= 140000)
9029 {
9030 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9031 "castsource, casttarget, castfunc, castcontext, "
9032 "castmethod "
9033 "FROM pg_cast c "
9034 "WHERE NOT EXISTS ( "
9035 "SELECT 1 FROM pg_range r "
9036 "WHERE c.castsource = r.rngtypid "
9037 "AND c.casttarget = r.rngmultitypid "
9038 ") "
9039 "ORDER BY 3,4");
9040 }
9041 else
9042 {
9043 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9044 "castsource, casttarget, castfunc, castcontext, "
9045 "castmethod "
9046 "FROM pg_cast ORDER BY 3,4");
9047 }
9048
9049 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9050
9051 ntups = PQntuples(res);
9052
9053 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9054
9055 i_tableoid = PQfnumber(res, "tableoid");
9056 i_oid = PQfnumber(res, "oid");
9057 i_castsource = PQfnumber(res, "castsource");
9058 i_casttarget = PQfnumber(res, "casttarget");
9059 i_castfunc = PQfnumber(res, "castfunc");
9060 i_castcontext = PQfnumber(res, "castcontext");
9061 i_castmethod = PQfnumber(res, "castmethod");
9062
9063 for (i = 0; i < ntups; i++)
9064 {
9065 PQExpBufferData namebuf;
9066 TypeInfo *sTypeInfo;
9067 TypeInfo *tTypeInfo;
9068
9069 castinfo[i].dobj.objType = DO_CAST;
9070 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9071 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9072 AssignDumpId(&castinfo[i].dobj);
9073 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9074 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9075 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9076 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9077 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9078
9079 /*
9080 * Try to name cast as concatenation of typnames. This is only used
9081 * for purposes of sorting. If we fail to find either type, the name
9082 * will be an empty string.
9083 */
9084 initPQExpBuffer(&namebuf);
9085 sTypeInfo = findTypeByOid(castinfo[i].castsource);
9086 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9087 if (sTypeInfo && tTypeInfo)
9088 appendPQExpBuffer(&namebuf, "%s %s",
9089 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9090 castinfo[i].dobj.name = namebuf.data;
9091
9092 /* Decide whether we want to dump it */
9093 selectDumpableCast(&(castinfo[i]), fout);
9094 }
9095
9096 PQclear(res);
9097
9098 destroyPQExpBuffer(query);
9099}
9100
9101static char *
9103{
9104 PQExpBuffer query;
9105 PGresult *res;
9106 char *lanname;
9107
9108 query = createPQExpBuffer();
9109 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9110 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9111 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9112 destroyPQExpBuffer(query);
9113 PQclear(res);
9114
9115 return lanname;
9116}
9117
9118/*
9119 * getTransforms
9120 * get basic information about every transform in the system
9121 */
9122void
9124{
9125 PGresult *res;
9126 int ntups;
9127 int i;
9128 PQExpBuffer query;
9129 TransformInfo *transforminfo;
9130 int i_tableoid;
9131 int i_oid;
9132 int i_trftype;
9133 int i_trflang;
9134 int i_trffromsql;
9135 int i_trftosql;
9136
9137 /* Transforms didn't exist pre-9.5 */
9138 if (fout->remoteVersion < 90500)
9139 return;
9140
9141 query = createPQExpBuffer();
9142
9143 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9144 "trftype, trflang, trffromsql::oid, trftosql::oid "
9145 "FROM pg_transform "
9146 "ORDER BY 3,4");
9147
9148 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9149
9150 ntups = PQntuples(res);
9151
9152 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9153
9154 i_tableoid = PQfnumber(res, "tableoid");
9155 i_oid = PQfnumber(res, "oid");
9156 i_trftype = PQfnumber(res, "trftype");
9157 i_trflang = PQfnumber(res, "trflang");
9158 i_trffromsql = PQfnumber(res, "trffromsql");
9159 i_trftosql = PQfnumber(res, "trftosql");
9160
9161 for (i = 0; i < ntups; i++)
9162 {
9163 PQExpBufferData namebuf;
9164 TypeInfo *typeInfo;
9165 char *lanname;
9166
9167 transforminfo[i].dobj.objType = DO_TRANSFORM;
9168 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9169 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9170 AssignDumpId(&transforminfo[i].dobj);
9171 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9172 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9173 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9174 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9175
9176 /*
9177 * Try to name transform as concatenation of type and language name.
9178 * This is only used for purposes of sorting. If we fail to find
9179 * either, the name will be an empty string.
9180 */
9181 initPQExpBuffer(&namebuf);
9182 typeInfo = findTypeByOid(transforminfo[i].trftype);
9183 lanname = get_language_name(fout, transforminfo[i].trflang);
9184 if (typeInfo && lanname)
9185 appendPQExpBuffer(&namebuf, "%s %s",
9186 typeInfo->dobj.name, lanname);
9187 transforminfo[i].dobj.name = namebuf.data;
9188 free(lanname);
9189
9190 /* Decide whether we want to dump it */
9191 selectDumpableObject(&(transforminfo[i].dobj), fout);
9192 }
9193
9194 PQclear(res);
9195
9196 destroyPQExpBuffer(query);
9197}
9198
9199/*
9200 * getTableAttrs -
9201 * for each interesting table, read info about its attributes
9202 * (names, types, default values, CHECK constraints, etc)
9203 *
9204 * modifies tblinfo
9205 */
9206void
9207getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9208{
9209 DumpOptions *dopt = fout->dopt;
9211 PQExpBuffer tbloids = createPQExpBuffer();
9212 PQExpBuffer checkoids = createPQExpBuffer();
9213 PQExpBuffer invalidnotnulloids = NULL;
9214 PGresult *res;
9215 int ntups;
9216 int curtblindx;
9217 int i_attrelid;
9218 int i_attnum;
9219 int i_attname;
9220 int i_atttypname;
9221 int i_attstattarget;
9222 int i_attstorage;
9223 int i_typstorage;
9224 int i_attidentity;
9225 int i_attgenerated;
9226 int i_attisdropped;
9227 int i_attlen;
9228 int i_attalign;
9229 int i_attislocal;
9230 int i_notnull_name;
9231 int i_notnull_comment;
9232 int i_notnull_noinherit;
9233 int i_notnull_islocal;
9234 int i_notnull_invalidoid;
9235 int i_attoptions;
9236 int i_attcollation;
9237 int i_attcompression;
9238 int i_attfdwoptions;
9239 int i_attmissingval;
9240 int i_atthasdef;
9241
9242 /*
9243 * We want to perform just one query against pg_attribute, and then just
9244 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9245 * (for CHECK constraints and for NOT NULL constraints). However, we
9246 * mustn't try to select every row of those catalogs and then sort it out
9247 * on the client side, because some of the server-side functions we need
9248 * would be unsafe to apply to tables we don't have lock on. Hence, we
9249 * build an array of the OIDs of tables we care about (and now have lock
9250 * on!), and use a WHERE clause to constrain which rows are selected.
9251 */
9252 appendPQExpBufferChar(tbloids, '{');
9253 appendPQExpBufferChar(checkoids, '{');
9254 for (int i = 0; i < numTables; i++)
9255 {
9256 TableInfo *tbinfo = &tblinfo[i];
9257
9258 /* Don't bother to collect info for sequences */
9259 if (tbinfo->relkind == RELKIND_SEQUENCE)
9260 continue;
9261
9262 /*
9263 * Don't bother with uninteresting tables, either. For binary
9264 * upgrades, this is bypassed for pg_largeobject_metadata and
9265 * pg_shdepend so that the columns names are collected for the
9266 * corresponding COPY commands. Restoring the data for those catalogs
9267 * is faster than restoring the equivalent set of large object
9268 * commands. We can only do this for upgrades from v12 and newer; in
9269 * older versions, pg_largeobject_metadata was created WITH OIDS, so
9270 * the OID column is hidden and won't be dumped.
9271 */
9272 if (!tbinfo->interesting &&
9273 !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9274 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9275 tbinfo->dobj.catId.oid == SharedDependRelationId)))
9276 continue;
9277
9278 /* OK, we need info for this table */
9279 if (tbloids->len > 1) /* do we have more than the '{'? */
9280 appendPQExpBufferChar(tbloids, ',');
9281 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9282
9283 if (tbinfo->ncheck > 0)
9284 {
9285 /* Also make a list of the ones with check constraints */
9286 if (checkoids->len > 1) /* do we have more than the '{'? */
9287 appendPQExpBufferChar(checkoids, ',');
9288 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9289 }
9290 }
9291 appendPQExpBufferChar(tbloids, '}');
9292 appendPQExpBufferChar(checkoids, '}');
9293
9294 /*
9295 * Find all the user attributes and their types.
9296 *
9297 * Since we only want to dump COLLATE clauses for attributes whose
9298 * collation is different from their type's default, we use a CASE here to
9299 * suppress uninteresting attcollations cheaply.
9300 */
9302 "SELECT\n"
9303 "a.attrelid,\n"
9304 "a.attnum,\n"
9305 "a.attname,\n"
9306 "a.attstattarget,\n"
9307 "a.attstorage,\n"
9308 "t.typstorage,\n"
9309 "a.atthasdef,\n"
9310 "a.attisdropped,\n"
9311 "a.attlen,\n"
9312 "a.attalign,\n"
9313 "a.attislocal,\n"
9314 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9315 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9316 "CASE WHEN a.attcollation <> t.typcollation "
9317 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9318 "pg_catalog.array_to_string(ARRAY("
9319 "SELECT pg_catalog.quote_ident(option_name) || "
9320 "' ' || pg_catalog.quote_literal(option_value) "
9321 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9322 "ORDER BY option_name"
9323 "), E',\n ') AS attfdwoptions,\n");
9324
9325 /*
9326 * Find out any NOT NULL markings for each column. In 18 and up we read
9327 * pg_constraint to obtain the constraint name, and for valid constraints
9328 * also pg_description to obtain its comment. notnull_noinherit is set
9329 * according to the NO INHERIT property. For versions prior to 18, we
9330 * store an empty string as the name when a constraint is marked as
9331 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9332 * without a name); also, such cases are never NO INHERIT.
9333 *
9334 * For invalid constraints, we need to store their OIDs for processing
9335 * elsewhere, so we bring the pg_constraint.oid value when the constraint
9336 * is invalid, and NULL otherwise. Their comments are handled not here
9337 * but by collectComments, because they're their own dumpable object.
9338 *
9339 * We track in notnull_islocal whether the constraint was defined directly
9340 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9341 * might modify this later; that routine is also in charge of determining
9342 * the correct inhcount.
9343 */
9344 if (fout->remoteVersion >= 180000)
9346 "co.conname AS notnull_name,\n"
9347 "CASE WHEN co.convalidated THEN pt.description"
9348 " ELSE NULL END AS notnull_comment,\n"
9349 "CASE WHEN NOT co.convalidated THEN co.oid "
9350 "ELSE NULL END AS notnull_invalidoid,\n"
9351 "co.connoinherit AS notnull_noinherit,\n"
9352 "co.conislocal AS notnull_islocal,\n");
9353 else
9355 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9356 "NULL AS notnull_comment,\n"
9357 "NULL AS notnull_invalidoid,\n"
9358 "false AS notnull_noinherit,\n"
9359 "a.attislocal AS notnull_islocal,\n");
9360
9361 if (fout->remoteVersion >= 140000)
9363 "a.attcompression AS attcompression,\n");
9364 else
9366 "'' AS attcompression,\n");
9367
9368 if (fout->remoteVersion >= 100000)
9370 "a.attidentity,\n");
9371 else
9373 "'' AS attidentity,\n");
9374
9375 if (fout->remoteVersion >= 110000)
9377 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9378 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9379 else
9381 "NULL AS attmissingval,\n");
9382
9383 if (fout->remoteVersion >= 120000)
9385 "a.attgenerated\n");
9386 else
9388 "'' AS attgenerated\n");
9389
9390 /* need left join to pg_type to not fail on dropped columns ... */
9392 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9393 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9394 "LEFT JOIN pg_catalog.pg_type t "
9395 "ON (a.atttypid = t.oid)\n",
9396 tbloids->data);
9397
9398 /*
9399 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9400 * entries and pg_description to get their comments.
9401 */
9402 if (fout->remoteVersion >= 180000)
9404 " LEFT JOIN pg_catalog.pg_constraint co ON "
9405 "(a.attrelid = co.conrelid\n"
9406 " AND co.contype = 'n' AND "
9407 "co.conkey = array[a.attnum])\n"
9408 " LEFT JOIN pg_catalog.pg_description pt ON "
9409 "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9410
9412 "WHERE a.attnum > 0::pg_catalog.int2\n"
9413 "ORDER BY a.attrelid, a.attnum");
9414
9415 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9416
9417 ntups = PQntuples(res);
9418
9419 i_attrelid = PQfnumber(res, "attrelid");
9420 i_attnum = PQfnumber(res, "attnum");
9421 i_attname = PQfnumber(res, "attname");
9422 i_atttypname = PQfnumber(res, "atttypname");
9423 i_attstattarget = PQfnumber(res, "attstattarget");
9424 i_attstorage = PQfnumber(res, "attstorage");
9425 i_typstorage = PQfnumber(res, "typstorage");
9426 i_attidentity = PQfnumber(res, "attidentity");
9427 i_attgenerated = PQfnumber(res, "attgenerated");
9428 i_attisdropped = PQfnumber(res, "attisdropped");
9429 i_attlen = PQfnumber(res, "attlen");
9430 i_attalign = PQfnumber(res, "attalign");
9431 i_attislocal = PQfnumber(res, "attislocal");
9432 i_notnull_name = PQfnumber(res, "notnull_name");
9433 i_notnull_comment = PQfnumber(res, "notnull_comment");
9434 i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9435 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9436 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9437 i_attoptions = PQfnumber(res, "attoptions");
9438 i_attcollation = PQfnumber(res, "attcollation");
9439 i_attcompression = PQfnumber(res, "attcompression");
9440 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9441 i_attmissingval = PQfnumber(res, "attmissingval");
9442 i_atthasdef = PQfnumber(res, "atthasdef");
9443
9444 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9445 resetPQExpBuffer(tbloids);
9446 appendPQExpBufferChar(tbloids, '{');
9447
9448 /*
9449 * Outer loop iterates once per table, not once per row. Incrementing of
9450 * r is handled by the inner loop.
9451 */
9452 curtblindx = -1;
9453 for (int r = 0; r < ntups;)
9454 {
9455 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9456 TableInfo *tbinfo = NULL;
9457 int numatts;
9458 bool hasdefaults;
9459
9460 /* Count rows for this table */
9461 for (numatts = 1; numatts < ntups - r; numatts++)
9462 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9463 break;
9464
9465 /*
9466 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9467 * order.
9468 */
9469 while (++curtblindx < numTables)
9470 {
9471 tbinfo = &tblinfo[curtblindx];
9472 if (tbinfo->dobj.catId.oid == attrelid)
9473 break;
9474 }
9475 if (curtblindx >= numTables)
9476 pg_fatal("unrecognized table OID %u", attrelid);
9477 /* cross-check that we only got requested tables */
9478 if (tbinfo->relkind == RELKIND_SEQUENCE ||
9479 (!tbinfo->interesting &&
9480 !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9481 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9482 tbinfo->dobj.catId.oid == SharedDependRelationId))))
9483 pg_fatal("unexpected column data for table \"%s\"",
9484 tbinfo->dobj.name);
9485
9486 /* Save data for this table */
9487 tbinfo->numatts = numatts;
9488 tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9489 tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9490 tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9491 tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9492 tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9493 tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9494 tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9495 tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9496 tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9497 tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9498 tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9499 tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9500 tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9501 tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9502 tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9503 tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9504 tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9505 tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9506 tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9507 tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9508 tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9509 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9510 hasdefaults = false;
9511
9512 for (int j = 0; j < numatts; j++, r++)
9513 {
9514 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9515 pg_fatal("invalid column numbering in table \"%s\"",
9516 tbinfo->dobj.name);
9517 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9518 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9519 if (PQgetisnull(res, r, i_attstattarget))
9520 tbinfo->attstattarget[j] = -1;
9521 else
9522 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9523 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9524 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9525 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9526 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9527 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9528 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9529 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9530 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9531 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9532
9533 /* Handle not-null constraint name and flags */
9534 determineNotNullFlags(fout, res, r,
9535 tbinfo, j,
9536 i_notnull_name,
9537 i_notnull_comment,
9538 i_notnull_invalidoid,
9539 i_notnull_noinherit,
9540 i_notnull_islocal,
9541 &invalidnotnulloids);
9542
9543 tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9544 NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9545 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9546 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9547 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9548 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9549 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9550 tbinfo->attrdefs[j] = NULL; /* fix below */
9551 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9552 hasdefaults = true;
9553 }
9554
9555 if (hasdefaults)
9556 {
9557 /* Collect OIDs of interesting tables that have defaults */
9558 if (tbloids->len > 1) /* do we have more than the '{'? */
9559 appendPQExpBufferChar(tbloids, ',');
9560 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9561 }
9562 }
9563
9564 /* If invalidnotnulloids has any data, finalize it */
9565 if (invalidnotnulloids != NULL)
9566 appendPQExpBufferChar(invalidnotnulloids, '}');
9567
9568 PQclear(res);
9569
9570 /*
9571 * Now get info about column defaults. This is skipped for a data-only
9572 * dump, as it is only needed for table schemas.
9573 */
9574 if (dopt->dumpSchema && tbloids->len > 1)
9575 {
9576 AttrDefInfo *attrdefs;
9577 int numDefaults;
9578 TableInfo *tbinfo = NULL;
9579
9580 pg_log_info("finding table default expressions");
9581
9582 appendPQExpBufferChar(tbloids, '}');
9583
9584 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9585 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9586 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9587 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9588 "ORDER BY a.adrelid, a.adnum",
9589 tbloids->data);
9590
9591 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9592
9593 numDefaults = PQntuples(res);
9594 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9595
9596 curtblindx = -1;
9597 for (int j = 0; j < numDefaults; j++)
9598 {
9599 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9600 Oid adoid = atooid(PQgetvalue(res, j, 1));
9601 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9602 int adnum = atoi(PQgetvalue(res, j, 3));
9603 char *adsrc = PQgetvalue(res, j, 4);
9604
9605 /*
9606 * Locate the associated TableInfo; we rely on tblinfo[] being in
9607 * OID order.
9608 */
9609 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9610 {
9611 while (++curtblindx < numTables)
9612 {
9613 tbinfo = &tblinfo[curtblindx];
9614 if (tbinfo->dobj.catId.oid == adrelid)
9615 break;
9616 }
9617 if (curtblindx >= numTables)
9618 pg_fatal("unrecognized table OID %u", adrelid);
9619 }
9620
9621 if (adnum <= 0 || adnum > tbinfo->numatts)
9622 pg_fatal("invalid adnum value %d for table \"%s\"",
9623 adnum, tbinfo->dobj.name);
9624
9625 /*
9626 * dropped columns shouldn't have defaults, but just in case,
9627 * ignore 'em
9628 */
9629 if (tbinfo->attisdropped[adnum - 1])
9630 continue;
9631
9632 attrdefs[j].dobj.objType = DO_ATTRDEF;
9633 attrdefs[j].dobj.catId.tableoid = adtableoid;
9634 attrdefs[j].dobj.catId.oid = adoid;
9635 AssignDumpId(&attrdefs[j].dobj);
9636 attrdefs[j].adtable = tbinfo;
9637 attrdefs[j].adnum = adnum;
9638 attrdefs[j].adef_expr = pg_strdup(adsrc);
9639
9640 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9641 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9642
9643 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9644
9645 /*
9646 * Figure out whether the default/generation expression should be
9647 * dumped as part of the main CREATE TABLE (or similar) command or
9648 * as a separate ALTER TABLE (or similar) command. The preference
9649 * is to put it into the CREATE command, but in some cases that's
9650 * not possible.
9651 */
9652 if (tbinfo->attgenerated[adnum - 1])
9653 {
9654 /*
9655 * Column generation expressions cannot be dumped separately,
9656 * because there is no syntax for it. By setting separate to
9657 * false here we prevent the "default" from being processed as
9658 * its own dumpable object. Later, flagInhAttrs() will mark
9659 * it as not to be dumped at all, if possible (that is, if it
9660 * can be inherited from a parent).
9661 */
9662 attrdefs[j].separate = false;
9663 }
9664 else if (tbinfo->relkind == RELKIND_VIEW)
9665 {
9666 /*
9667 * Defaults on a VIEW must always be dumped as separate ALTER
9668 * TABLE commands.
9669 */
9670 attrdefs[j].separate = true;
9671 }
9672 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9673 {
9674 /* column will be suppressed, print default separately */
9675 attrdefs[j].separate = true;
9676 }
9677 else
9678 {
9679 attrdefs[j].separate = false;
9680 }
9681
9682 if (!attrdefs[j].separate)
9683 {
9684 /*
9685 * Mark the default as needing to appear before the table, so
9686 * that any dependencies it has must be emitted before the
9687 * CREATE TABLE. If this is not possible, we'll change to
9688 * "separate" mode while sorting dependencies.
9689 */
9690 addObjectDependency(&tbinfo->dobj,
9691 attrdefs[j].dobj.dumpId);
9692 }
9693
9694 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9695 }
9696
9697 PQclear(res);
9698 }
9699
9700 /*
9701 * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9702 * data-only dump, as it is only needed for table schemas.
9703 */
9704 if (dopt->dumpSchema && invalidnotnulloids)
9705 {
9706 ConstraintInfo *constrs;
9707 int numConstrs;
9708 int i_tableoid;
9709 int i_oid;
9710 int i_conrelid;
9711 int i_conname;
9712 int i_consrc;
9713 int i_conislocal;
9714
9715 pg_log_info("finding invalid not-null constraints");
9716
9719 "SELECT c.tableoid, c.oid, conrelid, conname, "
9720 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9721 "conislocal, convalidated "
9722 "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9723 "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9724 "ORDER BY c.conrelid, c.conname",
9725 invalidnotnulloids->data);
9726
9727 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9728
9729 numConstrs = PQntuples(res);
9730 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9731
9732 i_tableoid = PQfnumber(res, "tableoid");
9733 i_oid = PQfnumber(res, "oid");
9734 i_conrelid = PQfnumber(res, "conrelid");
9735 i_conname = PQfnumber(res, "conname");
9736 i_consrc = PQfnumber(res, "consrc");
9737 i_conislocal = PQfnumber(res, "conislocal");
9738
9739 /* As above, this loop iterates once per table, not once per row */
9740 curtblindx = -1;
9741 for (int j = 0; j < numConstrs;)
9742 {
9743 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9744 TableInfo *tbinfo = NULL;
9745 int numcons;
9746
9747 /* Count rows for this table */
9748 for (numcons = 1; numcons < numConstrs - j; numcons++)
9749 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9750 break;
9751
9752 /*
9753 * Locate the associated TableInfo; we rely on tblinfo[] being in
9754 * OID order.
9755 */
9756 while (++curtblindx < numTables)
9757 {
9758 tbinfo = &tblinfo[curtblindx];
9759 if (tbinfo->dobj.catId.oid == conrelid)
9760 break;
9761 }
9762 if (curtblindx >= numTables)
9763 pg_fatal("unrecognized table OID %u", conrelid);
9764
9765 for (int c = 0; c < numcons; c++, j++)
9766 {
9767 constrs[j].dobj.objType = DO_CONSTRAINT;
9768 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9769 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9770 AssignDumpId(&constrs[j].dobj);
9771 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9772 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9773 constrs[j].contable = tbinfo;
9774 constrs[j].condomain = NULL;
9775 constrs[j].contype = 'n';
9776 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9777 constrs[j].confrelid = InvalidOid;
9778 constrs[j].conindex = 0;
9779 constrs[j].condeferrable = false;
9780 constrs[j].condeferred = false;
9781 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9782
9783 /*
9784 * All invalid not-null constraints must be dumped separately,
9785 * because CREATE TABLE would not create them as invalid, and
9786 * also because they must be created after potentially
9787 * violating data has been loaded.
9788 */
9789 constrs[j].separate = true;
9790
9791 constrs[j].dobj.dump = tbinfo->dobj.dump;
9792 }
9793 }
9794 PQclear(res);
9795 }
9796
9797 /*
9798 * Get info about table CHECK constraints. This is skipped for a
9799 * data-only dump, as it is only needed for table schemas.
9800 */
9801 if (dopt->dumpSchema && checkoids->len > 2)
9802 {
9803 ConstraintInfo *constrs;
9804 int numConstrs;
9805 int i_tableoid;
9806 int i_oid;
9807 int i_conrelid;
9808 int i_conname;
9809 int i_consrc;
9810 int i_conislocal;
9811 int i_convalidated;
9812
9813 pg_log_info("finding table check constraints");
9814
9817 "SELECT c.tableoid, c.oid, conrelid, conname, "
9818 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9819 "conislocal, convalidated "
9820 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9821 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9822 "WHERE contype = 'c' "
9823 "ORDER BY c.conrelid, c.conname",
9824 checkoids->data);
9825
9826 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9827
9828 numConstrs = PQntuples(res);
9829 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9830
9831 i_tableoid = PQfnumber(res, "tableoid");
9832 i_oid = PQfnumber(res, "oid");
9833 i_conrelid = PQfnumber(res, "conrelid");
9834 i_conname = PQfnumber(res, "conname");
9835 i_consrc = PQfnumber(res, "consrc");
9836 i_conislocal = PQfnumber(res, "conislocal");
9837 i_convalidated = PQfnumber(res, "convalidated");
9838
9839 /* As above, this loop iterates once per table, not once per row */
9840 curtblindx = -1;
9841 for (int j = 0; j < numConstrs;)
9842 {
9843 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9844 TableInfo *tbinfo = NULL;
9845 int numcons;
9846
9847 /* Count rows for this table */
9848 for (numcons = 1; numcons < numConstrs - j; numcons++)
9849 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9850 break;
9851
9852 /*
9853 * Locate the associated TableInfo; we rely on tblinfo[] being in
9854 * OID order.
9855 */
9856 while (++curtblindx < numTables)
9857 {
9858 tbinfo = &tblinfo[curtblindx];
9859 if (tbinfo->dobj.catId.oid == conrelid)
9860 break;
9861 }
9862 if (curtblindx >= numTables)
9863 pg_fatal("unrecognized table OID %u", conrelid);
9864
9865 if (numcons != tbinfo->ncheck)
9866 {
9867 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9868 "expected %d check constraints on table \"%s\" but found %d",
9869 tbinfo->ncheck),
9870 tbinfo->ncheck, tbinfo->dobj.name, numcons);
9871 pg_log_error_hint("The system catalogs might be corrupted.");
9872 exit_nicely(1);
9873 }
9874
9875 tbinfo->checkexprs = constrs + j;
9876
9877 for (int c = 0; c < numcons; c++, j++)
9878 {
9879 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9880
9881 constrs[j].dobj.objType = DO_CONSTRAINT;
9882 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9883 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9884 AssignDumpId(&constrs[j].dobj);
9885 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9886 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9887 constrs[j].contable = tbinfo;
9888 constrs[j].condomain = NULL;
9889 constrs[j].contype = 'c';
9890 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9891 constrs[j].confrelid = InvalidOid;
9892 constrs[j].conindex = 0;
9893 constrs[j].condeferrable = false;
9894 constrs[j].condeferred = false;
9895 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9896
9897 /*
9898 * An unvalidated constraint needs to be dumped separately, so
9899 * that potentially-violating existing data is loaded before
9900 * the constraint.
9901 */
9902 constrs[j].separate = !validated;
9903
9904 constrs[j].dobj.dump = tbinfo->dobj.dump;
9905
9906 /*
9907 * Mark the constraint as needing to appear before the table
9908 * --- this is so that any other dependencies of the
9909 * constraint will be emitted before we try to create the
9910 * table. If the constraint is to be dumped separately, it
9911 * will be dumped after data is loaded anyway, so don't do it.
9912 * (There's an automatic dependency in the opposite direction
9913 * anyway, so don't need to add one manually here.)
9914 */
9915 if (!constrs[j].separate)
9916 addObjectDependency(&tbinfo->dobj,
9917 constrs[j].dobj.dumpId);
9918
9919 /*
9920 * We will detect later whether the constraint must be split
9921 * out from the table definition.
9922 */
9923 }
9924 }
9925
9926 PQclear(res);
9927 }
9928
9930 destroyPQExpBuffer(tbloids);
9931 destroyPQExpBuffer(checkoids);
9932}
9933
9934/*
9935 * Based on the getTableAttrs query's row corresponding to one column, set
9936 * the name and flags to handle a not-null constraint for that column in
9937 * the tbinfo struct.
9938 *
9939 * Result row 'r' is for tbinfo's attribute 'j'.
9940 *
9941 * There are four possibilities:
9942 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9943 * (the constraint name) remains NULL.
9944 * 2) The column has a constraint with no name (this is the case when
9945 * constraints come from pre-18 servers). In this case, ->notnull_constrs
9946 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9947 * 3) The column has an invalid not-null constraint. This must be treated
9948 * as a separate object (because it must be created after the table data
9949 * is loaded). So we add its OID to invalidnotnulloids for processing
9950 * elsewhere and do nothing further with it here. We distinguish this
9951 * case because the "notnull_invalidoid" column has been set to a non-NULL
9952 * value, which is the constraint OID. Valid constraints have a null OID.
9953 * 4) The column has a constraint with a known name; in that case
9954 * notnull_constrs carries that name and dumpTableSchema will print
9955 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9956 * (table_column_not_null) and there's no comment on the constraint,
9957 * there's no need to print that name in the dump, so notnull_constrs
9958 * is set to the empty string and it behaves as case 2.
9959 *
9960 * In a child table that inherits from a parent already containing NOT NULL
9961 * constraints and the columns in the child don't have their own NOT NULL
9962 * declarations, we suppress printing constraints in the child: the
9963 * constraints are acquired at the point where the child is attached to the
9964 * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9965 * set not here but in flagInhAttrs. That flag is also used when the
9966 * constraint was validated in a child but all its parent have it as NOT
9967 * VALID.
9968 *
9969 * Any of these constraints might have the NO INHERIT bit. If so we set
9970 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9971 *
9972 * In case 4 above, the name comparison is a bit of a hack; it actually fails
9973 * to do the right thing in all but the trivial case. However, the downside
9974 * of getting it wrong is simply that the name is printed rather than
9975 * suppressed, so it's not a big deal.
9976 *
9977 * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9978 * constraints are found, it is initialized and filled with the array of
9979 * OIDs of such constraints, for later processing.
9980 */
9981static void
9983 TableInfo *tbinfo, int j,
9984 int i_notnull_name,
9985 int i_notnull_comment,
9986 int i_notnull_invalidoid,
9987 int i_notnull_noinherit,
9988 int i_notnull_islocal,
9989 PQExpBuffer *invalidnotnulloids)
9990{
9991 DumpOptions *dopt = fout->dopt;
9992
9993 /*
9994 * If this not-null constraint is not valid, list its OID in
9995 * invalidnotnulloids and do nothing further. It'll be processed
9996 * elsewhere later.
9997 *
9998 * Because invalid not-null constraints are rare, we don't want to malloc
9999 * invalidnotnulloids until we're sure we're going it need it, which
10000 * happens here.
10001 */
10002 if (!PQgetisnull(res, r, i_notnull_invalidoid))
10003 {
10004 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10005
10006 if (*invalidnotnulloids == NULL)
10007 {
10008 *invalidnotnulloids = createPQExpBuffer();
10009 appendPQExpBufferChar(*invalidnotnulloids, '{');
10010 appendPQExpBufferStr(*invalidnotnulloids, constroid);
10011 }
10012 else
10013 appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10014
10015 /*
10016 * Track when a parent constraint is invalid for the cases where a
10017 * child constraint has been validated independenly.
10018 */
10019 tbinfo->notnull_invalid[j] = true;
10020
10021 /* nothing else to do */
10022 tbinfo->notnull_constrs[j] = NULL;
10023 return;
10024 }
10025
10026 /*
10027 * notnull_noinh is straight from the query result. notnull_islocal also,
10028 * though flagInhAttrs may change that one later.
10029 */
10030 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10031 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10032 tbinfo->notnull_invalid[j] = false;
10033
10034 /*
10035 * Determine a constraint name to use. If the column is not marked not-
10036 * null, we set NULL which cues ... to do nothing. An empty string says
10037 * to print an unnamed NOT NULL, and anything else is a constraint name to
10038 * use.
10039 */
10040 if (fout->remoteVersion < 180000)
10041 {
10042 /*
10043 * < 18 doesn't have not-null names, so an unnamed constraint is
10044 * sufficient.
10045 */
10046 if (PQgetisnull(res, r, i_notnull_name))
10047 tbinfo->notnull_constrs[j] = NULL;
10048 else
10049 tbinfo->notnull_constrs[j] = "";
10050 }
10051 else
10052 {
10053 if (PQgetisnull(res, r, i_notnull_name))
10054 tbinfo->notnull_constrs[j] = NULL;
10055 else
10056 {
10057 /*
10058 * In binary upgrade of inheritance child tables, must have a
10059 * constraint name that we can UPDATE later; same if there's a
10060 * comment on the constraint.
10061 */
10062 if ((dopt->binary_upgrade &&
10063 !tbinfo->ispartition &&
10064 !tbinfo->notnull_islocal) ||
10065 !PQgetisnull(res, r, i_notnull_comment))
10066 {
10067 tbinfo->notnull_constrs[j] =
10068 pstrdup(PQgetvalue(res, r, i_notnull_name));
10069 }
10070 else
10071 {
10072 char *default_name;
10073
10074 /* XXX should match ChooseConstraintName better */
10075 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10076 tbinfo->attnames[j]);
10077 if (strcmp(default_name,
10078 PQgetvalue(res, r, i_notnull_name)) == 0)
10079 tbinfo->notnull_constrs[j] = "";
10080 else
10081 {
10082 tbinfo->notnull_constrs[j] =
10083 pstrdup(PQgetvalue(res, r, i_notnull_name));
10084 }
10085 free(default_name);
10086 }
10087 }
10088 }
10089}
10090
10091/*
10092 * Test whether a column should be printed as part of table's CREATE TABLE.
10093 * Column number is zero-based.
10094 *
10095 * Normally this is always true, but it's false for dropped columns, as well
10096 * as those that were inherited without any local definition. (If we print
10097 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10098 * For partitions, it's always true, because we want the partitions to be
10099 * created independently and ATTACH PARTITION used afterwards.
10100 *
10101 * In binary_upgrade mode, we must print all columns and fix the attislocal/
10102 * attisdropped state later, so as to keep control of the physical column
10103 * order.
10104 *
10105 * This function exists because there are scattered nonobvious places that
10106 * must be kept in sync with this decision.
10107 */
10108bool
10109shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10110{
10111 if (dopt->binary_upgrade)
10112 return true;
10113 if (tbinfo->attisdropped[colno])
10114 return false;
10115 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10116}
10117
10118
10119/*
10120 * getTSParsers:
10121 * get information about all text search parsers in the system catalogs
10122 */
10123void
10125{
10126 PGresult *res;
10127 int ntups;
10128 int i;
10129 PQExpBuffer query;
10130 TSParserInfo *prsinfo;
10131 int i_tableoid;
10132 int i_oid;
10133 int i_prsname;
10134 int i_prsnamespace;
10135 int i_prsstart;
10136 int i_prstoken;
10137 int i_prsend;
10138 int i_prsheadline;
10139 int i_prslextype;
10140
10141 query = createPQExpBuffer();
10142
10143 /*
10144 * find all text search objects, including builtin ones; we filter out
10145 * system-defined objects at dump-out time.
10146 */
10147
10148 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10149 "prsstart::oid, prstoken::oid, "
10150 "prsend::oid, prsheadline::oid, prslextype::oid "
10151 "FROM pg_ts_parser");
10152
10153 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10154
10155 ntups = PQntuples(res);
10156
10157 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10158
10159 i_tableoid = PQfnumber(res, "tableoid");
10160 i_oid = PQfnumber(res, "oid");
10161 i_prsname = PQfnumber(res, "prsname");
10162 i_prsnamespace = PQfnumber(res, "prsnamespace");
10163 i_prsstart = PQfnumber(res, "prsstart");
10164 i_prstoken = PQfnumber(res, "prstoken");
10165 i_prsend = PQfnumber(res, "prsend");
10166 i_prsheadline = PQfnumber(res, "prsheadline");
10167 i_prslextype = PQfnumber(res, "prslextype");
10168
10169 for (i = 0; i < ntups; i++)
10170 {
10171 prsinfo[i].dobj.objType = DO_TSPARSER;
10172 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10173 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10174 AssignDumpId(&prsinfo[i].dobj);
10175 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10176 prsinfo[i].dobj.namespace =
10177 findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10178 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10179 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10180 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10181 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10182 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10183
10184 /* Decide whether we want to dump it */
10185 selectDumpableObject(&(prsinfo[i].dobj), fout);
10186 }
10187
10188 PQclear(res);
10189
10190 destroyPQExpBuffer(query);
10191}
10192
10193/*
10194 * getTSDictionaries:
10195 * get information about all text search dictionaries in the system catalogs
10196 */
10197void
10199{
10200 PGresult *res;
10201 int ntups;
10202 int i;
10203 PQExpBuffer query;
10204 TSDictInfo *dictinfo;
10205 int i_tableoid;
10206 int i_oid;
10207 int i_dictname;
10208 int i_dictnamespace;
10209 int i_dictowner;
10210 int i_dicttemplate;
10211 int i_dictinitoption;
10212
10213 query = createPQExpBuffer();
10214
10215 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10216 "dictnamespace, dictowner, "
10217 "dicttemplate, dictinitoption "
10218 "FROM pg_ts_dict");
10219
10220 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10221
10222 ntups = PQntuples(res);
10223
10224 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10225
10226 i_tableoid = PQfnumber(res, "tableoid");
10227 i_oid = PQfnumber(res, "oid");
10228 i_dictname = PQfnumber(res, "dictname");
10229 i_dictnamespace = PQfnumber(res, "dictnamespace");
10230 i_dictowner = PQfnumber(res, "dictowner");
10231 i_dictinitoption = PQfnumber(res, "dictinitoption");
10232 i_dicttemplate = PQfnumber(res, "dicttemplate");
10233
10234 for (i = 0; i < ntups; i++)
10235 {
10236 dictinfo[i].dobj.objType = DO_TSDICT;
10237 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10238 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10239 AssignDumpId(&dictinfo[i].dobj);
10240 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10241 dictinfo[i].dobj.namespace =
10242 findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10243 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10244 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10245 if (PQgetisnull(res, i, i_dictinitoption))
10246 dictinfo[i].dictinitoption = NULL;
10247 else
10248 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10249
10250 /* Decide whether we want to dump it */
10251 selectDumpableObject(&(dictinfo[i].dobj), fout);
10252 }
10253
10254 PQclear(res);
10255
10256 destroyPQExpBuffer(query);
10257}
10258
10259/*
10260 * getTSTemplates:
10261 * get information about all text search templates in the system catalogs
10262 */
10263void
10265{
10266 PGresult *res;
10267 int ntups;
10268 int i;
10269 PQExpBuffer query;
10270 TSTemplateInfo *tmplinfo;
10271 int i_tableoid;
10272 int i_oid;
10273 int i_tmplname;
10274 int i_tmplnamespace;
10275 int i_tmplinit;
10276 int i_tmpllexize;
10277
10278 query = createPQExpBuffer();
10279
10280 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10281 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10282 "FROM pg_ts_template");
10283
10284 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10285
10286 ntups = PQntuples(res);
10287
10288 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10289
10290 i_tableoid = PQfnumber(res, "tableoid");
10291 i_oid = PQfnumber(res, "oid");
10292 i_tmplname = PQfnumber(res, "tmplname");
10293 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10294 i_tmplinit = PQfnumber(res, "tmplinit");
10295 i_tmpllexize = PQfnumber(res, "tmpllexize");
10296
10297 for (i = 0; i < ntups; i++)
10298 {
10299 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10300 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10301 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10302 AssignDumpId(&tmplinfo[i].dobj);
10303 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10304 tmplinfo[i].dobj.namespace =
10305 findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10306 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10307 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10308
10309 /* Decide whether we want to dump it */
10310 selectDumpableObject(&(tmplinfo[i].dobj), fout);
10311 }
10312
10313 PQclear(res);
10314
10315 destroyPQExpBuffer(query);
10316}
10317
10318/*
10319 * getTSConfigurations:
10320 * get information about all text search configurations
10321 */
10322void
10324{
10325 PGresult *res;
10326 int ntups;
10327 int i;
10328 PQExpBuffer query;
10329 TSConfigInfo *cfginfo;
10330 int i_tableoid;
10331 int i_oid;
10332 int i_cfgname;
10333 int i_cfgnamespace;
10334 int i_cfgowner;
10335 int i_cfgparser;
10336
10337 query = createPQExpBuffer();
10338
10339 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10340 "cfgnamespace, cfgowner, cfgparser "
10341 "FROM pg_ts_config");
10342
10343 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10344
10345 ntups = PQntuples(res);
10346
10347 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10348
10349 i_tableoid = PQfnumber(res, "tableoid");
10350 i_oid = PQfnumber(res, "oid");
10351 i_cfgname = PQfnumber(res, "cfgname");
10352 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10353 i_cfgowner = PQfnumber(res, "cfgowner");
10354 i_cfgparser = PQfnumber(res, "cfgparser");
10355
10356 for (i = 0; i < ntups; i++)
10357 {
10358 cfginfo[i].dobj.objType = DO_TSCONFIG;
10359 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10360 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10361 AssignDumpId(&cfginfo[i].dobj);
10362 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10363 cfginfo[i].dobj.namespace =
10364 findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10365 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10366 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10367
10368 /* Decide whether we want to dump it */
10369 selectDumpableObject(&(cfginfo[i].dobj), fout);
10370 }
10371
10372 PQclear(res);
10373
10374 destroyPQExpBuffer(query);
10375}
10376
10377/*
10378 * getForeignDataWrappers:
10379 * get information about all foreign-data wrappers in the system catalogs
10380 */
10381void
10383{
10384 PGresult *res;
10385 int ntups;
10386 int i;
10387 PQExpBuffer query;
10388 FdwInfo *fdwinfo;
10389 int i_tableoid;
10390 int i_oid;
10391 int i_fdwname;
10392 int i_fdwowner;
10393 int i_fdwhandler;
10394 int i_fdwvalidator;
10395 int i_fdwacl;
10396 int i_acldefault;
10397 int i_fdwoptions;
10398
10399 query = createPQExpBuffer();
10400
10401 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10402 "fdwowner, "
10403 "fdwhandler::pg_catalog.regproc, "
10404 "fdwvalidator::pg_catalog.regproc, "
10405 "fdwacl, "
10406 "acldefault('F', fdwowner) AS acldefault, "
10407 "array_to_string(ARRAY("
10408 "SELECT quote_ident(option_name) || ' ' || "
10409 "quote_literal(option_value) "
10410 "FROM pg_options_to_table(fdwoptions) "
10411 "ORDER BY option_name"
10412 "), E',\n ') AS fdwoptions "
10413 "FROM pg_foreign_data_wrapper");
10414
10415 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10416
10417 ntups = PQntuples(res);
10418
10419 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10420
10421 i_tableoid = PQfnumber(res, "tableoid");
10422 i_oid = PQfnumber(res, "oid");
10423 i_fdwname = PQfnumber(res, "fdwname");
10424 i_fdwowner = PQfnumber(res, "fdwowner");
10425 i_fdwhandler = PQfnumber(res, "fdwhandler");
10426 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10427 i_fdwacl = PQfnumber(res, "fdwacl");
10428 i_acldefault = PQfnumber(res, "acldefault");
10429 i_fdwoptions = PQfnumber(res, "fdwoptions");
10430
10431 for (i = 0; i < ntups; i++)
10432 {
10433 fdwinfo[i].dobj.objType = DO_FDW;
10434 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10435 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10436 AssignDumpId(&fdwinfo[i].dobj);
10437 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10438 fdwinfo[i].dobj.namespace = NULL;
10439 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10440 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10441 fdwinfo[i].dacl.privtype = 0;
10442 fdwinfo[i].dacl.initprivs = NULL;
10443 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10444 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10445 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10446 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10447
10448 /* Decide whether we want to dump it */
10449 selectDumpableObject(&(fdwinfo[i].dobj), fout);
10450
10451 /* Mark whether FDW has an ACL */
10452 if (!PQgetisnull(res, i, i_fdwacl))
10453 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10454 }
10455
10456 PQclear(res);
10457
10458 destroyPQExpBuffer(query);
10459}
10460
10461/*
10462 * getForeignServers:
10463 * get information about all foreign servers in the system catalogs
10464 */
10465void
10467{
10468 PGresult *res;
10469 int ntups;
10470 int i;
10471 PQExpBuffer query;
10472 ForeignServerInfo *srvinfo;
10473 int i_tableoid;
10474 int i_oid;
10475 int i_srvname;
10476 int i_srvowner;
10477 int i_srvfdw;
10478 int i_srvtype;
10479 int i_srvversion;
10480 int i_srvacl;
10481 int i_acldefault;
10482 int i_srvoptions;
10483
10484 query = createPQExpBuffer();
10485
10486 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10487 "srvowner, "
10488 "srvfdw, srvtype, srvversion, srvacl, "
10489 "acldefault('S', srvowner) AS acldefault, "
10490 "array_to_string(ARRAY("
10491 "SELECT quote_ident(option_name) || ' ' || "
10492 "quote_literal(option_value) "
10493 "FROM pg_options_to_table(srvoptions) "
10494 "ORDER BY option_name"
10495 "), E',\n ') AS srvoptions "
10496 "FROM pg_foreign_server");
10497
10498 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10499
10500 ntups = PQntuples(res);
10501
10502 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10503
10504 i_tableoid = PQfnumber(res, "tableoid");
10505 i_oid = PQfnumber(res, "oid");
10506 i_srvname = PQfnumber(res, "srvname");
10507 i_srvowner = PQfnumber(res, "srvowner");
10508 i_srvfdw = PQfnumber(res, "srvfdw");
10509 i_srvtype = PQfnumber(res, "srvtype");
10510 i_srvversion = PQfnumber(res, "srvversion");
10511 i_srvacl = PQfnumber(res, "srvacl");
10512 i_acldefault = PQfnumber(res, "acldefault");
10513 i_srvoptions = PQfnumber(res, "srvoptions");
10514
10515 for (i = 0; i < ntups; i++)
10516 {
10517 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10518 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10519 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10520 AssignDumpId(&srvinfo[i].dobj);
10521 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10522 srvinfo[i].dobj.namespace = NULL;
10523 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10524 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10525 srvinfo[i].dacl.privtype = 0;
10526 srvinfo[i].dacl.initprivs = NULL;
10527 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10528 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10529 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10530 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10531 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10532
10533 /* Decide whether we want to dump it */
10534 selectDumpableObject(&(srvinfo[i].dobj), fout);
10535
10536 /* Servers have user mappings */
10538
10539 /* Mark whether server has an ACL */
10540 if (!PQgetisnull(res, i, i_srvacl))
10541 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10542 }
10543
10544 PQclear(res);
10545
10546 destroyPQExpBuffer(query);
10547}
10548
10549/*
10550 * getDefaultACLs:
10551 * get information about all default ACL information in the system catalogs
10552 */
10553void
10555{
10556 DumpOptions *dopt = fout->dopt;
10557 DefaultACLInfo *daclinfo;
10558 PQExpBuffer query;
10559 PGresult *res;
10560 int i_oid;
10561 int i_tableoid;
10562 int i_defaclrole;
10563 int i_defaclnamespace;
10564 int i_defaclobjtype;
10565 int i_defaclacl;
10566 int i_acldefault;
10567 int i,
10568 ntups;
10569
10570 query = createPQExpBuffer();
10571
10572 /*
10573 * Global entries (with defaclnamespace=0) replace the hard-wired default
10574 * ACL for their object type. We should dump them as deltas from the
10575 * default ACL, since that will be used as a starting point for
10576 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10577 * non-global entries can only add privileges not revoke them. We must
10578 * dump those as-is (i.e., as deltas from an empty ACL).
10579 *
10580 * We can use defaclobjtype as the object type for acldefault(), except
10581 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10582 * 's'.
10583 */
10585 "SELECT oid, tableoid, "
10586 "defaclrole, "
10587 "defaclnamespace, "
10588 "defaclobjtype, "
10589 "defaclacl, "
10590 "CASE WHEN defaclnamespace = 0 THEN "
10591 "acldefault(CASE WHEN defaclobjtype = 'S' "
10592 "THEN 's'::\"char\" ELSE defaclobjtype END, "
10593 "defaclrole) ELSE '{}' END AS acldefault "
10594 "FROM pg_default_acl");
10595
10596 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10597
10598 ntups = PQntuples(res);
10599
10600 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10601
10602 i_oid = PQfnumber(res, "oid");
10603 i_tableoid = PQfnumber(res, "tableoid");
10604 i_defaclrole = PQfnumber(res, "defaclrole");
10605 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10606 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10607 i_defaclacl = PQfnumber(res, "defaclacl");
10608 i_acldefault = PQfnumber(res, "acldefault");
10609
10610 for (i = 0; i < ntups; i++)
10611 {
10612 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10613
10614 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10615 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10616 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10617 AssignDumpId(&daclinfo[i].dobj);
10618 /* cheesy ... is it worth coming up with a better object name? */
10619 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10620
10621 if (nspid != InvalidOid)
10622 daclinfo[i].dobj.namespace = findNamespace(nspid);
10623 else
10624 daclinfo[i].dobj.namespace = NULL;
10625
10626 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10627 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10628 daclinfo[i].dacl.privtype = 0;
10629 daclinfo[i].dacl.initprivs = NULL;
10630 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10631 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10632
10633 /* Default ACLs are ACLs, of course */
10634 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10635
10636 /* Decide whether we want to dump it */
10637 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10638 }
10639
10640 PQclear(res);
10641
10642 destroyPQExpBuffer(query);
10643}
10644
10645/*
10646 * getRoleName -- look up the name of a role, given its OID
10647 *
10648 * In current usage, we don't expect failures, so error out for a bad OID.
10649 */
10650static const char *
10651getRoleName(const char *roleoid_str)
10652{
10653 Oid roleoid = atooid(roleoid_str);
10654
10655 /*
10656 * Do binary search to find the appropriate item.
10657 */
10658 if (nrolenames > 0)
10659 {
10660 RoleNameItem *low = &rolenames[0];
10661 RoleNameItem *high = &rolenames[nrolenames - 1];
10662
10663 while (low <= high)
10664 {
10665 RoleNameItem *middle = low + (high - low) / 2;
10666
10667 if (roleoid < middle->roleoid)
10668 high = middle - 1;
10669 else if (roleoid > middle->roleoid)
10670 low = middle + 1;
10671 else
10672 return middle->rolename; /* found a match */
10673 }
10674 }
10675
10676 pg_fatal("role with OID %u does not exist", roleoid);
10677 return NULL; /* keep compiler quiet */
10678}
10679
10680/*
10681 * collectRoleNames --
10682 *
10683 * Construct a table of all known roles.
10684 * The table is sorted by OID for speed in lookup.
10685 */
10686static void
10688{
10689 PGresult *res;
10690 const char *query;
10691 int i;
10692
10693 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10694
10695 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10696
10697 nrolenames = PQntuples(res);
10698
10700
10701 for (i = 0; i < nrolenames; i++)
10702 {
10703 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10705 }
10706
10707 PQclear(res);
10708}
10709
10710/*
10711 * getAdditionalACLs
10712 *
10713 * We have now created all the DumpableObjects, and collected the ACL data
10714 * that appears in the directly-associated catalog entries. However, there's
10715 * more ACL-related info to collect. If any of a table's columns have ACLs,
10716 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10717 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10718 * Also, in versions having the pg_init_privs catalog, read that and load the
10719 * information into the relevant DumpableObjects.
10720 */
10721static void
10723{
10725 PGresult *res;
10726 int ntups,
10727 i;
10728
10729 /* Check for per-column ACLs */
10731 "SELECT DISTINCT attrelid FROM pg_attribute "
10732 "WHERE attacl IS NOT NULL");
10733
10734 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10735
10736 ntups = PQntuples(res);
10737 for (i = 0; i < ntups; i++)
10738 {
10739 Oid relid = atooid(PQgetvalue(res, i, 0));
10740 TableInfo *tblinfo;
10741
10742 tblinfo = findTableByOid(relid);
10743 /* OK to ignore tables we haven't got a DumpableObject for */
10744 if (tblinfo)
10745 {
10747 tblinfo->hascolumnACLs = true;
10748 }
10749 }
10750 PQclear(res);
10751
10752 /* Fetch initial-privileges data */
10753 if (fout->remoteVersion >= 90600)
10754 {
10755 printfPQExpBuffer(query,
10756 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10757 "FROM pg_init_privs");
10758
10759 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10760
10761 ntups = PQntuples(res);
10762 for (i = 0; i < ntups; i++)
10763 {
10764 Oid objoid = atooid(PQgetvalue(res, i, 0));
10765 Oid classoid = atooid(PQgetvalue(res, i, 1));
10766 int objsubid = atoi(PQgetvalue(res, i, 2));
10767 char privtype = *(PQgetvalue(res, i, 3));
10768 char *initprivs = PQgetvalue(res, i, 4);
10769 CatalogId objId;
10770 DumpableObject *dobj;
10771
10772 objId.tableoid = classoid;
10773 objId.oid = objoid;
10774 dobj = findObjectByCatalogId(objId);
10775 /* OK to ignore entries we haven't got a DumpableObject for */
10776 if (dobj)
10777 {
10778 /* Cope with sub-object initprivs */
10779 if (objsubid != 0)
10780 {
10781 if (dobj->objType == DO_TABLE)
10782 {
10783 /* For a column initprivs, set the table's ACL flags */
10785 ((TableInfo *) dobj)->hascolumnACLs = true;
10786 }
10787 else
10788 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10789 classoid, objoid, objsubid);
10790 continue;
10791 }
10792
10793 /*
10794 * We ignore any pg_init_privs.initprivs entry for the public
10795 * schema, as explained in getNamespaces().
10796 */
10797 if (dobj->objType == DO_NAMESPACE &&
10798 strcmp(dobj->name, "public") == 0)
10799 continue;
10800
10801 /* Else it had better be of a type we think has ACLs */
10802 if (dobj->objType == DO_NAMESPACE ||
10803 dobj->objType == DO_TYPE ||
10804 dobj->objType == DO_FUNC ||
10805 dobj->objType == DO_AGG ||
10806 dobj->objType == DO_TABLE ||
10807 dobj->objType == DO_PROCLANG ||
10808 dobj->objType == DO_FDW ||
10809 dobj->objType == DO_FOREIGN_SERVER)
10810 {
10812
10813 daobj->dacl.privtype = privtype;
10814 daobj->dacl.initprivs = pstrdup(initprivs);
10815 }
10816 else
10817 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10818 classoid, objoid, objsubid);
10819 }
10820 }
10821 PQclear(res);
10822 }
10823
10824 destroyPQExpBuffer(query);
10825}
10826
10827/*
10828 * dumpCommentExtended --
10829 *
10830 * This routine is used to dump any comments associated with the
10831 * object handed to this routine. The routine takes the object type
10832 * and object name (ready to print, except for schema decoration), plus
10833 * the namespace and owner of the object (for labeling the ArchiveEntry),
10834 * plus catalog ID and subid which are the lookup key for pg_description,
10835 * plus the dump ID for the object (for setting a dependency).
10836 * If a matching pg_description entry is found, it is dumped.
10837 *
10838 * Note: in some cases, such as comments for triggers and rules, the "type"
10839 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10840 * but it doesn't seem worth complicating the API for all callers to make
10841 * it cleaner.
10842 *
10843 * Note: although this routine takes a dumpId for dependency purposes,
10844 * that purpose is just to mark the dependency in the emitted dump file
10845 * for possible future use by pg_restore. We do NOT use it for determining
10846 * ordering of the comment in the dump file, because this routine is called
10847 * after dependency sorting occurs. This routine should be called just after
10848 * calling ArchiveEntry() for the specified object.
10849 */
10850static void
10852 const char *name, const char *namespace,
10853 const char *owner, CatalogId catalogId,
10854 int subid, DumpId dumpId,
10855 const char *initdb_comment)
10856{
10857 DumpOptions *dopt = fout->dopt;
10859 int ncomments;
10860
10861 /* do nothing, if --no-comments is supplied */
10862 if (dopt->no_comments)
10863 return;
10864
10865 /* Comments are schema not data ... except LO comments are data */
10866 if (strcmp(type, "LARGE OBJECT") != 0)
10867 {
10868 if (!dopt->dumpSchema)
10869 return;
10870 }
10871 else
10872 {
10873 /* We do dump LO comments in binary-upgrade mode */
10874 if (!dopt->dumpData && !dopt->binary_upgrade)
10875 return;
10876 }
10877
10878 /* Search for comments associated with catalogId, using table */
10879 ncomments = findComments(catalogId.tableoid, catalogId.oid,
10880 &comments);
10881
10882 /* Is there one matching the subid? */
10883 while (ncomments > 0)
10884 {
10885 if (comments->objsubid == subid)
10886 break;
10887 comments++;
10888 ncomments--;
10889 }
10890
10891 if (initdb_comment != NULL)
10892 {
10893 static CommentItem empty_comment = {.descr = ""};
10894
10895 /*
10896 * initdb creates this object with a comment. Skip dumping the
10897 * initdb-provided comment, which would complicate matters for
10898 * non-superuser use of pg_dump. When the DBA has removed initdb's
10899 * comment, replicate that.
10900 */
10901 if (ncomments == 0)
10902 {
10903 comments = &empty_comment;
10904 ncomments = 1;
10905 }
10906 else if (strcmp(comments->descr, initdb_comment) == 0)
10907 ncomments = 0;
10908 }
10909
10910 /* If a comment exists, build COMMENT ON statement */
10911 if (ncomments > 0)
10912 {
10915
10916 appendPQExpBuffer(query, "COMMENT ON %s ", type);
10917 if (namespace && *namespace)
10918 appendPQExpBuffer(query, "%s.", fmtId(namespace));
10919 appendPQExpBuffer(query, "%s IS ", name);
10920 appendStringLiteralAH(query, comments->descr, fout);
10921 appendPQExpBufferStr(query, ";\n");
10922
10923 appendPQExpBuffer(tag, "%s %s", type, name);
10924
10925 /*
10926 * We mark comments as SECTION_NONE because they really belong in the
10927 * same section as their parent, whether that is pre-data or
10928 * post-data.
10929 */
10931 ARCHIVE_OPTS(.tag = tag->data,
10932 .namespace = namespace,
10933 .owner = owner,
10934 .description = "COMMENT",
10935 .section = SECTION_NONE,
10936 .createStmt = query->data,
10937 .deps = &dumpId,
10938 .nDeps = 1));
10939
10940 destroyPQExpBuffer(query);
10941 destroyPQExpBuffer(tag);
10942 }
10943}
10944
10945/*
10946 * dumpComment --
10947 *
10948 * Typical simplification of the above function.
10949 */
10950static inline void
10951dumpComment(Archive *fout, const char *type,
10952 const char *name, const char *namespace,
10953 const char *owner, CatalogId catalogId,
10954 int subid, DumpId dumpId)
10955{
10956 dumpCommentExtended(fout, type, name, namespace, owner,
10957 catalogId, subid, dumpId, NULL);
10958}
10959
10960/*
10961 * appendNamedArgument --
10962 *
10963 * Convenience routine for constructing parameters of the form:
10964 * 'paraname', 'value'::type
10965 */
10966static void
10967appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10968 const char *argtype, const char *argval)
10969{
10970 appendPQExpBufferStr(out, ",\n\t");
10971
10972 appendStringLiteralAH(out, argname, fout);
10973 appendPQExpBufferStr(out, ", ");
10974
10975 appendStringLiteralAH(out, argval, fout);
10976 appendPQExpBuffer(out, "::%s", argtype);
10977}
10978
10979/*
10980 * fetchAttributeStats --
10981 *
10982 * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10983 */
10984static PGresult *
10986{
10987 ArchiveHandle *AH = (ArchiveHandle *) fout;
10988 PQExpBuffer nspnames = createPQExpBuffer();
10989 PQExpBuffer relnames = createPQExpBuffer();
10990 int count = 0;
10991 PGresult *res = NULL;
10992 static TocEntry *te;
10993 static bool restarted;
10994 int max_rels = MAX_ATTR_STATS_RELS;
10995
10996 /*
10997 * Our query for retrieving statistics for multiple relations uses WITH
10998 * ORDINALITY and multi-argument UNNEST(), both of which were introduced
10999 * in v9.4. For older versions, we resort to gathering statistics for a
11000 * single relation at a time.
11001 */
11002 if (fout->remoteVersion < 90400)
11003 max_rels = 1;
11004
11005 /* If we're just starting, set our TOC pointer. */
11006 if (!te)
11007 te = AH->toc->next;
11008
11009 /*
11010 * We can't easily avoid a second TOC scan for the tar format because it
11011 * writes restore.sql separately, which means we must execute the queries
11012 * twice. This feels risky, but there is no known reason it should
11013 * generate different output than the first pass. Even if it does, the
11014 * worst-case scenario is that restore.sql might have different statistics
11015 * data than the archive.
11016 */
11017 if (!restarted && te == AH->toc && AH->format == archTar)
11018 {
11019 te = AH->toc->next;
11020 restarted = true;
11021 }
11022
11023 appendPQExpBufferChar(nspnames, '{');
11024 appendPQExpBufferChar(relnames, '{');
11025
11026 /*
11027 * Scan the TOC for the next set of relevant stats entries. We assume
11028 * that statistics are dumped in the order they are listed in the TOC.
11029 * This is perhaps not the sturdiest assumption, so we verify it matches
11030 * reality in dumpRelationStats_dumper().
11031 */
11032 for (; te != AH->toc && count < max_rels; te = te->next)
11033 {
11034 if ((te->reqs & REQ_STATS) != 0 &&
11035 strcmp(te->desc, "STATISTICS DATA") == 0)
11036 {
11037 appendPGArray(nspnames, te->namespace);
11038 appendPGArray(relnames, te->tag);
11039 count++;
11040 }
11041 }
11042
11043 appendPQExpBufferChar(nspnames, '}');
11044 appendPQExpBufferChar(relnames, '}');
11045
11046 /* Execute the query for the next batch of relations. */
11047 if (count > 0)
11048 {
11050
11051 appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11052 appendStringLiteralAH(query, nspnames->data, fout);
11053 appendPQExpBufferStr(query, "::pg_catalog.name[],");
11054 appendStringLiteralAH(query, relnames->data, fout);
11055 appendPQExpBufferStr(query, "::pg_catalog.name[])");
11056 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11057 destroyPQExpBuffer(query);
11058 }
11059
11060 destroyPQExpBuffer(nspnames);
11061 destroyPQExpBuffer(relnames);
11062 return res;
11063}
11064
11065/*
11066 * dumpRelationStats_dumper --
11067 *
11068 * Generate command to import stats into the relation on the new database.
11069 * This routine is called by the Archiver when it wants the statistics to be
11070 * dumped.
11071 */
11072static char *
11073dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11074{
11075 const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11076 static PGresult *res;
11077 static int rownum;
11078 PQExpBuffer query;
11079 PQExpBufferData out_data;
11080 PQExpBuffer out = &out_data;
11081 int i_schemaname;
11082 int i_tablename;
11083 int i_attname;
11084 int i_inherited;
11085 int i_null_frac;
11086 int i_avg_width;
11087 int i_n_distinct;
11088 int i_most_common_vals;
11089 int i_most_common_freqs;
11090 int i_histogram_bounds;
11091 int i_correlation;
11092 int i_most_common_elems;
11093 int i_most_common_elem_freqs;
11094 int i_elem_count_histogram;
11095 int i_range_length_histogram;
11096 int i_range_empty_frac;
11097 int i_range_bounds_histogram;
11098 static TocEntry *expected_te;
11099
11100 /*
11101 * fetchAttributeStats() assumes that the statistics are dumped in the
11102 * order they are listed in the TOC. We verify that here for safety.
11103 */
11104 if (!expected_te)
11105 expected_te = ((ArchiveHandle *) fout)->toc;
11106
11107 expected_te = expected_te->next;
11108 while ((expected_te->reqs & REQ_STATS) == 0 ||
11109 strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11110 expected_te = expected_te->next;
11111
11112 if (te != expected_te)
11113 pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11114 te->dumpId, te->desc, te->tag,
11115 expected_te->dumpId, expected_te->desc, expected_te->tag);
11116
11117 query = createPQExpBuffer();
11119 {
11121 "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11122 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11123 "s.null_frac, s.avg_width, s.n_distinct, "
11124 "s.most_common_vals, s.most_common_freqs, "
11125 "s.histogram_bounds, s.correlation, "
11126 "s.most_common_elems, s.most_common_elem_freqs, "
11127 "s.elem_count_histogram, ");
11128
11129 if (fout->remoteVersion >= 170000)
11131 "s.range_length_histogram, "
11132 "s.range_empty_frac, "
11133 "s.range_bounds_histogram ");
11134 else
11136 "NULL AS range_length_histogram,"
11137 "NULL AS range_empty_frac,"
11138 "NULL AS range_bounds_histogram ");
11139
11140 /*
11141 * The results must be in the order of the relations supplied in the
11142 * parameters to ensure we remain in sync as we walk through the TOC.
11143 * The redundant filter clause on s.tablename = ANY(...) seems
11144 * sufficient to convince the planner to use
11145 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11146 * This may not work for all versions.
11147 *
11148 * Our query for retrieving statistics for multiple relations uses
11149 * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11150 * introduced in v9.4. For older versions, we resort to gathering
11151 * statistics for a single relation at a time.
11152 */
11153 if (fout->remoteVersion >= 90400)
11155 "FROM pg_catalog.pg_stats s "
11156 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11157 "ON s.schemaname = u.schemaname "
11158 "AND s.tablename = u.tablename "
11159 "WHERE s.tablename = ANY($2) "
11160 "ORDER BY u.ord, s.attname, s.inherited");
11161 else
11163 "FROM pg_catalog.pg_stats s "
11164 "WHERE s.schemaname = $1[1] "
11165 "AND s.tablename = $2[1] "
11166 "ORDER BY s.attname, s.inherited");
11167
11168 ExecuteSqlStatement(fout, query->data);
11169
11171 resetPQExpBuffer(query);
11172 }
11173
11174 initPQExpBuffer(out);
11175
11176 /* restore relation stats */
11177 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11178 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11179 fout->remoteVersion);
11180 appendPQExpBufferStr(out, "\t'schemaname', ");
11181 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11182 appendPQExpBufferStr(out, ",\n");
11183 appendPQExpBufferStr(out, "\t'relname', ");
11184 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11185 appendPQExpBufferStr(out, ",\n");
11186 appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11187
11188 /*
11189 * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11190 * the relation is empty, or it could mean that it hadn't yet been
11191 * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11192 * This ambiguity allegedly can cause the planner to choose inefficient
11193 * plans after restoring to v18 or newer. To deal with this, let's just
11194 * set reltuples to -1 in that case.
11195 */
11196 if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11197 appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11198 else
11199 appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11200
11201 appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11202 rsinfo->relallvisible);
11203
11204 if (fout->remoteVersion >= 180000)
11205 appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11206
11207 appendPQExpBufferStr(out, "\n);\n");
11208
11209 /* Fetch the next batch of attribute statistics if needed. */
11210 if (rownum >= PQntuples(res))
11211 {
11212 PQclear(res);
11213 res = fetchAttributeStats(fout);
11214 rownum = 0;
11215 }
11216
11217 i_schemaname = PQfnumber(res, "schemaname");
11218 i_tablename = PQfnumber(res, "tablename");
11219 i_attname = PQfnumber(res, "attname");
11220 i_inherited = PQfnumber(res, "inherited");
11221 i_null_frac = PQfnumber(res, "null_frac");
11222 i_avg_width = PQfnumber(res, "avg_width");
11223 i_n_distinct = PQfnumber(res, "n_distinct");
11224 i_most_common_vals = PQfnumber(res, "most_common_vals");
11225 i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11226 i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11227 i_correlation = PQfnumber(res, "correlation");
11228 i_most_common_elems = PQfnumber(res, "most_common_elems");
11229 i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11230 i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11231 i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11232 i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11233 i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11234
11235 /* restore attribute stats */
11236 for (; rownum < PQntuples(res); rownum++)
11237 {
11238 const char *attname;
11239
11240 /* Stop if the next stat row in our cache isn't for this relation. */
11241 if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11242 strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11243 break;
11244
11245 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11246 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11247 fout->remoteVersion);
11248 appendPQExpBufferStr(out, "\t'schemaname', ");
11249 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11250 appendPQExpBufferStr(out, ",\n\t'relname', ");
11251 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11252
11253 if (PQgetisnull(res, rownum, i_attname))
11254 pg_fatal("unexpected null attname");
11255 attname = PQgetvalue(res, rownum, i_attname);
11256
11257 /*
11258 * Indexes look up attname in indAttNames to derive attnum, all others
11259 * use attname directly. We must specify attnum for indexes, since
11260 * their attnames are not necessarily stable across dump/reload.
11261 */
11262 if (rsinfo->nindAttNames == 0)
11263 {
11264 appendPQExpBufferStr(out, ",\n\t'attname', ");
11265 appendStringLiteralAH(out, attname, fout);
11266 }
11267 else
11268 {
11269 bool found = false;
11270
11271 for (int i = 0; i < rsinfo->nindAttNames; i++)
11272 {
11273 if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11274 {
11275 appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11276 i + 1);
11277 found = true;
11278 break;
11279 }
11280 }
11281
11282 if (!found)
11283 pg_fatal("could not find index attname \"%s\"", attname);
11284 }
11285
11286 if (!PQgetisnull(res, rownum, i_inherited))
11287 appendNamedArgument(out, fout, "inherited", "boolean",
11288 PQgetvalue(res, rownum, i_inherited));
11289 if (!PQgetisnull(res, rownum, i_null_frac))
11290 appendNamedArgument(out, fout, "null_frac", "real",
11291 PQgetvalue(res, rownum, i_null_frac));
11292 if (!PQgetisnull(res, rownum, i_avg_width))
11293 appendNamedArgument(out, fout, "avg_width", "integer",
11294 PQgetvalue(res, rownum, i_avg_width));
11295 if (!PQgetisnull(res, rownum, i_n_distinct))
11296 appendNamedArgument(out, fout, "n_distinct", "real",
11297 PQgetvalue(res, rownum, i_n_distinct));
11298 if (!PQgetisnull(res, rownum, i_most_common_vals))
11299 appendNamedArgument(out, fout, "most_common_vals", "text",
11300 PQgetvalue(res, rownum, i_most_common_vals));
11301 if (!PQgetisnull(res, rownum, i_most_common_freqs))
11302 appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11303 PQgetvalue(res, rownum, i_most_common_freqs));
11304 if (!PQgetisnull(res, rownum, i_histogram_bounds))
11305 appendNamedArgument(out, fout, "histogram_bounds", "text",
11306 PQgetvalue(res, rownum, i_histogram_bounds));
11307 if (!PQgetisnull(res, rownum, i_correlation))
11308 appendNamedArgument(out, fout, "correlation", "real",
11309 PQgetvalue(res, rownum, i_correlation));
11310 if (!PQgetisnull(res, rownum, i_most_common_elems))
11311 appendNamedArgument(out, fout, "most_common_elems", "text",
11312 PQgetvalue(res, rownum, i_most_common_elems));
11313 if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11314 appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11315 PQgetvalue(res, rownum, i_most_common_elem_freqs));
11316 if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11317 appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11318 PQgetvalue(res, rownum, i_elem_count_histogram));
11319 if (fout->remoteVersion >= 170000)
11320 {
11321 if (!PQgetisnull(res, rownum, i_range_length_histogram))
11322 appendNamedArgument(out, fout, "range_length_histogram", "text",
11323 PQgetvalue(res, rownum, i_range_length_histogram));
11324 if (!PQgetisnull(res, rownum, i_range_empty_frac))
11325 appendNamedArgument(out, fout, "range_empty_frac", "real",
11326 PQgetvalue(res, rownum, i_range_empty_frac));
11327 if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11328 appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11329 PQgetvalue(res, rownum, i_range_bounds_histogram));
11330 }
11331 appendPQExpBufferStr(out, "\n);\n");
11332 }
11333
11334 destroyPQExpBuffer(query);
11335 return out->data;
11336}
11337
11338/*
11339 * dumpRelationStats --
11340 *
11341 * Make an ArchiveEntry for the relation statistics. The Archiver will take
11342 * care of gathering the statistics and generating the restore commands when
11343 * they are needed.
11344 */
11345static void
11347{
11348 const DumpableObject *dobj = &rsinfo->dobj;
11349
11350 /* nothing to do if we are not dumping statistics */
11351 if (!fout->dopt->dumpStatistics)
11352 return;
11353
11355 ARCHIVE_OPTS(.tag = dobj->name,
11356 .namespace = dobj->namespace->dobj.name,
11357 .description = "STATISTICS DATA",
11358 .section = rsinfo->section,
11359 .defnFn = dumpRelationStats_dumper,
11360 .defnArg = rsinfo,
11361 .deps = dobj->dependencies,
11362 .nDeps = dobj->nDeps));
11363}
11364
11365/*
11366 * dumpTableComment --
11367 *
11368 * As above, but dump comments for both the specified table (or view)
11369 * and its columns.
11370 */
11371static void
11373 const char *reltypename)
11374{
11375 DumpOptions *dopt = fout->dopt;
11377 int ncomments;
11378 PQExpBuffer query;
11379 PQExpBuffer tag;
11380
11381 /* do nothing, if --no-comments is supplied */
11382 if (dopt->no_comments)
11383 return;
11384
11385 /* Comments are SCHEMA not data */
11386 if (!dopt->dumpSchema)
11387 return;
11388
11389 /* Search for comments associated with relation, using table */
11391 tbinfo->dobj.catId.oid,
11392 &comments);
11393
11394 /* If comments exist, build COMMENT ON statements */
11395 if (ncomments <= 0)
11396 return;
11397
11398 query = createPQExpBuffer();
11399 tag = createPQExpBuffer();
11400
11401 while (ncomments > 0)
11402 {
11403 const char *descr = comments->descr;
11404 int objsubid = comments->objsubid;
11405
11406 if (objsubid == 0)
11407 {
11408 resetPQExpBuffer(tag);
11409 appendPQExpBuffer(tag, "%s %s", reltypename,
11410 fmtId(tbinfo->dobj.name));
11411
11412 resetPQExpBuffer(query);
11413 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11414 fmtQualifiedDumpable(tbinfo));
11415 appendStringLiteralAH(query, descr, fout);
11416 appendPQExpBufferStr(query, ";\n");
11417
11419 ARCHIVE_OPTS(.tag = tag->data,
11420 .namespace = tbinfo->dobj.namespace->dobj.name,
11421 .owner = tbinfo->rolname,
11422 .description = "COMMENT",
11423 .section = SECTION_NONE,
11424 .createStmt = query->data,
11425 .deps = &(tbinfo->dobj.dumpId),
11426 .nDeps = 1));
11427 }
11428 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11429 {
11430 resetPQExpBuffer(tag);
11431 appendPQExpBuffer(tag, "COLUMN %s.",
11432 fmtId(tbinfo->dobj.name));
11433 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11434
11435 resetPQExpBuffer(query);
11436 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11437 fmtQualifiedDumpable(tbinfo));
11438 appendPQExpBuffer(query, "%s IS ",
11439 fmtId(tbinfo->attnames[objsubid - 1]));
11440 appendStringLiteralAH(query, descr, fout);
11441 appendPQExpBufferStr(query, ";\n");
11442
11444 ARCHIVE_OPTS(.tag = tag->data,
11445 .namespace = tbinfo->dobj.namespace->dobj.name,
11446 .owner = tbinfo->rolname,
11447 .description = "COMMENT",
11448 .section = SECTION_NONE,
11449 .createStmt = query->data,
11450 .deps = &(tbinfo->dobj.dumpId),
11451 .nDeps = 1));
11452 }
11453
11454 comments++;
11455 ncomments--;
11456 }
11457
11458 destroyPQExpBuffer(query);
11459 destroyPQExpBuffer(tag);
11460}
11461
11462/*
11463 * findComments --
11464 *
11465 * Find the comment(s), if any, associated with the given object. All the
11466 * objsubid values associated with the given classoid/objoid are found with
11467 * one search.
11468 */
11469static int
11471{
11472 CommentItem *middle = NULL;
11473 CommentItem *low;
11474 CommentItem *high;
11475 int nmatch;
11476
11477 /*
11478 * Do binary search to find some item matching the object.
11479 */
11480 low = &comments[0];
11481 high = &comments[ncomments - 1];
11482 while (low <= high)
11483 {
11484 middle = low + (high - low) / 2;
11485
11486 if (classoid < middle->classoid)
11487 high = middle - 1;
11488 else if (classoid > middle->classoid)
11489 low = middle + 1;
11490 else if (objoid < middle->objoid)
11491 high = middle - 1;
11492 else if (objoid > middle->objoid)
11493 low = middle + 1;
11494 else
11495 break; /* found a match */
11496 }
11497
11498 if (low > high) /* no matches */
11499 {
11500 *items = NULL;
11501 return 0;
11502 }
11503
11504 /*
11505 * Now determine how many items match the object. The search loop
11506 * invariant still holds: only items between low and high inclusive could
11507 * match.
11508 */
11509 nmatch = 1;
11510 while (middle > low)
11511 {
11512 if (classoid != middle[-1].classoid ||
11513 objoid != middle[-1].objoid)
11514 break;
11515 middle--;
11516 nmatch++;
11517 }
11518
11519 *items = middle;
11520
11521 middle += nmatch;
11522 while (middle <= high)
11523 {
11524 if (classoid != middle->classoid ||
11525 objoid != middle->objoid)
11526 break;
11527 middle++;
11528 nmatch++;
11529 }
11530
11531 return nmatch;
11532}
11533
11534/*
11535 * collectComments --
11536 *
11537 * Construct a table of all comments available for database objects;
11538 * also set the has-comment component flag for each relevant object.
11539 *
11540 * We used to do per-object queries for the comments, but it's much faster
11541 * to pull them all over at once, and on most databases the memory cost
11542 * isn't high.
11543 *
11544 * The table is sorted by classoid/objid/objsubid for speed in lookup.
11545 */
11546static void
11548{
11549 PGresult *res;
11550 PQExpBuffer query;
11551 int i_description;
11552 int i_classoid;
11553 int i_objoid;
11554 int i_objsubid;
11555 int ntups;
11556 int i;
11557 DumpableObject *dobj;
11558
11559 query = createPQExpBuffer();
11560
11561 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11562 "FROM pg_catalog.pg_description "
11563 "ORDER BY classoid, objoid, objsubid");
11564
11565 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11566
11567 /* Construct lookup table containing OIDs in numeric form */
11568
11569 i_description = PQfnumber(res, "description");
11570 i_classoid = PQfnumber(res, "classoid");
11571 i_objoid = PQfnumber(res, "objoid");
11572 i_objsubid = PQfnumber(res, "objsubid");
11573
11574 ntups = PQntuples(res);
11575
11576 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11577 ncomments = 0;
11578 dobj = NULL;
11579
11580 for (i = 0; i < ntups; i++)
11581 {
11582 CatalogId objId;
11583 int subid;
11584
11585 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11586 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11587 subid = atoi(PQgetvalue(res, i, i_objsubid));
11588
11589 /* We needn't remember comments that don't match any dumpable object */
11590 if (dobj == NULL ||
11591 dobj->catId.tableoid != objId.tableoid ||
11592 dobj->catId.oid != objId.oid)
11593 dobj = findObjectByCatalogId(objId);
11594 if (dobj == NULL)
11595 continue;
11596
11597 /*
11598 * Comments on columns of composite types are linked to the type's
11599 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11600 * in the type's own DumpableObject.
11601 */
11602 if (subid != 0 && dobj->objType == DO_TABLE &&
11603 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11604 {
11605 TypeInfo *cTypeInfo;
11606
11607 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11608 if (cTypeInfo)
11610 }
11611 else
11612 dobj->components |= DUMP_COMPONENT_COMMENT;
11613
11614 comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11616 comments[ncomments].objoid = objId.oid;
11617 comments[ncomments].objsubid = subid;
11618 ncomments++;
11619 }
11620
11621 PQclear(res);
11622 destroyPQExpBuffer(query);
11623}
11624
11625/*
11626 * dumpDumpableObject
11627 *
11628 * This routine and its subsidiaries are responsible for creating
11629 * ArchiveEntries (TOC objects) for each object to be dumped.
11630 */
11631static void
11633{
11634 /*
11635 * Clear any dump-request bits for components that don't exist for this
11636 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11637 * request for every kind of object.)
11638 */
11639 dobj->dump &= dobj->components;
11640
11641 /* Now, short-circuit if there's nothing to be done here. */
11642 if (dobj->dump == 0)
11643 return;
11644
11645 switch (dobj->objType)
11646 {
11647 case DO_NAMESPACE:
11648 dumpNamespace(fout, (const NamespaceInfo *) dobj);
11649 break;
11650 case DO_EXTENSION:
11651 dumpExtension(fout, (const ExtensionInfo *) dobj);
11652 break;
11653 case DO_TYPE:
11654 dumpType(fout, (const TypeInfo *) dobj);
11655 break;
11656 case DO_SHELL_TYPE:
11657 dumpShellType(fout, (const ShellTypeInfo *) dobj);
11658 break;
11659 case DO_FUNC:
11660 dumpFunc(fout, (const FuncInfo *) dobj);
11661 break;
11662 case DO_AGG:
11663 dumpAgg(fout, (const AggInfo *) dobj);
11664 break;
11665 case DO_OPERATOR:
11666 dumpOpr(fout, (const OprInfo *) dobj);
11667 break;
11668 case DO_ACCESS_METHOD:
11669 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11670 break;
11671 case DO_OPCLASS:
11672 dumpOpclass(fout, (const OpclassInfo *) dobj);
11673 break;
11674 case DO_OPFAMILY:
11675 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11676 break;
11677 case DO_COLLATION:
11678 dumpCollation(fout, (const CollInfo *) dobj);
11679 break;
11680 case DO_CONVERSION:
11681 dumpConversion(fout, (const ConvInfo *) dobj);
11682 break;
11683 case DO_TABLE:
11684 dumpTable(fout, (const TableInfo *) dobj);
11685 break;
11686 case DO_TABLE_ATTACH:
11687 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11688 break;
11689 case DO_ATTRDEF:
11690 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11691 break;
11692 case DO_INDEX:
11693 dumpIndex(fout, (const IndxInfo *) dobj);
11694 break;
11695 case DO_INDEX_ATTACH:
11696 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11697 break;
11698 case DO_STATSEXT:
11699 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11700 break;
11701 case DO_REFRESH_MATVIEW:
11702 refreshMatViewData(fout, (const TableDataInfo *) dobj);
11703 break;
11704 case DO_RULE:
11705 dumpRule(fout, (const RuleInfo *) dobj);
11706 break;
11707 case DO_TRIGGER:
11708 dumpTrigger(fout, (const TriggerInfo *) dobj);
11709 break;
11710 case DO_EVENT_TRIGGER:
11711 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11712 break;
11713 case DO_CONSTRAINT:
11714 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11715 break;
11716 case DO_FK_CONSTRAINT:
11717 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11718 break;
11719 case DO_PROCLANG:
11720 dumpProcLang(fout, (const ProcLangInfo *) dobj);
11721 break;
11722 case DO_CAST:
11723 dumpCast(fout, (const CastInfo *) dobj);
11724 break;
11725 case DO_TRANSFORM:
11726 dumpTransform(fout, (const TransformInfo *) dobj);
11727 break;
11728 case DO_SEQUENCE_SET:
11729 dumpSequenceData(fout, (const TableDataInfo *) dobj);
11730 break;
11731 case DO_TABLE_DATA:
11732 dumpTableData(fout, (const TableDataInfo *) dobj);
11733 break;
11734 case DO_DUMMY_TYPE:
11735 /* table rowtypes and array types are never dumped separately */
11736 break;
11737 case DO_TSPARSER:
11738 dumpTSParser(fout, (const TSParserInfo *) dobj);
11739 break;
11740 case DO_TSDICT:
11741 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11742 break;
11743 case DO_TSTEMPLATE:
11744 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11745 break;
11746 case DO_TSCONFIG:
11747 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11748 break;
11749 case DO_FDW:
11750 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11751 break;
11752 case DO_FOREIGN_SERVER:
11753 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11754 break;
11755 case DO_DEFAULT_ACL:
11756 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11757 break;
11758 case DO_LARGE_OBJECT:
11759 dumpLO(fout, (const LoInfo *) dobj);
11760 break;
11762 if (dobj->dump & DUMP_COMPONENT_DATA)
11763 {
11764 LoInfo *loinfo;
11765 TocEntry *te;
11766
11767 loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11768 if (loinfo == NULL)
11769 pg_fatal("missing metadata for large objects \"%s\"",
11770 dobj->name);
11771
11772 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11773 ARCHIVE_OPTS(.tag = dobj->name,
11774 .owner = loinfo->rolname,
11775 .description = "BLOBS",
11776 .section = SECTION_DATA,
11777 .deps = dobj->dependencies,
11778 .nDeps = dobj->nDeps,
11779 .dumpFn = dumpLOs,
11780 .dumpArg = loinfo));
11781
11782 /*
11783 * Set the TocEntry's dataLength in case we are doing a
11784 * parallel dump and want to order dump jobs by table size.
11785 * (We need some size estimate for every TocEntry with a
11786 * DataDumper function.) We don't currently have any cheap
11787 * way to estimate the size of LOs, but fortunately it doesn't
11788 * matter too much as long as we get large batches of LOs
11789 * processed reasonably early. Assume 8K per blob.
11790 */
11791 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11792 }
11793 break;
11794 case DO_POLICY:
11795 dumpPolicy(fout, (const PolicyInfo *) dobj);
11796 break;
11797 case DO_PUBLICATION:
11798 dumpPublication(fout, (const PublicationInfo *) dobj);
11799 break;
11800 case DO_PUBLICATION_REL:
11801 dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11802 break;
11805 (const PublicationSchemaInfo *) dobj);
11806 break;
11807 case DO_SUBSCRIPTION:
11808 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11809 break;
11811 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11812 break;
11813 case DO_REL_STATS:
11814 dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11815 break;
11818 /* never dumped, nothing to do */
11819 break;
11820 }
11821}
11822
11823/*
11824 * dumpNamespace
11825 * writes out to fout the queries to recreate a user-defined namespace
11826 */
11827static void
11829{
11830 DumpOptions *dopt = fout->dopt;
11831 PQExpBuffer q;
11832 PQExpBuffer delq;
11833 char *qnspname;
11834
11835 /* Do nothing if not dumping schema */
11836 if (!dopt->dumpSchema)
11837 return;
11838
11839 q = createPQExpBuffer();
11840 delq = createPQExpBuffer();
11841
11842 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11843
11844 if (nspinfo->create)
11845 {
11846 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11847 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11848 }
11849 else
11850 {
11851 /* see selectDumpableNamespace() */
11853 "-- *not* dropping schema, since initdb creates it\n");
11855 "-- *not* creating schema, since initdb creates it\n");
11856 }
11857
11858 if (dopt->binary_upgrade)
11860 "SCHEMA", qnspname, NULL);
11861
11862 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11863 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11864 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11865 .owner = nspinfo->rolname,
11866 .description = "SCHEMA",
11867 .section = SECTION_PRE_DATA,
11868 .createStmt = q->data,
11869 .dropStmt = delq->data));
11870
11871 /* Dump Schema Comments and Security Labels */
11872 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11873 {
11874 const char *initdb_comment = NULL;
11875
11876 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11877 initdb_comment = "standard public schema";
11878 dumpCommentExtended(fout, "SCHEMA", qnspname,
11879 NULL, nspinfo->rolname,
11880 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11881 initdb_comment);
11882 }
11883
11884 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11885 dumpSecLabel(fout, "SCHEMA", qnspname,
11886 NULL, nspinfo->rolname,
11887 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11888
11889 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11890 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11891 qnspname, NULL, NULL,
11892 NULL, nspinfo->rolname, &nspinfo->dacl);
11893
11894 free(qnspname);
11895
11897 destroyPQExpBuffer(delq);
11898}
11899
11900/*
11901 * dumpExtension
11902 * writes out to fout the queries to recreate an extension
11903 */
11904static void
11906{
11907 DumpOptions *dopt = fout->dopt;
11908 PQExpBuffer q;
11909 PQExpBuffer delq;
11910 char *qextname;
11911
11912 /* Do nothing if not dumping schema */
11913 if (!dopt->dumpSchema)
11914 return;
11915
11916 q = createPQExpBuffer();
11917 delq = createPQExpBuffer();
11918
11919 qextname = pg_strdup(fmtId(extinfo->dobj.name));
11920
11921 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11922
11923 if (!dopt->binary_upgrade)
11924 {
11925 /*
11926 * In a regular dump, we simply create the extension, intentionally
11927 * not specifying a version, so that the destination installation's
11928 * default version is used.
11929 *
11930 * Use of IF NOT EXISTS here is unlike our behavior for other object
11931 * types; but there are various scenarios in which it's convenient to
11932 * manually create the desired extension before restoring, so we
11933 * prefer to allow it to exist already.
11934 */
11935 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11936 qextname, fmtId(extinfo->namespace));
11937 }
11938 else
11939 {
11940 /*
11941 * In binary-upgrade mode, it's critical to reproduce the state of the
11942 * database exactly, so our procedure is to create an empty extension,
11943 * restore all the contained objects normally, and add them to the
11944 * extension one by one. This function performs just the first of
11945 * those steps. binary_upgrade_extension_member() takes care of
11946 * adding member objects as they're created.
11947 */
11948 int i;
11949 int n;
11950
11951 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11952
11953 /*
11954 * We unconditionally create the extension, so we must drop it if it
11955 * exists. This could happen if the user deleted 'plpgsql' and then
11956 * readded it, causing its oid to be greater than g_last_builtin_oid.
11957 */
11958 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11959
11961 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11962 appendStringLiteralAH(q, extinfo->dobj.name, fout);
11963 appendPQExpBufferStr(q, ", ");
11964 appendStringLiteralAH(q, extinfo->namespace, fout);
11965 appendPQExpBufferStr(q, ", ");
11966 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11967 appendStringLiteralAH(q, extinfo->extversion, fout);
11968 appendPQExpBufferStr(q, ", ");
11969
11970 /*
11971 * Note that we're pushing extconfig (an OID array) back into
11972 * pg_extension exactly as-is. This is OK because pg_class OIDs are
11973 * preserved in binary upgrade.
11974 */
11975 if (strlen(extinfo->extconfig) > 2)
11976 appendStringLiteralAH(q, extinfo->extconfig, fout);
11977 else
11978 appendPQExpBufferStr(q, "NULL");
11979 appendPQExpBufferStr(q, ", ");
11980 if (strlen(extinfo->extcondition) > 2)
11981 appendStringLiteralAH(q, extinfo->extcondition, fout);
11982 else
11983 appendPQExpBufferStr(q, "NULL");
11984 appendPQExpBufferStr(q, ", ");
11985 appendPQExpBufferStr(q, "ARRAY[");
11986 n = 0;
11987 for (i = 0; i < extinfo->dobj.nDeps; i++)
11988 {
11989 DumpableObject *extobj;
11990
11991 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11992 if (extobj && extobj->objType == DO_EXTENSION)
11993 {
11994 if (n++ > 0)
11995 appendPQExpBufferChar(q, ',');
11996 appendStringLiteralAH(q, extobj->name, fout);
11997 }
11998 }
11999 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12000 appendPQExpBufferStr(q, ");\n");
12001 }
12002
12003 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12004 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12005 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12006 .description = "EXTENSION",
12007 .section = SECTION_PRE_DATA,
12008 .createStmt = q->data,
12009 .dropStmt = delq->data));
12010
12011 /* Dump Extension Comments and Security Labels */
12012 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12013 dumpComment(fout, "EXTENSION", qextname,
12014 NULL, "",
12015 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12016
12017 if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12018 dumpSecLabel(fout, "EXTENSION", qextname,
12019 NULL, "",
12020 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12021
12022 free(qextname);
12023
12025 destroyPQExpBuffer(delq);
12026}
12027
12028/*
12029 * dumpType
12030 * writes out to fout the queries to recreate a user-defined type
12031 */
12032static void
12033dumpType(Archive *fout, const TypeInfo *tyinfo)
12034{
12035 DumpOptions *dopt = fout->dopt;
12036
12037 /* Do nothing if not dumping schema */
12038 if (!dopt->dumpSchema)
12039 return;
12040
12041 /* Dump out in proper style */
12042 if (tyinfo->typtype == TYPTYPE_BASE)
12043 dumpBaseType(fout, tyinfo);
12044 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12045 dumpDomain(fout, tyinfo);
12046 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12047 dumpCompositeType(fout, tyinfo);
12048 else if (tyinfo->typtype == TYPTYPE_ENUM)
12049 dumpEnumType(fout, tyinfo);
12050 else if (tyinfo->typtype == TYPTYPE_RANGE)
12051 dumpRangeType(fout, tyinfo);
12052 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12053 dumpUndefinedType(fout, tyinfo);
12054 else
12055 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12056 tyinfo->dobj.name);
12057}
12058
12059/*
12060 * dumpEnumType
12061 * writes out to fout the queries to recreate a user-defined enum type
12062 */
12063static void
12064dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12065{
12066 DumpOptions *dopt = fout->dopt;
12070 PGresult *res;
12071 int num,
12072 i;
12073 Oid enum_oid;
12074 char *qtypname;
12075 char *qualtypname;
12076 char *label;
12077 int i_enumlabel;
12078 int i_oid;
12079
12081 {
12082 /* Set up query for enum-specific details */
12084 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12085 "SELECT oid, enumlabel "
12086 "FROM pg_catalog.pg_enum "
12087 "WHERE enumtypid = $1 "
12088 "ORDER BY enumsortorder");
12089
12090 ExecuteSqlStatement(fout, query->data);
12091
12092 fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12093 }
12094
12095 printfPQExpBuffer(query,
12096 "EXECUTE dumpEnumType('%u')",
12097 tyinfo->dobj.catId.oid);
12098
12099 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12100
12101 num = PQntuples(res);
12102
12103 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12104 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12105
12106 /*
12107 * CASCADE shouldn't be required here as for normal types since the I/O
12108 * functions are generic and do not get dropped.
12109 */
12110 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12111
12112 if (dopt->binary_upgrade)
12114 tyinfo->dobj.catId.oid,
12115 false, false);
12116
12117 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12118 qualtypname);
12119
12120 if (!dopt->binary_upgrade)
12121 {
12122 i_enumlabel = PQfnumber(res, "enumlabel");
12123
12124 /* Labels with server-assigned oids */
12125 for (i = 0; i < num; i++)
12126 {
12127 label = PQgetvalue(res, i, i_enumlabel);
12128 if (i > 0)
12129 appendPQExpBufferChar(q, ',');
12130 appendPQExpBufferStr(q, "\n ");
12131 appendStringLiteralAH(q, label, fout);
12132 }
12133 }
12134
12135 appendPQExpBufferStr(q, "\n);\n");
12136
12137 if (dopt->binary_upgrade)
12138 {
12139 i_oid = PQfnumber(res, "oid");
12140 i_enumlabel = PQfnumber(res, "enumlabel");
12141
12142 /* Labels with dump-assigned (preserved) oids */
12143 for (i = 0; i < num; i++)
12144 {
12145 enum_oid = atooid(PQgetvalue(res, i, i_oid));
12146 label = PQgetvalue(res, i, i_enumlabel);
12147
12148 if (i == 0)
12149 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12151 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12152 enum_oid);
12153 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12154 appendStringLiteralAH(q, label, fout);
12155 appendPQExpBufferStr(q, ";\n\n");
12156 }
12157 }
12158
12159 if (dopt->binary_upgrade)
12161 "TYPE", qtypname,
12162 tyinfo->dobj.namespace->dobj.name);
12163
12164 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12165 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12166 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12167 .namespace = tyinfo->dobj.namespace->dobj.name,
12168 .owner = tyinfo->rolname,
12169 .description = "TYPE",
12170 .section = SECTION_PRE_DATA,
12171 .createStmt = q->data,
12172 .dropStmt = delq->data));
12173
12174 /* Dump Type Comments and Security Labels */
12175 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12176 dumpComment(fout, "TYPE", qtypname,
12177 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12178 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12179
12180 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12181 dumpSecLabel(fout, "TYPE", qtypname,
12182 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12183 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12184
12185 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12186 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12187 qtypname, NULL,
12188 tyinfo->dobj.namespace->dobj.name,
12189 NULL, tyinfo->rolname, &tyinfo->dacl);
12190
12191 PQclear(res);
12193 destroyPQExpBuffer(delq);
12194 destroyPQExpBuffer(query);
12195 free(qtypname);
12196 free(qualtypname);
12197}
12198
12199/*
12200 * dumpRangeType
12201 * writes out to fout the queries to recreate a user-defined range type
12202 */
12203static void
12204dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12205{
12206 DumpOptions *dopt = fout->dopt;
12210 PGresult *res;
12211 Oid collationOid;
12212 char *qtypname;
12213 char *qualtypname;
12214 char *procname;
12215
12217 {
12218 /* Set up query for range-specific details */
12220 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12221
12223 "SELECT ");
12224
12225 if (fout->remoteVersion >= 140000)
12227 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12228 else
12230 "NULL AS rngmultitype, ");
12231
12233 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12234 "opc.opcname AS opcname, "
12235 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12236 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12237 "opc.opcdefault, "
12238 "CASE WHEN rngcollation = st.typcollation THEN 0 "
12239 " ELSE rngcollation END AS collation, "
12240 "rngcanonical, rngsubdiff "
12241 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12242 " pg_catalog.pg_opclass opc "
12243 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12244 "rngtypid = $1");
12245
12246 ExecuteSqlStatement(fout, query->data);
12247
12249 }
12250
12251 printfPQExpBuffer(query,
12252 "EXECUTE dumpRangeType('%u')",
12253 tyinfo->dobj.catId.oid);
12254
12255 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12256
12257 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12258 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12259
12260 /*
12261 * CASCADE shouldn't be required here as for normal types since the I/O
12262 * functions are generic and do not get dropped.
12263 */
12264 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12265
12266 if (dopt->binary_upgrade)
12268 tyinfo->dobj.catId.oid,
12269 false, true);
12270
12271 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12272 qualtypname);
12273
12274 appendPQExpBuffer(q, "\n subtype = %s",
12275 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12276
12277 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12278 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12279 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12280
12281 /* print subtype_opclass only if not default for subtype */
12282 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12283 {
12284 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12285 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12286
12287 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12288 fmtId(nspname));
12289 appendPQExpBufferStr(q, fmtId(opcname));
12290 }
12291
12292 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12293 if (OidIsValid(collationOid))
12294 {
12295 CollInfo *coll = findCollationByOid(collationOid);
12296
12297 if (coll)
12298 appendPQExpBuffer(q, ",\n collation = %s",
12299 fmtQualifiedDumpable(coll));
12300 }
12301
12302 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12303 if (strcmp(procname, "-") != 0)
12304 appendPQExpBuffer(q, ",\n canonical = %s", procname);
12305
12306 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12307 if (strcmp(procname, "-") != 0)
12308 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12309
12310 appendPQExpBufferStr(q, "\n);\n");
12311
12312 if (dopt->binary_upgrade)
12314 "TYPE", qtypname,
12315 tyinfo->dobj.namespace->dobj.name);
12316
12317 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12318 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12319 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12320 .namespace = tyinfo->dobj.namespace->dobj.name,
12321 .owner = tyinfo->rolname,
12322 .description = "TYPE",
12323 .section = SECTION_PRE_DATA,
12324 .createStmt = q->data,
12325 .dropStmt = delq->data));
12326
12327 /* Dump Type Comments and Security Labels */
12328 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12329 dumpComment(fout, "TYPE", qtypname,
12330 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12331 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12332
12333 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12334 dumpSecLabel(fout, "TYPE", qtypname,
12335 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12336 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12337
12338 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12339 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12340 qtypname, NULL,
12341 tyinfo->dobj.namespace->dobj.name,
12342 NULL, tyinfo->rolname, &tyinfo->dacl);
12343
12344 PQclear(res);
12346 destroyPQExpBuffer(delq);
12347 destroyPQExpBuffer(query);
12348 free(qtypname);
12349 free(qualtypname);
12350}
12351
12352/*
12353 * dumpUndefinedType
12354 * writes out to fout the queries to recreate a !typisdefined type
12355 *
12356 * This is a shell type, but we use different terminology to distinguish
12357 * this case from where we have to emit a shell type definition to break
12358 * circular dependencies. An undefined type shouldn't ever have anything
12359 * depending on it.
12360 */
12361static void
12363{
12364 DumpOptions *dopt = fout->dopt;
12367 char *qtypname;
12368 char *qualtypname;
12369
12370 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12371 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12372
12373 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12374
12375 if (dopt->binary_upgrade)
12377 tyinfo->dobj.catId.oid,
12378 false, false);
12379
12380 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12381 qualtypname);
12382
12383 if (dopt->binary_upgrade)
12385 "TYPE", qtypname,
12386 tyinfo->dobj.namespace->dobj.name);
12387
12388 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12389 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12390 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12391 .namespace = tyinfo->dobj.namespace->dobj.name,
12392 .owner = tyinfo->rolname,
12393 .description = "TYPE",
12394 .section = SECTION_PRE_DATA,
12395 .createStmt = q->data,
12396 .dropStmt = delq->data));
12397
12398 /* Dump Type Comments and Security Labels */
12399 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12400 dumpComment(fout, "TYPE", qtypname,
12401 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12402 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12403
12404 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12405 dumpSecLabel(fout, "TYPE", qtypname,
12406 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12407 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12408
12409 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12410 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12411 qtypname, NULL,
12412 tyinfo->dobj.namespace->dobj.name,
12413 NULL, tyinfo->rolname, &tyinfo->dacl);
12414
12416 destroyPQExpBuffer(delq);
12417 free(qtypname);
12418 free(qualtypname);
12419}
12420
12421/*
12422 * dumpBaseType
12423 * writes out to fout the queries to recreate a user-defined base type
12424 */
12425static void
12426dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12427{
12428 DumpOptions *dopt = fout->dopt;
12432 PGresult *res;
12433 char *qtypname;
12434 char *qualtypname;
12435 char *typlen;
12436 char *typinput;
12437 char *typoutput;
12438 char *typreceive;
12439 char *typsend;
12440 char *typmodin;
12441 char *typmodout;
12442 char *typanalyze;
12443 char *typsubscript;
12444 Oid typreceiveoid;
12445 Oid typsendoid;
12446 Oid typmodinoid;
12447 Oid typmodoutoid;
12448 Oid typanalyzeoid;
12449 Oid typsubscriptoid;
12450 char *typcategory;
12451 char *typispreferred;
12452 char *typdelim;
12453 char *typbyval;
12454 char *typalign;
12455 char *typstorage;
12456 char *typcollatable;
12457 char *typdefault;
12458 bool typdefault_is_literal = false;
12459
12461 {
12462 /* Set up query for type-specific details */
12464 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12465 "SELECT typlen, "
12466 "typinput, typoutput, typreceive, typsend, "
12467 "typreceive::pg_catalog.oid AS typreceiveoid, "
12468 "typsend::pg_catalog.oid AS typsendoid, "
12469 "typanalyze, "
12470 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12471 "typdelim, typbyval, typalign, typstorage, "
12472 "typmodin, typmodout, "
12473 "typmodin::pg_catalog.oid AS typmodinoid, "
12474 "typmodout::pg_catalog.oid AS typmodoutoid, "
12475 "typcategory, typispreferred, "
12476 "(typcollation <> 0) AS typcollatable, "
12477 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12478
12479 if (fout->remoteVersion >= 140000)
12481 "typsubscript, "
12482 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12483 else
12485 "'-' AS typsubscript, 0 AS typsubscriptoid ");
12486
12487 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12488 "WHERE oid = $1");
12489
12490 ExecuteSqlStatement(fout, query->data);
12491
12492 fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12493 }
12494
12495 printfPQExpBuffer(query,
12496 "EXECUTE dumpBaseType('%u')",
12497 tyinfo->dobj.catId.oid);
12498
12499 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12500
12501 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12502 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12503 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12504 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12505 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12506 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12507 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12508 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12509 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12510 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12511 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12512 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12513 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12514 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12515 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12516 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12517 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12518 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12519 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12520 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12521 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12522 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12523 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12524 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12525 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12526 {
12527 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12528 typdefault_is_literal = true; /* it needs quotes */
12529 }
12530 else
12531 typdefault = NULL;
12532
12533 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12534 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12535
12536 /*
12537 * The reason we include CASCADE is that the circular dependency between
12538 * the type and its I/O functions makes it impossible to drop the type any
12539 * other way.
12540 */
12541 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12542
12543 /*
12544 * We might already have a shell type, but setting pg_type_oid is
12545 * harmless, and in any case we'd better set the array type OID.
12546 */
12547 if (dopt->binary_upgrade)
12549 tyinfo->dobj.catId.oid,
12550 false, false);
12551
12553 "CREATE TYPE %s (\n"
12554 " INTERNALLENGTH = %s",
12555 qualtypname,
12556 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12557
12558 /* regproc result is sufficiently quoted already */
12559 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12560 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12561 if (OidIsValid(typreceiveoid))
12562 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12563 if (OidIsValid(typsendoid))
12564 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12565 if (OidIsValid(typmodinoid))
12566 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12567 if (OidIsValid(typmodoutoid))
12568 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12569 if (OidIsValid(typanalyzeoid))
12570 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12571
12572 if (strcmp(typcollatable, "t") == 0)
12573 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12574
12575 if (typdefault != NULL)
12576 {
12577 appendPQExpBufferStr(q, ",\n DEFAULT = ");
12578 if (typdefault_is_literal)
12579 appendStringLiteralAH(q, typdefault, fout);
12580 else
12581 appendPQExpBufferStr(q, typdefault);
12582 }
12583
12584 if (OidIsValid(typsubscriptoid))
12585 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12586
12587 if (OidIsValid(tyinfo->typelem))
12588 appendPQExpBuffer(q, ",\n ELEMENT = %s",
12589 getFormattedTypeName(fout, tyinfo->typelem,
12590 zeroIsError));
12591
12592 if (strcmp(typcategory, "U") != 0)
12593 {
12594 appendPQExpBufferStr(q, ",\n CATEGORY = ");
12595 appendStringLiteralAH(q, typcategory, fout);
12596 }
12597
12598 if (strcmp(typispreferred, "t") == 0)
12599 appendPQExpBufferStr(q, ",\n PREFERRED = true");
12600
12601 if (typdelim && strcmp(typdelim, ",") != 0)
12602 {
12603 appendPQExpBufferStr(q, ",\n DELIMITER = ");
12604 appendStringLiteralAH(q, typdelim, fout);
12605 }
12606
12607 if (*typalign == TYPALIGN_CHAR)
12608 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12609 else if (*typalign == TYPALIGN_SHORT)
12610 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12611 else if (*typalign == TYPALIGN_INT)
12612 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12613 else if (*typalign == TYPALIGN_DOUBLE)
12614 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12615
12616 if (*typstorage == TYPSTORAGE_PLAIN)
12617 appendPQExpBufferStr(q, ",\n STORAGE = plain");
12618 else if (*typstorage == TYPSTORAGE_EXTERNAL)
12619 appendPQExpBufferStr(q, ",\n STORAGE = external");
12620 else if (*typstorage == TYPSTORAGE_EXTENDED)
12621 appendPQExpBufferStr(q, ",\n STORAGE = extended");
12622 else if (*typstorage == TYPSTORAGE_MAIN)
12623 appendPQExpBufferStr(q, ",\n STORAGE = main");
12624
12625 if (strcmp(typbyval, "t") == 0)
12626 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12627
12628 appendPQExpBufferStr(q, "\n);\n");
12629
12630 if (dopt->binary_upgrade)
12632 "TYPE", qtypname,
12633 tyinfo->dobj.namespace->dobj.name);
12634
12635 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12636 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12637 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12638 .namespace = tyinfo->dobj.namespace->dobj.name,
12639 .owner = tyinfo->rolname,
12640 .description = "TYPE",
12641 .section = SECTION_PRE_DATA,
12642 .createStmt = q->data,
12643 .dropStmt = delq->data));
12644
12645 /* Dump Type Comments and Security Labels */
12646 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12647 dumpComment(fout, "TYPE", qtypname,
12648 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12649 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12650
12651 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12652 dumpSecLabel(fout, "TYPE", qtypname,
12653 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12654 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12655
12656 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12657 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12658 qtypname, NULL,
12659 tyinfo->dobj.namespace->dobj.name,
12660 NULL, tyinfo->rolname, &tyinfo->dacl);
12661
12662 PQclear(res);
12664 destroyPQExpBuffer(delq);
12665 destroyPQExpBuffer(query);
12666 free(qtypname);
12667 free(qualtypname);
12668}
12669
12670/*
12671 * dumpDomain
12672 * writes out to fout the queries to recreate a user-defined domain
12673 */
12674static void
12675dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12676{
12677 DumpOptions *dopt = fout->dopt;
12681 PGresult *res;
12682 int i;
12683 char *qtypname;
12684 char *qualtypname;
12685 char *typnotnull;
12686 char *typdefn;
12687 char *typdefault;
12688 Oid typcollation;
12689 bool typdefault_is_literal = false;
12690
12692 {
12693 /* Set up query for domain-specific details */
12695 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12696
12697 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12698 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12699 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12700 "t.typdefault, "
12701 "CASE WHEN t.typcollation <> u.typcollation "
12702 "THEN t.typcollation ELSE 0 END AS typcollation "
12703 "FROM pg_catalog.pg_type t "
12704 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12705 "WHERE t.oid = $1");
12706
12707 ExecuteSqlStatement(fout, query->data);
12708
12709 fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12710 }
12711
12712 printfPQExpBuffer(query,
12713 "EXECUTE dumpDomain('%u')",
12714 tyinfo->dobj.catId.oid);
12715
12716 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12717
12718 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12719 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12720 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12721 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12722 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12723 {
12724 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12725 typdefault_is_literal = true; /* it needs quotes */
12726 }
12727 else
12728 typdefault = NULL;
12729 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12730
12731 if (dopt->binary_upgrade)
12733 tyinfo->dobj.catId.oid,
12734 true, /* force array type */
12735 false); /* force multirange type */
12736
12737 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12738 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12739
12741 "CREATE DOMAIN %s AS %s",
12742 qualtypname,
12743 typdefn);
12744
12745 /* Print collation only if different from base type's collation */
12746 if (OidIsValid(typcollation))
12747 {
12748 CollInfo *coll;
12749
12750 coll = findCollationByOid(typcollation);
12751 if (coll)
12752 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12753 }
12754
12755 /*
12756 * Print a not-null constraint if there's one. In servers older than 17
12757 * these don't have names, so just print it unadorned; in newer ones they
12758 * do, but most of the time it's going to be the standard generated one,
12759 * so omit the name in that case also.
12760 */
12761 if (typnotnull[0] == 't')
12762 {
12763 if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12764 appendPQExpBufferStr(q, " NOT NULL");
12765 else
12766 {
12767 ConstraintInfo *notnull = tyinfo->notnull;
12768
12769 if (!notnull->separate)
12770 {
12771 char *default_name;
12772
12773 /* XXX should match ChooseConstraintName better */
12774 default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12775
12776 if (strcmp(default_name, notnull->dobj.name) == 0)
12777 appendPQExpBufferStr(q, " NOT NULL");
12778 else
12779 appendPQExpBuffer(q, " CONSTRAINT %s %s",
12780 fmtId(notnull->dobj.name), notnull->condef);
12781 free(default_name);
12782 }
12783 }
12784 }
12785
12786 if (typdefault != NULL)
12787 {
12788 appendPQExpBufferStr(q, " DEFAULT ");
12789 if (typdefault_is_literal)
12790 appendStringLiteralAH(q, typdefault, fout);
12791 else
12792 appendPQExpBufferStr(q, typdefault);
12793 }
12794
12795 PQclear(res);
12796
12797 /*
12798 * Add any CHECK constraints for the domain
12799 */
12800 for (i = 0; i < tyinfo->nDomChecks; i++)
12801 {
12802 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12803
12804 if (!domcheck->separate && domcheck->contype == 'c')
12805 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12806 fmtId(domcheck->dobj.name), domcheck->condef);
12807 }
12808
12809 appendPQExpBufferStr(q, ";\n");
12810
12811 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12812
12813 if (dopt->binary_upgrade)
12815 "DOMAIN", qtypname,
12816 tyinfo->dobj.namespace->dobj.name);
12817
12818 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12819 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12820 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12821 .namespace = tyinfo->dobj.namespace->dobj.name,
12822 .owner = tyinfo->rolname,
12823 .description = "DOMAIN",
12824 .section = SECTION_PRE_DATA,
12825 .createStmt = q->data,
12826 .dropStmt = delq->data));
12827
12828 /* Dump Domain Comments and Security Labels */
12829 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12830 dumpComment(fout, "DOMAIN", qtypname,
12831 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12832 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12833
12834 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12835 dumpSecLabel(fout, "DOMAIN", qtypname,
12836 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12837 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12838
12839 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12840 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12841 qtypname, NULL,
12842 tyinfo->dobj.namespace->dobj.name,
12843 NULL, tyinfo->rolname, &tyinfo->dacl);
12844
12845 /* Dump any per-constraint comments */
12846 for (i = 0; i < tyinfo->nDomChecks; i++)
12847 {
12848 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12849 PQExpBuffer conprefix;
12850
12851 /* but only if the constraint itself was dumped here */
12852 if (domcheck->separate)
12853 continue;
12854
12855 conprefix = createPQExpBuffer();
12856 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12857 fmtId(domcheck->dobj.name));
12858
12859 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12860 dumpComment(fout, conprefix->data, qtypname,
12861 tyinfo->dobj.namespace->dobj.name,
12862 tyinfo->rolname,
12863 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12864
12865 destroyPQExpBuffer(conprefix);
12866 }
12867
12868 /*
12869 * And a comment on the not-null constraint, if there's one -- but only if
12870 * the constraint itself was dumped here
12871 */
12872 if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12873 {
12874 PQExpBuffer conprefix = createPQExpBuffer();
12875
12876 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12877 fmtId(tyinfo->notnull->dobj.name));
12878
12879 if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12880 dumpComment(fout, conprefix->data, qtypname,
12881 tyinfo->dobj.namespace->dobj.name,
12882 tyinfo->rolname,
12883 tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12884 destroyPQExpBuffer(conprefix);
12885 }
12886
12888 destroyPQExpBuffer(delq);
12889 destroyPQExpBuffer(query);
12890 free(qtypname);
12891 free(qualtypname);
12892}
12893
12894/*
12895 * dumpCompositeType
12896 * writes out to fout the queries to recreate a user-defined stand-alone
12897 * composite type
12898 */
12899static void
12901{
12902 DumpOptions *dopt = fout->dopt;
12904 PQExpBuffer dropped = createPQExpBuffer();
12907 PGresult *res;
12908 char *qtypname;
12909 char *qualtypname;
12910 int ntups;
12911 int i_attname;
12912 int i_atttypdefn;
12913 int i_attlen;
12914 int i_attalign;
12915 int i_attisdropped;
12916 int i_attcollation;
12917 int i;
12918 int actual_atts;
12919
12921 {
12922 /*
12923 * Set up query for type-specific details.
12924 *
12925 * Since we only want to dump COLLATE clauses for attributes whose
12926 * collation is different from their type's default, we use a CASE
12927 * here to suppress uninteresting attcollations cheaply. atttypid
12928 * will be 0 for dropped columns; collation does not matter for those.
12929 */
12931 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12932 "SELECT a.attname, a.attnum, "
12933 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12934 "a.attlen, a.attalign, a.attisdropped, "
12935 "CASE WHEN a.attcollation <> at.typcollation "
12936 "THEN a.attcollation ELSE 0 END AS attcollation "
12937 "FROM pg_catalog.pg_type ct "
12938 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12939 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12940 "WHERE ct.oid = $1 "
12941 "ORDER BY a.attnum");
12942
12943 ExecuteSqlStatement(fout, query->data);
12944
12946 }
12947
12948 printfPQExpBuffer(query,
12949 "EXECUTE dumpCompositeType('%u')",
12950 tyinfo->dobj.catId.oid);
12951
12952 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12953
12954 ntups = PQntuples(res);
12955
12956 i_attname = PQfnumber(res, "attname");
12957 i_atttypdefn = PQfnumber(res, "atttypdefn");
12958 i_attlen = PQfnumber(res, "attlen");
12959 i_attalign = PQfnumber(res, "attalign");
12960 i_attisdropped = PQfnumber(res, "attisdropped");
12961 i_attcollation = PQfnumber(res, "attcollation");
12962
12963 if (dopt->binary_upgrade)
12964 {
12966 tyinfo->dobj.catId.oid,
12967 false, false);
12969 }
12970
12971 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12972 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12973
12974 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12975 qualtypname);
12976
12977 actual_atts = 0;
12978 for (i = 0; i < ntups; i++)
12979 {
12980 char *attname;
12981 char *atttypdefn;
12982 char *attlen;
12983 char *attalign;
12984 bool attisdropped;
12985 Oid attcollation;
12986
12987 attname = PQgetvalue(res, i, i_attname);
12988 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12989 attlen = PQgetvalue(res, i, i_attlen);
12990 attalign = PQgetvalue(res, i, i_attalign);
12991 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12992 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12993
12994 if (attisdropped && !dopt->binary_upgrade)
12995 continue;
12996
12997 /* Format properly if not first attr */
12998 if (actual_atts++ > 0)
12999 appendPQExpBufferChar(q, ',');
13000 appendPQExpBufferStr(q, "\n\t");
13001
13002 if (!attisdropped)
13003 {
13004 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13005
13006 /* Add collation if not default for the column type */
13007 if (OidIsValid(attcollation))
13008 {
13009 CollInfo *coll;
13010
13011 coll = findCollationByOid(attcollation);
13012 if (coll)
13013 appendPQExpBuffer(q, " COLLATE %s",
13014 fmtQualifiedDumpable(coll));
13015 }
13016 }
13017 else
13018 {
13019 /*
13020 * This is a dropped attribute and we're in binary_upgrade mode.
13021 * Insert a placeholder for it in the CREATE TYPE command, and set
13022 * length and alignment with direct UPDATE to the catalogs
13023 * afterwards. See similar code in dumpTableSchema().
13024 */
13025 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13026
13027 /* stash separately for insertion after the CREATE TYPE */
13028 appendPQExpBufferStr(dropped,
13029 "\n-- For binary upgrade, recreate dropped column.\n");
13030 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13031 "SET attlen = %s, "
13032 "attalign = '%s', attbyval = false\n"
13033 "WHERE attname = ", attlen, attalign);
13034 appendStringLiteralAH(dropped, attname, fout);
13035 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13036 appendStringLiteralAH(dropped, qualtypname, fout);
13037 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13038
13039 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13040 qualtypname);
13041 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13042 fmtId(attname));
13043 }
13044 }
13045 appendPQExpBufferStr(q, "\n);\n");
13046 appendPQExpBufferStr(q, dropped->data);
13047
13048 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13049
13050 if (dopt->binary_upgrade)
13052 "TYPE", qtypname,
13053 tyinfo->dobj.namespace->dobj.name);
13054
13055 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13056 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13057 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13058 .namespace = tyinfo->dobj.namespace->dobj.name,
13059 .owner = tyinfo->rolname,
13060 .description = "TYPE",
13061 .section = SECTION_PRE_DATA,
13062 .createStmt = q->data,
13063 .dropStmt = delq->data));
13064
13065
13066 /* Dump Type Comments and Security Labels */
13067 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13068 dumpComment(fout, "TYPE", qtypname,
13069 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13070 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13071
13072 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13073 dumpSecLabel(fout, "TYPE", qtypname,
13074 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13075 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13076
13077 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13078 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13079 qtypname, NULL,
13080 tyinfo->dobj.namespace->dobj.name,
13081 NULL, tyinfo->rolname, &tyinfo->dacl);
13082
13083 /* Dump any per-column comments */
13084 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13085 dumpCompositeTypeColComments(fout, tyinfo, res);
13086
13087 PQclear(res);
13089 destroyPQExpBuffer(dropped);
13090 destroyPQExpBuffer(delq);
13091 destroyPQExpBuffer(query);
13092 free(qtypname);
13093 free(qualtypname);
13094}
13095
13096/*
13097 * dumpCompositeTypeColComments
13098 * writes out to fout the queries to recreate comments on the columns of
13099 * a user-defined stand-alone composite type.
13100 *
13101 * The caller has already made a query to collect the names and attnums
13102 * of the type's columns, so we just pass that result into here rather
13103 * than reading them again.
13104 */
13105static void
13107 PGresult *res)
13108{
13110 int ncomments;
13111 PQExpBuffer query;
13112 PQExpBuffer target;
13113 int i;
13114 int ntups;
13115 int i_attname;
13116 int i_attnum;
13117 int i_attisdropped;
13118
13119 /* do nothing, if --no-comments is supplied */
13120 if (fout->dopt->no_comments)
13121 return;
13122
13123 /* Search for comments associated with type's pg_class OID */
13124 ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13125 &comments);
13126
13127 /* If no comments exist, we're done */
13128 if (ncomments <= 0)
13129 return;
13130
13131 /* Build COMMENT ON statements */
13132 query = createPQExpBuffer();
13133 target = createPQExpBuffer();
13134
13135 ntups = PQntuples(res);
13136 i_attnum = PQfnumber(res, "attnum");
13137 i_attname = PQfnumber(res, "attname");
13138 i_attisdropped = PQfnumber(res, "attisdropped");
13139 while (ncomments > 0)
13140 {
13141 const char *attname;
13142
13143 attname = NULL;
13144 for (i = 0; i < ntups; i++)
13145 {
13146 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13147 PQgetvalue(res, i, i_attisdropped)[0] != 't')
13148 {
13149 attname = PQgetvalue(res, i, i_attname);
13150 break;
13151 }
13152 }
13153 if (attname) /* just in case we don't find it */
13154 {
13155 const char *descr = comments->descr;
13156
13157 resetPQExpBuffer(target);
13158 appendPQExpBuffer(target, "COLUMN %s.",
13159 fmtId(tyinfo->dobj.name));
13161
13162 resetPQExpBuffer(query);
13163 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13164 fmtQualifiedDumpable(tyinfo));
13165 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13166 appendStringLiteralAH(query, descr, fout);
13167 appendPQExpBufferStr(query, ";\n");
13168
13170 ARCHIVE_OPTS(.tag = target->data,
13171 .namespace = tyinfo->dobj.namespace->dobj.name,
13172 .owner = tyinfo->rolname,
13173 .description = "COMMENT",
13174 .section = SECTION_NONE,
13175 .createStmt = query->data,
13176 .deps = &(tyinfo->dobj.dumpId),
13177 .nDeps = 1));
13178 }
13179
13180 comments++;
13181 ncomments--;
13182 }
13183
13184 destroyPQExpBuffer(query);
13185 destroyPQExpBuffer(target);
13186}
13187
13188/*
13189 * dumpShellType
13190 * writes out to fout the queries to create a shell type
13191 *
13192 * We dump a shell definition in advance of the I/O functions for the type.
13193 */
13194static void
13196{
13197 DumpOptions *dopt = fout->dopt;
13198 PQExpBuffer q;
13199
13200 /* Do nothing if not dumping schema */
13201 if (!dopt->dumpSchema)
13202 return;
13203
13204 q = createPQExpBuffer();
13205
13206 /*
13207 * Note the lack of a DROP command for the shell type; any required DROP
13208 * is driven off the base type entry, instead. This interacts with
13209 * _printTocEntry()'s use of the presence of a DROP command to decide
13210 * whether an entry needs an ALTER OWNER command. We don't want to alter
13211 * the shell type's owner immediately on creation; that should happen only
13212 * after it's filled in, otherwise the backend complains.
13213 */
13214
13215 if (dopt->binary_upgrade)
13217 stinfo->baseType->dobj.catId.oid,
13218 false, false);
13219
13220 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13221 fmtQualifiedDumpable(stinfo));
13222
13223 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13224 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13225 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13226 .namespace = stinfo->dobj.namespace->dobj.name,
13227 .owner = stinfo->baseType->rolname,
13228 .description = "SHELL TYPE",
13229 .section = SECTION_PRE_DATA,
13230 .createStmt = q->data));
13231
13233}
13234
13235/*
13236 * dumpProcLang
13237 * writes out to fout the queries to recreate a user-defined
13238 * procedural language
13239 */
13240static void
13242{
13243 DumpOptions *dopt = fout->dopt;
13244 PQExpBuffer defqry;
13245 PQExpBuffer delqry;
13246 bool useParams;
13247 char *qlanname;
13248 FuncInfo *funcInfo;
13249 FuncInfo *inlineInfo = NULL;
13250 FuncInfo *validatorInfo = NULL;
13251
13252 /* Do nothing if not dumping schema */
13253 if (!dopt->dumpSchema)
13254 return;
13255
13256 /*
13257 * Try to find the support function(s). It is not an error if we don't
13258 * find them --- if the functions are in the pg_catalog schema, as is
13259 * standard in 8.1 and up, then we won't have loaded them. (In this case
13260 * we will emit a parameterless CREATE LANGUAGE command, which will
13261 * require PL template knowledge in the backend to reload.)
13262 */
13263
13264 funcInfo = findFuncByOid(plang->lanplcallfoid);
13265 if (funcInfo != NULL && !funcInfo->dobj.dump)
13266 funcInfo = NULL; /* treat not-dumped same as not-found */
13267
13268 if (OidIsValid(plang->laninline))
13269 {
13270 inlineInfo = findFuncByOid(plang->laninline);
13271 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13272 inlineInfo = NULL;
13273 }
13274
13275 if (OidIsValid(plang->lanvalidator))
13276 {
13277 validatorInfo = findFuncByOid(plang->lanvalidator);
13278 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13279 validatorInfo = NULL;
13280 }
13281
13282 /*
13283 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13284 * parameters. Otherwise, we'll write a parameterless command, which will
13285 * be interpreted as CREATE EXTENSION.
13286 */
13287 useParams = (funcInfo != NULL &&
13288 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13289 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13290
13291 defqry = createPQExpBuffer();
13292 delqry = createPQExpBuffer();
13293
13294 qlanname = pg_strdup(fmtId(plang->dobj.name));
13295
13296 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13297 qlanname);
13298
13299 if (useParams)
13300 {
13301 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13302 plang->lanpltrusted ? "TRUSTED " : "",
13303 qlanname);
13304 appendPQExpBuffer(defqry, " HANDLER %s",
13305 fmtQualifiedDumpable(funcInfo));
13306 if (OidIsValid(plang->laninline))
13307 appendPQExpBuffer(defqry, " INLINE %s",
13308 fmtQualifiedDumpable(inlineInfo));
13309 if (OidIsValid(plang->lanvalidator))
13310 appendPQExpBuffer(defqry, " VALIDATOR %s",
13311 fmtQualifiedDumpable(validatorInfo));
13312 }
13313 else
13314 {
13315 /*
13316 * If not dumping parameters, then use CREATE OR REPLACE so that the
13317 * command will not fail if the language is preinstalled in the target
13318 * database.
13319 *
13320 * Modern servers will interpret this as CREATE EXTENSION IF NOT
13321 * EXISTS; perhaps we should emit that instead? But it might just add
13322 * confusion.
13323 */
13324 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13325 qlanname);
13326 }
13327 appendPQExpBufferStr(defqry, ";\n");
13328
13329 if (dopt->binary_upgrade)
13330 binary_upgrade_extension_member(defqry, &plang->dobj,
13331 "LANGUAGE", qlanname, NULL);
13332
13333 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13334 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13335 ARCHIVE_OPTS(.tag = plang->dobj.name,
13336 .owner = plang->lanowner,
13337 .description = "PROCEDURAL LANGUAGE",
13338 .section = SECTION_PRE_DATA,
13339 .createStmt = defqry->data,
13340 .dropStmt = delqry->data,
13341 ));
13342
13343 /* Dump Proc Lang Comments and Security Labels */
13344 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13345 dumpComment(fout, "LANGUAGE", qlanname,
13346 NULL, plang->lanowner,
13347 plang->dobj.catId, 0, plang->dobj.dumpId);
13348
13349 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13350 dumpSecLabel(fout, "LANGUAGE", qlanname,
13351 NULL, plang->lanowner,
13352 plang->dobj.catId, 0, plang->dobj.dumpId);
13353
13354 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13355 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13356 qlanname, NULL, NULL,
13357 NULL, plang->lanowner, &plang->dacl);
13358
13359 free(qlanname);
13360
13361 destroyPQExpBuffer(defqry);
13362 destroyPQExpBuffer(delqry);
13363}
13364
13365/*
13366 * format_function_arguments: generate function name and argument list
13367 *
13368 * This is used when we can rely on pg_get_function_arguments to format
13369 * the argument list. Note, however, that pg_get_function_arguments
13370 * does not special-case zero-argument aggregates.
13371 */
13372static char *
13373format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13374{
13376
13379 if (is_agg && finfo->nargs == 0)
13380 appendPQExpBufferStr(&fn, "(*)");
13381 else
13382 appendPQExpBuffer(&fn, "(%s)", funcargs);
13383 return fn.data;
13384}
13385
13386/*
13387 * format_function_signature: generate function name and argument list
13388 *
13389 * Only a minimal list of input argument types is generated; this is
13390 * sufficient to reference the function, but not to define it.
13391 *
13392 * If honor_quotes is false then the function name is never quoted.
13393 * This is appropriate for use in TOC tags, but not in SQL commands.
13394 */
13395static char *
13396format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13397{
13399 int j;
13400
13402 if (honor_quotes)
13403 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13404 else
13405 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13406 for (j = 0; j < finfo->nargs; j++)
13407 {
13408 if (j > 0)
13409 appendPQExpBufferStr(&fn, ", ");
13410
13412 getFormattedTypeName(fout, finfo->argtypes[j],
13413 zeroIsError));
13414 }
13416 return fn.data;
13417}
13418
13419
13420/*
13421 * dumpFunc:
13422 * dump out one function
13423 */
13424static void
13425dumpFunc(Archive *fout, const FuncInfo *finfo)
13426{
13427 DumpOptions *dopt = fout->dopt;
13428 PQExpBuffer query;
13429 PQExpBuffer q;
13430 PQExpBuffer delqry;
13431 PQExpBuffer asPart;
13432 PGresult *res;
13433 char *funcsig; /* identity signature */
13434 char *funcfullsig = NULL; /* full signature */
13435 char *funcsig_tag;
13436 char *qual_funcsig;
13437 char *proretset;
13438 char *prosrc;
13439 char *probin;
13440 char *prosqlbody;
13441 char *funcargs;
13442 char *funciargs;
13443 char *funcresult;
13444 char *protrftypes;
13445 char *prokind;
13446 char *provolatile;
13447 char *proisstrict;
13448 char *prosecdef;
13449 char *proleakproof;
13450 char *proconfig;
13451 char *procost;
13452 char *prorows;
13453 char *prosupport;
13454 char *proparallel;
13455 char *lanname;
13456 char **configitems = NULL;
13457 int nconfigitems = 0;
13458 const char *keyword;
13459
13460 /* Do nothing if not dumping schema */
13461 if (!dopt->dumpSchema)
13462 return;
13463
13464 query = createPQExpBuffer();
13465 q = createPQExpBuffer();
13466 delqry = createPQExpBuffer();
13467 asPart = createPQExpBuffer();
13468
13469 if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13470 {
13471 /* Set up query for function-specific details */
13473 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13474
13476 "SELECT\n"
13477 "proretset,\n"
13478 "prosrc,\n"
13479 "probin,\n"
13480 "provolatile,\n"
13481 "proisstrict,\n"
13482 "prosecdef,\n"
13483 "lanname,\n"
13484 "proconfig,\n"
13485 "procost,\n"
13486 "prorows,\n"
13487 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13488 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13489 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13490 "proleakproof,\n");
13491
13492 if (fout->remoteVersion >= 90500)
13494 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13495 else
13497 "NULL AS protrftypes,\n");
13498
13499 if (fout->remoteVersion >= 90600)
13501 "proparallel,\n");
13502 else
13504 "'u' AS proparallel,\n");
13505
13506 if (fout->remoteVersion >= 110000)
13508 "prokind,\n");
13509 else
13511 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13512
13513 if (fout->remoteVersion >= 120000)
13515 "prosupport,\n");
13516 else
13518 "'-' AS prosupport,\n");
13519
13520 if (fout->remoteVersion >= 140000)
13522 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13523 else
13525 "NULL AS prosqlbody\n");
13526
13528 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13529 "WHERE p.oid = $1 "
13530 "AND l.oid = p.prolang");
13531
13532 ExecuteSqlStatement(fout, query->data);
13533
13534 fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13535 }
13536
13537 printfPQExpBuffer(query,
13538 "EXECUTE dumpFunc('%u')",
13539 finfo->dobj.catId.oid);
13540
13541 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13542
13543 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13544 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13545 {
13546 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13547 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13548 prosqlbody = NULL;
13549 }
13550 else
13551 {
13552 prosrc = NULL;
13553 probin = NULL;
13554 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13555 }
13556 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13557 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13558 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13559 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13560 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13561 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13562 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13563 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13564 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13565 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13566 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13567 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13568 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13569 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13570 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13571
13572 /*
13573 * See backend/commands/functioncmds.c for details of how the 'AS' clause
13574 * is used.
13575 */
13576 if (prosqlbody)
13577 {
13578 appendPQExpBufferStr(asPart, prosqlbody);
13579 }
13580 else if (probin[0] != '\0')
13581 {
13582 appendPQExpBufferStr(asPart, "AS ");
13583 appendStringLiteralAH(asPart, probin, fout);
13584 if (prosrc[0] != '\0')
13585 {
13586 appendPQExpBufferStr(asPart, ", ");
13587
13588 /*
13589 * where we have bin, use dollar quoting if allowed and src
13590 * contains quote or backslash; else use regular quoting.
13591 */
13592 if (dopt->disable_dollar_quoting ||
13593 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13594 appendStringLiteralAH(asPart, prosrc, fout);
13595 else
13596 appendStringLiteralDQ(asPart, prosrc, NULL);
13597 }
13598 }
13599 else
13600 {
13601 appendPQExpBufferStr(asPart, "AS ");
13602 /* with no bin, dollar quote src unconditionally if allowed */
13603 if (dopt->disable_dollar_quoting)
13604 appendStringLiteralAH(asPart, prosrc, fout);
13605 else
13606 appendStringLiteralDQ(asPart, prosrc, NULL);
13607 }
13608
13609 if (*proconfig)
13610 {
13611 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13612 pg_fatal("could not parse %s array", "proconfig");
13613 }
13614 else
13615 {
13616 configitems = NULL;
13617 nconfigitems = 0;
13618 }
13619
13620 funcfullsig = format_function_arguments(finfo, funcargs, false);
13621 funcsig = format_function_arguments(finfo, funciargs, false);
13622
13623 funcsig_tag = format_function_signature(fout, finfo, false);
13624
13625 qual_funcsig = psprintf("%s.%s",
13626 fmtId(finfo->dobj.namespace->dobj.name),
13627 funcsig);
13628
13629 if (prokind[0] == PROKIND_PROCEDURE)
13630 keyword = "PROCEDURE";
13631 else
13632 keyword = "FUNCTION"; /* works for window functions too */
13633
13634 appendPQExpBuffer(delqry, "DROP %s %s;\n",
13635 keyword, qual_funcsig);
13636
13637 appendPQExpBuffer(q, "CREATE %s %s.%s",
13638 keyword,
13639 fmtId(finfo->dobj.namespace->dobj.name),
13640 funcfullsig ? funcfullsig :
13641 funcsig);
13642
13643 if (prokind[0] == PROKIND_PROCEDURE)
13644 /* no result type to output */ ;
13645 else if (funcresult)
13646 appendPQExpBuffer(q, " RETURNS %s", funcresult);
13647 else
13648 appendPQExpBuffer(q, " RETURNS %s%s",
13649 (proretset[0] == 't') ? "SETOF " : "",
13650 getFormattedTypeName(fout, finfo->prorettype,
13651 zeroIsError));
13652
13653 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13654
13655 if (*protrftypes)
13656 {
13657 Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13658 int i;
13659
13660 appendPQExpBufferStr(q, " TRANSFORM ");
13661 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13662 for (i = 0; typeids[i]; i++)
13663 {
13664 if (i != 0)
13665 appendPQExpBufferStr(q, ", ");
13666 appendPQExpBuffer(q, "FOR TYPE %s",
13667 getFormattedTypeName(fout, typeids[i], zeroAsNone));
13668 }
13669
13670 free(typeids);
13671 }
13672
13673 if (prokind[0] == PROKIND_WINDOW)
13674 appendPQExpBufferStr(q, " WINDOW");
13675
13676 if (provolatile[0] != PROVOLATILE_VOLATILE)
13677 {
13678 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13679 appendPQExpBufferStr(q, " IMMUTABLE");
13680 else if (provolatile[0] == PROVOLATILE_STABLE)
13681 appendPQExpBufferStr(q, " STABLE");
13682 else if (provolatile[0] != PROVOLATILE_VOLATILE)
13683 pg_fatal("unrecognized provolatile value for function \"%s\"",
13684 finfo->dobj.name);
13685 }
13686
13687 if (proisstrict[0] == 't')
13688 appendPQExpBufferStr(q, " STRICT");
13689
13690 if (prosecdef[0] == 't')
13691 appendPQExpBufferStr(q, " SECURITY DEFINER");
13692
13693 if (proleakproof[0] == 't')
13694 appendPQExpBufferStr(q, " LEAKPROOF");
13695
13696 /*
13697 * COST and ROWS are emitted only if present and not default, so as not to
13698 * break backwards-compatibility of the dump without need. Keep this code
13699 * in sync with the defaults in functioncmds.c.
13700 */
13701 if (strcmp(procost, "0") != 0)
13702 {
13703 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13704 {
13705 /* default cost is 1 */
13706 if (strcmp(procost, "1") != 0)
13707 appendPQExpBuffer(q, " COST %s", procost);
13708 }
13709 else
13710 {
13711 /* default cost is 100 */
13712 if (strcmp(procost, "100") != 0)
13713 appendPQExpBuffer(q, " COST %s", procost);
13714 }
13715 }
13716 if (proretset[0] == 't' &&
13717 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13718 appendPQExpBuffer(q, " ROWS %s", prorows);
13719
13720 if (strcmp(prosupport, "-") != 0)
13721 {
13722 /* We rely on regprocout to provide quoting and qualification */
13723 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13724 }
13725
13726 if (proparallel[0] != PROPARALLEL_UNSAFE)
13727 {
13728 if (proparallel[0] == PROPARALLEL_SAFE)
13729 appendPQExpBufferStr(q, " PARALLEL SAFE");
13730 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13731 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13732 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13733 pg_fatal("unrecognized proparallel value for function \"%s\"",
13734 finfo->dobj.name);
13735 }
13736
13737 for (int i = 0; i < nconfigitems; i++)
13738 {
13739 /* we feel free to scribble on configitems[] here */
13740 char *configitem = configitems[i];
13741 char *pos;
13742
13743 pos = strchr(configitem, '=');
13744 if (pos == NULL)
13745 continue;
13746 *pos++ = '\0';
13747 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13748
13749 /*
13750 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13751 * by flatten_set_variable_args() before they were put into the
13752 * proconfig array. However, because the quoting rules used there
13753 * aren't exactly like SQL's, we have to break the list value apart
13754 * and then quote the elements as string literals. (The elements may
13755 * be double-quoted as-is, but we can't just feed them to the SQL
13756 * parser; it would do the wrong thing with elements that are
13757 * zero-length or longer than NAMEDATALEN.)
13758 *
13759 * Variables that are not so marked should just be emitted as simple
13760 * string literals. If the variable is not known to
13761 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13762 * to use GUC_LIST_QUOTE for extension variables.
13763 */
13764 if (variable_is_guc_list_quote(configitem))
13765 {
13766 char **namelist;
13767 char **nameptr;
13768
13769 /* Parse string into list of identifiers */
13770 /* this shouldn't fail really */
13771 if (SplitGUCList(pos, ',', &namelist))
13772 {
13773 for (nameptr = namelist; *nameptr; nameptr++)
13774 {
13775 if (nameptr != namelist)
13776 appendPQExpBufferStr(q, ", ");
13777 appendStringLiteralAH(q, *nameptr, fout);
13778 }
13779 }
13780 pg_free(namelist);
13781 }
13782 else
13783 appendStringLiteralAH(q, pos, fout);
13784 }
13785
13786 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13787
13788 append_depends_on_extension(fout, q, &finfo->dobj,
13789 "pg_catalog.pg_proc", keyword,
13790 qual_funcsig);
13791
13792 if (dopt->binary_upgrade)
13794 keyword, funcsig,
13795 finfo->dobj.namespace->dobj.name);
13796
13797 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13798 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13799 ARCHIVE_OPTS(.tag = funcsig_tag,
13800 .namespace = finfo->dobj.namespace->dobj.name,
13801 .owner = finfo->rolname,
13802 .description = keyword,
13803 .section = finfo->postponed_def ?
13805 .createStmt = q->data,
13806 .dropStmt = delqry->data));
13807
13808 /* Dump Function Comments and Security Labels */
13809 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13810 dumpComment(fout, keyword, funcsig,
13811 finfo->dobj.namespace->dobj.name, finfo->rolname,
13812 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13813
13814 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13815 dumpSecLabel(fout, keyword, funcsig,
13816 finfo->dobj.namespace->dobj.name, finfo->rolname,
13817 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13818
13819 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13820 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13821 funcsig, NULL,
13822 finfo->dobj.namespace->dobj.name,
13823 NULL, finfo->rolname, &finfo->dacl);
13824
13825 PQclear(res);
13826
13827 destroyPQExpBuffer(query);
13829 destroyPQExpBuffer(delqry);
13830 destroyPQExpBuffer(asPart);
13831 free(funcsig);
13832 free(funcfullsig);
13833 free(funcsig_tag);
13834 free(qual_funcsig);
13835 free(configitems);
13836}
13837
13838
13839/*
13840 * Dump a user-defined cast
13841 */
13842static void
13843dumpCast(Archive *fout, const CastInfo *cast)
13844{
13845 DumpOptions *dopt = fout->dopt;
13846 PQExpBuffer defqry;
13847 PQExpBuffer delqry;
13848 PQExpBuffer labelq;
13849 PQExpBuffer castargs;
13850 FuncInfo *funcInfo = NULL;
13851 const char *sourceType;
13852 const char *targetType;
13853
13854 /* Do nothing if not dumping schema */
13855 if (!dopt->dumpSchema)
13856 return;
13857
13858 /* Cannot dump if we don't have the cast function's info */
13859 if (OidIsValid(cast->castfunc))
13860 {
13861 funcInfo = findFuncByOid(cast->castfunc);
13862 if (funcInfo == NULL)
13863 pg_fatal("could not find function definition for function with OID %u",
13864 cast->castfunc);
13865 }
13866
13867 defqry = createPQExpBuffer();
13868 delqry = createPQExpBuffer();
13869 labelq = createPQExpBuffer();
13870 castargs = createPQExpBuffer();
13871
13872 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13873 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13874 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13875 sourceType, targetType);
13876
13877 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13878 sourceType, targetType);
13879
13880 switch (cast->castmethod)
13881 {
13882 case COERCION_METHOD_BINARY:
13883 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13884 break;
13885 case COERCION_METHOD_INOUT:
13886 appendPQExpBufferStr(defqry, "WITH INOUT");
13887 break;
13888 case COERCION_METHOD_FUNCTION:
13889 if (funcInfo)
13890 {
13891 char *fsig = format_function_signature(fout, funcInfo, true);
13892
13893 /*
13894 * Always qualify the function name (format_function_signature
13895 * won't qualify it).
13896 */
13897 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13898 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13899 free(fsig);
13900 }
13901 else
13902 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13903 break;
13904 default:
13905 pg_log_warning("bogus value in pg_cast.castmethod field");
13906 }
13907
13908 if (cast->castcontext == 'a')
13909 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13910 else if (cast->castcontext == 'i')
13911 appendPQExpBufferStr(defqry, " AS IMPLICIT");
13912 appendPQExpBufferStr(defqry, ";\n");
13913
13914 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13915 sourceType, targetType);
13916
13917 appendPQExpBuffer(castargs, "(%s AS %s)",
13918 sourceType, targetType);
13919
13920 if (dopt->binary_upgrade)
13922 "CAST", castargs->data, NULL);
13923
13925 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13926 ARCHIVE_OPTS(.tag = labelq->data,
13927 .description = "CAST",
13928 .section = SECTION_PRE_DATA,
13929 .createStmt = defqry->data,
13930 .dropStmt = delqry->data));
13931
13932 /* Dump Cast Comments */
13933 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13934 dumpComment(fout, "CAST", castargs->data,
13935 NULL, "",
13936 cast->dobj.catId, 0, cast->dobj.dumpId);
13937
13938 destroyPQExpBuffer(defqry);
13939 destroyPQExpBuffer(delqry);
13940 destroyPQExpBuffer(labelq);
13941 destroyPQExpBuffer(castargs);
13942}
13943
13944/*
13945 * Dump a transform
13946 */
13947static void
13948dumpTransform(Archive *fout, const TransformInfo *transform)
13949{
13950 DumpOptions *dopt = fout->dopt;
13951 PQExpBuffer defqry;
13952 PQExpBuffer delqry;
13953 PQExpBuffer labelq;
13954 PQExpBuffer transformargs;
13955 FuncInfo *fromsqlFuncInfo = NULL;
13956 FuncInfo *tosqlFuncInfo = NULL;
13957 char *lanname;
13958 const char *transformType;
13959
13960 /* Do nothing if not dumping schema */
13961 if (!dopt->dumpSchema)
13962 return;
13963
13964 /* Cannot dump if we don't have the transform functions' info */
13965 if (OidIsValid(transform->trffromsql))
13966 {
13967 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13968 if (fromsqlFuncInfo == NULL)
13969 pg_fatal("could not find function definition for function with OID %u",
13970 transform->trffromsql);
13971 }
13972 if (OidIsValid(transform->trftosql))
13973 {
13974 tosqlFuncInfo = findFuncByOid(transform->trftosql);
13975 if (tosqlFuncInfo == NULL)
13976 pg_fatal("could not find function definition for function with OID %u",
13977 transform->trftosql);
13978 }
13979
13980 defqry = createPQExpBuffer();
13981 delqry = createPQExpBuffer();
13982 labelq = createPQExpBuffer();
13983 transformargs = createPQExpBuffer();
13984
13985 lanname = get_language_name(fout, transform->trflang);
13986 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13987
13988 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13989 transformType, lanname);
13990
13991 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13992 transformType, lanname);
13993
13994 if (!transform->trffromsql && !transform->trftosql)
13995 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13996
13997 if (transform->trffromsql)
13998 {
13999 if (fromsqlFuncInfo)
14000 {
14001 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14002
14003 /*
14004 * Always qualify the function name (format_function_signature
14005 * won't qualify it).
14006 */
14007 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14008 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14009 free(fsig);
14010 }
14011 else
14012 pg_log_warning("bogus value in pg_transform.trffromsql field");
14013 }
14014
14015 if (transform->trftosql)
14016 {
14017 if (transform->trffromsql)
14018 appendPQExpBufferStr(defqry, ", ");
14019
14020 if (tosqlFuncInfo)
14021 {
14022 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14023
14024 /*
14025 * Always qualify the function name (format_function_signature
14026 * won't qualify it).
14027 */
14028 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14029 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14030 free(fsig);
14031 }
14032 else
14033 pg_log_warning("bogus value in pg_transform.trftosql field");
14034 }
14035
14036 appendPQExpBufferStr(defqry, ");\n");
14037
14038 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14039 transformType, lanname);
14040
14041 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14042 transformType, lanname);
14043
14044 if (dopt->binary_upgrade)
14045 binary_upgrade_extension_member(defqry, &transform->dobj,
14046 "TRANSFORM", transformargs->data, NULL);
14047
14048 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14049 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14050 ARCHIVE_OPTS(.tag = labelq->data,
14051 .description = "TRANSFORM",
14052 .section = SECTION_PRE_DATA,
14053 .createStmt = defqry->data,
14054 .dropStmt = delqry->data,
14055 .deps = transform->dobj.dependencies,
14056 .nDeps = transform->dobj.nDeps));
14057
14058 /* Dump Transform Comments */
14059 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14060 dumpComment(fout, "TRANSFORM", transformargs->data,
14061 NULL, "",
14062 transform->dobj.catId, 0, transform->dobj.dumpId);
14063
14064 free(lanname);
14065 destroyPQExpBuffer(defqry);
14066 destroyPQExpBuffer(delqry);
14067 destroyPQExpBuffer(labelq);
14068 destroyPQExpBuffer(transformargs);
14069}
14070
14071
14072/*
14073 * dumpOpr
14074 * write out a single operator definition
14075 */
14076static void
14077dumpOpr(Archive *fout, const OprInfo *oprinfo)
14078{
14079 DumpOptions *dopt = fout->dopt;
14080 PQExpBuffer query;
14081 PQExpBuffer q;
14082 PQExpBuffer delq;
14084 PQExpBuffer details;
14085 PGresult *res;
14086 int i_oprkind;
14087 int i_oprcode;
14088 int i_oprleft;
14089 int i_oprright;
14090 int i_oprcom;
14091 int i_oprnegate;
14092 int i_oprrest;
14093 int i_oprjoin;
14094 int i_oprcanmerge;
14095 int i_oprcanhash;
14096 char *oprkind;
14097 char *oprcode;
14098 char *oprleft;
14099 char *oprright;
14100 char *oprcom;
14101 char *oprnegate;
14102 char *oprrest;
14103 char *oprjoin;
14104 char *oprcanmerge;
14105 char *oprcanhash;
14106 char *oprregproc;
14107 char *oprref;
14108
14109 /* Do nothing if not dumping schema */
14110 if (!dopt->dumpSchema)
14111 return;
14112
14113 /*
14114 * some operators are invalid because they were the result of user
14115 * defining operators before commutators exist
14116 */
14117 if (!OidIsValid(oprinfo->oprcode))
14118 return;
14119
14120 query = createPQExpBuffer();
14121 q = createPQExpBuffer();
14122 delq = createPQExpBuffer();
14124 details = createPQExpBuffer();
14125
14126 if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14127 {
14128 /* Set up query for operator-specific details */
14130 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14131 "SELECT oprkind, "
14132 "oprcode::pg_catalog.regprocedure, "
14133 "oprleft::pg_catalog.regtype, "
14134 "oprright::pg_catalog.regtype, "
14135 "oprcom, "
14136 "oprnegate, "
14137 "oprrest::pg_catalog.regprocedure, "
14138 "oprjoin::pg_catalog.regprocedure, "
14139 "oprcanmerge, oprcanhash "
14140 "FROM pg_catalog.pg_operator "
14141 "WHERE oid = $1");
14142
14143 ExecuteSqlStatement(fout, query->data);
14144
14145 fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14146 }
14147
14148 printfPQExpBuffer(query,
14149 "EXECUTE dumpOpr('%u')",
14150 oprinfo->dobj.catId.oid);
14151
14152 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14153
14154 i_oprkind = PQfnumber(res, "oprkind");
14155 i_oprcode = PQfnumber(res, "oprcode");
14156 i_oprleft = PQfnumber(res, "oprleft");
14157 i_oprright = PQfnumber(res, "oprright");
14158 i_oprcom = PQfnumber(res, "oprcom");
14159 i_oprnegate = PQfnumber(res, "oprnegate");
14160 i_oprrest = PQfnumber(res, "oprrest");
14161 i_oprjoin = PQfnumber(res, "oprjoin");
14162 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14163 i_oprcanhash = PQfnumber(res, "oprcanhash");
14164
14165 oprkind = PQgetvalue(res, 0, i_oprkind);
14166 oprcode = PQgetvalue(res, 0, i_oprcode);
14167 oprleft = PQgetvalue(res, 0, i_oprleft);
14168 oprright = PQgetvalue(res, 0, i_oprright);
14169 oprcom = PQgetvalue(res, 0, i_oprcom);
14170 oprnegate = PQgetvalue(res, 0, i_oprnegate);
14171 oprrest = PQgetvalue(res, 0, i_oprrest);
14172 oprjoin = PQgetvalue(res, 0, i_oprjoin);
14173 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14174 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14175
14176 /* In PG14 upwards postfix operator support does not exist anymore. */
14177 if (strcmp(oprkind, "r") == 0)
14178 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14179 oprcode);
14180
14181 oprregproc = convertRegProcReference(oprcode);
14182 if (oprregproc)
14183 {
14184 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14185 free(oprregproc);
14186 }
14187
14188 appendPQExpBuffer(oprid, "%s (",
14189 oprinfo->dobj.name);
14190
14191 /*
14192 * right unary means there's a left arg and left unary means there's a
14193 * right arg. (Although the "r" case is dead code for PG14 and later,
14194 * continue to support it in case we're dumping from an old server.)
14195 */
14196 if (strcmp(oprkind, "r") == 0 ||
14197 strcmp(oprkind, "b") == 0)
14198 {
14199 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14200 appendPQExpBufferStr(oprid, oprleft);
14201 }
14202 else
14203 appendPQExpBufferStr(oprid, "NONE");
14204
14205 if (strcmp(oprkind, "l") == 0 ||
14206 strcmp(oprkind, "b") == 0)
14207 {
14208 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14209 appendPQExpBuffer(oprid, ", %s)", oprright);
14210 }
14211 else
14212 appendPQExpBufferStr(oprid, ", NONE)");
14213
14214 oprref = getFormattedOperatorName(oprcom);
14215 if (oprref)
14216 {
14217 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14218 free(oprref);
14219 }
14220
14221 oprref = getFormattedOperatorName(oprnegate);
14222 if (oprref)
14223 {
14224 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14225 free(oprref);
14226 }
14227
14228 if (strcmp(oprcanmerge, "t") == 0)
14229 appendPQExpBufferStr(details, ",\n MERGES");
14230
14231 if (strcmp(oprcanhash, "t") == 0)
14232 appendPQExpBufferStr(details, ",\n HASHES");
14233
14234 oprregproc = convertRegProcReference(oprrest);
14235 if (oprregproc)
14236 {
14237 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14238 free(oprregproc);
14239 }
14240
14241 oprregproc = convertRegProcReference(oprjoin);
14242 if (oprregproc)
14243 {
14244 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14245 free(oprregproc);
14246 }
14247
14248 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14249 fmtId(oprinfo->dobj.namespace->dobj.name),
14250 oprid->data);
14251
14252 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14253 fmtId(oprinfo->dobj.namespace->dobj.name),
14254 oprinfo->dobj.name, details->data);
14255
14256 if (dopt->binary_upgrade)
14258 "OPERATOR", oprid->data,
14259 oprinfo->dobj.namespace->dobj.name);
14260
14261 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14262 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14263 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14264 .namespace = oprinfo->dobj.namespace->dobj.name,
14265 .owner = oprinfo->rolname,
14266 .description = "OPERATOR",
14267 .section = SECTION_PRE_DATA,
14268 .createStmt = q->data,
14269 .dropStmt = delq->data));
14270
14271 /* Dump Operator Comments */
14272 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14273 dumpComment(fout, "OPERATOR", oprid->data,
14274 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14275 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14276
14277 PQclear(res);
14278
14279 destroyPQExpBuffer(query);
14281 destroyPQExpBuffer(delq);
14283 destroyPQExpBuffer(details);
14284}
14285
14286/*
14287 * Convert a function reference obtained from pg_operator
14288 *
14289 * Returns allocated string of what to print, or NULL if function references
14290 * is InvalidOid. Returned string is expected to be free'd by the caller.
14291 *
14292 * The input is a REGPROCEDURE display; we have to strip the argument-types
14293 * part.
14294 */
14295static char *
14297{
14298 char *name;
14299 char *paren;
14300 bool inquote;
14301
14302 /* In all cases "-" means a null reference */
14303 if (strcmp(proc, "-") == 0)
14304 return NULL;
14305
14306 name = pg_strdup(proc);
14307 /* find non-double-quoted left paren */
14308 inquote = false;
14309 for (paren = name; *paren; paren++)
14310 {
14311 if (*paren == '(' && !inquote)
14312 {
14313 *paren = '\0';
14314 break;
14315 }
14316 if (*paren == '"')
14317 inquote = !inquote;
14318 }
14319 return name;
14320}
14321
14322/*
14323 * getFormattedOperatorName - retrieve the operator name for the
14324 * given operator OID (presented in string form).
14325 *
14326 * Returns an allocated string, or NULL if the given OID is invalid.
14327 * Caller is responsible for free'ing result string.
14328 *
14329 * What we produce has the format "OPERATOR(schema.oprname)". This is only
14330 * useful in commands where the operator's argument types can be inferred from
14331 * context. We always schema-qualify the name, though. The predecessor to
14332 * this code tried to skip the schema qualification if possible, but that led
14333 * to wrong results in corner cases, such as if an operator and its negator
14334 * are in different schemas.
14335 */
14336static char *
14337getFormattedOperatorName(const char *oproid)
14338{
14339 OprInfo *oprInfo;
14340
14341 /* In all cases "0" means a null reference */
14342 if (strcmp(oproid, "0") == 0)
14343 return NULL;
14344
14345 oprInfo = findOprByOid(atooid(oproid));
14346 if (oprInfo == NULL)
14347 {
14348 pg_log_warning("could not find operator with OID %s",
14349 oproid);
14350 return NULL;
14351 }
14352
14353 return psprintf("OPERATOR(%s.%s)",
14354 fmtId(oprInfo->dobj.namespace->dobj.name),
14355 oprInfo->dobj.name);
14356}
14357
14358/*
14359 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14360 *
14361 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14362 * argument lists of these functions are predetermined. Note that the
14363 * caller should ensure we are in the proper schema, because the results
14364 * are search path dependent!
14365 */
14366static char *
14368{
14369 char *result;
14370 char query[128];
14371 PGresult *res;
14372
14373 snprintf(query, sizeof(query),
14374 "SELECT '%u'::pg_catalog.regproc", funcOid);
14375 res = ExecuteSqlQueryForSingleRow(fout, query);
14376
14377 result = pg_strdup(PQgetvalue(res, 0, 0));
14378
14379 PQclear(res);
14380
14381 return result;
14382}
14383
14384/*
14385 * dumpAccessMethod
14386 * write out a single access method definition
14387 */
14388static void
14390{
14391 DumpOptions *dopt = fout->dopt;
14392 PQExpBuffer q;
14393 PQExpBuffer delq;
14394 char *qamname;
14395
14396 /* Do nothing if not dumping schema */
14397 if (!dopt->dumpSchema)
14398 return;
14399
14400 q = createPQExpBuffer();
14401 delq = createPQExpBuffer();
14402
14403 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14404
14405 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14406
14407 switch (aminfo->amtype)
14408 {
14409 case AMTYPE_INDEX:
14410 appendPQExpBufferStr(q, "TYPE INDEX ");
14411 break;
14412 case AMTYPE_TABLE:
14413 appendPQExpBufferStr(q, "TYPE TABLE ");
14414 break;
14415 default:
14416 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14417 aminfo->amtype, qamname);
14419 destroyPQExpBuffer(delq);
14420 free(qamname);
14421 return;
14422 }
14423
14424 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14425
14426 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14427 qamname);
14428
14429 if (dopt->binary_upgrade)
14431 "ACCESS METHOD", qamname, NULL);
14432
14433 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14434 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14435 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14436 .description = "ACCESS METHOD",
14437 .section = SECTION_PRE_DATA,
14438 .createStmt = q->data,
14439 .dropStmt = delq->data));
14440
14441 /* Dump Access Method Comments */
14442 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14443 dumpComment(fout, "ACCESS METHOD", qamname,
14444 NULL, "",
14445 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14446
14448 destroyPQExpBuffer(delq);
14449 free(qamname);
14450}
14451
14452/*
14453 * dumpOpclass
14454 * write out a single operator class definition
14455 */
14456static void
14457dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14458{
14459 DumpOptions *dopt = fout->dopt;
14460 PQExpBuffer query;
14461 PQExpBuffer q;
14462 PQExpBuffer delq;
14463 PQExpBuffer nameusing;
14464 PGresult *res;
14465 int ntups;
14466 int i_opcintype;
14467 int i_opckeytype;
14468 int i_opcdefault;
14469 int i_opcfamily;
14470 int i_opcfamilyname;
14471 int i_opcfamilynsp;
14472 int i_amname;
14473 int i_amopstrategy;
14474 int i_amopopr;
14475 int i_sortfamily;
14476 int i_sortfamilynsp;
14477 int i_amprocnum;
14478 int i_amproc;
14479 int i_amproclefttype;
14480 int i_amprocrighttype;
14481 char *opcintype;
14482 char *opckeytype;
14483 char *opcdefault;
14484 char *opcfamily;
14485 char *opcfamilyname;
14486 char *opcfamilynsp;
14487 char *amname;
14488 char *amopstrategy;
14489 char *amopopr;
14490 char *sortfamily;
14491 char *sortfamilynsp;
14492 char *amprocnum;
14493 char *amproc;
14494 char *amproclefttype;
14495 char *amprocrighttype;
14496 bool needComma;
14497 int i;
14498
14499 /* Do nothing if not dumping schema */
14500 if (!dopt->dumpSchema)
14501 return;
14502
14503 query = createPQExpBuffer();
14504 q = createPQExpBuffer();
14505 delq = createPQExpBuffer();
14506 nameusing = createPQExpBuffer();
14507
14508 /* Get additional fields from the pg_opclass row */
14509 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14510 "opckeytype::pg_catalog.regtype, "
14511 "opcdefault, opcfamily, "
14512 "opfname AS opcfamilyname, "
14513 "nspname AS opcfamilynsp, "
14514 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14515 "FROM pg_catalog.pg_opclass c "
14516 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14517 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14518 "WHERE c.oid = '%u'::pg_catalog.oid",
14519 opcinfo->dobj.catId.oid);
14520
14521 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14522
14523 i_opcintype = PQfnumber(res, "opcintype");
14524 i_opckeytype = PQfnumber(res, "opckeytype");
14525 i_opcdefault = PQfnumber(res, "opcdefault");
14526 i_opcfamily = PQfnumber(res, "opcfamily");
14527 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14528 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14529 i_amname = PQfnumber(res, "amname");
14530
14531 /* opcintype may still be needed after we PQclear res */
14532 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14533 opckeytype = PQgetvalue(res, 0, i_opckeytype);
14534 opcdefault = PQgetvalue(res, 0, i_opcdefault);
14535 /* opcfamily will still be needed after we PQclear res */
14536 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14537 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14538 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14539 /* amname will still be needed after we PQclear res */
14540 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14541
14542 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14543 fmtQualifiedDumpable(opcinfo));
14544 appendPQExpBuffer(delq, " USING %s;\n",
14545 fmtId(amname));
14546
14547 /* Build the fixed portion of the CREATE command */
14548 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14549 fmtQualifiedDumpable(opcinfo));
14550 if (strcmp(opcdefault, "t") == 0)
14551 appendPQExpBufferStr(q, "DEFAULT ");
14552 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14553 opcintype,
14554 fmtId(amname));
14555 if (strlen(opcfamilyname) > 0)
14556 {
14557 appendPQExpBufferStr(q, " FAMILY ");
14558 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14559 appendPQExpBufferStr(q, fmtId(opcfamilyname));
14560 }
14561 appendPQExpBufferStr(q, " AS\n ");
14562
14563 needComma = false;
14564
14565 if (strcmp(opckeytype, "-") != 0)
14566 {
14567 appendPQExpBuffer(q, "STORAGE %s",
14568 opckeytype);
14569 needComma = true;
14570 }
14571
14572 PQclear(res);
14573
14574 /*
14575 * Now fetch and print the OPERATOR entries (pg_amop rows).
14576 *
14577 * Print only those opfamily members that are tied to the opclass by
14578 * pg_depend entries.
14579 */
14580 resetPQExpBuffer(query);
14581 appendPQExpBuffer(query, "SELECT amopstrategy, "
14582 "amopopr::pg_catalog.regoperator, "
14583 "opfname AS sortfamily, "
14584 "nspname AS sortfamilynsp "
14585 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14586 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14587 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14588 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14589 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14590 "AND refobjid = '%u'::pg_catalog.oid "
14591 "AND amopfamily = '%s'::pg_catalog.oid "
14592 "ORDER BY amopstrategy",
14593 opcinfo->dobj.catId.oid,
14594 opcfamily);
14595
14596 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14597
14598 ntups = PQntuples(res);
14599
14600 i_amopstrategy = PQfnumber(res, "amopstrategy");
14601 i_amopopr = PQfnumber(res, "amopopr");
14602 i_sortfamily = PQfnumber(res, "sortfamily");
14603 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14604
14605 for (i = 0; i < ntups; i++)
14606 {
14607 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14608 amopopr = PQgetvalue(res, i, i_amopopr);
14609 sortfamily = PQgetvalue(res, i, i_sortfamily);
14610 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14611
14612 if (needComma)
14613 appendPQExpBufferStr(q, " ,\n ");
14614
14615 appendPQExpBuffer(q, "OPERATOR %s %s",
14616 amopstrategy, amopopr);
14617
14618 if (strlen(sortfamily) > 0)
14619 {
14620 appendPQExpBufferStr(q, " FOR ORDER BY ");
14621 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14622 appendPQExpBufferStr(q, fmtId(sortfamily));
14623 }
14624
14625 needComma = true;
14626 }
14627
14628 PQclear(res);
14629
14630 /*
14631 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14632 *
14633 * Print only those opfamily members that are tied to the opclass by
14634 * pg_depend entries.
14635 *
14636 * We print the amproclefttype/amprocrighttype even though in most cases
14637 * the backend could deduce the right values, because of the corner case
14638 * of a btree sort support function for a cross-type comparison.
14639 */
14640 resetPQExpBuffer(query);
14641
14642 appendPQExpBuffer(query, "SELECT amprocnum, "
14643 "amproc::pg_catalog.regprocedure, "
14644 "amproclefttype::pg_catalog.regtype, "
14645 "amprocrighttype::pg_catalog.regtype "
14646 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14647 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14648 "AND refobjid = '%u'::pg_catalog.oid "
14649 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14650 "AND objid = ap.oid "
14651 "ORDER BY amprocnum",
14652 opcinfo->dobj.catId.oid);
14653
14654 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14655
14656 ntups = PQntuples(res);
14657
14658 i_amprocnum = PQfnumber(res, "amprocnum");
14659 i_amproc = PQfnumber(res, "amproc");
14660 i_amproclefttype = PQfnumber(res, "amproclefttype");
14661 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14662
14663 for (i = 0; i < ntups; i++)
14664 {
14665 amprocnum = PQgetvalue(res, i, i_amprocnum);
14666 amproc = PQgetvalue(res, i, i_amproc);
14667 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14668 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14669
14670 if (needComma)
14671 appendPQExpBufferStr(q, " ,\n ");
14672
14673 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14674
14675 if (*amproclefttype && *amprocrighttype)
14676 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14677
14678 appendPQExpBuffer(q, " %s", amproc);
14679
14680 needComma = true;
14681 }
14682
14683 PQclear(res);
14684
14685 /*
14686 * If needComma is still false it means we haven't added anything after
14687 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14688 * clause with the same datatype. This isn't sanctioned by the
14689 * documentation, but actually DefineOpClass will treat it as a no-op.
14690 */
14691 if (!needComma)
14692 appendPQExpBuffer(q, "STORAGE %s", opcintype);
14693
14694 appendPQExpBufferStr(q, ";\n");
14695
14696 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14697 appendPQExpBuffer(nameusing, " USING %s",
14698 fmtId(amname));
14699
14700 if (dopt->binary_upgrade)
14702 "OPERATOR CLASS", nameusing->data,
14703 opcinfo->dobj.namespace->dobj.name);
14704
14705 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14706 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14707 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14708 .namespace = opcinfo->dobj.namespace->dobj.name,
14709 .owner = opcinfo->rolname,
14710 .description = "OPERATOR CLASS",
14711 .section = SECTION_PRE_DATA,
14712 .createStmt = q->data,
14713 .dropStmt = delq->data));
14714
14715 /* Dump Operator Class Comments */
14716 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14717 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14718 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14719 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14720
14721 free(opcintype);
14722 free(opcfamily);
14723 free(amname);
14724 destroyPQExpBuffer(query);
14726 destroyPQExpBuffer(delq);
14727 destroyPQExpBuffer(nameusing);
14728}
14729
14730/*
14731 * dumpOpfamily
14732 * write out a single operator family definition
14733 *
14734 * Note: this also dumps any "loose" operator members that aren't bound to a
14735 * specific opclass within the opfamily.
14736 */
14737static void
14738dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14739{
14740 DumpOptions *dopt = fout->dopt;
14741 PQExpBuffer query;
14742 PQExpBuffer q;
14743 PQExpBuffer delq;
14744 PQExpBuffer nameusing;
14745 PGresult *res;
14746 PGresult *res_ops;
14747 PGresult *res_procs;
14748 int ntups;
14749 int i_amname;
14750 int i_amopstrategy;
14751 int i_amopopr;
14752 int i_sortfamily;
14753 int i_sortfamilynsp;
14754 int i_amprocnum;
14755 int i_amproc;
14756 int i_amproclefttype;
14757 int i_amprocrighttype;
14758 char *amname;
14759 char *amopstrategy;
14760 char *amopopr;
14761 char *sortfamily;
14762 char *sortfamilynsp;
14763 char *amprocnum;
14764 char *amproc;
14765 char *amproclefttype;
14766 char *amprocrighttype;
14767 bool needComma;
14768 int i;
14769
14770 /* Do nothing if not dumping schema */
14771 if (!dopt->dumpSchema)
14772 return;
14773
14774 query = createPQExpBuffer();
14775 q = createPQExpBuffer();
14776 delq = createPQExpBuffer();
14777 nameusing = createPQExpBuffer();
14778
14779 /*
14780 * Fetch only those opfamily members that are tied directly to the
14781 * opfamily by pg_depend entries.
14782 */
14783 appendPQExpBuffer(query, "SELECT amopstrategy, "
14784 "amopopr::pg_catalog.regoperator, "
14785 "opfname AS sortfamily, "
14786 "nspname AS sortfamilynsp "
14787 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14788 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14789 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14790 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14791 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14792 "AND refobjid = '%u'::pg_catalog.oid "
14793 "AND amopfamily = '%u'::pg_catalog.oid "
14794 "ORDER BY amopstrategy",
14795 opfinfo->dobj.catId.oid,
14796 opfinfo->dobj.catId.oid);
14797
14798 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14799
14800 resetPQExpBuffer(query);
14801
14802 appendPQExpBuffer(query, "SELECT amprocnum, "
14803 "amproc::pg_catalog.regprocedure, "
14804 "amproclefttype::pg_catalog.regtype, "
14805 "amprocrighttype::pg_catalog.regtype "
14806 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14807 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14808 "AND refobjid = '%u'::pg_catalog.oid "
14809 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14810 "AND objid = ap.oid "
14811 "ORDER BY amprocnum",
14812 opfinfo->dobj.catId.oid);
14813
14814 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14815
14816 /* Get additional fields from the pg_opfamily row */
14817 resetPQExpBuffer(query);
14818
14819 appendPQExpBuffer(query, "SELECT "
14820 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14821 "FROM pg_catalog.pg_opfamily "
14822 "WHERE oid = '%u'::pg_catalog.oid",
14823 opfinfo->dobj.catId.oid);
14824
14825 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14826
14827 i_amname = PQfnumber(res, "amname");
14828
14829 /* amname will still be needed after we PQclear res */
14830 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14831
14832 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14833 fmtQualifiedDumpable(opfinfo));
14834 appendPQExpBuffer(delq, " USING %s;\n",
14835 fmtId(amname));
14836
14837 /* Build the fixed portion of the CREATE command */
14838 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14839 fmtQualifiedDumpable(opfinfo));
14840 appendPQExpBuffer(q, " USING %s;\n",
14841 fmtId(amname));
14842
14843 PQclear(res);
14844
14845 /* Do we need an ALTER to add loose members? */
14846 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14847 {
14848 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14849 fmtQualifiedDumpable(opfinfo));
14850 appendPQExpBuffer(q, " USING %s ADD\n ",
14851 fmtId(amname));
14852
14853 needComma = false;
14854
14855 /*
14856 * Now fetch and print the OPERATOR entries (pg_amop rows).
14857 */
14858 ntups = PQntuples(res_ops);
14859
14860 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14861 i_amopopr = PQfnumber(res_ops, "amopopr");
14862 i_sortfamily = PQfnumber(res_ops, "sortfamily");
14863 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14864
14865 for (i = 0; i < ntups; i++)
14866 {
14867 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14868 amopopr = PQgetvalue(res_ops, i, i_amopopr);
14869 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14870 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14871
14872 if (needComma)
14873 appendPQExpBufferStr(q, " ,\n ");
14874
14875 appendPQExpBuffer(q, "OPERATOR %s %s",
14876 amopstrategy, amopopr);
14877
14878 if (strlen(sortfamily) > 0)
14879 {
14880 appendPQExpBufferStr(q, " FOR ORDER BY ");
14881 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14882 appendPQExpBufferStr(q, fmtId(sortfamily));
14883 }
14884
14885 needComma = true;
14886 }
14887
14888 /*
14889 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14890 */
14891 ntups = PQntuples(res_procs);
14892
14893 i_amprocnum = PQfnumber(res_procs, "amprocnum");
14894 i_amproc = PQfnumber(res_procs, "amproc");
14895 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14896 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14897
14898 for (i = 0; i < ntups; i++)
14899 {
14900 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14901 amproc = PQgetvalue(res_procs, i, i_amproc);
14902 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14903 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14904
14905 if (needComma)
14906 appendPQExpBufferStr(q, " ,\n ");
14907
14908 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14909 amprocnum, amproclefttype, amprocrighttype,
14910 amproc);
14911
14912 needComma = true;
14913 }
14914
14915 appendPQExpBufferStr(q, ";\n");
14916 }
14917
14918 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14919 appendPQExpBuffer(nameusing, " USING %s",
14920 fmtId(amname));
14921
14922 if (dopt->binary_upgrade)
14924 "OPERATOR FAMILY", nameusing->data,
14925 opfinfo->dobj.namespace->dobj.name);
14926
14927 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14928 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14929 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14930 .namespace = opfinfo->dobj.namespace->dobj.name,
14931 .owner = opfinfo->rolname,
14932 .description = "OPERATOR FAMILY",
14933 .section = SECTION_PRE_DATA,
14934 .createStmt = q->data,
14935 .dropStmt = delq->data));
14936
14937 /* Dump Operator Family Comments */
14938 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14939 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14940 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14941 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14942
14943 free(amname);
14944 PQclear(res_ops);
14945 PQclear(res_procs);
14946 destroyPQExpBuffer(query);
14948 destroyPQExpBuffer(delq);
14949 destroyPQExpBuffer(nameusing);
14950}
14951
14952/*
14953 * dumpCollation
14954 * write out a single collation definition
14955 */
14956static void
14957dumpCollation(Archive *fout, const CollInfo *collinfo)
14958{
14959 DumpOptions *dopt = fout->dopt;
14960 PQExpBuffer query;
14961 PQExpBuffer q;
14962 PQExpBuffer delq;
14963 char *qcollname;
14964 PGresult *res;
14965 int i_collprovider;
14966 int i_collisdeterministic;
14967 int i_collcollate;
14968 int i_collctype;
14969 int i_colllocale;
14970 int i_collicurules;
14971 const char *collprovider;
14972 const char *collcollate;
14973 const char *collctype;
14974 const char *colllocale;
14975 const char *collicurules;
14976
14977 /* Do nothing if not dumping schema */
14978 if (!dopt->dumpSchema)
14979 return;
14980
14981 query = createPQExpBuffer();
14982 q = createPQExpBuffer();
14983 delq = createPQExpBuffer();
14984
14985 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14986
14987 /* Get collation-specific details */
14988 appendPQExpBufferStr(query, "SELECT ");
14989
14990 if (fout->remoteVersion >= 100000)
14992 "collprovider, "
14993 "collversion, ");
14994 else
14996 "'c' AS collprovider, "
14997 "NULL AS collversion, ");
14998
14999 if (fout->remoteVersion >= 120000)
15001 "collisdeterministic, ");
15002 else
15004 "true AS collisdeterministic, ");
15005
15006 if (fout->remoteVersion >= 170000)
15008 "colllocale, ");
15009 else if (fout->remoteVersion >= 150000)
15011 "colliculocale AS colllocale, ");
15012 else
15014 "NULL AS colllocale, ");
15015
15016 if (fout->remoteVersion >= 160000)
15018 "collicurules, ");
15019 else
15021 "NULL AS collicurules, ");
15022
15023 appendPQExpBuffer(query,
15024 "collcollate, "
15025 "collctype "
15026 "FROM pg_catalog.pg_collation c "
15027 "WHERE c.oid = '%u'::pg_catalog.oid",
15028 collinfo->dobj.catId.oid);
15029
15030 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15031
15032 i_collprovider = PQfnumber(res, "collprovider");
15033 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15034 i_collcollate = PQfnumber(res, "collcollate");
15035 i_collctype = PQfnumber(res, "collctype");
15036 i_colllocale = PQfnumber(res, "colllocale");
15037 i_collicurules = PQfnumber(res, "collicurules");
15038
15039 collprovider = PQgetvalue(res, 0, i_collprovider);
15040
15041 if (!PQgetisnull(res, 0, i_collcollate))
15042 collcollate = PQgetvalue(res, 0, i_collcollate);
15043 else
15044 collcollate = NULL;
15045
15046 if (!PQgetisnull(res, 0, i_collctype))
15047 collctype = PQgetvalue(res, 0, i_collctype);
15048 else
15049 collctype = NULL;
15050
15051 /*
15052 * Before version 15, collcollate and collctype were of type NAME and
15053 * non-nullable. Treat empty strings as NULL for consistency.
15054 */
15055 if (fout->remoteVersion < 150000)
15056 {
15057 if (collcollate[0] == '\0')
15058 collcollate = NULL;
15059 if (collctype[0] == '\0')
15060 collctype = NULL;
15061 }
15062
15063 if (!PQgetisnull(res, 0, i_colllocale))
15064 colllocale = PQgetvalue(res, 0, i_colllocale);
15065 else
15066 colllocale = NULL;
15067
15068 if (!PQgetisnull(res, 0, i_collicurules))
15069 collicurules = PQgetvalue(res, 0, i_collicurules);
15070 else
15071 collicurules = NULL;
15072
15073 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15074 fmtQualifiedDumpable(collinfo));
15075
15076 appendPQExpBuffer(q, "CREATE COLLATION %s (",
15077 fmtQualifiedDumpable(collinfo));
15078
15079 appendPQExpBufferStr(q, "provider = ");
15080 if (collprovider[0] == 'b')
15081 appendPQExpBufferStr(q, "builtin");
15082 else if (collprovider[0] == 'c')
15083 appendPQExpBufferStr(q, "libc");
15084 else if (collprovider[0] == 'i')
15085 appendPQExpBufferStr(q, "icu");
15086 else if (collprovider[0] == 'd')
15087 /* to allow dumping pg_catalog; not accepted on input */
15088 appendPQExpBufferStr(q, "default");
15089 else
15090 pg_fatal("unrecognized collation provider: %s",
15091 collprovider);
15092
15093 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15094 appendPQExpBufferStr(q, ", deterministic = false");
15095
15096 if (collprovider[0] == 'd')
15097 {
15098 if (collcollate || collctype || colllocale || collicurules)
15099 pg_log_warning("invalid collation \"%s\"", qcollname);
15100
15101 /* no locale -- the default collation cannot be reloaded anyway */
15102 }
15103 else if (collprovider[0] == 'b')
15104 {
15105 if (collcollate || collctype || !colllocale || collicurules)
15106 pg_log_warning("invalid collation \"%s\"", qcollname);
15107
15108 appendPQExpBufferStr(q, ", locale = ");
15109 appendStringLiteralAH(q, colllocale ? colllocale : "",
15110 fout);
15111 }
15112 else if (collprovider[0] == 'i')
15113 {
15114 if (fout->remoteVersion >= 150000)
15115 {
15116 if (collcollate || collctype || !colllocale)
15117 pg_log_warning("invalid collation \"%s\"", qcollname);
15118
15119 appendPQExpBufferStr(q, ", locale = ");
15120 appendStringLiteralAH(q, colllocale ? colllocale : "",
15121 fout);
15122 }
15123 else
15124 {
15125 if (!collcollate || !collctype || colllocale ||
15126 strcmp(collcollate, collctype) != 0)
15127 pg_log_warning("invalid collation \"%s\"", qcollname);
15128
15129 appendPQExpBufferStr(q, ", locale = ");
15130 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15131 }
15132
15133 if (collicurules)
15134 {
15135 appendPQExpBufferStr(q, ", rules = ");
15136 appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15137 }
15138 }
15139 else if (collprovider[0] == 'c')
15140 {
15141 if (colllocale || collicurules || !collcollate || !collctype)
15142 pg_log_warning("invalid collation \"%s\"", qcollname);
15143
15144 if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15145 {
15146 appendPQExpBufferStr(q, ", locale = ");
15147 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15148 }
15149 else
15150 {
15151 appendPQExpBufferStr(q, ", lc_collate = ");
15152 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15153 appendPQExpBufferStr(q, ", lc_ctype = ");
15154 appendStringLiteralAH(q, collctype ? collctype : "", fout);
15155 }
15156 }
15157 else
15158 pg_fatal("unrecognized collation provider: %s", collprovider);
15159
15160 /*
15161 * For binary upgrade, carry over the collation version. For normal
15162 * dump/restore, omit the version, so that it is computed upon restore.
15163 */
15164 if (dopt->binary_upgrade)
15165 {
15166 int i_collversion;
15167
15168 i_collversion = PQfnumber(res, "collversion");
15169 if (!PQgetisnull(res, 0, i_collversion))
15170 {
15171 appendPQExpBufferStr(q, ", version = ");
15173 PQgetvalue(res, 0, i_collversion),
15174 fout);
15175 }
15176 }
15177
15178 appendPQExpBufferStr(q, ");\n");
15179
15180 if (dopt->binary_upgrade)
15182 "COLLATION", qcollname,
15183 collinfo->dobj.namespace->dobj.name);
15184
15185 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15186 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15187 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15188 .namespace = collinfo->dobj.namespace->dobj.name,
15189 .owner = collinfo->rolname,
15190 .description = "COLLATION",
15191 .section = SECTION_PRE_DATA,
15192 .createStmt = q->data,
15193 .dropStmt = delq->data));
15194
15195 /* Dump Collation Comments */
15196 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15197 dumpComment(fout, "COLLATION", qcollname,
15198 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15199 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15200
15201 PQclear(res);
15202
15203 destroyPQExpBuffer(query);
15205 destroyPQExpBuffer(delq);
15206 free(qcollname);
15207}
15208
15209/*
15210 * dumpConversion
15211 * write out a single conversion definition
15212 */
15213static void
15214dumpConversion(Archive *fout, const ConvInfo *convinfo)
15215{
15216 DumpOptions *dopt = fout->dopt;
15217 PQExpBuffer query;
15218 PQExpBuffer q;
15219 PQExpBuffer delq;
15220 char *qconvname;
15221 PGresult *res;
15222 int i_conforencoding;
15223 int i_contoencoding;
15224 int i_conproc;
15225 int i_condefault;
15226 const char *conforencoding;
15227 const char *contoencoding;
15228 const char *conproc;
15229 bool condefault;
15230
15231 /* Do nothing if not dumping schema */
15232 if (!dopt->dumpSchema)
15233 return;
15234
15235 query = createPQExpBuffer();
15236 q = createPQExpBuffer();
15237 delq = createPQExpBuffer();
15238
15239 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15240
15241 /* Get conversion-specific details */
15242 appendPQExpBuffer(query, "SELECT "
15243 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15244 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15245 "conproc, condefault "
15246 "FROM pg_catalog.pg_conversion c "
15247 "WHERE c.oid = '%u'::pg_catalog.oid",
15248 convinfo->dobj.catId.oid);
15249
15250 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15251
15252 i_conforencoding = PQfnumber(res, "conforencoding");
15253 i_contoencoding = PQfnumber(res, "contoencoding");
15254 i_conproc = PQfnumber(res, "conproc");
15255 i_condefault = PQfnumber(res, "condefault");
15256
15257 conforencoding = PQgetvalue(res, 0, i_conforencoding);
15258 contoencoding = PQgetvalue(res, 0, i_contoencoding);
15259 conproc = PQgetvalue(res, 0, i_conproc);
15260 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15261
15262 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15263 fmtQualifiedDumpable(convinfo));
15264
15265 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15266 (condefault) ? "DEFAULT " : "",
15267 fmtQualifiedDumpable(convinfo));
15268 appendStringLiteralAH(q, conforencoding, fout);
15269 appendPQExpBufferStr(q, " TO ");
15270 appendStringLiteralAH(q, contoencoding, fout);
15271 /* regproc output is already sufficiently quoted */
15272 appendPQExpBuffer(q, " FROM %s;\n", conproc);
15273
15274 if (dopt->binary_upgrade)
15276 "CONVERSION", qconvname,
15277 convinfo->dobj.namespace->dobj.name);
15278
15279 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15280 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15281 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15282 .namespace = convinfo->dobj.namespace->dobj.name,
15283 .owner = convinfo->rolname,
15284 .description = "CONVERSION",
15285 .section = SECTION_PRE_DATA,
15286 .createStmt = q->data,
15287 .dropStmt = delq->data));
15288
15289 /* Dump Conversion Comments */
15290 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15291 dumpComment(fout, "CONVERSION", qconvname,
15292 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15293 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15294
15295 PQclear(res);
15296
15297 destroyPQExpBuffer(query);
15299 destroyPQExpBuffer(delq);
15300 free(qconvname);
15301}
15302
15303/*
15304 * format_aggregate_signature: generate aggregate name and argument list
15305 *
15306 * The argument type names are qualified if needed. The aggregate name
15307 * is never qualified.
15308 */
15309static char *
15310format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15311{
15313 int j;
15314
15316 if (honor_quotes)
15317 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15318 else
15319 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15320
15321 if (agginfo->aggfn.nargs == 0)
15322 appendPQExpBufferStr(&buf, "(*)");
15323 else
15324 {
15326 for (j = 0; j < agginfo->aggfn.nargs; j++)
15327 appendPQExpBuffer(&buf, "%s%s",
15328 (j > 0) ? ", " : "",
15330 agginfo->aggfn.argtypes[j],
15331 zeroIsError));
15333 }
15334 return buf.data;
15335}
15336
15337/*
15338 * dumpAgg
15339 * write out a single aggregate definition
15340 */
15341static void
15342dumpAgg(Archive *fout, const AggInfo *agginfo)
15343{
15344 DumpOptions *dopt = fout->dopt;
15345 PQExpBuffer query;
15346 PQExpBuffer q;
15347 PQExpBuffer delq;
15348 PQExpBuffer details;
15349 char *aggsig; /* identity signature */
15350 char *aggfullsig = NULL; /* full signature */
15351 char *aggsig_tag;
15352 PGresult *res;
15353 int i_agginitval;
15354 int i_aggminitval;
15355 const char *aggtransfn;
15356 const char *aggfinalfn;
15357 const char *aggcombinefn;
15358 const char *aggserialfn;
15359 const char *aggdeserialfn;
15360 const char *aggmtransfn;
15361 const char *aggminvtransfn;
15362 const char *aggmfinalfn;
15363 bool aggfinalextra;
15364 bool aggmfinalextra;
15365 char aggfinalmodify;
15366 char aggmfinalmodify;
15367 const char *aggsortop;
15368 char *aggsortconvop;
15369 char aggkind;
15370 const char *aggtranstype;
15371 const char *aggtransspace;
15372 const char *aggmtranstype;
15373 const char *aggmtransspace;
15374 const char *agginitval;
15375 const char *aggminitval;
15376 const char *proparallel;
15377 char defaultfinalmodify;
15378
15379 /* Do nothing if not dumping schema */
15380 if (!dopt->dumpSchema)
15381 return;
15382
15383 query = createPQExpBuffer();
15384 q = createPQExpBuffer();
15385 delq = createPQExpBuffer();
15386 details = createPQExpBuffer();
15387
15388 if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15389 {
15390 /* Set up query for aggregate-specific details */
15392 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15393
15395 "SELECT "
15396 "aggtransfn,\n"
15397 "aggfinalfn,\n"
15398 "aggtranstype::pg_catalog.regtype,\n"
15399 "agginitval,\n"
15400 "aggsortop,\n"
15401 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15402 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15403
15404 if (fout->remoteVersion >= 90400)
15406 "aggkind,\n"
15407 "aggmtransfn,\n"
15408 "aggminvtransfn,\n"
15409 "aggmfinalfn,\n"
15410 "aggmtranstype::pg_catalog.regtype,\n"
15411 "aggfinalextra,\n"
15412 "aggmfinalextra,\n"
15413 "aggtransspace,\n"
15414 "aggmtransspace,\n"
15415 "aggminitval,\n");
15416 else
15418 "'n' AS aggkind,\n"
15419 "'-' AS aggmtransfn,\n"
15420 "'-' AS aggminvtransfn,\n"
15421 "'-' AS aggmfinalfn,\n"
15422 "0 AS aggmtranstype,\n"
15423 "false AS aggfinalextra,\n"
15424 "false AS aggmfinalextra,\n"
15425 "0 AS aggtransspace,\n"
15426 "0 AS aggmtransspace,\n"
15427 "NULL AS aggminitval,\n");
15428
15429 if (fout->remoteVersion >= 90600)
15431 "aggcombinefn,\n"
15432 "aggserialfn,\n"
15433 "aggdeserialfn,\n"
15434 "proparallel,\n");
15435 else
15437 "'-' AS aggcombinefn,\n"
15438 "'-' AS aggserialfn,\n"
15439 "'-' AS aggdeserialfn,\n"
15440 "'u' AS proparallel,\n");
15441
15442 if (fout->remoteVersion >= 110000)
15444 "aggfinalmodify,\n"
15445 "aggmfinalmodify\n");
15446 else
15448 "'0' AS aggfinalmodify,\n"
15449 "'0' AS aggmfinalmodify\n");
15450
15452 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15453 "WHERE a.aggfnoid = p.oid "
15454 "AND p.oid = $1");
15455
15456 ExecuteSqlStatement(fout, query->data);
15457
15458 fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15459 }
15460
15461 printfPQExpBuffer(query,
15462 "EXECUTE dumpAgg('%u')",
15463 agginfo->aggfn.dobj.catId.oid);
15464
15465 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15466
15467 i_agginitval = PQfnumber(res, "agginitval");
15468 i_aggminitval = PQfnumber(res, "aggminitval");
15469
15470 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15471 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15472 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15473 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15474 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15475 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15476 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15477 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15478 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15479 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15480 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15481 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15482 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15483 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15484 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15485 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15486 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15487 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15488 agginitval = PQgetvalue(res, 0, i_agginitval);
15489 aggminitval = PQgetvalue(res, 0, i_aggminitval);
15490 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15491
15492 {
15493 char *funcargs;
15494 char *funciargs;
15495
15496 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15497 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15498 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15499 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15500 }
15501
15502 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15503
15504 /* identify default modify flag for aggkind (must match DefineAggregate) */
15505 defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15506 /* replace omitted flags for old versions */
15507 if (aggfinalmodify == '0')
15508 aggfinalmodify = defaultfinalmodify;
15509 if (aggmfinalmodify == '0')
15510 aggmfinalmodify = defaultfinalmodify;
15511
15512 /* regproc and regtype output is already sufficiently quoted */
15513 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15514 aggtransfn, aggtranstype);
15515
15516 if (strcmp(aggtransspace, "0") != 0)
15517 {
15518 appendPQExpBuffer(details, ",\n SSPACE = %s",
15519 aggtransspace);
15520 }
15521
15522 if (!PQgetisnull(res, 0, i_agginitval))
15523 {
15524 appendPQExpBufferStr(details, ",\n INITCOND = ");
15525 appendStringLiteralAH(details, agginitval, fout);
15526 }
15527
15528 if (strcmp(aggfinalfn, "-") != 0)
15529 {
15530 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15531 aggfinalfn);
15532 if (aggfinalextra)
15533 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15534 if (aggfinalmodify != defaultfinalmodify)
15535 {
15536 switch (aggfinalmodify)
15537 {
15538 case AGGMODIFY_READ_ONLY:
15539 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15540 break;
15541 case AGGMODIFY_SHAREABLE:
15542 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15543 break;
15544 case AGGMODIFY_READ_WRITE:
15545 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15546 break;
15547 default:
15548 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15549 agginfo->aggfn.dobj.name);
15550 break;
15551 }
15552 }
15553 }
15554
15555 if (strcmp(aggcombinefn, "-") != 0)
15556 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15557
15558 if (strcmp(aggserialfn, "-") != 0)
15559 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15560
15561 if (strcmp(aggdeserialfn, "-") != 0)
15562 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15563
15564 if (strcmp(aggmtransfn, "-") != 0)
15565 {
15566 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15567 aggmtransfn,
15568 aggminvtransfn,
15569 aggmtranstype);
15570 }
15571
15572 if (strcmp(aggmtransspace, "0") != 0)
15573 {
15574 appendPQExpBuffer(details, ",\n MSSPACE = %s",
15575 aggmtransspace);
15576 }
15577
15578 if (!PQgetisnull(res, 0, i_aggminitval))
15579 {
15580 appendPQExpBufferStr(details, ",\n MINITCOND = ");
15581 appendStringLiteralAH(details, aggminitval, fout);
15582 }
15583
15584 if (strcmp(aggmfinalfn, "-") != 0)
15585 {
15586 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15587 aggmfinalfn);
15588 if (aggmfinalextra)
15589 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15590 if (aggmfinalmodify != defaultfinalmodify)
15591 {
15592 switch (aggmfinalmodify)
15593 {
15594 case AGGMODIFY_READ_ONLY:
15595 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15596 break;
15597 case AGGMODIFY_SHAREABLE:
15598 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15599 break;
15600 case AGGMODIFY_READ_WRITE:
15601 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15602 break;
15603 default:
15604 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15605 agginfo->aggfn.dobj.name);
15606 break;
15607 }
15608 }
15609 }
15610
15611 aggsortconvop = getFormattedOperatorName(aggsortop);
15612 if (aggsortconvop)
15613 {
15614 appendPQExpBuffer(details, ",\n SORTOP = %s",
15615 aggsortconvop);
15616 free(aggsortconvop);
15617 }
15618
15619 if (aggkind == AGGKIND_HYPOTHETICAL)
15620 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15621
15622 if (proparallel[0] != PROPARALLEL_UNSAFE)
15623 {
15624 if (proparallel[0] == PROPARALLEL_SAFE)
15625 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15626 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15627 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15628 else if (proparallel[0] != PROPARALLEL_UNSAFE)
15629 pg_fatal("unrecognized proparallel value for function \"%s\"",
15630 agginfo->aggfn.dobj.name);
15631 }
15632
15633 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15634 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15635 aggsig);
15636
15637 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15638 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15639 aggfullsig ? aggfullsig : aggsig, details->data);
15640
15641 if (dopt->binary_upgrade)
15642 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15643 "AGGREGATE", aggsig,
15644 agginfo->aggfn.dobj.namespace->dobj.name);
15645
15646 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15647 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15648 agginfo->aggfn.dobj.dumpId,
15649 ARCHIVE_OPTS(.tag = aggsig_tag,
15650 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15651 .owner = agginfo->aggfn.rolname,
15652 .description = "AGGREGATE",
15653 .section = SECTION_PRE_DATA,
15654 .createStmt = q->data,
15655 .dropStmt = delq->data));
15656
15657 /* Dump Aggregate Comments */
15658 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15659 dumpComment(fout, "AGGREGATE", aggsig,
15660 agginfo->aggfn.dobj.namespace->dobj.name,
15661 agginfo->aggfn.rolname,
15662 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15663
15664 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15665 dumpSecLabel(fout, "AGGREGATE", aggsig,
15666 agginfo->aggfn.dobj.namespace->dobj.name,
15667 agginfo->aggfn.rolname,
15668 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15669
15670 /*
15671 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15672 * command look like a function's GRANT; in particular this affects the
15673 * syntax for zero-argument aggregates and ordered-set aggregates.
15674 */
15675 free(aggsig);
15676
15677 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15678
15679 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15680 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15681 "FUNCTION", aggsig, NULL,
15682 agginfo->aggfn.dobj.namespace->dobj.name,
15683 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15684
15685 free(aggsig);
15686 free(aggfullsig);
15687 free(aggsig_tag);
15688
15689 PQclear(res);
15690
15691 destroyPQExpBuffer(query);
15693 destroyPQExpBuffer(delq);
15694 destroyPQExpBuffer(details);
15695}
15696
15697/*
15698 * dumpTSParser
15699 * write out a single text search parser
15700 */
15701static void
15702dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15703{
15704 DumpOptions *dopt = fout->dopt;
15705 PQExpBuffer q;
15706 PQExpBuffer delq;
15707 char *qprsname;
15708
15709 /* Do nothing if not dumping schema */
15710 if (!dopt->dumpSchema)
15711 return;
15712
15713 q = createPQExpBuffer();
15714 delq = createPQExpBuffer();
15715
15716 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15717
15718 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15719 fmtQualifiedDumpable(prsinfo));
15720
15721 appendPQExpBuffer(q, " START = %s,\n",
15722 convertTSFunction(fout, prsinfo->prsstart));
15723 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15724 convertTSFunction(fout, prsinfo->prstoken));
15725 appendPQExpBuffer(q, " END = %s,\n",
15726 convertTSFunction(fout, prsinfo->prsend));
15727 if (prsinfo->prsheadline != InvalidOid)
15728 appendPQExpBuffer(q, " HEADLINE = %s,\n",
15729 convertTSFunction(fout, prsinfo->prsheadline));
15730 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15731 convertTSFunction(fout, prsinfo->prslextype));
15732
15733 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15734 fmtQualifiedDumpable(prsinfo));
15735
15736 if (dopt->binary_upgrade)
15738 "TEXT SEARCH PARSER", qprsname,
15739 prsinfo->dobj.namespace->dobj.name);
15740
15741 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15742 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15743 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15744 .namespace = prsinfo->dobj.namespace->dobj.name,
15745 .description = "TEXT SEARCH PARSER",
15746 .section = SECTION_PRE_DATA,
15747 .createStmt = q->data,
15748 .dropStmt = delq->data));
15749
15750 /* Dump Parser Comments */
15751 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15752 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15753 prsinfo->dobj.namespace->dobj.name, "",
15754 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15755
15757 destroyPQExpBuffer(delq);
15758 free(qprsname);
15759}
15760
15761/*
15762 * dumpTSDictionary
15763 * write out a single text search dictionary
15764 */
15765static void
15766dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15767{
15768 DumpOptions *dopt = fout->dopt;
15769 PQExpBuffer q;
15770 PQExpBuffer delq;
15771 PQExpBuffer query;
15772 char *qdictname;
15773 PGresult *res;
15774 char *nspname;
15775 char *tmplname;
15776
15777 /* Do nothing if not dumping schema */
15778 if (!dopt->dumpSchema)
15779 return;
15780
15781 q = createPQExpBuffer();
15782 delq = createPQExpBuffer();
15783 query = createPQExpBuffer();
15784
15785 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15786
15787 /* Fetch name and namespace of the dictionary's template */
15788 appendPQExpBuffer(query, "SELECT nspname, tmplname "
15789 "FROM pg_ts_template p, pg_namespace n "
15790 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15791 dictinfo->dicttemplate);
15792 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15793 nspname = PQgetvalue(res, 0, 0);
15794 tmplname = PQgetvalue(res, 0, 1);
15795
15796 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15797 fmtQualifiedDumpable(dictinfo));
15798
15799 appendPQExpBufferStr(q, " TEMPLATE = ");
15800 appendPQExpBuffer(q, "%s.", fmtId(nspname));
15801 appendPQExpBufferStr(q, fmtId(tmplname));
15802
15803 PQclear(res);
15804
15805 /* the dictinitoption can be dumped straight into the command */
15806 if (dictinfo->dictinitoption)
15807 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15808
15809 appendPQExpBufferStr(q, " );\n");
15810
15811 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15812 fmtQualifiedDumpable(dictinfo));
15813
15814 if (dopt->binary_upgrade)
15816 "TEXT SEARCH DICTIONARY", qdictname,
15817 dictinfo->dobj.namespace->dobj.name);
15818
15819 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15820 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15821 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15822 .namespace = dictinfo->dobj.namespace->dobj.name,
15823 .owner = dictinfo->rolname,
15824 .description = "TEXT SEARCH DICTIONARY",
15825 .section = SECTION_PRE_DATA,
15826 .createStmt = q->data,
15827 .dropStmt = delq->data));
15828
15829 /* Dump Dictionary Comments */
15830 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15831 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15832 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15833 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15834
15836 destroyPQExpBuffer(delq);
15837 destroyPQExpBuffer(query);
15838 free(qdictname);
15839}
15840
15841/*
15842 * dumpTSTemplate
15843 * write out a single text search template
15844 */
15845static void
15847{
15848 DumpOptions *dopt = fout->dopt;
15849 PQExpBuffer q;
15850 PQExpBuffer delq;
15851 char *qtmplname;
15852
15853 /* Do nothing if not dumping schema */
15854 if (!dopt->dumpSchema)
15855 return;
15856
15857 q = createPQExpBuffer();
15858 delq = createPQExpBuffer();
15859
15860 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15861
15862 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15863 fmtQualifiedDumpable(tmplinfo));
15864
15865 if (tmplinfo->tmplinit != InvalidOid)
15866 appendPQExpBuffer(q, " INIT = %s,\n",
15867 convertTSFunction(fout, tmplinfo->tmplinit));
15868 appendPQExpBuffer(q, " LEXIZE = %s );\n",
15869 convertTSFunction(fout, tmplinfo->tmpllexize));
15870
15871 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15872 fmtQualifiedDumpable(tmplinfo));
15873
15874 if (dopt->binary_upgrade)
15876 "TEXT SEARCH TEMPLATE", qtmplname,
15877 tmplinfo->dobj.namespace->dobj.name);
15878
15879 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15880 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15881 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15882 .namespace = tmplinfo->dobj.namespace->dobj.name,
15883 .description = "TEXT SEARCH TEMPLATE",
15884 .section = SECTION_PRE_DATA,
15885 .createStmt = q->data,
15886 .dropStmt = delq->data));
15887
15888 /* Dump Template Comments */
15889 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15890 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15891 tmplinfo->dobj.namespace->dobj.name, "",
15892 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15893
15895 destroyPQExpBuffer(delq);
15896 free(qtmplname);
15897}
15898
15899/*
15900 * dumpTSConfig
15901 * write out a single text search configuration
15902 */
15903static void
15904dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15905{
15906 DumpOptions *dopt = fout->dopt;
15907 PQExpBuffer q;
15908 PQExpBuffer delq;
15909 PQExpBuffer query;
15910 char *qcfgname;
15911 PGresult *res;
15912 char *nspname;
15913 char *prsname;
15914 int ntups,
15915 i;
15916 int i_tokenname;
15917 int i_dictname;
15918
15919 /* Do nothing if not dumping schema */
15920 if (!dopt->dumpSchema)
15921 return;
15922
15923 q = createPQExpBuffer();
15924 delq = createPQExpBuffer();
15925 query = createPQExpBuffer();
15926
15927 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15928
15929 /* Fetch name and namespace of the config's parser */
15930 appendPQExpBuffer(query, "SELECT nspname, prsname "
15931 "FROM pg_ts_parser p, pg_namespace n "
15932 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15933 cfginfo->cfgparser);
15934 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15935 nspname = PQgetvalue(res, 0, 0);
15936 prsname = PQgetvalue(res, 0, 1);
15937
15938 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15939 fmtQualifiedDumpable(cfginfo));
15940
15941 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15942 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15943
15944 PQclear(res);
15945
15946 resetPQExpBuffer(query);
15947 appendPQExpBuffer(query,
15948 "SELECT\n"
15949 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15950 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15951 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15952 "FROM pg_catalog.pg_ts_config_map AS m\n"
15953 "WHERE m.mapcfg = '%u'\n"
15954 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15955 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15956
15957 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15958 ntups = PQntuples(res);
15959
15960 i_tokenname = PQfnumber(res, "tokenname");
15961 i_dictname = PQfnumber(res, "dictname");
15962
15963 for (i = 0; i < ntups; i++)
15964 {
15965 char *tokenname = PQgetvalue(res, i, i_tokenname);
15966 char *dictname = PQgetvalue(res, i, i_dictname);
15967
15968 if (i == 0 ||
15969 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15970 {
15971 /* starting a new token type, so start a new command */
15972 if (i > 0)
15973 appendPQExpBufferStr(q, ";\n");
15974 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15975 fmtQualifiedDumpable(cfginfo));
15976 /* tokenname needs quoting, dictname does NOT */
15977 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15978 fmtId(tokenname), dictname);
15979 }
15980 else
15981 appendPQExpBuffer(q, ", %s", dictname);
15982 }
15983
15984 if (ntups > 0)
15985 appendPQExpBufferStr(q, ";\n");
15986
15987 PQclear(res);
15988
15989 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15990 fmtQualifiedDumpable(cfginfo));
15991
15992 if (dopt->binary_upgrade)
15994 "TEXT SEARCH CONFIGURATION", qcfgname,
15995 cfginfo->dobj.namespace->dobj.name);
15996
15997 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15998 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15999 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16000 .namespace = cfginfo->dobj.namespace->dobj.name,
16001 .owner = cfginfo->rolname,
16002 .description = "TEXT SEARCH CONFIGURATION",
16003 .section = SECTION_PRE_DATA,
16004 .createStmt = q->data,
16005 .dropStmt = delq->data));
16006
16007 /* Dump Configuration Comments */
16008 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16009 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16010 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16011 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16012
16014 destroyPQExpBuffer(delq);
16015 destroyPQExpBuffer(query);
16016 free(qcfgname);
16017}
16018
16019/*
16020 * dumpForeignDataWrapper
16021 * write out a single foreign-data wrapper definition
16022 */
16023static void
16025{
16026 DumpOptions *dopt = fout->dopt;
16027 PQExpBuffer q;
16028 PQExpBuffer delq;
16029 char *qfdwname;
16030
16031 /* Do nothing if not dumping schema */
16032 if (!dopt->dumpSchema)
16033 return;
16034
16035 q = createPQExpBuffer();
16036 delq = createPQExpBuffer();
16037
16038 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16039
16040 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16041 qfdwname);
16042
16043 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16044 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16045
16046 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16047 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16048
16049 if (strlen(fdwinfo->fdwoptions) > 0)
16050 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16051
16052 appendPQExpBufferStr(q, ";\n");
16053
16054 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16055 qfdwname);
16056
16057 if (dopt->binary_upgrade)
16059 "FOREIGN DATA WRAPPER", qfdwname,
16060 NULL);
16061
16062 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16063 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16064 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16065 .owner = fdwinfo->rolname,
16066 .description = "FOREIGN DATA WRAPPER",
16067 .section = SECTION_PRE_DATA,
16068 .createStmt = q->data,
16069 .dropStmt = delq->data));
16070
16071 /* Dump Foreign Data Wrapper Comments */
16072 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16073 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16074 NULL, fdwinfo->rolname,
16075 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16076
16077 /* Handle the ACL */
16078 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16079 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16080 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16081 NULL, fdwinfo->rolname, &fdwinfo->dacl);
16082
16083 free(qfdwname);
16084
16086 destroyPQExpBuffer(delq);
16087}
16088
16089/*
16090 * dumpForeignServer
16091 * write out a foreign server definition
16092 */
16093static void
16095{
16096 DumpOptions *dopt = fout->dopt;
16097 PQExpBuffer q;
16098 PQExpBuffer delq;
16099 PQExpBuffer query;
16100 PGresult *res;
16101 char *qsrvname;
16102 char *fdwname;
16103
16104 /* Do nothing if not dumping schema */
16105 if (!dopt->dumpSchema)
16106 return;
16107
16108 q = createPQExpBuffer();
16109 delq = createPQExpBuffer();
16110 query = createPQExpBuffer();
16111
16112 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16113
16114 /* look up the foreign-data wrapper */
16115 appendPQExpBuffer(query, "SELECT fdwname "
16116 "FROM pg_foreign_data_wrapper w "
16117 "WHERE w.oid = '%u'",
16118 srvinfo->srvfdw);
16119 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16120 fdwname = PQgetvalue(res, 0, 0);
16121
16122 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16123 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16124 {
16125 appendPQExpBufferStr(q, " TYPE ");
16126 appendStringLiteralAH(q, srvinfo->srvtype, fout);
16127 }
16128 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16129 {
16130 appendPQExpBufferStr(q, " VERSION ");
16131 appendStringLiteralAH(q, srvinfo->srvversion, fout);
16132 }
16133
16134 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16135 appendPQExpBufferStr(q, fmtId(fdwname));
16136
16137 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16138 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16139
16140 appendPQExpBufferStr(q, ";\n");
16141
16142 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16143 qsrvname);
16144
16145 if (dopt->binary_upgrade)
16147 "SERVER", qsrvname, NULL);
16148
16149 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16150 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16151 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16152 .owner = srvinfo->rolname,
16153 .description = "SERVER",
16154 .section = SECTION_PRE_DATA,
16155 .createStmt = q->data,
16156 .dropStmt = delq->data));
16157
16158 /* Dump Foreign Server Comments */
16159 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16160 dumpComment(fout, "SERVER", qsrvname,
16161 NULL, srvinfo->rolname,
16162 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16163
16164 /* Handle the ACL */
16165 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16166 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16167 "FOREIGN SERVER", qsrvname, NULL, NULL,
16168 NULL, srvinfo->rolname, &srvinfo->dacl);
16169
16170 /* Dump user mappings */
16171 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16172 dumpUserMappings(fout,
16173 srvinfo->dobj.name, NULL,
16174 srvinfo->rolname,
16175 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16176
16177 PQclear(res);
16178
16179 free(qsrvname);
16180
16182 destroyPQExpBuffer(delq);
16183 destroyPQExpBuffer(query);
16184}
16185
16186/*
16187 * dumpUserMappings
16188 *
16189 * This routine is used to dump any user mappings associated with the
16190 * server handed to this routine. Should be called after ArchiveEntry()
16191 * for the server.
16192 */
16193static void
16195 const char *servername, const char *namespace,
16196 const char *owner,
16197 CatalogId catalogId, DumpId dumpId)
16198{
16199 PQExpBuffer q;
16200 PQExpBuffer delq;
16201 PQExpBuffer query;
16202 PQExpBuffer tag;
16203 PGresult *res;
16204 int ntups;
16205 int i_usename;
16206 int i_umoptions;
16207 int i;
16208
16209 q = createPQExpBuffer();
16210 tag = createPQExpBuffer();
16211 delq = createPQExpBuffer();
16212 query = createPQExpBuffer();
16213
16214 /*
16215 * We read from the publicly accessible view pg_user_mappings, so as not
16216 * to fail if run by a non-superuser. Note that the view will show
16217 * umoptions as null if the user hasn't got privileges for the associated
16218 * server; this means that pg_dump will dump such a mapping, but with no
16219 * OPTIONS clause. A possible alternative is to skip such mappings
16220 * altogether, but it's not clear that that's an improvement.
16221 */
16222 appendPQExpBuffer(query,
16223 "SELECT usename, "
16224 "array_to_string(ARRAY("
16225 "SELECT quote_ident(option_name) || ' ' || "
16226 "quote_literal(option_value) "
16227 "FROM pg_options_to_table(umoptions) "
16228 "ORDER BY option_name"
16229 "), E',\n ') AS umoptions "
16230 "FROM pg_user_mappings "
16231 "WHERE srvid = '%u' "
16232 "ORDER BY usename",
16233 catalogId.oid);
16234
16235 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16236
16237 ntups = PQntuples(res);
16238 i_usename = PQfnumber(res, "usename");
16239 i_umoptions = PQfnumber(res, "umoptions");
16240
16241 for (i = 0; i < ntups; i++)
16242 {
16243 char *usename;
16244 char *umoptions;
16245
16246 usename = PQgetvalue(res, i, i_usename);
16247 umoptions = PQgetvalue(res, i, i_umoptions);
16248
16250 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16251 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16252
16253 if (umoptions && strlen(umoptions) > 0)
16254 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16255
16256 appendPQExpBufferStr(q, ";\n");
16257
16258 resetPQExpBuffer(delq);
16259 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16260 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16261
16262 resetPQExpBuffer(tag);
16263 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16264 usename, servername);
16265
16267 ARCHIVE_OPTS(.tag = tag->data,
16268 .namespace = namespace,
16269 .owner = owner,
16270 .description = "USER MAPPING",
16271 .section = SECTION_PRE_DATA,
16272 .createStmt = q->data,
16273 .dropStmt = delq->data));
16274 }
16275
16276 PQclear(res);
16277
16278 destroyPQExpBuffer(query);
16279 destroyPQExpBuffer(delq);
16280 destroyPQExpBuffer(tag);
16282}
16283
16284/*
16285 * Write out default privileges information
16286 */
16287static void
16289{
16290 DumpOptions *dopt = fout->dopt;
16291 PQExpBuffer q;
16292 PQExpBuffer tag;
16293 const char *type;
16294
16295 /* Do nothing if not dumping schema, or if we're skipping ACLs */
16296 if (!dopt->dumpSchema || dopt->aclsSkip)
16297 return;
16298
16299 q = createPQExpBuffer();
16300 tag = createPQExpBuffer();
16301
16302 switch (daclinfo->defaclobjtype)
16303 {
16304 case DEFACLOBJ_RELATION:
16305 type = "TABLES";
16306 break;
16307 case DEFACLOBJ_SEQUENCE:
16308 type = "SEQUENCES";
16309 break;
16310 case DEFACLOBJ_FUNCTION:
16311 type = "FUNCTIONS";
16312 break;
16313 case DEFACLOBJ_TYPE:
16314 type = "TYPES";
16315 break;
16316 case DEFACLOBJ_NAMESPACE:
16317 type = "SCHEMAS";
16318 break;
16319 case DEFACLOBJ_LARGEOBJECT:
16320 type = "LARGE OBJECTS";
16321 break;
16322 default:
16323 /* shouldn't get here */
16324 pg_fatal("unrecognized object type in default privileges: %d",
16325 (int) daclinfo->defaclobjtype);
16326 type = ""; /* keep compiler quiet */
16327 }
16328
16329 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16330
16331 /* build the actual command(s) for this tuple */
16333 daclinfo->dobj.namespace != NULL ?
16334 daclinfo->dobj.namespace->dobj.name : NULL,
16335 daclinfo->dacl.acl,
16336 daclinfo->dacl.acldefault,
16337 daclinfo->defaclrole,
16338 fout->remoteVersion,
16339 q))
16340 pg_fatal("could not parse default ACL list (%s)",
16341 daclinfo->dacl.acl);
16342
16343 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16344 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16345 ARCHIVE_OPTS(.tag = tag->data,
16346 .namespace = daclinfo->dobj.namespace ?
16347 daclinfo->dobj.namespace->dobj.name : NULL,
16348 .owner = daclinfo->defaclrole,
16349 .description = "DEFAULT ACL",
16350 .section = SECTION_POST_DATA,
16351 .createStmt = q->data));
16352
16353 destroyPQExpBuffer(tag);
16355}
16356
16357/*----------
16358 * Write out grant/revoke information
16359 *
16360 * 'objDumpId' is the dump ID of the underlying object.
16361 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16362 * or InvalidDumpId if there is no need for a second dependency.
16363 * 'type' must be one of
16364 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16365 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16366 * 'name' is the formatted name of the object. Must be quoted etc. already.
16367 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16368 * (Currently we assume that subname is only provided for table columns.)
16369 * 'nspname' is the namespace the object is in (NULL if none).
16370 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16371 * to use the default for the object type.
16372 * 'owner' is the owner, NULL if there is no owner (for languages).
16373 * 'dacl' is the DumpableAcl struct for the object.
16374 *
16375 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16376 * no ACL entry was created.
16377 *----------
16378 */
16379static DumpId
16380dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16381 const char *type, const char *name, const char *subname,
16382 const char *nspname, const char *tag, const char *owner,
16383 const DumpableAcl *dacl)
16384{
16385 DumpId aclDumpId = InvalidDumpId;
16386 DumpOptions *dopt = fout->dopt;
16387 const char *acls = dacl->acl;
16388 const char *acldefault = dacl->acldefault;
16389 char privtype = dacl->privtype;
16390 const char *initprivs = dacl->initprivs;
16391 const char *baseacls;
16392 PQExpBuffer sql;
16393
16394 /* Do nothing if ACL dump is not enabled */
16395 if (dopt->aclsSkip)
16396 return InvalidDumpId;
16397
16398 /* --data-only skips ACLs *except* large object ACLs */
16399 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16400 return InvalidDumpId;
16401
16402 sql = createPQExpBuffer();
16403
16404 /*
16405 * In binary upgrade mode, we don't run an extension's script but instead
16406 * dump out the objects independently and then recreate them. To preserve
16407 * any initial privileges which were set on extension objects, we need to
16408 * compute the set of GRANT and REVOKE commands necessary to get from the
16409 * default privileges of an object to its initial privileges as recorded
16410 * in pg_init_privs.
16411 *
16412 * At restore time, we apply these commands after having called
16413 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16414 * copy the results into pg_init_privs. This is how we preserve the
16415 * contents of that catalog across binary upgrades.
16416 */
16417 if (dopt->binary_upgrade && privtype == 'e' &&
16418 initprivs && *initprivs != '\0')
16419 {
16420 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16421 if (!buildACLCommands(name, subname, nspname, type,
16422 initprivs, acldefault, owner,
16423 "", fout->remoteVersion, sql))
16424 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16425 initprivs, acldefault, name, type);
16426 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16427 }
16428
16429 /*
16430 * Now figure the GRANT and REVOKE commands needed to get to the object's
16431 * actual current ACL, starting from the initprivs if given, else from the
16432 * object-type-specific default. Also, while buildACLCommands will assume
16433 * that a NULL/empty acls string means it needn't do anything, what that
16434 * actually represents is the object-type-specific default; so we need to
16435 * substitute the acldefault string to get the right results in that case.
16436 */
16437 if (initprivs && *initprivs != '\0')
16438 {
16439 baseacls = initprivs;
16440 if (acls == NULL || *acls == '\0')
16441 acls = acldefault;
16442 }
16443 else
16444 baseacls = acldefault;
16445
16446 if (!buildACLCommands(name, subname, nspname, type,
16447 acls, baseacls, owner,
16448 "", fout->remoteVersion, sql))
16449 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16450 acls, baseacls, name, type);
16451
16452 if (sql->len > 0)
16453 {
16454 PQExpBuffer tagbuf = createPQExpBuffer();
16455 DumpId aclDeps[2];
16456 int nDeps = 0;
16457
16458 if (tag)
16459 appendPQExpBufferStr(tagbuf, tag);
16460 else if (subname)
16461 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16462 else
16463 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16464
16465 aclDeps[nDeps++] = objDumpId;
16466 if (altDumpId != InvalidDumpId)
16467 aclDeps[nDeps++] = altDumpId;
16468
16469 aclDumpId = createDumpId();
16470
16471 ArchiveEntry(fout, nilCatalogId, aclDumpId,
16472 ARCHIVE_OPTS(.tag = tagbuf->data,
16473 .namespace = nspname,
16474 .owner = owner,
16475 .description = "ACL",
16476 .section = SECTION_NONE,
16477 .createStmt = sql->data,
16478 .deps = aclDeps,
16479 .nDeps = nDeps));
16480
16481 destroyPQExpBuffer(tagbuf);
16482 }
16483
16484 destroyPQExpBuffer(sql);
16485
16486 return aclDumpId;
16487}
16488
16489/*
16490 * dumpSecLabel
16491 *
16492 * This routine is used to dump any security labels associated with the
16493 * object handed to this routine. The routine takes the object type
16494 * and object name (ready to print, except for schema decoration), plus
16495 * the namespace and owner of the object (for labeling the ArchiveEntry),
16496 * plus catalog ID and subid which are the lookup key for pg_seclabel,
16497 * plus the dump ID for the object (for setting a dependency).
16498 * If a matching pg_seclabel entry is found, it is dumped.
16499 *
16500 * Note: although this routine takes a dumpId for dependency purposes,
16501 * that purpose is just to mark the dependency in the emitted dump file
16502 * for possible future use by pg_restore. We do NOT use it for determining
16503 * ordering of the label in the dump file, because this routine is called
16504 * after dependency sorting occurs. This routine should be called just after
16505 * calling ArchiveEntry() for the specified object.
16506 */
16507static void
16508dumpSecLabel(Archive *fout, const char *type, const char *name,
16509 const char *namespace, const char *owner,
16510 CatalogId catalogId, int subid, DumpId dumpId)
16511{
16512 DumpOptions *dopt = fout->dopt;
16513 SecLabelItem *labels;
16514 int nlabels;
16515 int i;
16516 PQExpBuffer query;
16517
16518 /* do nothing, if --no-security-labels is supplied */
16519 if (dopt->no_security_labels)
16520 return;
16521
16522 /*
16523 * Security labels are schema not data ... except large object labels are
16524 * data
16525 */
16526 if (strcmp(type, "LARGE OBJECT") != 0)
16527 {
16528 if (!dopt->dumpSchema)
16529 return;
16530 }
16531 else
16532 {
16533 /* We do dump large object security labels in binary-upgrade mode */
16534 if (!dopt->dumpData && !dopt->binary_upgrade)
16535 return;
16536 }
16537
16538 /* Search for security labels associated with catalogId, using table */
16539 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16540
16541 query = createPQExpBuffer();
16542
16543 for (i = 0; i < nlabels; i++)
16544 {
16545 /*
16546 * Ignore label entries for which the subid doesn't match.
16547 */
16548 if (labels[i].objsubid != subid)
16549 continue;
16550
16551 appendPQExpBuffer(query,
16552 "SECURITY LABEL FOR %s ON %s ",
16553 fmtId(labels[i].provider), type);
16554 if (namespace && *namespace)
16555 appendPQExpBuffer(query, "%s.", fmtId(namespace));
16556 appendPQExpBuffer(query, "%s IS ", name);
16557 appendStringLiteralAH(query, labels[i].label, fout);
16558 appendPQExpBufferStr(query, ";\n");
16559 }
16560
16561 if (query->len > 0)
16562 {
16564
16565 appendPQExpBuffer(tag, "%s %s", type, name);
16567 ARCHIVE_OPTS(.tag = tag->data,
16568 .namespace = namespace,
16569 .owner = owner,
16570 .description = "SECURITY LABEL",
16571 .section = SECTION_NONE,
16572 .createStmt = query->data,
16573 .deps = &dumpId,
16574 .nDeps = 1));
16575 destroyPQExpBuffer(tag);
16576 }
16577
16578 destroyPQExpBuffer(query);
16579}
16580
16581/*
16582 * dumpTableSecLabel
16583 *
16584 * As above, but dump security label for both the specified table (or view)
16585 * and its columns.
16586 */
16587static void
16588dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16589{
16590 DumpOptions *dopt = fout->dopt;
16591 SecLabelItem *labels;
16592 int nlabels;
16593 int i;
16594 PQExpBuffer query;
16595 PQExpBuffer target;
16596
16597 /* do nothing, if --no-security-labels is supplied */
16598 if (dopt->no_security_labels)
16599 return;
16600
16601 /* SecLabel are SCHEMA not data */
16602 if (!dopt->dumpSchema)
16603 return;
16604
16605 /* Search for comments associated with relation, using table */
16606 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16607 tbinfo->dobj.catId.oid,
16608 &labels);
16609
16610 /* If security labels exist, build SECURITY LABEL statements */
16611 if (nlabels <= 0)
16612 return;
16613
16614 query = createPQExpBuffer();
16615 target = createPQExpBuffer();
16616
16617 for (i = 0; i < nlabels; i++)
16618 {
16619 const char *colname;
16620 const char *provider = labels[i].provider;
16621 const char *label = labels[i].label;
16622 int objsubid = labels[i].objsubid;
16623
16624 resetPQExpBuffer(target);
16625 if (objsubid == 0)
16626 {
16627 appendPQExpBuffer(target, "%s %s", reltypename,
16628 fmtQualifiedDumpable(tbinfo));
16629 }
16630 else
16631 {
16632 colname = getAttrName(objsubid, tbinfo);
16633 /* first fmtXXX result must be consumed before calling again */
16634 appendPQExpBuffer(target, "COLUMN %s",
16635 fmtQualifiedDumpable(tbinfo));
16636 appendPQExpBuffer(target, ".%s", fmtId(colname));
16637 }
16638 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16639 fmtId(provider), target->data);
16640 appendStringLiteralAH(query, label, fout);
16641 appendPQExpBufferStr(query, ";\n");
16642 }
16643 if (query->len > 0)
16644 {
16645 resetPQExpBuffer(target);
16646 appendPQExpBuffer(target, "%s %s", reltypename,
16647 fmtId(tbinfo->dobj.name));
16649 ARCHIVE_OPTS(.tag = target->data,
16650 .namespace = tbinfo->dobj.namespace->dobj.name,
16651 .owner = tbinfo->rolname,
16652 .description = "SECURITY LABEL",
16653 .section = SECTION_NONE,
16654 .createStmt = query->data,
16655 .deps = &(tbinfo->dobj.dumpId),
16656 .nDeps = 1));
16657 }
16658 destroyPQExpBuffer(query);
16659 destroyPQExpBuffer(target);
16660}
16661
16662/*
16663 * findSecLabels
16664 *
16665 * Find the security label(s), if any, associated with the given object.
16666 * All the objsubid values associated with the given classoid/objoid are
16667 * found with one search.
16668 */
16669static int
16671{
16672 SecLabelItem *middle = NULL;
16673 SecLabelItem *low;
16674 SecLabelItem *high;
16675 int nmatch;
16676
16677 if (nseclabels <= 0) /* no labels, so no match is possible */
16678 {
16679 *items = NULL;
16680 return 0;
16681 }
16682
16683 /*
16684 * Do binary search to find some item matching the object.
16685 */
16686 low = &seclabels[0];
16687 high = &seclabels[nseclabels - 1];
16688 while (low <= high)
16689 {
16690 middle = low + (high - low) / 2;
16691
16692 if (classoid < middle->classoid)
16693 high = middle - 1;
16694 else if (classoid > middle->classoid)
16695 low = middle + 1;
16696 else if (objoid < middle->objoid)
16697 high = middle - 1;
16698 else if (objoid > middle->objoid)
16699 low = middle + 1;
16700 else
16701 break; /* found a match */
16702 }
16703
16704 if (low > high) /* no matches */
16705 {
16706 *items = NULL;
16707 return 0;
16708 }
16709
16710 /*
16711 * Now determine how many items match the object. The search loop
16712 * invariant still holds: only items between low and high inclusive could
16713 * match.
16714 */
16715 nmatch = 1;
16716 while (middle > low)
16717 {
16718 if (classoid != middle[-1].classoid ||
16719 objoid != middle[-1].objoid)
16720 break;
16721 middle--;
16722 nmatch++;
16723 }
16724
16725 *items = middle;
16726
16727 middle += nmatch;
16728 while (middle <= high)
16729 {
16730 if (classoid != middle->classoid ||
16731 objoid != middle->objoid)
16732 break;
16733 middle++;
16734 nmatch++;
16735 }
16736
16737 return nmatch;
16738}
16739
16740/*
16741 * collectSecLabels
16742 *
16743 * Construct a table of all security labels available for database objects;
16744 * also set the has-seclabel component flag for each relevant object.
16745 *
16746 * The table is sorted by classoid/objid/objsubid for speed in lookup.
16747 */
16748static void
16750{
16751 PGresult *res;
16752 PQExpBuffer query;
16753 int i_label;
16754 int i_provider;
16755 int i_classoid;
16756 int i_objoid;
16757 int i_objsubid;
16758 int ntups;
16759 int i;
16760 DumpableObject *dobj;
16761
16762 query = createPQExpBuffer();
16763
16765 "SELECT label, provider, classoid, objoid, objsubid "
16766 "FROM pg_catalog.pg_seclabels "
16767 "ORDER BY classoid, objoid, objsubid");
16768
16769 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16770
16771 /* Construct lookup table containing OIDs in numeric form */
16772 i_label = PQfnumber(res, "label");
16773 i_provider = PQfnumber(res, "provider");
16774 i_classoid = PQfnumber(res, "classoid");
16775 i_objoid = PQfnumber(res, "objoid");
16776 i_objsubid = PQfnumber(res, "objsubid");
16777
16778 ntups = PQntuples(res);
16779
16780 seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16781 nseclabels = 0;
16782 dobj = NULL;
16783
16784 for (i = 0; i < ntups; i++)
16785 {
16786 CatalogId objId;
16787 int subid;
16788
16789 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16790 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16791 subid = atoi(PQgetvalue(res, i, i_objsubid));
16792
16793 /* We needn't remember labels that don't match any dumpable object */
16794 if (dobj == NULL ||
16795 dobj->catId.tableoid != objId.tableoid ||
16796 dobj->catId.oid != objId.oid)
16797 dobj = findObjectByCatalogId(objId);
16798 if (dobj == NULL)
16799 continue;
16800
16801 /*
16802 * Labels on columns of composite types are linked to the type's
16803 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16804 * in the type's own DumpableObject.
16805 */
16806 if (subid != 0 && dobj->objType == DO_TABLE &&
16807 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16808 {
16809 TypeInfo *cTypeInfo;
16810
16811 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16812 if (cTypeInfo)
16814 }
16815 else
16816 dobj->components |= DUMP_COMPONENT_SECLABEL;
16817
16818 seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16819 seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16821 seclabels[nseclabels].objoid = objId.oid;
16822 seclabels[nseclabels].objsubid = subid;
16823 nseclabels++;
16824 }
16825
16826 PQclear(res);
16827 destroyPQExpBuffer(query);
16828}
16829
16830/*
16831 * dumpTable
16832 * write out to fout the declarations (not data) of a user-defined table
16833 */
16834static void
16835dumpTable(Archive *fout, const TableInfo *tbinfo)
16836{
16837 DumpOptions *dopt = fout->dopt;
16838 DumpId tableAclDumpId = InvalidDumpId;
16839 char *namecopy;
16840
16841 /* Do nothing if not dumping schema */
16842 if (!dopt->dumpSchema)
16843 return;
16844
16845 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16846 {
16847 if (tbinfo->relkind == RELKIND_SEQUENCE)
16848 dumpSequence(fout, tbinfo);
16849 else
16850 dumpTableSchema(fout, tbinfo);
16851 }
16852
16853 /* Handle the ACL here */
16854 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16855 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16856 {
16857 const char *objtype =
16858 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16859
16860 tableAclDumpId =
16861 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16862 objtype, namecopy, NULL,
16863 tbinfo->dobj.namespace->dobj.name,
16864 NULL, tbinfo->rolname, &tbinfo->dacl);
16865 }
16866
16867 /*
16868 * Handle column ACLs, if any. Note: we pull these with a separate query
16869 * rather than trying to fetch them during getTableAttrs, so that we won't
16870 * miss ACLs on system columns. Doing it this way also allows us to dump
16871 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16872 */
16873 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16874 {
16876 PGresult *res;
16877 int i;
16878
16880 {
16881 /* Set up query for column ACLs */
16883 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16884
16885 if (fout->remoteVersion >= 90600)
16886 {
16887 /*
16888 * In principle we should call acldefault('c', relowner) to
16889 * get the default ACL for a column. However, we don't
16890 * currently store the numeric OID of the relowner in
16891 * TableInfo. We could convert the owner name using regrole,
16892 * but that creates a risk of failure due to concurrent role
16893 * renames. Given that the default ACL for columns is empty
16894 * and is likely to stay that way, it's not worth extra cycles
16895 * and risk to avoid hard-wiring that knowledge here.
16896 */
16898 "SELECT at.attname, "
16899 "at.attacl, "
16900 "'{}' AS acldefault, "
16901 "pip.privtype, pip.initprivs "
16902 "FROM pg_catalog.pg_attribute at "
16903 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16904 "(at.attrelid = pip.objoid "
16905 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16906 "AND at.attnum = pip.objsubid) "
16907 "WHERE at.attrelid = $1 AND "
16908 "NOT at.attisdropped "
16909 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16910 "ORDER BY at.attnum");
16911 }
16912 else
16913 {
16915 "SELECT attname, attacl, '{}' AS acldefault, "
16916 "NULL AS privtype, NULL AS initprivs "
16917 "FROM pg_catalog.pg_attribute "
16918 "WHERE attrelid = $1 AND NOT attisdropped "
16919 "AND attacl IS NOT NULL "
16920 "ORDER BY attnum");
16921 }
16922
16923 ExecuteSqlStatement(fout, query->data);
16924
16926 }
16927
16928 printfPQExpBuffer(query,
16929 "EXECUTE getColumnACLs('%u')",
16930 tbinfo->dobj.catId.oid);
16931
16932 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16933
16934 for (i = 0; i < PQntuples(res); i++)
16935 {
16936 char *attname = PQgetvalue(res, i, 0);
16937 char *attacl = PQgetvalue(res, i, 1);
16938 char *acldefault = PQgetvalue(res, i, 2);
16939 char privtype = *(PQgetvalue(res, i, 3));
16940 char *initprivs = PQgetvalue(res, i, 4);
16941 DumpableAcl coldacl;
16942 char *attnamecopy;
16943
16944 coldacl.acl = attacl;
16945 coldacl.acldefault = acldefault;
16946 coldacl.privtype = privtype;
16947 coldacl.initprivs = initprivs;
16948 attnamecopy = pg_strdup(fmtId(attname));
16949
16950 /*
16951 * Column's GRANT type is always TABLE. Each column ACL depends
16952 * on the table-level ACL, since we can restore column ACLs in
16953 * parallel but the table-level ACL has to be done first.
16954 */
16955 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16956 "TABLE", namecopy, attnamecopy,
16957 tbinfo->dobj.namespace->dobj.name,
16958 NULL, tbinfo->rolname, &coldacl);
16959 free(attnamecopy);
16960 }
16961 PQclear(res);
16962 destroyPQExpBuffer(query);
16963 }
16964
16965 free(namecopy);
16966}
16967
16968/*
16969 * Create the AS clause for a view or materialized view. The semicolon is
16970 * stripped because a materialized view must add a WITH NO DATA clause.
16971 *
16972 * This returns a new buffer which must be freed by the caller.
16973 */
16974static PQExpBuffer
16976{
16978 PQExpBuffer result = createPQExpBuffer();
16979 PGresult *res;
16980 int len;
16981
16982 /* Fetch the view definition */
16983 appendPQExpBuffer(query,
16984 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16985 tbinfo->dobj.catId.oid);
16986
16987 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16988
16989 if (PQntuples(res) != 1)
16990 {
16991 if (PQntuples(res) < 1)
16992 pg_fatal("query to obtain definition of view \"%s\" returned no data",
16993 tbinfo->dobj.name);
16994 else
16995 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16996 tbinfo->dobj.name);
16997 }
16998
16999 len = PQgetlength(res, 0, 0);
17000
17001 if (len == 0)
17002 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17003 tbinfo->dobj.name);
17004
17005 /* Strip off the trailing semicolon so that other things may follow. */
17006 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17007 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17008
17009 PQclear(res);
17010 destroyPQExpBuffer(query);
17011
17012 return result;
17013}
17014
17015/*
17016 * Create a dummy AS clause for a view. This is used when the real view
17017 * definition has to be postponed because of circular dependencies.
17018 * We must duplicate the view's external properties -- column names and types
17019 * (including collation) -- so that it works for subsequent references.
17020 *
17021 * This returns a new buffer which must be freed by the caller.
17022 */
17023static PQExpBuffer
17025{
17026 PQExpBuffer result = createPQExpBuffer();
17027 int j;
17028
17029 appendPQExpBufferStr(result, "SELECT");
17030
17031 for (j = 0; j < tbinfo->numatts; j++)
17032 {
17033 if (j > 0)
17034 appendPQExpBufferChar(result, ',');
17035 appendPQExpBufferStr(result, "\n ");
17036
17037 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17038
17039 /*
17040 * Must add collation if not default for the type, because CREATE OR
17041 * REPLACE VIEW won't change it
17042 */
17043 if (OidIsValid(tbinfo->attcollation[j]))
17044 {
17045 CollInfo *coll;
17046
17047 coll = findCollationByOid(tbinfo->attcollation[j]);
17048 if (coll)
17049 appendPQExpBuffer(result, " COLLATE %s",
17050 fmtQualifiedDumpable(coll));
17051 }
17052
17053 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17054 }
17055
17056 return result;
17057}
17058
17059/*
17060 * dumpTableSchema
17061 * write the declaration (not data) of one user-defined table or view
17062 */
17063static void
17064dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17065{
17066 DumpOptions *dopt = fout->dopt;
17070 char *qrelname;
17071 char *qualrelname;
17072 int numParents;
17073 TableInfo **parents;
17074 int actual_atts; /* number of attrs in this CREATE statement */
17075 const char *reltypename;
17076 char *storage;
17077 int j,
17078 k;
17079
17080 /* We had better have loaded per-column details about this table */
17081 Assert(tbinfo->interesting);
17082
17083 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17084 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17085
17086 if (tbinfo->hasoids)
17087 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17088 qrelname);
17089
17090 if (dopt->binary_upgrade)
17091 binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17092
17093 /* Is it a table or a view? */
17094 if (tbinfo->relkind == RELKIND_VIEW)
17095 {
17096 PQExpBuffer result;
17097
17098 /*
17099 * Note: keep this code in sync with the is_view case in dumpRule()
17100 */
17101
17102 reltypename = "VIEW";
17103
17104 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17105
17106 if (dopt->binary_upgrade)
17108 tbinfo->dobj.catId.oid);
17109
17110 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17111
17112 if (tbinfo->dummy_view)
17113 result = createDummyViewAsClause(fout, tbinfo);
17114 else
17115 {
17116 if (nonemptyReloptions(tbinfo->reloptions))
17117 {
17118 appendPQExpBufferStr(q, " WITH (");
17119 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17120 appendPQExpBufferChar(q, ')');
17121 }
17122 result = createViewAsClause(fout, tbinfo);
17123 }
17124 appendPQExpBuffer(q, " AS\n%s", result->data);
17125 destroyPQExpBuffer(result);
17126
17127 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17128 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17129 appendPQExpBufferStr(q, ";\n");
17130 }
17131 else
17132 {
17133 char *partkeydef = NULL;
17134 char *ftoptions = NULL;
17135 char *srvname = NULL;
17136 const char *foreign = "";
17137
17138 /*
17139 * Set reltypename, and collect any relkind-specific data that we
17140 * didn't fetch during getTables().
17141 */
17142 switch (tbinfo->relkind)
17143 {
17144 case RELKIND_PARTITIONED_TABLE:
17145 {
17147 PGresult *res;
17148
17149 reltypename = "TABLE";
17150
17151 /* retrieve partition key definition */
17152 appendPQExpBuffer(query,
17153 "SELECT pg_get_partkeydef('%u')",
17154 tbinfo->dobj.catId.oid);
17155 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17156 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17157 PQclear(res);
17158 destroyPQExpBuffer(query);
17159 break;
17160 }
17161 case RELKIND_FOREIGN_TABLE:
17162 {
17164 PGresult *res;
17165 int i_srvname;
17166 int i_ftoptions;
17167
17168 reltypename = "FOREIGN TABLE";
17169
17170 /* retrieve name of foreign server and generic options */
17171 appendPQExpBuffer(query,
17172 "SELECT fs.srvname, "
17173 "pg_catalog.array_to_string(ARRAY("
17174 "SELECT pg_catalog.quote_ident(option_name) || "
17175 "' ' || pg_catalog.quote_literal(option_value) "
17176 "FROM pg_catalog.pg_options_to_table(ftoptions) "
17177 "ORDER BY option_name"
17178 "), E',\n ') AS ftoptions "
17179 "FROM pg_catalog.pg_foreign_table ft "
17180 "JOIN pg_catalog.pg_foreign_server fs "
17181 "ON (fs.oid = ft.ftserver) "
17182 "WHERE ft.ftrelid = '%u'",
17183 tbinfo->dobj.catId.oid);
17184 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17185 i_srvname = PQfnumber(res, "srvname");
17186 i_ftoptions = PQfnumber(res, "ftoptions");
17187 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17188 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17189 PQclear(res);
17190 destroyPQExpBuffer(query);
17191
17192 foreign = "FOREIGN ";
17193 break;
17194 }
17195 case RELKIND_MATVIEW:
17196 reltypename = "MATERIALIZED VIEW";
17197 break;
17198 default:
17199 reltypename = "TABLE";
17200 break;
17201 }
17202
17203 numParents = tbinfo->numParents;
17204 parents = tbinfo->parents;
17205
17206 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17207
17208 if (dopt->binary_upgrade)
17210 tbinfo->dobj.catId.oid);
17211
17212 /*
17213 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17214 * ignore it when dumping if it was set in this case.
17215 */
17216 appendPQExpBuffer(q, "CREATE %s%s %s",
17217 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17218 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17219 "UNLOGGED " : "",
17220 reltypename,
17221 qualrelname);
17222
17223 /*
17224 * Attach to type, if reloftype; except in case of a binary upgrade,
17225 * we dump the table normally and attach it to the type afterward.
17226 */
17227 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17228 appendPQExpBuffer(q, " OF %s",
17229 getFormattedTypeName(fout, tbinfo->reloftype,
17230 zeroIsError));
17231
17232 if (tbinfo->relkind != RELKIND_MATVIEW)
17233 {
17234 /* Dump the attributes */
17235 actual_atts = 0;
17236 for (j = 0; j < tbinfo->numatts; j++)
17237 {
17238 /*
17239 * Normally, dump if it's locally defined in this table, and
17240 * not dropped. But for binary upgrade, we'll dump all the
17241 * columns, and then fix up the dropped and nonlocal cases
17242 * below.
17243 */
17244 if (shouldPrintColumn(dopt, tbinfo, j))
17245 {
17246 bool print_default;
17247 bool print_notnull;
17248
17249 /*
17250 * Default value --- suppress if to be printed separately
17251 * or not at all.
17252 */
17253 print_default = (tbinfo->attrdefs[j] != NULL &&
17254 tbinfo->attrdefs[j]->dobj.dump &&
17255 !tbinfo->attrdefs[j]->separate);
17256
17257 /*
17258 * Not Null constraint --- print it if it is locally
17259 * defined, or if binary upgrade. (In the latter case, we
17260 * reset conislocal below.)
17261 */
17262 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17263 (tbinfo->notnull_islocal[j] ||
17264 dopt->binary_upgrade ||
17265 tbinfo->ispartition));
17266
17267 /*
17268 * Skip column if fully defined by reloftype, except in
17269 * binary upgrade
17270 */
17271 if (OidIsValid(tbinfo->reloftype) &&
17272 !print_default && !print_notnull &&
17273 !dopt->binary_upgrade)
17274 continue;
17275
17276 /* Format properly if not first attr */
17277 if (actual_atts == 0)
17278 appendPQExpBufferStr(q, " (");
17279 else
17280 appendPQExpBufferChar(q, ',');
17281 appendPQExpBufferStr(q, "\n ");
17282 actual_atts++;
17283
17284 /* Attribute name */
17285 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17286
17287 if (tbinfo->attisdropped[j])
17288 {
17289 /*
17290 * ALTER TABLE DROP COLUMN clears
17291 * pg_attribute.atttypid, so we will not have gotten a
17292 * valid type name; insert INTEGER as a stopgap. We'll
17293 * clean things up later.
17294 */
17295 appendPQExpBufferStr(q, " INTEGER /* dummy */");
17296 /* and skip to the next column */
17297 continue;
17298 }
17299
17300 /*
17301 * Attribute type; print it except when creating a typed
17302 * table ('OF type_name'), but in binary-upgrade mode,
17303 * print it in that case too.
17304 */
17305 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17306 {
17307 appendPQExpBuffer(q, " %s",
17308 tbinfo->atttypnames[j]);
17309 }
17310
17311 if (print_default)
17312 {
17313 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17314 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17315 tbinfo->attrdefs[j]->adef_expr);
17316 else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17317 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17318 tbinfo->attrdefs[j]->adef_expr);
17319 else
17320 appendPQExpBuffer(q, " DEFAULT %s",
17321 tbinfo->attrdefs[j]->adef_expr);
17322 }
17323
17324 if (print_notnull)
17325 {
17326 if (tbinfo->notnull_constrs[j][0] == '\0')
17327 appendPQExpBufferStr(q, " NOT NULL");
17328 else
17329 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17330 fmtId(tbinfo->notnull_constrs[j]));
17331
17332 if (tbinfo->notnull_noinh[j])
17333 appendPQExpBufferStr(q, " NO INHERIT");
17334 }
17335
17336 /* Add collation if not default for the type */
17337 if (OidIsValid(tbinfo->attcollation[j]))
17338 {
17339 CollInfo *coll;
17340
17341 coll = findCollationByOid(tbinfo->attcollation[j]);
17342 if (coll)
17343 appendPQExpBuffer(q, " COLLATE %s",
17344 fmtQualifiedDumpable(coll));
17345 }
17346 }
17347
17348 /*
17349 * On the other hand, if we choose not to print a column
17350 * (likely because it is created by inheritance), but the
17351 * column has a locally-defined not-null constraint, we need
17352 * to dump the constraint as a standalone object.
17353 *
17354 * This syntax isn't SQL-conforming, but if you wanted
17355 * standard output you wouldn't be creating non-standard
17356 * objects to begin with.
17357 */
17358 if (!shouldPrintColumn(dopt, tbinfo, j) &&
17359 !tbinfo->attisdropped[j] &&
17360 tbinfo->notnull_constrs[j] != NULL &&
17361 tbinfo->notnull_islocal[j])
17362 {
17363 /* Format properly if not first attr */
17364 if (actual_atts == 0)
17365 appendPQExpBufferStr(q, " (");
17366 else
17367 appendPQExpBufferChar(q, ',');
17368 appendPQExpBufferStr(q, "\n ");
17369 actual_atts++;
17370
17371 if (tbinfo->notnull_constrs[j][0] == '\0')
17372 appendPQExpBuffer(q, "NOT NULL %s",
17373 fmtId(tbinfo->attnames[j]));
17374 else
17375 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17376 tbinfo->notnull_constrs[j],
17377 fmtId(tbinfo->attnames[j]));
17378 }
17379 }
17380
17381 /*
17382 * Add non-inherited CHECK constraints, if any.
17383 *
17384 * For partitions, we need to include check constraints even if
17385 * they're not defined locally, because the ALTER TABLE ATTACH
17386 * PARTITION that we'll emit later expects the constraint to be
17387 * there. (No need to fix conislocal: ATTACH PARTITION does that)
17388 */
17389 for (j = 0; j < tbinfo->ncheck; j++)
17390 {
17391 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17392
17393 if (constr->separate ||
17394 (!constr->conislocal && !tbinfo->ispartition))
17395 continue;
17396
17397 if (actual_atts == 0)
17398 appendPQExpBufferStr(q, " (\n ");
17399 else
17400 appendPQExpBufferStr(q, ",\n ");
17401
17402 appendPQExpBuffer(q, "CONSTRAINT %s ",
17403 fmtId(constr->dobj.name));
17404 appendPQExpBufferStr(q, constr->condef);
17405
17406 actual_atts++;
17407 }
17408
17409 if (actual_atts)
17410 appendPQExpBufferStr(q, "\n)");
17411 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17412 {
17413 /*
17414 * No attributes? we must have a parenthesized attribute list,
17415 * even though empty, when not using the OF TYPE syntax.
17416 */
17417 appendPQExpBufferStr(q, " (\n)");
17418 }
17419
17420 /*
17421 * Emit the INHERITS clause (not for partitions), except in
17422 * binary-upgrade mode.
17423 */
17424 if (numParents > 0 && !tbinfo->ispartition &&
17425 !dopt->binary_upgrade)
17426 {
17427 appendPQExpBufferStr(q, "\nINHERITS (");
17428 for (k = 0; k < numParents; k++)
17429 {
17430 TableInfo *parentRel = parents[k];
17431
17432 if (k > 0)
17433 appendPQExpBufferStr(q, ", ");
17435 }
17436 appendPQExpBufferChar(q, ')');
17437 }
17438
17439 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17440 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17441
17442 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17443 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17444 }
17445
17446 if (nonemptyReloptions(tbinfo->reloptions) ||
17448 {
17449 bool addcomma = false;
17450
17451 appendPQExpBufferStr(q, "\nWITH (");
17452 if (nonemptyReloptions(tbinfo->reloptions))
17453 {
17454 addcomma = true;
17455 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17456 }
17458 {
17459 if (addcomma)
17460 appendPQExpBufferStr(q, ", ");
17461 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17462 fout);
17463 }
17464 appendPQExpBufferChar(q, ')');
17465 }
17466
17467 /* Dump generic options if any */
17468 if (ftoptions && ftoptions[0])
17469 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17470
17471 /*
17472 * For materialized views, create the AS clause just like a view. At
17473 * this point, we always mark the view as not populated.
17474 */
17475 if (tbinfo->relkind == RELKIND_MATVIEW)
17476 {
17477 PQExpBuffer result;
17478
17479 result = createViewAsClause(fout, tbinfo);
17480 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17481 result->data);
17482 destroyPQExpBuffer(result);
17483 }
17484 else
17485 appendPQExpBufferStr(q, ";\n");
17486
17487 /* Materialized views can depend on extensions */
17488 if (tbinfo->relkind == RELKIND_MATVIEW)
17489 append_depends_on_extension(fout, q, &tbinfo->dobj,
17490 "pg_catalog.pg_class",
17491 "MATERIALIZED VIEW",
17492 qualrelname);
17493
17494 /*
17495 * in binary upgrade mode, update the catalog with any missing values
17496 * that might be present.
17497 */
17498 if (dopt->binary_upgrade)
17499 {
17500 for (j = 0; j < tbinfo->numatts; j++)
17501 {
17502 if (tbinfo->attmissingval[j][0] != '\0')
17503 {
17504 appendPQExpBufferStr(q, "\n-- set missing value.\n");
17506 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17507 appendStringLiteralAH(q, qualrelname, fout);
17508 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17509 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17510 appendPQExpBufferChar(q, ',');
17511 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17512 appendPQExpBufferStr(q, ");\n\n");
17513 }
17514 }
17515 }
17516
17517 /*
17518 * To create binary-compatible heap files, we have to ensure the same
17519 * physical column order, including dropped columns, as in the
17520 * original. Therefore, we create dropped columns above and drop them
17521 * here, also updating their attlen/attalign values so that the
17522 * dropped column can be skipped properly. (We do not bother with
17523 * restoring the original attbyval setting.) Also, inheritance
17524 * relationships are set up by doing ALTER TABLE INHERIT rather than
17525 * using an INHERITS clause --- the latter would possibly mess up the
17526 * column order. That also means we have to take care about setting
17527 * attislocal correctly, plus fix up any inherited CHECK constraints.
17528 * Analogously, we set up typed tables using ALTER TABLE / OF here.
17529 *
17530 * We process foreign and partitioned tables here, even though they
17531 * lack heap storage, because they can participate in inheritance
17532 * relationships and we want this stuff to be consistent across the
17533 * inheritance tree. We can exclude indexes, toast tables, sequences
17534 * and matviews, even though they have storage, because we don't
17535 * support altering or dropping columns in them, nor can they be part
17536 * of inheritance trees.
17537 */
17538 if (dopt->binary_upgrade &&
17539 (tbinfo->relkind == RELKIND_RELATION ||
17540 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17541 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17542 {
17543 bool firstitem;
17544 bool firstitem_extra;
17545
17546 /*
17547 * Drop any dropped columns. Merge the pg_attribute manipulations
17548 * into a single SQL command, so that we don't cause repeated
17549 * relcache flushes on the target table. Otherwise we risk O(N^2)
17550 * relcache bloat while dropping N columns.
17551 */
17552 resetPQExpBuffer(extra);
17553 firstitem = true;
17554 for (j = 0; j < tbinfo->numatts; j++)
17555 {
17556 if (tbinfo->attisdropped[j])
17557 {
17558 if (firstitem)
17559 {
17560 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17561 "UPDATE pg_catalog.pg_attribute\n"
17562 "SET attlen = v.dlen, "
17563 "attalign = v.dalign, "
17564 "attbyval = false\n"
17565 "FROM (VALUES ");
17566 firstitem = false;
17567 }
17568 else
17569 appendPQExpBufferStr(q, ",\n ");
17570 appendPQExpBufferChar(q, '(');
17571 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17572 appendPQExpBuffer(q, ", %d, '%c')",
17573 tbinfo->attlen[j],
17574 tbinfo->attalign[j]);
17575 /* The ALTER ... DROP COLUMN commands must come after */
17576 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17577 foreign, qualrelname);
17578 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17579 fmtId(tbinfo->attnames[j]));
17580 }
17581 }
17582 if (!firstitem)
17583 {
17584 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17585 "WHERE attrelid = ");
17586 appendStringLiteralAH(q, qualrelname, fout);
17587 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17588 " AND attname = v.dname;\n");
17589 /* Now we can issue the actual DROP COLUMN commands */
17590 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17591 }
17592
17593 /*
17594 * Fix up inherited columns. As above, do the pg_attribute
17595 * manipulations in a single SQL command.
17596 */
17597 firstitem = true;
17598 for (j = 0; j < tbinfo->numatts; j++)
17599 {
17600 if (!tbinfo->attisdropped[j] &&
17601 !tbinfo->attislocal[j])
17602 {
17603 if (firstitem)
17604 {
17605 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17606 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17607 "SET attislocal = false\n"
17608 "WHERE attrelid = ");
17609 appendStringLiteralAH(q, qualrelname, fout);
17610 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17611 " AND attname IN (");
17612 firstitem = false;
17613 }
17614 else
17615 appendPQExpBufferStr(q, ", ");
17616 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17617 }
17618 }
17619 if (!firstitem)
17620 appendPQExpBufferStr(q, ");\n");
17621
17622 /*
17623 * Fix up not-null constraints that come from inheritance. As
17624 * above, do the pg_constraint manipulations in a single SQL
17625 * command. (Actually, two in special cases, if we're doing an
17626 * upgrade from < 18).
17627 */
17628 firstitem = true;
17629 firstitem_extra = true;
17630 resetPQExpBuffer(extra);
17631 for (j = 0; j < tbinfo->numatts; j++)
17632 {
17633 /*
17634 * If a not-null constraint comes from inheritance, reset
17635 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17636 * below. Special hack: in versions < 18, columns with no
17637 * local definition need their constraint to be matched by
17638 * column number in conkeys instead of by constraint name,
17639 * because the latter is not available. (We distinguish the
17640 * case because the constraint name is the empty string.)
17641 */
17642 if (tbinfo->notnull_constrs[j] != NULL &&
17643 !tbinfo->notnull_islocal[j])
17644 {
17645 if (tbinfo->notnull_constrs[j][0] != '\0')
17646 {
17647 if (firstitem)
17648 {
17649 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17650 "SET conislocal = false\n"
17651 "WHERE contype = 'n' AND conrelid = ");
17652 appendStringLiteralAH(q, qualrelname, fout);
17653 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17654 "conname IN (");
17655 firstitem = false;
17656 }
17657 else
17658 appendPQExpBufferStr(q, ", ");
17659 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17660 }
17661 else
17662 {
17663 if (firstitem_extra)
17664 {
17665 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17666 "SET conislocal = false\n"
17667 "WHERE contype = 'n' AND conrelid = ");
17668 appendStringLiteralAH(extra, qualrelname, fout);
17669 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17670 "conkey IN (");
17671 firstitem_extra = false;
17672 }
17673 else
17674 appendPQExpBufferStr(extra, ", ");
17675 appendPQExpBuffer(extra, "'{%d}'", j + 1);
17676 }
17677 }
17678 }
17679 if (!firstitem)
17680 appendPQExpBufferStr(q, ");\n");
17681 if (!firstitem_extra)
17682 appendPQExpBufferStr(extra, ");\n");
17683
17684 if (extra->len > 0)
17685 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17686
17687 /*
17688 * Add inherited CHECK constraints, if any.
17689 *
17690 * For partitions, they were already dumped, and conislocal
17691 * doesn't need fixing.
17692 *
17693 * As above, issue only one direct manipulation of pg_constraint.
17694 * Although it is tempting to merge the ALTER ADD CONSTRAINT
17695 * commands into one as well, refrain for now due to concern about
17696 * possible backend memory bloat if there are many such
17697 * constraints.
17698 */
17699 resetPQExpBuffer(extra);
17700 firstitem = true;
17701 for (k = 0; k < tbinfo->ncheck; k++)
17702 {
17703 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17704
17705 if (constr->separate || constr->conislocal || tbinfo->ispartition)
17706 continue;
17707
17708 if (firstitem)
17709 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17710 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17711 foreign, qualrelname,
17712 fmtId(constr->dobj.name),
17713 constr->condef);
17714 /* Update pg_constraint after all the ALTER TABLEs */
17715 if (firstitem)
17716 {
17717 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17718 "SET conislocal = false\n"
17719 "WHERE contype = 'c' AND conrelid = ");
17720 appendStringLiteralAH(extra, qualrelname, fout);
17721 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17722 appendPQExpBufferStr(extra, " AND conname IN (");
17723 firstitem = false;
17724 }
17725 else
17726 appendPQExpBufferStr(extra, ", ");
17727 appendStringLiteralAH(extra, constr->dobj.name, fout);
17728 }
17729 if (!firstitem)
17730 {
17731 appendPQExpBufferStr(extra, ");\n");
17732 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17733 }
17734
17735 if (numParents > 0 && !tbinfo->ispartition)
17736 {
17737 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17738 for (k = 0; k < numParents; k++)
17739 {
17740 TableInfo *parentRel = parents[k];
17741
17742 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17743 qualrelname,
17744 fmtQualifiedDumpable(parentRel));
17745 }
17746 }
17747
17748 if (OidIsValid(tbinfo->reloftype))
17749 {
17750 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17751 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17752 qualrelname,
17753 getFormattedTypeName(fout, tbinfo->reloftype,
17754 zeroIsError));
17755 }
17756 }
17757
17758 /*
17759 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17760 * relminmxid of all vacuumable relations. (While vacuum.c processes
17761 * TOAST tables semi-independently, here we see them only as children
17762 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17763 * child toast table is handled below.)
17764 */
17765 if (dopt->binary_upgrade &&
17766 (tbinfo->relkind == RELKIND_RELATION ||
17767 tbinfo->relkind == RELKIND_MATVIEW))
17768 {
17769 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17770 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17771 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17772 "WHERE oid = ",
17773 tbinfo->frozenxid, tbinfo->minmxid);
17774 appendStringLiteralAH(q, qualrelname, fout);
17775 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17776
17777 if (tbinfo->toast_oid)
17778 {
17779 /*
17780 * The toast table will have the same OID at restore, so we
17781 * can safely target it by OID.
17782 */
17783 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17784 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17785 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17786 "WHERE oid = '%u';\n",
17787 tbinfo->toast_frozenxid,
17788 tbinfo->toast_minmxid, tbinfo->toast_oid);
17789 }
17790 }
17791
17792 /*
17793 * In binary_upgrade mode, restore matviews' populated status by
17794 * poking pg_class directly. This is pretty ugly, but we can't use
17795 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17796 * matview is not populated even though this matview is; in any case,
17797 * we want to transfer the matview's heap storage, not run REFRESH.
17798 */
17799 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17800 tbinfo->relispopulated)
17801 {
17802 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17803 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17804 "SET relispopulated = 't'\n"
17805 "WHERE oid = ");
17806 appendStringLiteralAH(q, qualrelname, fout);
17807 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17808 }
17809
17810 /*
17811 * Dump additional per-column properties that we can't handle in the
17812 * main CREATE TABLE command.
17813 */
17814 for (j = 0; j < tbinfo->numatts; j++)
17815 {
17816 /* None of this applies to dropped columns */
17817 if (tbinfo->attisdropped[j])
17818 continue;
17819
17820 /*
17821 * Dump per-column statistics information. We only issue an ALTER
17822 * TABLE statement if the attstattarget entry for this column is
17823 * not the default value.
17824 */
17825 if (tbinfo->attstattarget[j] >= 0)
17826 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17827 foreign, qualrelname,
17828 fmtId(tbinfo->attnames[j]),
17829 tbinfo->attstattarget[j]);
17830
17831 /*
17832 * Dump per-column storage information. The statement is only
17833 * dumped if the storage has been changed from the type's default.
17834 */
17835 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17836 {
17837 switch (tbinfo->attstorage[j])
17838 {
17839 case TYPSTORAGE_PLAIN:
17840 storage = "PLAIN";
17841 break;
17842 case TYPSTORAGE_EXTERNAL:
17843 storage = "EXTERNAL";
17844 break;
17845 case TYPSTORAGE_EXTENDED:
17846 storage = "EXTENDED";
17847 break;
17848 case TYPSTORAGE_MAIN:
17849 storage = "MAIN";
17850 break;
17851 default:
17852 storage = NULL;
17853 }
17854
17855 /*
17856 * Only dump the statement if it's a storage type we recognize
17857 */
17858 if (storage != NULL)
17859 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17860 foreign, qualrelname,
17861 fmtId(tbinfo->attnames[j]),
17862 storage);
17863 }
17864
17865 /*
17866 * Dump per-column compression, if it's been set.
17867 */
17868 if (!dopt->no_toast_compression)
17869 {
17870 const char *cmname;
17871
17872 switch (tbinfo->attcompression[j])
17873 {
17874 case 'p':
17875 cmname = "pglz";
17876 break;
17877 case 'l':
17878 cmname = "lz4";
17879 break;
17880 default:
17881 cmname = NULL;
17882 break;
17883 }
17884
17885 if (cmname != NULL)
17886 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17887 foreign, qualrelname,
17888 fmtId(tbinfo->attnames[j]),
17889 cmname);
17890 }
17891
17892 /*
17893 * Dump per-column attributes.
17894 */
17895 if (tbinfo->attoptions[j][0] != '\0')
17896 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17897 foreign, qualrelname,
17898 fmtId(tbinfo->attnames[j]),
17899 tbinfo->attoptions[j]);
17900
17901 /*
17902 * Dump per-column fdw options.
17903 */
17904 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17905 tbinfo->attfdwoptions[j][0] != '\0')
17907 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17908 " %s\n"
17909 ");\n",
17910 qualrelname,
17911 fmtId(tbinfo->attnames[j]),
17912 tbinfo->attfdwoptions[j]);
17913 } /* end loop over columns */
17914
17915 free(partkeydef);
17916 free(ftoptions);
17917 free(srvname);
17918 }
17919
17920 /*
17921 * dump properties we only have ALTER TABLE syntax for
17922 */
17923 if ((tbinfo->relkind == RELKIND_RELATION ||
17924 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17925 tbinfo->relkind == RELKIND_MATVIEW) &&
17926 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17927 {
17928 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17929 {
17930 /* nothing to do, will be set when the index is dumped */
17931 }
17932 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17933 {
17934 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17935 qualrelname);
17936 }
17937 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17938 {
17939 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17940 qualrelname);
17941 }
17942 }
17943
17944 if (tbinfo->forcerowsec)
17945 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17946 qualrelname);
17947
17948 if (dopt->binary_upgrade)
17950 reltypename, qrelname,
17951 tbinfo->dobj.namespace->dobj.name);
17952
17953 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17954 {
17955 char *tablespace = NULL;
17956 char *tableam = NULL;
17957
17958 /*
17959 * _selectTablespace() relies on tablespace-enabled objects in the
17960 * default tablespace to have a tablespace of "" (empty string) versus
17961 * non-tablespace-enabled objects to have a tablespace of NULL.
17962 * getTables() sets tbinfo->reltablespace to "" for the default
17963 * tablespace (not NULL).
17964 */
17965 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17966 tablespace = tbinfo->reltablespace;
17967
17968 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17969 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17970 tableam = tbinfo->amname;
17971
17972 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17973 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17974 .namespace = tbinfo->dobj.namespace->dobj.name,
17975 .tablespace = tablespace,
17976 .tableam = tableam,
17977 .relkind = tbinfo->relkind,
17978 .owner = tbinfo->rolname,
17979 .description = reltypename,
17980 .section = tbinfo->postponed_def ?
17982 .createStmt = q->data,
17983 .dropStmt = delq->data));
17984 }
17985
17986 /* Dump Table Comments */
17987 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17988 dumpTableComment(fout, tbinfo, reltypename);
17989
17990 /* Dump Table Security Labels */
17991 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17992 dumpTableSecLabel(fout, tbinfo, reltypename);
17993
17994 /*
17995 * Dump comments for not-null constraints that aren't to be dumped
17996 * separately (those are processed by collectComments/dumpComment).
17997 */
17998 if (!fout->dopt->no_comments && dopt->dumpSchema &&
17999 fout->remoteVersion >= 180000)
18000 {
18001 PQExpBuffer comment = NULL;
18002 PQExpBuffer tag = NULL;
18003
18004 for (j = 0; j < tbinfo->numatts; j++)
18005 {
18006 if (tbinfo->notnull_constrs[j] != NULL &&
18007 tbinfo->notnull_comment[j] != NULL)
18008 {
18009 if (comment == NULL)
18010 {
18012 tag = createPQExpBuffer();
18013 }
18014 else
18015 {
18017 resetPQExpBuffer(tag);
18018 }
18019
18020 appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18021 fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18024
18025 appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18026 fmtId(tbinfo->notnull_constrs[j]), qrelname);
18027
18029 ARCHIVE_OPTS(.tag = tag->data,
18030 .namespace = tbinfo->dobj.namespace->dobj.name,
18031 .owner = tbinfo->rolname,
18032 .description = "COMMENT",
18033 .section = SECTION_NONE,
18034 .createStmt = comment->data,
18035 .deps = &(tbinfo->dobj.dumpId),
18036 .nDeps = 1));
18037 }
18038 }
18039
18041 destroyPQExpBuffer(tag);
18042 }
18043
18044 /* Dump comments on inlined table constraints */
18045 for (j = 0; j < tbinfo->ncheck; j++)
18046 {
18047 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18048
18049 if (constr->separate || !constr->conislocal)
18050 continue;
18051
18052 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18053 dumpTableConstraintComment(fout, constr);
18054 }
18055
18057 destroyPQExpBuffer(delq);
18058 destroyPQExpBuffer(extra);
18059 free(qrelname);
18060 free(qualrelname);
18061}
18062
18063/*
18064 * dumpTableAttach
18065 * write to fout the commands to attach a child partition
18066 *
18067 * Child partitions are always made by creating them separately
18068 * and then using ATTACH PARTITION, rather than using
18069 * CREATE TABLE ... PARTITION OF. This is important for preserving
18070 * any possible discrepancy in column layout, to allow assigning the
18071 * correct tablespace if different, and so that it's possible to restore
18072 * a partition without restoring its parent. (You'll get an error from
18073 * the ATTACH PARTITION command, but that can be ignored, or skipped
18074 * using "pg_restore -L" if you prefer.) The last point motivates
18075 * treating ATTACH PARTITION as a completely separate ArchiveEntry
18076 * rather than emitting it within the child partition's ArchiveEntry.
18077 */
18078static void
18079dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18080{
18081 DumpOptions *dopt = fout->dopt;
18082 PQExpBuffer q;
18083 PGresult *res;
18084 char *partbound;
18085
18086 /* Do nothing if not dumping schema */
18087 if (!dopt->dumpSchema)
18088 return;
18089
18090 q = createPQExpBuffer();
18091
18093 {
18094 /* Set up query for partbound details */
18096 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18097
18099 "SELECT pg_get_expr(c.relpartbound, c.oid) "
18100 "FROM pg_class c "
18101 "WHERE c.oid = $1");
18102
18103 ExecuteSqlStatement(fout, q->data);
18104
18106 }
18107
18109 "EXECUTE dumpTableAttach('%u')",
18110 attachinfo->partitionTbl->dobj.catId.oid);
18111
18112 res = ExecuteSqlQueryForSingleRow(fout, q->data);
18113 partbound = PQgetvalue(res, 0, 0);
18114
18115 /* Perform ALTER TABLE on the parent */
18117 "ALTER TABLE ONLY %s ",
18118 fmtQualifiedDumpable(attachinfo->parentTbl));
18120 "ATTACH PARTITION %s %s;\n",
18122 partbound);
18123
18124 /*
18125 * There is no point in creating a drop query as the drop is done by table
18126 * drop. (If you think to change this, see also _printTocEntry().)
18127 * Although this object doesn't really have ownership as such, set the
18128 * owner field anyway to ensure that the command is run by the correct
18129 * role at restore time.
18130 */
18131 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18132 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18133 .namespace = attachinfo->dobj.namespace->dobj.name,
18134 .owner = attachinfo->partitionTbl->rolname,
18135 .description = "TABLE ATTACH",
18136 .section = SECTION_PRE_DATA,
18137 .createStmt = q->data));
18138
18139 PQclear(res);
18141}
18142
18143/*
18144 * dumpAttrDef --- dump an attribute's default-value declaration
18145 */
18146static void
18147dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18148{
18149 DumpOptions *dopt = fout->dopt;
18150 TableInfo *tbinfo = adinfo->adtable;
18151 int adnum = adinfo->adnum;
18152 PQExpBuffer q;
18153 PQExpBuffer delq;
18154 char *qualrelname;
18155 char *tag;
18156 char *foreign;
18157
18158 /* Do nothing if not dumping schema */
18159 if (!dopt->dumpSchema)
18160 return;
18161
18162 /* Skip if not "separate"; it was dumped in the table's definition */
18163 if (!adinfo->separate)
18164 return;
18165
18166 q = createPQExpBuffer();
18167 delq = createPQExpBuffer();
18168
18169 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18170
18171 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18172
18174 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18175 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18176 adinfo->adef_expr);
18177
18178 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18179 foreign, qualrelname,
18180 fmtId(tbinfo->attnames[adnum - 1]));
18181
18182 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18183
18184 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18185 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18186 ARCHIVE_OPTS(.tag = tag,
18187 .namespace = tbinfo->dobj.namespace->dobj.name,
18188 .owner = tbinfo->rolname,
18189 .description = "DEFAULT",
18190 .section = SECTION_PRE_DATA,
18191 .createStmt = q->data,
18192 .dropStmt = delq->data));
18193
18194 free(tag);
18196 destroyPQExpBuffer(delq);
18197 free(qualrelname);
18198}
18199
18200/*
18201 * getAttrName: extract the correct name for an attribute
18202 *
18203 * The array tblInfo->attnames[] only provides names of user attributes;
18204 * if a system attribute number is supplied, we have to fake it.
18205 * We also do a little bit of bounds checking for safety's sake.
18206 */
18207static const char *
18208getAttrName(int attrnum, const TableInfo *tblInfo)
18209{
18210 if (attrnum > 0 && attrnum <= tblInfo->numatts)
18211 return tblInfo->attnames[attrnum - 1];
18212 switch (attrnum)
18213 {
18215 return "ctid";
18217 return "xmin";
18219 return "cmin";
18221 return "xmax";
18223 return "cmax";
18225 return "tableoid";
18226 }
18227 pg_fatal("invalid column number %d for table \"%s\"",
18228 attrnum, tblInfo->dobj.name);
18229 return NULL; /* keep compiler quiet */
18230}
18231
18232/*
18233 * dumpIndex
18234 * write out to fout a user-defined index
18235 */
18236static void
18237dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18238{
18239 DumpOptions *dopt = fout->dopt;
18240 TableInfo *tbinfo = indxinfo->indextable;
18241 bool is_constraint = (indxinfo->indexconstraint != 0);
18242 PQExpBuffer q;
18243 PQExpBuffer delq;
18244 char *qindxname;
18245 char *qqindxname;
18246
18247 /* Do nothing if not dumping schema */
18248 if (!dopt->dumpSchema)
18249 return;
18250
18251 q = createPQExpBuffer();
18252 delq = createPQExpBuffer();
18253
18254 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18255 qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18256
18257 /*
18258 * If there's an associated constraint, don't dump the index per se, but
18259 * do dump any comment for it. (This is safe because dependency ordering
18260 * will have ensured the constraint is emitted first.) Note that the
18261 * emitted comment has to be shown as depending on the constraint, not the
18262 * index, in such cases.
18263 */
18264 if (!is_constraint)
18265 {
18266 char *indstatcols = indxinfo->indstatcols;
18267 char *indstatvals = indxinfo->indstatvals;
18268 char **indstatcolsarray = NULL;
18269 char **indstatvalsarray = NULL;
18270 int nstatcols = 0;
18271 int nstatvals = 0;
18272
18273 if (dopt->binary_upgrade)
18275 indxinfo->dobj.catId.oid);
18276
18277 /* Plain secondary index */
18278 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18279
18280 /*
18281 * Append ALTER TABLE commands as needed to set properties that we
18282 * only have ALTER TABLE syntax for. Keep this in sync with the
18283 * similar code in dumpConstraint!
18284 */
18285
18286 /* If the index is clustered, we need to record that. */
18287 if (indxinfo->indisclustered)
18288 {
18289 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18290 fmtQualifiedDumpable(tbinfo));
18291 /* index name is not qualified in this syntax */
18292 appendPQExpBuffer(q, " ON %s;\n",
18293 qindxname);
18294 }
18295
18296 /*
18297 * If the index has any statistics on some of its columns, generate
18298 * the associated ALTER INDEX queries.
18299 */
18300 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18301 {
18302 int j;
18303
18304 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18305 pg_fatal("could not parse index statistic columns");
18306 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18307 pg_fatal("could not parse index statistic values");
18308 if (nstatcols != nstatvals)
18309 pg_fatal("mismatched number of columns and values for index statistics");
18310
18311 for (j = 0; j < nstatcols; j++)
18312 {
18313 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18314
18315 /*
18316 * Note that this is a column number, so no quotes should be
18317 * used.
18318 */
18319 appendPQExpBuffer(q, "ALTER COLUMN %s ",
18320 indstatcolsarray[j]);
18321 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18322 indstatvalsarray[j]);
18323 }
18324 }
18325
18326 /* Indexes can depend on extensions */
18327 append_depends_on_extension(fout, q, &indxinfo->dobj,
18328 "pg_catalog.pg_class",
18329 "INDEX", qqindxname);
18330
18331 /* If the index defines identity, we need to record that. */
18332 if (indxinfo->indisreplident)
18333 {
18334 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18335 fmtQualifiedDumpable(tbinfo));
18336 /* index name is not qualified in this syntax */
18337 appendPQExpBuffer(q, " INDEX %s;\n",
18338 qindxname);
18339 }
18340
18341 /*
18342 * If this index is a member of a partitioned index, the backend will
18343 * not allow us to drop it separately, so don't try. It will go away
18344 * automatically when we drop either the index's table or the
18345 * partitioned index. (If, in a selective restore with --clean, we
18346 * drop neither of those, then this index will not be dropped either.
18347 * But that's fine, and even if you think it's not, the backend won't
18348 * let us do differently.)
18349 */
18350 if (indxinfo->parentidx == 0)
18351 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18352
18353 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18354 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18355 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18356 .namespace = tbinfo->dobj.namespace->dobj.name,
18357 .tablespace = indxinfo->tablespace,
18358 .owner = tbinfo->rolname,
18359 .description = "INDEX",
18360 .section = SECTION_POST_DATA,
18361 .createStmt = q->data,
18362 .dropStmt = delq->data));
18363
18364 free(indstatcolsarray);
18365 free(indstatvalsarray);
18366 }
18367
18368 /* Dump Index Comments */
18369 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18370 dumpComment(fout, "INDEX", qindxname,
18371 tbinfo->dobj.namespace->dobj.name,
18372 tbinfo->rolname,
18373 indxinfo->dobj.catId, 0,
18374 is_constraint ? indxinfo->indexconstraint :
18375 indxinfo->dobj.dumpId);
18376
18378 destroyPQExpBuffer(delq);
18379 free(qindxname);
18380 free(qqindxname);
18381}
18382
18383/*
18384 * dumpIndexAttach
18385 * write out to fout a partitioned-index attachment clause
18386 */
18387static void
18388dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18389{
18390 /* Do nothing if not dumping schema */
18391 if (!fout->dopt->dumpSchema)
18392 return;
18393
18395 {
18397
18398 appendPQExpBuffer(q, "ALTER INDEX %s ",
18399 fmtQualifiedDumpable(attachinfo->parentIdx));
18400 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18401 fmtQualifiedDumpable(attachinfo->partitionIdx));
18402
18403 /*
18404 * There is no need for a dropStmt since the drop is done implicitly
18405 * when we drop either the index's table or the partitioned index.
18406 * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18407 * there's no way to do it anyway. (If you think to change this,
18408 * consider also what to do with --if-exists.)
18409 *
18410 * Although this object doesn't really have ownership as such, set the
18411 * owner field anyway to ensure that the command is run by the correct
18412 * role at restore time.
18413 */
18414 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18415 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18416 .namespace = attachinfo->dobj.namespace->dobj.name,
18417 .owner = attachinfo->parentIdx->indextable->rolname,
18418 .description = "INDEX ATTACH",
18419 .section = SECTION_POST_DATA,
18420 .createStmt = q->data));
18421
18423 }
18424}
18425
18426/*
18427 * dumpStatisticsExt
18428 * write out to fout an extended statistics object
18429 */
18430static void
18431dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18432{
18433 DumpOptions *dopt = fout->dopt;
18434 PQExpBuffer q;
18435 PQExpBuffer delq;
18436 PQExpBuffer query;
18437 char *qstatsextname;
18438 PGresult *res;
18439 char *stxdef;
18440
18441 /* Do nothing if not dumping schema */
18442 if (!dopt->dumpSchema)
18443 return;
18444
18445 q = createPQExpBuffer();
18446 delq = createPQExpBuffer();
18447 query = createPQExpBuffer();
18448
18449 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18450
18451 appendPQExpBuffer(query, "SELECT "
18452 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18453 statsextinfo->dobj.catId.oid);
18454
18455 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18456
18457 stxdef = PQgetvalue(res, 0, 0);
18458
18459 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18460 appendPQExpBuffer(q, "%s;\n", stxdef);
18461
18462 /*
18463 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18464 * for this statistics object is not the default value.
18465 */
18466 if (statsextinfo->stattarget >= 0)
18467 {
18468 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18469 fmtQualifiedDumpable(statsextinfo));
18470 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18471 statsextinfo->stattarget);
18472 }
18473
18474 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18475 fmtQualifiedDumpable(statsextinfo));
18476
18477 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18478 ArchiveEntry(fout, statsextinfo->dobj.catId,
18479 statsextinfo->dobj.dumpId,
18480 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18481 .namespace = statsextinfo->dobj.namespace->dobj.name,
18482 .owner = statsextinfo->rolname,
18483 .description = "STATISTICS",
18484 .section = SECTION_POST_DATA,
18485 .createStmt = q->data,
18486 .dropStmt = delq->data));
18487
18488 /* Dump Statistics Comments */
18489 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18490 dumpComment(fout, "STATISTICS", qstatsextname,
18491 statsextinfo->dobj.namespace->dobj.name,
18492 statsextinfo->rolname,
18493 statsextinfo->dobj.catId, 0,
18494 statsextinfo->dobj.dumpId);
18495
18496 PQclear(res);
18498 destroyPQExpBuffer(delq);
18499 destroyPQExpBuffer(query);
18500 free(qstatsextname);
18501}
18502
18503/*
18504 * dumpConstraint
18505 * write out to fout a user-defined constraint
18506 */
18507static void
18509{
18510 DumpOptions *dopt = fout->dopt;
18511 TableInfo *tbinfo = coninfo->contable;
18512 PQExpBuffer q;
18513 PQExpBuffer delq;
18514 char *tag = NULL;
18515 char *foreign;
18516
18517 /* Do nothing if not dumping schema */
18518 if (!dopt->dumpSchema)
18519 return;
18520
18521 q = createPQExpBuffer();
18522 delq = createPQExpBuffer();
18523
18524 foreign = tbinfo &&
18525 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18526
18527 if (coninfo->contype == 'p' ||
18528 coninfo->contype == 'u' ||
18529 coninfo->contype == 'x')
18530 {
18531 /* Index-related constraint */
18532 IndxInfo *indxinfo;
18533 int k;
18534
18535 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18536
18537 if (indxinfo == NULL)
18538 pg_fatal("missing index for constraint \"%s\"",
18539 coninfo->dobj.name);
18540
18541 if (dopt->binary_upgrade)
18543 indxinfo->dobj.catId.oid);
18544
18545 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18546 fmtQualifiedDumpable(tbinfo));
18547 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18548 fmtId(coninfo->dobj.name));
18549
18550 if (coninfo->condef)
18551 {
18552 /* pg_get_constraintdef should have provided everything */
18553 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18554 }
18555 else
18556 {
18558 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18559
18560 /*
18561 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18562 * indexes. Being able to create this was fixed, but we need to
18563 * make the index distinct in order to be able to restore the
18564 * dump.
18565 */
18566 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18567 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18568 appendPQExpBufferStr(q, " (");
18569 for (k = 0; k < indxinfo->indnkeyattrs; k++)
18570 {
18571 int indkey = (int) indxinfo->indkeys[k];
18572 const char *attname;
18573
18574 if (indkey == InvalidAttrNumber)
18575 break;
18576 attname = getAttrName(indkey, tbinfo);
18577
18578 appendPQExpBuffer(q, "%s%s",
18579 (k == 0) ? "" : ", ",
18580 fmtId(attname));
18581 }
18582 if (coninfo->conperiod)
18583 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18584
18585 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18586 appendPQExpBufferStr(q, ") INCLUDE (");
18587
18588 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18589 {
18590 int indkey = (int) indxinfo->indkeys[k];
18591 const char *attname;
18592
18593 if (indkey == InvalidAttrNumber)
18594 break;
18595 attname = getAttrName(indkey, tbinfo);
18596
18597 appendPQExpBuffer(q, "%s%s",
18598 (k == indxinfo->indnkeyattrs) ? "" : ", ",
18599 fmtId(attname));
18600 }
18601
18602 appendPQExpBufferChar(q, ')');
18603
18604 if (nonemptyReloptions(indxinfo->indreloptions))
18605 {
18606 appendPQExpBufferStr(q, " WITH (");
18607 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18608 appendPQExpBufferChar(q, ')');
18609 }
18610
18611 if (coninfo->condeferrable)
18612 {
18613 appendPQExpBufferStr(q, " DEFERRABLE");
18614 if (coninfo->condeferred)
18615 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18616 }
18617
18618 appendPQExpBufferStr(q, ";\n");
18619 }
18620
18621 /*
18622 * Append ALTER TABLE commands as needed to set properties that we
18623 * only have ALTER TABLE syntax for. Keep this in sync with the
18624 * similar code in dumpIndex!
18625 */
18626
18627 /* If the index is clustered, we need to record that. */
18628 if (indxinfo->indisclustered)
18629 {
18630 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18631 fmtQualifiedDumpable(tbinfo));
18632 /* index name is not qualified in this syntax */
18633 appendPQExpBuffer(q, " ON %s;\n",
18634 fmtId(indxinfo->dobj.name));
18635 }
18636
18637 /* If the index defines identity, we need to record that. */
18638 if (indxinfo->indisreplident)
18639 {
18640 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18641 fmtQualifiedDumpable(tbinfo));
18642 /* index name is not qualified in this syntax */
18643 appendPQExpBuffer(q, " INDEX %s;\n",
18644 fmtId(indxinfo->dobj.name));
18645 }
18646
18647 /* Indexes can depend on extensions */
18648 append_depends_on_extension(fout, q, &indxinfo->dobj,
18649 "pg_catalog.pg_class", "INDEX",
18650 fmtQualifiedDumpable(indxinfo));
18651
18652 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18653 fmtQualifiedDumpable(tbinfo));
18654 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18655 fmtId(coninfo->dobj.name));
18656
18657 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18658
18659 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18660 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18661 ARCHIVE_OPTS(.tag = tag,
18662 .namespace = tbinfo->dobj.namespace->dobj.name,
18663 .tablespace = indxinfo->tablespace,
18664 .owner = tbinfo->rolname,
18665 .description = "CONSTRAINT",
18666 .section = SECTION_POST_DATA,
18667 .createStmt = q->data,
18668 .dropStmt = delq->data));
18669 }
18670 else if (coninfo->contype == 'f')
18671 {
18672 char *only;
18673
18674 /*
18675 * Foreign keys on partitioned tables are always declared as
18676 * inheriting to partitions; for all other cases, emit them as
18677 * applying ONLY directly to the named table, because that's how they
18678 * work for regular inherited tables.
18679 */
18680 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18681
18682 /*
18683 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18684 * current table data is not processed
18685 */
18686 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18687 only, fmtQualifiedDumpable(tbinfo));
18688 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18689 fmtId(coninfo->dobj.name),
18690 coninfo->condef);
18691
18692 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18693 only, fmtQualifiedDumpable(tbinfo));
18694 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18695 fmtId(coninfo->dobj.name));
18696
18697 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18698
18699 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18700 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18701 ARCHIVE_OPTS(.tag = tag,
18702 .namespace = tbinfo->dobj.namespace->dobj.name,
18703 .owner = tbinfo->rolname,
18704 .description = "FK CONSTRAINT",
18705 .section = SECTION_POST_DATA,
18706 .createStmt = q->data,
18707 .dropStmt = delq->data));
18708 }
18709 else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18710 {
18711 /* CHECK or invalid not-null constraint on a table */
18712
18713 /* Ignore if not to be dumped separately, or if it was inherited */
18714 if (coninfo->separate && coninfo->conislocal)
18715 {
18716 const char *keyword;
18717
18718 if (coninfo->contype == 'c')
18719 keyword = "CHECK CONSTRAINT";
18720 else
18721 keyword = "CONSTRAINT";
18722
18723 /* not ONLY since we want it to propagate to children */
18724 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18725 fmtQualifiedDumpable(tbinfo));
18726 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18727 fmtId(coninfo->dobj.name),
18728 coninfo->condef);
18729
18730 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18731 fmtQualifiedDumpable(tbinfo));
18732 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18733 fmtId(coninfo->dobj.name));
18734
18735 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18736
18737 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18738 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18739 ARCHIVE_OPTS(.tag = tag,
18740 .namespace = tbinfo->dobj.namespace->dobj.name,
18741 .owner = tbinfo->rolname,
18742 .description = keyword,
18743 .section = SECTION_POST_DATA,
18744 .createStmt = q->data,
18745 .dropStmt = delq->data));
18746 }
18747 }
18748 else if (tbinfo == NULL)
18749 {
18750 /* CHECK, NOT NULL constraint on a domain */
18751 TypeInfo *tyinfo = coninfo->condomain;
18752
18753 Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18754
18755 /* Ignore if not to be dumped separately */
18756 if (coninfo->separate)
18757 {
18758 const char *keyword;
18759
18760 if (coninfo->contype == 'c')
18761 keyword = "CHECK CONSTRAINT";
18762 else
18763 keyword = "CONSTRAINT";
18764
18765 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18766 fmtQualifiedDumpable(tyinfo));
18767 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18768 fmtId(coninfo->dobj.name),
18769 coninfo->condef);
18770
18771 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18772 fmtQualifiedDumpable(tyinfo));
18773 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18774 fmtId(coninfo->dobj.name));
18775
18776 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18777
18778 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18779 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18780 ARCHIVE_OPTS(.tag = tag,
18781 .namespace = tyinfo->dobj.namespace->dobj.name,
18782 .owner = tyinfo->rolname,
18783 .description = keyword,
18784 .section = SECTION_POST_DATA,
18785 .createStmt = q->data,
18786 .dropStmt = delq->data));
18787
18788 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18789 {
18790 PQExpBuffer conprefix = createPQExpBuffer();
18791 char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18792
18793 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18794 fmtId(coninfo->dobj.name));
18795
18796 dumpComment(fout, conprefix->data, qtypname,
18797 tyinfo->dobj.namespace->dobj.name,
18798 tyinfo->rolname,
18799 coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18800 destroyPQExpBuffer(conprefix);
18801 free(qtypname);
18802 }
18803 }
18804 }
18805 else
18806 {
18807 pg_fatal("unrecognized constraint type: %c",
18808 coninfo->contype);
18809 }
18810
18811 /* Dump Constraint Comments --- only works for table constraints */
18812 if (tbinfo && coninfo->separate &&
18813 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18814 dumpTableConstraintComment(fout, coninfo);
18815
18816 free(tag);
18818 destroyPQExpBuffer(delq);
18819}
18820
18821/*
18822 * dumpTableConstraintComment --- dump a constraint's comment if any
18823 *
18824 * This is split out because we need the function in two different places
18825 * depending on whether the constraint is dumped as part of CREATE TABLE
18826 * or as a separate ALTER command.
18827 */
18828static void
18830{
18831 TableInfo *tbinfo = coninfo->contable;
18832 PQExpBuffer conprefix = createPQExpBuffer();
18833 char *qtabname;
18834
18835 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18836
18837 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18838 fmtId(coninfo->dobj.name));
18839
18840 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18841 dumpComment(fout, conprefix->data, qtabname,
18842 tbinfo->dobj.namespace->dobj.name,
18843 tbinfo->rolname,
18844 coninfo->dobj.catId, 0,
18845 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18846
18847 destroyPQExpBuffer(conprefix);
18848 free(qtabname);
18849}
18850
18851static inline SeqType
18853{
18854 for (int i = 0; i < lengthof(SeqTypeNames); i++)
18855 {
18856 if (strcmp(SeqTypeNames[i], name) == 0)
18857 return (SeqType) i;
18858 }
18859
18860 pg_fatal("unrecognized sequence type: %s", name);
18861 return (SeqType) 0; /* keep compiler quiet */
18862}
18863
18864/*
18865 * bsearch() comparator for SequenceItem
18866 */
18867static int
18868SequenceItemCmp(const void *p1, const void *p2)
18869{
18870 SequenceItem v1 = *((const SequenceItem *) p1);
18871 SequenceItem v2 = *((const SequenceItem *) p2);
18872
18873 return pg_cmp_u32(v1.oid, v2.oid);
18874}
18875
18876/*
18877 * collectSequences
18878 *
18879 * Construct a table of sequence information. This table is sorted by OID for
18880 * speed in lookup.
18881 */
18882static void
18884{
18885 PGresult *res;
18886 const char *query;
18887
18888 /*
18889 * Before Postgres 10, sequence metadata is in the sequence itself. With
18890 * some extra effort, we might be able to use the sorted table for those
18891 * versions, but for now it seems unlikely to be worth it.
18892 *
18893 * Since version 18, we can gather the sequence data in this query with
18894 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18895 */
18896 if (fout->remoteVersion < 100000)
18897 return;
18898 else if (fout->remoteVersion < 180000 ||
18899 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18900 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18901 "seqstart, seqincrement, "
18902 "seqmax, seqmin, "
18903 "seqcache, seqcycle, "
18904 "NULL, 'f' "
18905 "FROM pg_catalog.pg_sequence "
18906 "ORDER BY seqrelid";
18907 else
18908 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18909 "seqstart, seqincrement, "
18910 "seqmax, seqmin, "
18911 "seqcache, seqcycle, "
18912 "last_value, is_called "
18913 "FROM pg_catalog.pg_sequence, "
18914 "pg_get_sequence_data(seqrelid) "
18915 "ORDER BY seqrelid;";
18916
18917 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18918
18919 nsequences = PQntuples(res);
18921
18922 for (int i = 0; i < nsequences; i++)
18923 {
18924 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18926 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18927 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18928 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18929 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18930 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18931 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18932 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18933 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18934 }
18935
18936 PQclear(res);
18937}
18938
18939/*
18940 * dumpSequence
18941 * write the declaration (not data) of one user-defined sequence
18942 */
18943static void
18944dumpSequence(Archive *fout, const TableInfo *tbinfo)
18945{
18946 DumpOptions *dopt = fout->dopt;
18947 SequenceItem *seq;
18948 bool is_ascending;
18949 int64 default_minv,
18950 default_maxv;
18952 PQExpBuffer delqry = createPQExpBuffer();
18953 char *qseqname;
18954 TableInfo *owning_tab = NULL;
18955
18956 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18957
18958 /*
18959 * For versions >= 10, the sequence information is gathered in a sorted
18960 * table before any calls to dumpSequence(). See collectSequences() for
18961 * more information.
18962 */
18963 if (fout->remoteVersion >= 100000)
18964 {
18965 SequenceItem key = {0};
18966
18968
18969 key.oid = tbinfo->dobj.catId.oid;
18970 seq = bsearch(&key, sequences, nsequences,
18971 sizeof(SequenceItem), SequenceItemCmp);
18972 }
18973 else
18974 {
18975 PGresult *res;
18976
18977 /*
18978 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18979 *
18980 * Note: it might seem that 'bigint' potentially needs to be
18981 * schema-qualified, but actually that's a keyword.
18982 */
18983 appendPQExpBuffer(query,
18984 "SELECT 'bigint' AS sequence_type, "
18985 "start_value, increment_by, max_value, min_value, "
18986 "cache_value, is_cycled FROM %s",
18987 fmtQualifiedDumpable(tbinfo));
18988
18989 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18990
18991 if (PQntuples(res) != 1)
18992 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18993 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18994 PQntuples(res)),
18995 tbinfo->dobj.name, PQntuples(res));
18996
18997 seq = pg_malloc0(sizeof(SequenceItem));
18998 seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18999 seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19000 seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19001 seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19002 seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19003 seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19004 seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19005
19006 PQclear(res);
19007 }
19008
19009 /* Calculate default limits for a sequence of this type */
19010 is_ascending = (seq->incby >= 0);
19011 if (seq->seqtype == SEQTYPE_SMALLINT)
19012 {
19013 default_minv = is_ascending ? 1 : PG_INT16_MIN;
19014 default_maxv = is_ascending ? PG_INT16_MAX : -1;
19015 }
19016 else if (seq->seqtype == SEQTYPE_INTEGER)
19017 {
19018 default_minv = is_ascending ? 1 : PG_INT32_MIN;
19019 default_maxv = is_ascending ? PG_INT32_MAX : -1;
19020 }
19021 else if (seq->seqtype == SEQTYPE_BIGINT)
19022 {
19023 default_minv = is_ascending ? 1 : PG_INT64_MIN;
19024 default_maxv = is_ascending ? PG_INT64_MAX : -1;
19025 }
19026 else
19027 {
19028 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19029 default_minv = default_maxv = 0; /* keep compiler quiet */
19030 }
19031
19032 /*
19033 * Identity sequences are not to be dropped separately.
19034 */
19035 if (!tbinfo->is_identity_sequence)
19036 {
19037 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19038 fmtQualifiedDumpable(tbinfo));
19039 }
19040
19041 resetPQExpBuffer(query);
19042
19043 if (dopt->binary_upgrade)
19044 {
19046 tbinfo->dobj.catId.oid);
19047
19048 /*
19049 * In older PG versions a sequence will have a pg_type entry, but v14
19050 * and up don't use that, so don't attempt to preserve the type OID.
19051 */
19052 }
19053
19054 if (tbinfo->is_identity_sequence)
19055 {
19056 owning_tab = findTableByOid(tbinfo->owning_tab);
19057
19058 appendPQExpBuffer(query,
19059 "ALTER TABLE %s ",
19060 fmtQualifiedDumpable(owning_tab));
19061 appendPQExpBuffer(query,
19062 "ALTER COLUMN %s ADD GENERATED ",
19063 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19064 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19065 appendPQExpBufferStr(query, "ALWAYS");
19066 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19067 appendPQExpBufferStr(query, "BY DEFAULT");
19068 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19069 fmtQualifiedDumpable(tbinfo));
19070
19071 /*
19072 * Emit persistence option only if it's different from the owning
19073 * table's. This avoids using this new syntax unnecessarily.
19074 */
19075 if (tbinfo->relpersistence != owning_tab->relpersistence)
19076 appendPQExpBuffer(query, " %s\n",
19077 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19078 "UNLOGGED" : "LOGGED");
19079 }
19080 else
19081 {
19082 appendPQExpBuffer(query,
19083 "CREATE %sSEQUENCE %s\n",
19084 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19085 "UNLOGGED " : "",
19086 fmtQualifiedDumpable(tbinfo));
19087
19088 if (seq->seqtype != SEQTYPE_BIGINT)
19089 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19090 }
19091
19092 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19093
19094 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19095
19096 if (seq->minv != default_minv)
19097 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19098 else
19099 appendPQExpBufferStr(query, " NO MINVALUE\n");
19100
19101 if (seq->maxv != default_maxv)
19102 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19103 else
19104 appendPQExpBufferStr(query, " NO MAXVALUE\n");
19105
19106 appendPQExpBuffer(query,
19107 " CACHE " INT64_FORMAT "%s",
19108 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19109
19110 if (tbinfo->is_identity_sequence)
19111 appendPQExpBufferStr(query, "\n);\n");
19112 else
19113 appendPQExpBufferStr(query, ";\n");
19114
19115 /* binary_upgrade: no need to clear TOAST table oid */
19116
19117 if (dopt->binary_upgrade)
19118 binary_upgrade_extension_member(query, &tbinfo->dobj,
19119 "SEQUENCE", qseqname,
19120 tbinfo->dobj.namespace->dobj.name);
19121
19122 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19123 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19124 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19125 .namespace = tbinfo->dobj.namespace->dobj.name,
19126 .owner = tbinfo->rolname,
19127 .description = "SEQUENCE",
19128 .section = SECTION_PRE_DATA,
19129 .createStmt = query->data,
19130 .dropStmt = delqry->data));
19131
19132 /*
19133 * If the sequence is owned by a table column, emit the ALTER for it as a
19134 * separate TOC entry immediately following the sequence's own entry. It's
19135 * OK to do this rather than using full sorting logic, because the
19136 * dependency that tells us it's owned will have forced the table to be
19137 * created first. We can't just include the ALTER in the TOC entry
19138 * because it will fail if we haven't reassigned the sequence owner to
19139 * match the table's owner.
19140 *
19141 * We need not schema-qualify the table reference because both sequence
19142 * and table must be in the same schema.
19143 */
19144 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19145 {
19146 owning_tab = findTableByOid(tbinfo->owning_tab);
19147
19148 if (owning_tab == NULL)
19149 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19150 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19151
19152 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19153 {
19154 resetPQExpBuffer(query);
19155 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19156 fmtQualifiedDumpable(tbinfo));
19157 appendPQExpBuffer(query, " OWNED BY %s",
19158 fmtQualifiedDumpable(owning_tab));
19159 appendPQExpBuffer(query, ".%s;\n",
19160 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19161
19162 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19164 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19165 .namespace = tbinfo->dobj.namespace->dobj.name,
19166 .owner = tbinfo->rolname,
19167 .description = "SEQUENCE OWNED BY",
19168 .section = SECTION_PRE_DATA,
19169 .createStmt = query->data,
19170 .deps = &(tbinfo->dobj.dumpId),
19171 .nDeps = 1));
19172 }
19173 }
19174
19175 /* Dump Sequence Comments and Security Labels */
19176 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19177 dumpComment(fout, "SEQUENCE", qseqname,
19178 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19179 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19180
19181 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19182 dumpSecLabel(fout, "SEQUENCE", qseqname,
19183 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19184 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19185
19186 if (fout->remoteVersion < 100000)
19187 pg_free(seq);
19188 destroyPQExpBuffer(query);
19189 destroyPQExpBuffer(delqry);
19190 free(qseqname);
19191}
19192
19193/*
19194 * dumpSequenceData
19195 * write the data of one user-defined sequence
19196 */
19197static void
19199{
19200 TableInfo *tbinfo = tdinfo->tdtable;
19201 int64 last;
19202 bool called;
19204
19205 /*
19206 * For versions >= 18, the sequence information is gathered in the sorted
19207 * array before any calls to dumpSequenceData(). See collectSequences()
19208 * for more information.
19209 *
19210 * For older versions, we have to query the sequence relations
19211 * individually.
19212 */
19213 if (fout->remoteVersion < 180000)
19214 {
19215 PGresult *res;
19216
19217 appendPQExpBuffer(query,
19218 "SELECT last_value, is_called FROM %s",
19219 fmtQualifiedDumpable(tbinfo));
19220
19221 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19222
19223 if (PQntuples(res) != 1)
19224 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19225 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19226 PQntuples(res)),
19227 tbinfo->dobj.name, PQntuples(res));
19228
19229 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19230 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19231
19232 PQclear(res);
19233 }
19234 else
19235 {
19236 SequenceItem key = {0};
19237 SequenceItem *entry;
19238
19240 Assert(tbinfo->dobj.catId.oid);
19241
19242 key.oid = tbinfo->dobj.catId.oid;
19243 entry = bsearch(&key, sequences, nsequences,
19244 sizeof(SequenceItem), SequenceItemCmp);
19245
19246 last = entry->last_value;
19247 called = entry->is_called;
19248 }
19249
19250 resetPQExpBuffer(query);
19251 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19252 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19253 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19254 last, (called ? "true" : "false"));
19255
19256 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19258 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19259 .namespace = tbinfo->dobj.namespace->dobj.name,
19260 .owner = tbinfo->rolname,
19261 .description = "SEQUENCE SET",
19262 .section = SECTION_DATA,
19263 .createStmt = query->data,
19264 .deps = &(tbinfo->dobj.dumpId),
19265 .nDeps = 1));
19266
19267 destroyPQExpBuffer(query);
19268}
19269
19270/*
19271 * dumpTrigger
19272 * write the declaration of one user-defined table trigger
19273 */
19274static void
19275dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19276{
19277 DumpOptions *dopt = fout->dopt;
19278 TableInfo *tbinfo = tginfo->tgtable;
19279 PQExpBuffer query;
19280 PQExpBuffer delqry;
19281 PQExpBuffer trigprefix;
19282 PQExpBuffer trigidentity;
19283 char *qtabname;
19284 char *tag;
19285
19286 /* Do nothing if not dumping schema */
19287 if (!dopt->dumpSchema)
19288 return;
19289
19290 query = createPQExpBuffer();
19291 delqry = createPQExpBuffer();
19292 trigprefix = createPQExpBuffer();
19293 trigidentity = createPQExpBuffer();
19294
19295 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19296
19297 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19298 appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19299
19300 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19301 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19302
19303 /* Triggers can depend on extensions */
19304 append_depends_on_extension(fout, query, &tginfo->dobj,
19305 "pg_catalog.pg_trigger", "TRIGGER",
19306 trigidentity->data);
19307
19308 if (tginfo->tgispartition)
19309 {
19310 Assert(tbinfo->ispartition);
19311
19312 /*
19313 * Partition triggers only appear here because their 'tgenabled' flag
19314 * differs from its parent's. The trigger is created already, so
19315 * remove the CREATE and replace it with an ALTER. (Clear out the
19316 * DROP query too, so that pg_dump --create does not cause errors.)
19317 */
19318 resetPQExpBuffer(query);
19319 resetPQExpBuffer(delqry);
19320 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19321 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19322 fmtQualifiedDumpable(tbinfo));
19323 switch (tginfo->tgenabled)
19324 {
19325 case 'f':
19326 case 'D':
19327 appendPQExpBufferStr(query, "DISABLE");
19328 break;
19329 case 't':
19330 case 'O':
19331 appendPQExpBufferStr(query, "ENABLE");
19332 break;
19333 case 'R':
19334 appendPQExpBufferStr(query, "ENABLE REPLICA");
19335 break;
19336 case 'A':
19337 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19338 break;
19339 }
19340 appendPQExpBuffer(query, " TRIGGER %s;\n",
19341 fmtId(tginfo->dobj.name));
19342 }
19343 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19344 {
19345 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19346 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19347 fmtQualifiedDumpable(tbinfo));
19348 switch (tginfo->tgenabled)
19349 {
19350 case 'D':
19351 case 'f':
19352 appendPQExpBufferStr(query, "DISABLE");
19353 break;
19354 case 'A':
19355 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19356 break;
19357 case 'R':
19358 appendPQExpBufferStr(query, "ENABLE REPLICA");
19359 break;
19360 default:
19361 appendPQExpBufferStr(query, "ENABLE");
19362 break;
19363 }
19364 appendPQExpBuffer(query, " TRIGGER %s;\n",
19365 fmtId(tginfo->dobj.name));
19366 }
19367
19368 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19369 fmtId(tginfo->dobj.name));
19370
19371 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19372
19373 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19374 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19375 ARCHIVE_OPTS(.tag = tag,
19376 .namespace = tbinfo->dobj.namespace->dobj.name,
19377 .owner = tbinfo->rolname,
19378 .description = "TRIGGER",
19379 .section = SECTION_POST_DATA,
19380 .createStmt = query->data,
19381 .dropStmt = delqry->data));
19382
19383 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19384 dumpComment(fout, trigprefix->data, qtabname,
19385 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19386 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19387
19388 free(tag);
19389 destroyPQExpBuffer(query);
19390 destroyPQExpBuffer(delqry);
19391 destroyPQExpBuffer(trigprefix);
19392 destroyPQExpBuffer(trigidentity);
19393 free(qtabname);
19394}
19395
19396/*
19397 * dumpEventTrigger
19398 * write the declaration of one user-defined event trigger
19399 */
19400static void
19402{
19403 DumpOptions *dopt = fout->dopt;
19404 PQExpBuffer query;
19405 PQExpBuffer delqry;
19406 char *qevtname;
19407
19408 /* Do nothing if not dumping schema */
19409 if (!dopt->dumpSchema)
19410 return;
19411
19412 query = createPQExpBuffer();
19413 delqry = createPQExpBuffer();
19414
19415 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19416
19417 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19418 appendPQExpBufferStr(query, qevtname);
19419 appendPQExpBufferStr(query, " ON ");
19420 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19421
19422 if (strcmp("", evtinfo->evttags) != 0)
19423 {
19424 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19425 appendPQExpBufferStr(query, evtinfo->evttags);
19426 appendPQExpBufferChar(query, ')');
19427 }
19428
19429 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19430 appendPQExpBufferStr(query, evtinfo->evtfname);
19431 appendPQExpBufferStr(query, "();\n");
19432
19433 if (evtinfo->evtenabled != 'O')
19434 {
19435 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19436 qevtname);
19437 switch (evtinfo->evtenabled)
19438 {
19439 case 'D':
19440 appendPQExpBufferStr(query, "DISABLE");
19441 break;
19442 case 'A':
19443 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19444 break;
19445 case 'R':
19446 appendPQExpBufferStr(query, "ENABLE REPLICA");
19447 break;
19448 default:
19449 appendPQExpBufferStr(query, "ENABLE");
19450 break;
19451 }
19452 appendPQExpBufferStr(query, ";\n");
19453 }
19454
19455 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19456 qevtname);
19457
19458 if (dopt->binary_upgrade)
19459 binary_upgrade_extension_member(query, &evtinfo->dobj,
19460 "EVENT TRIGGER", qevtname, NULL);
19461
19462 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19463 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19464 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19465 .owner = evtinfo->evtowner,
19466 .description = "EVENT TRIGGER",
19467 .section = SECTION_POST_DATA,
19468 .createStmt = query->data,
19469 .dropStmt = delqry->data));
19470
19471 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19472 dumpComment(fout, "EVENT TRIGGER", qevtname,
19473 NULL, evtinfo->evtowner,
19474 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19475
19476 if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19477 dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19478 NULL, evtinfo->evtowner,
19479 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19480
19481 destroyPQExpBuffer(query);
19482 destroyPQExpBuffer(delqry);
19483 free(qevtname);
19484}
19485
19486/*
19487 * dumpRule
19488 * Dump a rule
19489 */
19490static void
19491dumpRule(Archive *fout, const RuleInfo *rinfo)
19492{
19493 DumpOptions *dopt = fout->dopt;
19494 TableInfo *tbinfo = rinfo->ruletable;
19495 bool is_view;
19496 PQExpBuffer query;
19497 PQExpBuffer cmd;
19498 PQExpBuffer delcmd;
19499 PQExpBuffer ruleprefix;
19500 char *qtabname;
19501 PGresult *res;
19502 char *tag;
19503
19504 /* Do nothing if not dumping schema */
19505 if (!dopt->dumpSchema)
19506 return;
19507
19508 /*
19509 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19510 * we do not want to dump it as a separate object.
19511 */
19512 if (!rinfo->separate)
19513 return;
19514
19515 /*
19516 * If it's an ON SELECT rule, we want to print it as a view definition,
19517 * instead of a rule.
19518 */
19519 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19520
19521 query = createPQExpBuffer();
19522 cmd = createPQExpBuffer();
19523 delcmd = createPQExpBuffer();
19524 ruleprefix = createPQExpBuffer();
19525
19526 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19527
19528 if (is_view)
19529 {
19530 PQExpBuffer result;
19531
19532 /*
19533 * We need OR REPLACE here because we'll be replacing a dummy view.
19534 * Otherwise this should look largely like the regular view dump code.
19535 */
19536 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19537 fmtQualifiedDumpable(tbinfo));
19538 if (nonemptyReloptions(tbinfo->reloptions))
19539 {
19540 appendPQExpBufferStr(cmd, " WITH (");
19541 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19542 appendPQExpBufferChar(cmd, ')');
19543 }
19544 result = createViewAsClause(fout, tbinfo);
19545 appendPQExpBuffer(cmd, " AS\n%s", result->data);
19546 destroyPQExpBuffer(result);
19547 if (tbinfo->checkoption != NULL)
19548 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19549 tbinfo->checkoption);
19550 appendPQExpBufferStr(cmd, ";\n");
19551 }
19552 else
19553 {
19554 /* In the rule case, just print pg_get_ruledef's result verbatim */
19555 appendPQExpBuffer(query,
19556 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19557 rinfo->dobj.catId.oid);
19558
19559 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19560
19561 if (PQntuples(res) != 1)
19562 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19563 rinfo->dobj.name, tbinfo->dobj.name);
19564
19565 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19566
19567 PQclear(res);
19568 }
19569
19570 /*
19571 * Add the command to alter the rules replication firing semantics if it
19572 * differs from the default.
19573 */
19574 if (rinfo->ev_enabled != 'O')
19575 {
19576 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19577 switch (rinfo->ev_enabled)
19578 {
19579 case 'A':
19580 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19581 fmtId(rinfo->dobj.name));
19582 break;
19583 case 'R':
19584 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19585 fmtId(rinfo->dobj.name));
19586 break;
19587 case 'D':
19588 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19589 fmtId(rinfo->dobj.name));
19590 break;
19591 }
19592 }
19593
19594 if (is_view)
19595 {
19596 /*
19597 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19598 * REPLACE VIEW to replace the rule with something with minimal
19599 * dependencies.
19600 */
19601 PQExpBuffer result;
19602
19603 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19604 fmtQualifiedDumpable(tbinfo));
19605 result = createDummyViewAsClause(fout, tbinfo);
19606 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19607 destroyPQExpBuffer(result);
19608 }
19609 else
19610 {
19611 appendPQExpBuffer(delcmd, "DROP RULE %s ",
19612 fmtId(rinfo->dobj.name));
19613 appendPQExpBuffer(delcmd, "ON %s;\n",
19614 fmtQualifiedDumpable(tbinfo));
19615 }
19616
19617 appendPQExpBuffer(ruleprefix, "RULE %s ON",
19618 fmtId(rinfo->dobj.name));
19619
19620 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19621
19622 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19623 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19624 ARCHIVE_OPTS(.tag = tag,
19625 .namespace = tbinfo->dobj.namespace->dobj.name,
19626 .owner = tbinfo->rolname,
19627 .description = "RULE",
19628 .section = SECTION_POST_DATA,
19629 .createStmt = cmd->data,
19630 .dropStmt = delcmd->data));
19631
19632 /* Dump rule comments */
19633 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19634 dumpComment(fout, ruleprefix->data, qtabname,
19635 tbinfo->dobj.namespace->dobj.name,
19636 tbinfo->rolname,
19637 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19638
19639 free(tag);
19640 destroyPQExpBuffer(query);
19641 destroyPQExpBuffer(cmd);
19642 destroyPQExpBuffer(delcmd);
19643 destroyPQExpBuffer(ruleprefix);
19644 free(qtabname);
19645}
19646
19647/*
19648 * getExtensionMembership --- obtain extension membership data
19649 *
19650 * We need to identify objects that are extension members as soon as they're
19651 * loaded, so that we can correctly determine whether they need to be dumped.
19652 * Generally speaking, extension member objects will get marked as *not* to
19653 * be dumped, as they will be recreated by the single CREATE EXTENSION
19654 * command. However, in binary upgrade mode we still need to dump the members
19655 * individually.
19656 */
19657void
19659 int numExtensions)
19660{
19661 PQExpBuffer query;
19662 PGresult *res;
19663 int ntups,
19664 i;
19665 int i_classid,
19666 i_objid,
19667 i_refobjid;
19668 ExtensionInfo *ext;
19669
19670 /* Nothing to do if no extensions */
19671 if (numExtensions == 0)
19672 return;
19673
19674 query = createPQExpBuffer();
19675
19676 /* refclassid constraint is redundant but may speed the search */
19677 appendPQExpBufferStr(query, "SELECT "
19678 "classid, objid, refobjid "
19679 "FROM pg_depend "
19680 "WHERE refclassid = 'pg_extension'::regclass "
19681 "AND deptype = 'e' "
19682 "ORDER BY 3");
19683
19684 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19685
19686 ntups = PQntuples(res);
19687
19688 i_classid = PQfnumber(res, "classid");
19689 i_objid = PQfnumber(res, "objid");
19690 i_refobjid = PQfnumber(res, "refobjid");
19691
19692 /*
19693 * Since we ordered the SELECT by referenced ID, we can expect that
19694 * multiple entries for the same extension will appear together; this
19695 * saves on searches.
19696 */
19697 ext = NULL;
19698
19699 for (i = 0; i < ntups; i++)
19700 {
19701 CatalogId objId;
19702 Oid extId;
19703
19704 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19705 objId.oid = atooid(PQgetvalue(res, i, i_objid));
19706 extId = atooid(PQgetvalue(res, i, i_refobjid));
19707
19708 if (ext == NULL ||
19709 ext->dobj.catId.oid != extId)
19710 ext = findExtensionByOid(extId);
19711
19712 if (ext == NULL)
19713 {
19714 /* shouldn't happen */
19715 pg_log_warning("could not find referenced extension %u", extId);
19716 continue;
19717 }
19718
19719 recordExtensionMembership(objId, ext);
19720 }
19721
19722 PQclear(res);
19723
19724 destroyPQExpBuffer(query);
19725}
19726
19727/*
19728 * processExtensionTables --- deal with extension configuration tables
19729 *
19730 * There are two parts to this process:
19731 *
19732 * 1. Identify and create dump records for extension configuration tables.
19733 *
19734 * Extensions can mark tables as "configuration", which means that the user
19735 * is able and expected to modify those tables after the extension has been
19736 * loaded. For these tables, we dump out only the data- the structure is
19737 * expected to be handled at CREATE EXTENSION time, including any indexes or
19738 * foreign keys, which brings us to-
19739 *
19740 * 2. Record FK dependencies between configuration tables.
19741 *
19742 * Due to the FKs being created at CREATE EXTENSION time and therefore before
19743 * the data is loaded, we have to work out what the best order for reloading
19744 * the data is, to avoid FK violations when the tables are restored. This is
19745 * not perfect- we can't handle circular dependencies and if any exist they
19746 * will cause an invalid dump to be produced (though at least all of the data
19747 * is included for a user to manually restore). This is currently documented
19748 * but perhaps we can provide a better solution in the future.
19749 */
19750void
19752 int numExtensions)
19753{
19754 DumpOptions *dopt = fout->dopt;
19755 PQExpBuffer query;
19756 PGresult *res;
19757 int ntups,
19758 i;
19759 int i_conrelid,
19760 i_confrelid;
19761
19762 /* Nothing to do if no extensions */
19763 if (numExtensions == 0)
19764 return;
19765
19766 /*
19767 * Identify extension configuration tables and create TableDataInfo
19768 * objects for them, ensuring their data will be dumped even though the
19769 * tables themselves won't be.
19770 *
19771 * Note that we create TableDataInfo objects even in schema-only mode, ie,
19772 * user data in a configuration table is treated like schema data. This
19773 * seems appropriate since system data in a config table would get
19774 * reloaded by CREATE EXTENSION. If the extension is not listed in the
19775 * list of extensions to be included, none of its data is dumped.
19776 */
19777 for (i = 0; i < numExtensions; i++)
19778 {
19779 ExtensionInfo *curext = &(extinfo[i]);
19780 char *extconfig = curext->extconfig;
19781 char *extcondition = curext->extcondition;
19782 char **extconfigarray = NULL;
19783 char **extconditionarray = NULL;
19784 int nconfigitems = 0;
19785 int nconditionitems = 0;
19786
19787 /*
19788 * Check if this extension is listed as to include in the dump. If
19789 * not, any table data associated with it is discarded.
19790 */
19791 if (extension_include_oids.head != NULL &&
19793 curext->dobj.catId.oid))
19794 continue;
19795
19796 /*
19797 * Check if this extension is listed as to exclude in the dump. If
19798 * yes, any table data associated with it is discarded.
19799 */
19800 if (extension_exclude_oids.head != NULL &&
19802 curext->dobj.catId.oid))
19803 continue;
19804
19805 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19806 {
19807 int j;
19808
19809 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19810 pg_fatal("could not parse %s array", "extconfig");
19811 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19812 pg_fatal("could not parse %s array", "extcondition");
19813 if (nconfigitems != nconditionitems)
19814 pg_fatal("mismatched number of configurations and conditions for extension");
19815
19816 for (j = 0; j < nconfigitems; j++)
19817 {
19818 TableInfo *configtbl;
19819 Oid configtbloid = atooid(extconfigarray[j]);
19820 bool dumpobj =
19822
19823 configtbl = findTableByOid(configtbloid);
19824 if (configtbl == NULL)
19825 continue;
19826
19827 /*
19828 * Tables of not-to-be-dumped extensions shouldn't be dumped
19829 * unless the table or its schema is explicitly included
19830 */
19831 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19832 {
19833 /* check table explicitly requested */
19834 if (table_include_oids.head != NULL &&
19836 configtbloid))
19837 dumpobj = true;
19838
19839 /* check table's schema explicitly requested */
19840 if (configtbl->dobj.namespace->dobj.dump &
19842 dumpobj = true;
19843 }
19844
19845 /* check table excluded by an exclusion switch */
19846 if (table_exclude_oids.head != NULL &&
19848 configtbloid))
19849 dumpobj = false;
19850
19851 /* check schema excluded by an exclusion switch */
19853 configtbl->dobj.namespace->dobj.catId.oid))
19854 dumpobj = false;
19855
19856 if (dumpobj)
19857 {
19858 makeTableDataInfo(dopt, configtbl);
19859 if (configtbl->dataObj != NULL)
19860 {
19861 if (strlen(extconditionarray[j]) > 0)
19862 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19863 }
19864 }
19865 }
19866 }
19867 if (extconfigarray)
19868 free(extconfigarray);
19869 if (extconditionarray)
19870 free(extconditionarray);
19871 }
19872
19873 /*
19874 * Now that all the TableDataInfo objects have been created for all the
19875 * extensions, check their FK dependencies and register them to try and
19876 * dump the data out in an order that they can be restored in.
19877 *
19878 * Note that this is not a problem for user tables as their FKs are
19879 * recreated after the data has been loaded.
19880 */
19881
19882 query = createPQExpBuffer();
19883
19884 printfPQExpBuffer(query,
19885 "SELECT conrelid, confrelid "
19886 "FROM pg_constraint "
19887 "JOIN pg_depend ON (objid = confrelid) "
19888 "WHERE contype = 'f' "
19889 "AND refclassid = 'pg_extension'::regclass "
19890 "AND classid = 'pg_class'::regclass;");
19891
19892 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19893 ntups = PQntuples(res);
19894
19895 i_conrelid = PQfnumber(res, "conrelid");
19896 i_confrelid = PQfnumber(res, "confrelid");
19897
19898 /* Now get the dependencies and register them */
19899 for (i = 0; i < ntups; i++)
19900 {
19901 Oid conrelid,
19902 confrelid;
19903 TableInfo *reftable,
19904 *contable;
19905
19906 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19907 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19908 contable = findTableByOid(conrelid);
19909 reftable = findTableByOid(confrelid);
19910
19911 if (reftable == NULL ||
19912 reftable->dataObj == NULL ||
19913 contable == NULL ||
19914 contable->dataObj == NULL)
19915 continue;
19916
19917 /*
19918 * Make referencing TABLE_DATA object depend on the referenced table's
19919 * TABLE_DATA object.
19920 */
19921 addObjectDependency(&contable->dataObj->dobj,
19922 reftable->dataObj->dobj.dumpId);
19923 }
19924 PQclear(res);
19925 destroyPQExpBuffer(query);
19926}
19927
19928/*
19929 * getDependencies --- obtain available dependency data
19930 */
19931static void
19933{
19934 PQExpBuffer query;
19935 PGresult *res;
19936 int ntups,
19937 i;
19938 int i_classid,
19939 i_objid,
19940 i_refclassid,
19941 i_refobjid,
19942 i_deptype;
19943 DumpableObject *dobj,
19944 *refdobj;
19945
19946 pg_log_info("reading dependency data");
19947
19948 query = createPQExpBuffer();
19949
19950 /*
19951 * Messy query to collect the dependency data we need. Note that we
19952 * ignore the sub-object column, so that dependencies of or on a column
19953 * look the same as dependencies of or on a whole table.
19954 *
19955 * PIN dependencies aren't interesting, and EXTENSION dependencies were
19956 * already processed by getExtensionMembership.
19957 */
19958 appendPQExpBufferStr(query, "SELECT "
19959 "classid, objid, refclassid, refobjid, deptype "
19960 "FROM pg_depend "
19961 "WHERE deptype != 'p' AND deptype != 'e'\n");
19962
19963 /*
19964 * Since we don't treat pg_amop entries as separate DumpableObjects, we
19965 * have to translate their dependencies into dependencies of their parent
19966 * opfamily. Ignore internal dependencies though, as those will point to
19967 * their parent opclass, which we needn't consider here (and if we did,
19968 * it'd just result in circular dependencies). Also, "loose" opfamily
19969 * entries will have dependencies on their parent opfamily, which we
19970 * should drop since they'd likewise become useless self-dependencies.
19971 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19972 */
19973 appendPQExpBufferStr(query, "UNION ALL\n"
19974 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19975 "FROM pg_depend d, pg_amop o "
19976 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19977 "classid = 'pg_amop'::regclass AND objid = o.oid "
19978 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19979
19980 /* Likewise for pg_amproc entries */
19981 appendPQExpBufferStr(query, "UNION ALL\n"
19982 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19983 "FROM pg_depend d, pg_amproc p "
19984 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19985 "classid = 'pg_amproc'::regclass AND objid = p.oid "
19986 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19987
19988 /* Sort the output for efficiency below */
19989 appendPQExpBufferStr(query, "ORDER BY 1,2");
19990
19991 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19992
19993 ntups = PQntuples(res);
19994
19995 i_classid = PQfnumber(res, "classid");
19996 i_objid = PQfnumber(res, "objid");
19997 i_refclassid = PQfnumber(res, "refclassid");
19998 i_refobjid = PQfnumber(res, "refobjid");
19999 i_deptype = PQfnumber(res, "deptype");
20000
20001 /*
20002 * Since we ordered the SELECT by referencing ID, we can expect that
20003 * multiple entries for the same object will appear together; this saves
20004 * on searches.
20005 */
20006 dobj = NULL;
20007
20008 for (i = 0; i < ntups; i++)
20009 {
20010 CatalogId objId;
20011 CatalogId refobjId;
20012 char deptype;
20013
20014 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20015 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20016 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20017 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20018 deptype = *(PQgetvalue(res, i, i_deptype));
20019
20020 if (dobj == NULL ||
20021 dobj->catId.tableoid != objId.tableoid ||
20022 dobj->catId.oid != objId.oid)
20023 dobj = findObjectByCatalogId(objId);
20024
20025 /*
20026 * Failure to find objects mentioned in pg_depend is not unexpected,
20027 * since for example we don't collect info about TOAST tables.
20028 */
20029 if (dobj == NULL)
20030 {
20031#ifdef NOT_USED
20032 pg_log_warning("no referencing object %u %u",
20033 objId.tableoid, objId.oid);
20034#endif
20035 continue;
20036 }
20037
20038 refdobj = findObjectByCatalogId(refobjId);
20039
20040 if (refdobj == NULL)
20041 {
20042#ifdef NOT_USED
20043 pg_log_warning("no referenced object %u %u",
20044 refobjId.tableoid, refobjId.oid);
20045#endif
20046 continue;
20047 }
20048
20049 /*
20050 * For 'x' dependencies, mark the object for later; we still add the
20051 * normal dependency, for possible ordering purposes. Currently
20052 * pg_dump_sort.c knows to put extensions ahead of all object types
20053 * that could possibly depend on them, but this is safer.
20054 */
20055 if (deptype == 'x')
20056 dobj->depends_on_ext = true;
20057
20058 /*
20059 * Ordinarily, table rowtypes have implicit dependencies on their
20060 * tables. However, for a composite type the implicit dependency goes
20061 * the other way in pg_depend; which is the right thing for DROP but
20062 * it doesn't produce the dependency ordering we need. So in that one
20063 * case, we reverse the direction of the dependency.
20064 */
20065 if (deptype == 'i' &&
20066 dobj->objType == DO_TABLE &&
20067 refdobj->objType == DO_TYPE)
20068 addObjectDependency(refdobj, dobj->dumpId);
20069 else
20070 /* normal case */
20071 addObjectDependency(dobj, refdobj->dumpId);
20072 }
20073
20074 PQclear(res);
20075
20076 destroyPQExpBuffer(query);
20077}
20078
20079
20080/*
20081 * createBoundaryObjects - create dummy DumpableObjects to represent
20082 * dump section boundaries.
20083 */
20084static DumpableObject *
20086{
20087 DumpableObject *dobjs;
20088
20089 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20090
20091 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20092 dobjs[0].catId = nilCatalogId;
20093 AssignDumpId(dobjs + 0);
20094 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20095
20096 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20097 dobjs[1].catId = nilCatalogId;
20098 AssignDumpId(dobjs + 1);
20099 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20100
20101 return dobjs;
20102}
20103
20104/*
20105 * addBoundaryDependencies - add dependencies as needed to enforce the dump
20106 * section boundaries.
20107 */
20108static void
20110 DumpableObject *boundaryObjs)
20111{
20112 DumpableObject *preDataBound = boundaryObjs + 0;
20113 DumpableObject *postDataBound = boundaryObjs + 1;
20114 int i;
20115
20116 for (i = 0; i < numObjs; i++)
20117 {
20118 DumpableObject *dobj = dobjs[i];
20119
20120 /*
20121 * The classification of object types here must match the SECTION_xxx
20122 * values assigned during subsequent ArchiveEntry calls!
20123 */
20124 switch (dobj->objType)
20125 {
20126 case DO_NAMESPACE:
20127 case DO_EXTENSION:
20128 case DO_TYPE:
20129 case DO_SHELL_TYPE:
20130 case DO_FUNC:
20131 case DO_AGG:
20132 case DO_OPERATOR:
20133 case DO_ACCESS_METHOD:
20134 case DO_OPCLASS:
20135 case DO_OPFAMILY:
20136 case DO_COLLATION:
20137 case DO_CONVERSION:
20138 case DO_TABLE:
20139 case DO_TABLE_ATTACH:
20140 case DO_ATTRDEF:
20141 case DO_PROCLANG:
20142 case DO_CAST:
20143 case DO_DUMMY_TYPE:
20144 case DO_TSPARSER:
20145 case DO_TSDICT:
20146 case DO_TSTEMPLATE:
20147 case DO_TSCONFIG:
20148 case DO_FDW:
20149 case DO_FOREIGN_SERVER:
20150 case DO_TRANSFORM:
20151 /* Pre-data objects: must come before the pre-data boundary */
20152 addObjectDependency(preDataBound, dobj->dumpId);
20153 break;
20154 case DO_TABLE_DATA:
20155 case DO_SEQUENCE_SET:
20156 case DO_LARGE_OBJECT:
20158 /* Data objects: must come between the boundaries */
20159 addObjectDependency(dobj, preDataBound->dumpId);
20160 addObjectDependency(postDataBound, dobj->dumpId);
20161 break;
20162 case DO_INDEX:
20163 case DO_INDEX_ATTACH:
20164 case DO_STATSEXT:
20165 case DO_REFRESH_MATVIEW:
20166 case DO_TRIGGER:
20167 case DO_EVENT_TRIGGER:
20168 case DO_DEFAULT_ACL:
20169 case DO_POLICY:
20170 case DO_PUBLICATION:
20171 case DO_PUBLICATION_REL:
20173 case DO_SUBSCRIPTION:
20175 /* Post-data objects: must come after the post-data boundary */
20176 addObjectDependency(dobj, postDataBound->dumpId);
20177 break;
20178 case DO_RULE:
20179 /* Rules are post-data, but only if dumped separately */
20180 if (((RuleInfo *) dobj)->separate)
20181 addObjectDependency(dobj, postDataBound->dumpId);
20182 break;
20183 case DO_CONSTRAINT:
20184 case DO_FK_CONSTRAINT:
20185 /* Constraints are post-data, but only if dumped separately */
20186 if (((ConstraintInfo *) dobj)->separate)
20187 addObjectDependency(dobj, postDataBound->dumpId);
20188 break;
20190 /* nothing to do */
20191 break;
20193 /* must come after the pre-data boundary */
20194 addObjectDependency(dobj, preDataBound->dumpId);
20195 break;
20196 case DO_REL_STATS:
20197 /* stats section varies by parent object type, DATA or POST */
20198 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20199 {
20200 addObjectDependency(dobj, preDataBound->dumpId);
20201 addObjectDependency(postDataBound, dobj->dumpId);
20202 }
20203 else
20204 addObjectDependency(dobj, postDataBound->dumpId);
20205 break;
20206 }
20207 }
20208}
20209
20210
20211/*
20212 * BuildArchiveDependencies - create dependency data for archive TOC entries
20213 *
20214 * The raw dependency data obtained by getDependencies() is not terribly
20215 * useful in an archive dump, because in many cases there are dependency
20216 * chains linking through objects that don't appear explicitly in the dump.
20217 * For example, a view will depend on its _RETURN rule while the _RETURN rule
20218 * will depend on other objects --- but the rule will not appear as a separate
20219 * object in the dump. We need to adjust the view's dependencies to include
20220 * whatever the rule depends on that is included in the dump.
20221 *
20222 * Just to make things more complicated, there are also "special" dependencies
20223 * such as the dependency of a TABLE DATA item on its TABLE, which we must
20224 * not rearrange because pg_restore knows that TABLE DATA only depends on
20225 * its table. In these cases we must leave the dependencies strictly as-is
20226 * even if they refer to not-to-be-dumped objects.
20227 *
20228 * To handle this, the convention is that "special" dependencies are created
20229 * during ArchiveEntry calls, and an archive TOC item that has any such
20230 * entries will not be touched here. Otherwise, we recursively search the
20231 * DumpableObject data structures to build the correct dependencies for each
20232 * archive TOC item.
20233 */
20234static void
20236{
20237 ArchiveHandle *AH = (ArchiveHandle *) fout;
20238 TocEntry *te;
20239
20240 /* Scan all TOC entries in the archive */
20241 for (te = AH->toc->next; te != AH->toc; te = te->next)
20242 {
20243 DumpableObject *dobj;
20244 DumpId *dependencies;
20245 int nDeps;
20246 int allocDeps;
20247
20248 /* No need to process entries that will not be dumped */
20249 if (te->reqs == 0)
20250 continue;
20251 /* Ignore entries that already have "special" dependencies */
20252 if (te->nDeps > 0)
20253 continue;
20254 /* Otherwise, look up the item's original DumpableObject, if any */
20255 dobj = findObjectByDumpId(te->dumpId);
20256 if (dobj == NULL)
20257 continue;
20258 /* No work if it has no dependencies */
20259 if (dobj->nDeps <= 0)
20260 continue;
20261 /* Set up work array */
20262 allocDeps = 64;
20263 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20264 nDeps = 0;
20265 /* Recursively find all dumpable dependencies */
20266 findDumpableDependencies(AH, dobj,
20267 &dependencies, &nDeps, &allocDeps);
20268 /* And save 'em ... */
20269 if (nDeps > 0)
20270 {
20271 dependencies = (DumpId *) pg_realloc(dependencies,
20272 nDeps * sizeof(DumpId));
20273 te->dependencies = dependencies;
20274 te->nDeps = nDeps;
20275 }
20276 else
20277 free(dependencies);
20278 }
20279}
20280
20281/* Recursive search subroutine for BuildArchiveDependencies */
20282static void
20284 DumpId **dependencies, int *nDeps, int *allocDeps)
20285{
20286 int i;
20287
20288 /*
20289 * Ignore section boundary objects: if we search through them, we'll
20290 * report lots of bogus dependencies.
20291 */
20292 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20294 return;
20295
20296 for (i = 0; i < dobj->nDeps; i++)
20297 {
20298 DumpId depid = dobj->dependencies[i];
20299
20300 if (TocIDRequired(AH, depid) != 0)
20301 {
20302 /* Object will be dumped, so just reference it as a dependency */
20303 if (*nDeps >= *allocDeps)
20304 {
20305 *allocDeps *= 2;
20306 *dependencies = (DumpId *) pg_realloc(*dependencies,
20307 *allocDeps * sizeof(DumpId));
20308 }
20309 (*dependencies)[*nDeps] = depid;
20310 (*nDeps)++;
20311 }
20312 else
20313 {
20314 /*
20315 * Object will not be dumped, so recursively consider its deps. We
20316 * rely on the assumption that sortDumpableObjects already broke
20317 * any dependency loops, else we might recurse infinitely.
20318 */
20319 DumpableObject *otherdobj = findObjectByDumpId(depid);
20320
20321 if (otherdobj)
20322 findDumpableDependencies(AH, otherdobj,
20323 dependencies, nDeps, allocDeps);
20324 }
20325 }
20326}
20327
20328
20329/*
20330 * getFormattedTypeName - retrieve a nicely-formatted type name for the
20331 * given type OID.
20332 *
20333 * This does not guarantee to schema-qualify the output, so it should not
20334 * be used to create the target object name for CREATE or ALTER commands.
20335 *
20336 * Note that the result is cached and must not be freed by the caller.
20337 */
20338static const char *
20340{
20341 TypeInfo *typeInfo;
20342 char *result;
20343 PQExpBuffer query;
20344 PGresult *res;
20345
20346 if (oid == 0)
20347 {
20348 if ((opts & zeroAsStar) != 0)
20349 return "*";
20350 else if ((opts & zeroAsNone) != 0)
20351 return "NONE";
20352 }
20353
20354 /* see if we have the result cached in the type's TypeInfo record */
20355 typeInfo = findTypeByOid(oid);
20356 if (typeInfo && typeInfo->ftypname)
20357 return typeInfo->ftypname;
20358
20359 query = createPQExpBuffer();
20360 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20361 oid);
20362
20363 res = ExecuteSqlQueryForSingleRow(fout, query->data);
20364
20365 /* result of format_type is already quoted */
20366 result = pg_strdup(PQgetvalue(res, 0, 0));
20367
20368 PQclear(res);
20369 destroyPQExpBuffer(query);
20370
20371 /*
20372 * Cache the result for re-use in later requests, if possible. If we
20373 * don't have a TypeInfo for the type, the string will be leaked once the
20374 * caller is done with it ... but that case really should not happen, so
20375 * leaking if it does seems acceptable.
20376 */
20377 if (typeInfo)
20378 typeInfo->ftypname = result;
20379
20380 return result;
20381}
20382
20383/*
20384 * Return a column list clause for the given relation.
20385 *
20386 * Special case: if there are no undropped columns in the relation, return
20387 * "", not an invalid "()" column list.
20388 */
20389static const char *
20391{
20392 int numatts = ti->numatts;
20393 char **attnames = ti->attnames;
20394 bool *attisdropped = ti->attisdropped;
20395 char *attgenerated = ti->attgenerated;
20396 bool needComma;
20397 int i;
20398
20399 appendPQExpBufferChar(buffer, '(');
20400 needComma = false;
20401 for (i = 0; i < numatts; i++)
20402 {
20403 if (attisdropped[i])
20404 continue;
20405 if (attgenerated[i])
20406 continue;
20407 if (needComma)
20408 appendPQExpBufferStr(buffer, ", ");
20409 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20410 needComma = true;
20411 }
20412
20413 if (!needComma)
20414 return ""; /* no undropped columns */
20415
20416 appendPQExpBufferChar(buffer, ')');
20417 return buffer->data;
20418}
20419
20420/*
20421 * Check if a reloptions array is nonempty.
20422 */
20423static bool
20424nonemptyReloptions(const char *reloptions)
20425{
20426 /* Don't want to print it if it's just "{}" */
20427 return (reloptions != NULL && strlen(reloptions) > 2);
20428}
20429
20430/*
20431 * Format a reloptions array and append it to the given buffer.
20432 *
20433 * "prefix" is prepended to the option names; typically it's "" or "toast.".
20434 */
20435static void
20436appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20437 const char *prefix, Archive *fout)
20438{
20439 bool res;
20440
20441 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20442 fout->std_strings);
20443 if (!res)
20444 pg_log_warning("could not parse %s array", "reloptions");
20445}
20446
20447/*
20448 * read_dump_filters - retrieve object identifier patterns from file
20449 *
20450 * Parse the specified filter file for include and exclude patterns, and add
20451 * them to the relevant lists. If the filename is "-" then filters will be
20452 * read from STDIN rather than a file.
20453 */
20454static void
20456{
20457 FilterStateData fstate;
20458 char *objname;
20459 FilterCommandType comtype;
20460 FilterObjectType objtype;
20461
20462 filter_init(&fstate, filename, exit_nicely);
20463
20464 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20465 {
20466 if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20467 {
20468 switch (objtype)
20469 {
20471 break;
20478 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20479 "include",
20480 filter_object_type_name(objtype));
20481 exit_nicely(1);
20482 break; /* unreachable */
20483
20486 break;
20489 break;
20492 dopt->include_everything = false;
20493 break;
20496 dopt->include_everything = false;
20497 break;
20500 objname);
20501 dopt->include_everything = false;
20502 break;
20503 }
20504 }
20505 else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20506 {
20507 switch (objtype)
20508 {
20510 break;
20516 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20517 "exclude",
20518 filter_object_type_name(objtype));
20519 exit_nicely(1);
20520 break;
20521
20524 break;
20527 objname);
20528 break;
20531 objname);
20532 break;
20535 break;
20538 break;
20541 objname);
20542 break;
20543 }
20544 }
20545 else
20546 {
20547 Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20548 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20549 }
20550
20551 if (objname)
20552 free(objname);
20553 }
20554
20555 filter_free(&fstate);
20556}
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:108
#define PG_INT32_MAX
Definition: c.h:595
#define ngettext(s, p, n)
Definition: c.h:1181
#define INT64_FORMAT
Definition: c.h:557
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1214
int64_t int64
Definition: c.h:536
#define PG_INT16_MIN
Definition: c.h:591
#define CppAsString2(x)
Definition: c.h:419
int32_t int32
Definition: c.h:535
#define PG_INT64_MAX
Definition: c.h:598
#define PG_INT64_MIN
Definition: c.h:597
uint32_t uint32
Definition: c.h:539
#define lengthof(array)
Definition: c.h:788
#define PG_INT32_MIN
Definition: c.h:594
#define PG_INT16_MAX
Definition: c.h:592
#define OidIsValid(objectId)
Definition: c.h:775
int nspid
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:429
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:205
char * generate_restrict_key(void)
Definition: dumputils.c:968
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:992
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:7513
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
Definition: fe-connect.c:7634
int PQclientEncoding(const PGconn *conn)
Definition: fe-connect.c:7769
char * PQerrorMessage(const PGconn *conn)
Definition: fe-connect.c:7679
int PQsetClientEncoding(PGconn *conn, const char *encoding)
Definition: fe-connect.c:7777
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 @166 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:55
#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:8264
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:16380
static SimpleStringList schema_include_patterns
Definition: pg_dump.c:165
static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
Definition: pg_dump.c:18147
ExtensionInfo * getExtensions(Archive *fout, int *numExtensions)
Definition: pg_dump.c:6096
static void selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
Definition: pg_dump.c:2201
static void collectBinaryUpgradeClassOids(Archive *fout)
Definition: pg_dump.c:5796
static PQExpBuffer createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17024
static void dumpUserMappings(Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
Definition: pg_dump.c:16194
static void dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
Definition: pg_dump.c:4936
static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
Definition: pg_dump.c:20109
void getPublicationNamespaces(Archive *fout)
Definition: pg_dump.c:4729
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:20085
static char * convertTSFunction(Archive *fout, Oid funcOid)
Definition: pg_dump.c:14367
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:5609
static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
Definition: pg_dump.c:5654
static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
Definition: pg_dump.c:11828
static bool forcePartitionRootLoad(const TableInfo *tbinfo)
Definition: pg_dump.c:2828
static void dumpCast(Archive *fout, const CastInfo *cast)
Definition: pg_dump.c:13843
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:18237
void getPartitioningInfo(Archive *fout)
Definition: pg_dump.c:7756
static int nbinaryUpgradeClassOids
Definition: pg_dump.c:209
static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12426
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:11073
static SimpleOidList extension_include_oids
Definition: pg_dump.c:184
static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
Definition: pg_dump.c:15766
static void dumpAgg(Archive *fout, const AggInfo *agginfo)
Definition: pg_dump.c:15342
static int extra_float_digits
Definition: pg_dump.c:193
static int SequenceItemCmp(const void *p1, const void *p2)
Definition: pg_dump.c:18868
static void dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
Definition: pg_dump.c:11346
static void dumpTableComment(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition: pg_dump.c:11372
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:7700
void getForeignDataWrappers(Archive *fout)
Definition: pg_dump.c:10382
static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
Definition: pg_dump.c:19275
static void binary_upgrade_set_type_oids_by_rel(Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
Definition: pg_dump.c:5765
static void dumpTable(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:16835
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:16975
static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
Definition: pg_dump.c:18431
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:12204
void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition: pg_dump.c:19658
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:10951
static char * getFormattedOperatorName(const char *oproid)
Definition: pg_dump.c:14337
static char * format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
Definition: pg_dump.c:13396
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:18508
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12033
static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
Definition: pg_dump.c:18079
static void help(const char *progname)
Definition: pg_dump.c:1304
void getTypes(Archive *fout)
Definition: pg_dump.c:6171
static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
Definition: pg_dump.c:14389
int main(int argc, char **argv)
Definition: pg_dump.c:421
static void dumpOpr(Archive *fout, const OprInfo *oprinfo)
Definition: pg_dump.c:14077
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:19198
static void dumpFunc(Archive *fout, const FuncInfo *finfo)
Definition: pg_dump.c:13425
static void selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
Definition: pg_dump.c:2154
static void BuildArchiveDependencies(Archive *fout)
Definition: pg_dump.c:20235
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:7081
static const char *const SeqTypeNames[]
Definition: pg_dump.c:118
void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:7635
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:18208
static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
Definition: pg_dump.c:16094
static RoleNameItem * rolenames
Definition: pg_dump.c:196
static void collectRoleNames(Archive *fout)
Definition: pg_dump.c:10687
static PGresult * fetchAttributeStats(Archive *fout)
Definition: pg_dump.c:10985
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
Definition: pg_dump.c:20436
void getOpclasses(Archive *fout)
Definition: pg_dump.c:6617
void getForeignServers(Archive *fout)
Definition: pg_dump.c:10466
void getFuncs(Archive *fout)
Definition: pg_dump.c:6886
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:20339
static void getDependencies(Archive *fout)
Definition: pg_dump.c:19932
static void buildMatViewRefreshDependencies(Archive *fout)
Definition: pg_dump.c:3112
void getTSDictionaries(Archive *fout)
Definition: pg_dump.c:10198
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:5685
#define DUMP_DEFAULT_ROWS_PER_INSERT
Definition: pg_dump.c:228
void getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
Definition: pg_dump.c:4809
static SeqType parse_sequence_type(const char *name)
Definition: pg_dump.c:18852
static const char * getRoleName(const char *roleoid_str)
Definition: pg_dump.c:10651
static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
Definition: pg_dump.c:13195
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:11470
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:9013
static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
Definition: pg_dump.c:4630
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:7816
static void setupDumpWorker(Archive *AH)
Definition: pg_dump.c:1582
static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
Definition: pg_dump.c:8423
void getTSConfigurations(Archive *fout)
Definition: pg_dump.c:10323
static int nrolenames
Definition: pg_dump.c:197
static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
Definition: pg_dump.c:16670
static SimpleStringList table_include_patterns_and_children
Definition: pg_dump.c:171
static char * convertRegProcReference(const char *proc)
Definition: pg_dump.c:14296
static void getAdditionalACLs(Archive *fout)
Definition: pg_dump.c:10722
StaticAssertDecl(lengthof(SeqTypeNames)==(SEQTYPE_BIGINT+1), "array length mismatch")
static bool is_superuser(Archive *fout)
Definition: pg_dump.c:5040
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:6543
void getConversions(Archive *fout)
Definition: pg_dump.c:6481
void getRules(Archive *fout)
Definition: pg_dump.c:8558
static void dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12675
void getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
Definition: pg_dump.c:9207
static void collectComments(Archive *fout)
Definition: pg_dump.c:11547
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
Definition: pg_dump.c:8446
static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
Definition: pg_dump.c:14738
static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
Definition: pg_dump.c:16288
void getSubscriptionTables(Archive *fout)
Definition: pg_dump.c:5300
static void selectDumpableObject(DumpableObject *dobj, Archive *fout)
Definition: pg_dump.c:2344
static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
Definition: pg_dump.c:15846
static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
Definition: pg_dump.c:18388
static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
Definition: pg_dump.c:5386
void getCollations(Archive *fout)
Definition: pg_dump.c:6415
static char * format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
Definition: pg_dump.c:13373
static int strict_names
Definition: pg_dump.c:155
static void dumpTransform(Archive *fout, const TransformInfo *transform)
Definition: pg_dump.c:13948
void getAggregates(Archive *fout)
Definition: pg_dump.c:6745
static void dumpLO(Archive *fout, const LoInfo *loinfo)
Definition: pg_dump.c:4097
void getNamespaces(Archive *fout)
Definition: pg_dump.c:5964
static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
Definition: pg_dump.c:4979
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:5920
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
Definition: pg_dump.c:11632
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:10124
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:18829
static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12362
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:20390
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:20283
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:9982
static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:17064
static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
Definition: pg_dump.c:15702
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:7158
static void dumpRule(Archive *fout, const RuleInfo *rinfo)
Definition: pg_dump.c:19491
static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12900
static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
Definition: pg_dump.c:12064
static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
Definition: pg_dump.c:11905
#define fmtQualifiedDumpable(obj)
Definition: pg_dump.c:240
static bool nonemptyReloptions(const char *reloptions)
Definition: pg_dump.c:20424
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:8185
static NamespaceInfo * findNamespace(Oid nsoid)
Definition: pg_dump.c:6078
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:5455
static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
Definition: pg_dump.c:10967
void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition: pg_dump.c:19751
static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
Definition: pg_dump.c:19401
static int BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
Definition: pg_dump.c:5780
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:10851
void getDefaultACLs(Archive *fout)
Definition: pg_dump.c:10554
static SimpleStringList tabledata_exclude_patterns
Definition: pg_dump.c:176
static void dumpConversion(Archive *fout, const ConvInfo *convinfo)
Definition: pg_dump.c:15214
static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
Definition: pg_dump.c:16024
static void dumpProcLang(Archive *fout, const ProcLangInfo *plang)
Definition: pg_dump.c:13241
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:16508
void getSubscriptions(Archive *fout)
Definition: pg_dump.c:5080
static void collectSecLabels(Archive *fout)
Definition: pg_dump.c:16749
static void selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
Definition: pg_dump.c:2269
static void collectSequences(Archive *fout)
Definition: pg_dump.c:18883
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:8655
void getTransforms(Archive *fout)
Definition: pg_dump.c:9123
void getEventTriggers(Archive *fout)
Definition: pg_dump.c:8851
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:20455
static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
Definition: pg_dump.c:15904
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:9102
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:5830
void getTSTemplates(Archive *fout)
Definition: pg_dump.c:10264
static void set_restrict_relation_kind(Archive *AH, const char *value)
Definition: pg_dump.c:5059
static char * format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
Definition: pg_dump.c:15310
void getProcLangs(Archive *fout)
Definition: pg_dump.c:8929
static void dumpSequence(Archive *fout, const TableInfo *tbinfo)
Definition: pg_dump.c:18944
bool shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
Definition: pg_dump.c:10109
static TableInfo * getRootTableInfo(const TableInfo *tbinfo)
Definition: pg_dump.c:2803
void getOperators(Archive *fout)
Definition: pg_dump.c:6339
static SimpleOidList foreign_servers_include_oids
Definition: pg_dump.c:181
static void dumpCollation(Archive *fout, const CollInfo *collinfo)
Definition: pg_dump.c:14957
static void dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition: pg_dump.c:16588
static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res)
Definition: pg_dump.c:13106
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:6680
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:14457
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:547
#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 puballtables
Definition: pg_dump.h:671
bool pubtruncate
Definition: pg_dump.h:675
PublishGencolsType pubgencols_type
Definition: pg_dump.h:677
DumpableObject dobj
Definition: pg_dump.h:669
NamespaceInfo * pubschema
Definition: pg_dump.h:701
DumpableObject dobj
Definition: pg_dump.h:699
PublicationInfo * publication
Definition: pg_dump.h:700
DumpableObject dobj
Definition: pg_dump.h:741
char * srsublsn
Definition: pg_dump.h:745
SubscriptionInfo * subinfo
Definition: pg_dump.h:742
TableInfo * tblinfo
Definition: pg_dump.h:743
char srsubstate
Definition: pg_dump.h:744
char * suboriginremotelsn
Definition: pg_dump.h:726
bool subpasswordrequired
Definition: pg_dump.h:716
char * suborigin
Definition: pg_dump.h:725
const char * rolname
Definition: pg_dump.h:710
char * subsynccommit
Definition: pg_dump.h:723
char * subpublications
Definition: pg_dump.h:724
bool subdisableonerr
Definition: pg_dump.h:715
bool subrunasowner
Definition: pg_dump.h:717
char * subslotname
Definition: pg_dump.h:722
char subtwophasestate
Definition: pg_dump.h:714
bool subretaindeadtuples
Definition: pg_dump.h:719
char * subconninfo
Definition: pg_dump.h:721
DumpableObject dobj
Definition: pg_dump.h:709
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:137
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