PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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-2026, 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"
75#include "storage/block.h"
76
77typedef struct
78{
79 Oid roleoid; /* role's OID */
80 const char *rolename; /* role's name */
82
83typedef struct
84{
85 const char *descr; /* comment for an object */
86 Oid classoid; /* object class (catalog OID) */
87 Oid objoid; /* object OID */
88 int objsubid; /* subobject (table column #) */
90
91typedef struct
92{
93 const char *provider; /* label provider of this security label */
94 const char *label; /* security label for an object */
95 Oid classoid; /* object class (catalog OID) */
96 Oid objoid; /* object OID */
97 int objsubid; /* subobject (table column #) */
99
100typedef struct
101{
102 Oid oid; /* object OID */
103 char relkind; /* object kind */
104 RelFileNumber relfilenumber; /* object filenode */
105 Oid toast_oid; /* toast table OID */
106 RelFileNumber toast_relfilenumber; /* toast table filenode */
107 Oid toast_index_oid; /* toast table index OID */
108 RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
110
111/* sequence types */
118
119static const char *const SeqTypeNames[] =
120{
121 [SEQTYPE_SMALLINT] = "smallint",
122 [SEQTYPE_INTEGER] = "integer",
123 [SEQTYPE_BIGINT] = "bigint",
124};
125
127 "array length mismatch");
128
129typedef struct
130{
131 Oid oid; /* sequence OID */
132 SeqType seqtype; /* data type of sequence */
133 bool cycled; /* whether sequence cycles */
134 int64 minv; /* minimum value */
135 int64 maxv; /* maximum value */
136 int64 startv; /* start value */
137 int64 incby; /* increment value */
138 int64 cache; /* cache size */
139 int64 last_value; /* last value of sequence */
140 bool is_called; /* whether nextval advances before returning */
141 bool null_seqtuple; /* did pg_get_sequence_data return nulls? */
143
150
151/* global decls */
152static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
153
154static Oid g_last_builtin_oid; /* value of the last builtin oid */
155
156/* The specified names/patterns should to match at least one entity */
157static int strict_names = 0;
158
160
161/*
162 * Object inclusion/exclusion lists
163 *
164 * The string lists record the patterns given by command-line switches,
165 * which we then convert to lists of OIDs of matching objects.
166 */
171
181
184
187
190
191static const CatalogId nilCatalogId = {0, 0};
192
193/* override for standard extra_float_digits setting */
194static bool have_extra_float_digits = false;
196
197/* sorted table of role names */
199static int nrolenames = 0;
200
201/* sorted table of comments */
203static int ncomments = 0;
204
205/* sorted table of security labels */
207static int nseclabels = 0;
208
209/* sorted table of pg_class information for binary upgrade */
212
213/* sorted table of sequences */
215static int nsequences = 0;
216
217/* Maximum number of relations to fetch in a fetchAttributeStats() call. */
218#define MAX_ATTR_STATS_RELS 64
219
220/*
221 * The default number of rows per INSERT when
222 * --inserts is specified without --rows-per-insert
223 */
224#define DUMP_DEFAULT_ROWS_PER_INSERT 1
225
226/*
227 * Maximum number of large objects to group into a single ArchiveEntry.
228 * At some point we might want to make this user-controllable, but for now
229 * a hard-wired setting will suffice.
230 */
231#define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
232
233/*
234 * Macro for producing quoted, schema-qualified name of a dumpable object.
235 */
236#define fmtQualifiedDumpable(obj) \
237 fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
238 (obj)->dobj.name)
239
240static void help(const char *progname);
241static void setup_connection(Archive *AH,
242 const char *dumpencoding, const char *dumpsnapshot,
243 char *use_role);
247 SimpleOidList *oids,
248 bool strict_names);
251 SimpleOidList *oids,
252 bool strict_names);
255 SimpleOidList *oids);
258 SimpleOidList *oids,
259 bool strict_names,
260 bool with_child_tables);
261static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
262 const char *pattern);
263
265static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
267static const char *getRoleName(const char *roleoid_str);
268static void collectRoleNames(Archive *fout);
269static void getAdditionalACLs(Archive *fout);
270static void dumpCommentExtended(Archive *fout, const char *type,
271 const char *name, const char *namespace,
272 const char *owner, CatalogId catalogId,
273 int subid, DumpId dumpId,
274 const char *initdb_comment);
275static inline void dumpComment(Archive *fout, const char *type,
276 const char *name, const char *namespace,
277 const char *owner, CatalogId catalogId,
278 int subid, DumpId dumpId);
279static int findComments(Oid classoid, Oid objoid, CommentItem **items);
280static void collectComments(Archive *fout);
281static void dumpSecLabel(Archive *fout, const char *type, const char *name,
282 const char *namespace, const char *owner,
283 CatalogId catalogId, int subid, DumpId dumpId);
284static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
285static void collectSecLabels(Archive *fout);
286static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
287static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
288static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
289static void dumpType(Archive *fout, const TypeInfo *tyinfo);
290static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
291static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
292static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
293static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
294static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
295static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
297 PGresult *res);
298static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
299static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
300static void dumpFunc(Archive *fout, const FuncInfo *finfo);
301static void dumpCast(Archive *fout, const CastInfo *cast);
302static void dumpTransform(Archive *fout, const TransformInfo *transform);
303static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
305static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
306static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
307static void dumpCollation(Archive *fout, const CollInfo *collinfo);
308static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
309static void dumpRule(Archive *fout, const RuleInfo *rinfo);
310static void dumpAgg(Archive *fout, const AggInfo *agginfo);
311static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
313static void dumpTable(Archive *fout, const TableInfo *tbinfo);
314static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
316static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
317static void collectSequences(Archive *fout);
318static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
319static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
320static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
324static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
326static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
327static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
329static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
332static void dumpUserMappings(Archive *fout,
333 const char *servername, const char *namespace,
334 const char *owner, CatalogId catalogId, DumpId dumpId);
336
338 const char *type, const char *name, const char *subname,
339 const char *nspname, const char *tag, const char *owner,
340 const DumpableAcl *dacl);
341
342static void getDependencies(Archive *fout);
344static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
345 DumpId **dependencies, int *nDeps, int *allocDeps);
346
350
351static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
353static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
354static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
356static void getTableDataFKConstraints(void);
357static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
358 TableInfo *tbinfo, int j,
359 int i_notnull_name,
365static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
366 bool is_agg);
368 const FuncInfo *finfo, bool honor_quotes);
369static char *convertRegProcReference(const char *proc);
370static char *getFormattedOperatorName(const char *oproid);
371static char *convertTSFunction(Archive *fout, Oid funcOid);
372static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
373static void getLOs(Archive *fout);
374static void dumpLO(Archive *fout, const LoInfo *loinfo);
375static int dumpLOs(Archive *fout, const void *arg);
376static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
379static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
381static void dumpDatabase(Archive *fout);
382static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
383 const char *dbname, Oid dboid);
384static void dumpEncoding(Archive *AH);
385static void dumpStdStrings(Archive *AH);
386static void dumpSearchPath(Archive *AH);
390 bool force_array_type,
394 const TableInfo *tbinfo);
400 const DumpableObject *dobj,
401 const char *objtype,
402 const char *objname,
403 const char *objnamespace);
404static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
405static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
406static bool nonemptyReloptions(const char *reloptions);
407static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
408 const char *prefix, Archive *fout);
410static void set_restrict_relation_kind(Archive *AH, const char *value);
411static void setupDumpWorker(Archive *AH);
413static bool forcePartitionRootLoad(const TableInfo *tbinfo);
414static void read_dump_filters(const char *filename, DumpOptions *dopt);
415
416
417int
418main(int argc, char **argv)
419{
420 int c;
421 const char *filename = NULL;
422 const char *format = "p";
423 TableInfo *tblinfo;
424 int numTables;
426 int numObjs;
428 int i;
429 int optindex;
430 RestoreOptions *ropt;
431 Archive *fout; /* the script file */
432 bool g_verbose = false;
433 const char *dumpencoding = NULL;
434 const char *dumpsnapshot = NULL;
435 char *use_role = NULL;
436 int numWorkers = 1;
437 int plainText = 0;
440 pg_compress_specification compression_spec = {0};
441 char *compression_detail = NULL;
442 char *compression_algorithm_str = "none";
443 char *error_detail = NULL;
444 bool user_compression_defined = false;
446 bool data_only = false;
447 bool schema_only = false;
448 bool statistics_only = false;
449 bool with_statistics = false;
450 bool no_data = false;
451 bool no_schema = false;
452 bool no_statistics = false;
453
454 static DumpOptions dopt;
455
456 static struct option long_options[] = {
457 {"data-only", no_argument, NULL, 'a'},
458 {"blobs", no_argument, NULL, 'b'},
459 {"large-objects", no_argument, NULL, 'b'},
460 {"no-blobs", no_argument, NULL, 'B'},
461 {"no-large-objects", no_argument, NULL, 'B'},
462 {"clean", no_argument, NULL, 'c'},
463 {"create", no_argument, NULL, 'C'},
464 {"dbname", required_argument, NULL, 'd'},
465 {"extension", required_argument, NULL, 'e'},
466 {"file", required_argument, NULL, 'f'},
467 {"format", required_argument, NULL, 'F'},
468 {"host", required_argument, NULL, 'h'},
469 {"jobs", 1, NULL, 'j'},
470 {"no-reconnect", no_argument, NULL, 'R'},
471 {"no-owner", no_argument, NULL, 'O'},
472 {"port", required_argument, NULL, 'p'},
473 {"schema", required_argument, NULL, 'n'},
474 {"exclude-schema", required_argument, NULL, 'N'},
475 {"schema-only", no_argument, NULL, 's'},
476 {"superuser", required_argument, NULL, 'S'},
477 {"table", required_argument, NULL, 't'},
478 {"exclude-table", required_argument, NULL, 'T'},
479 {"no-password", no_argument, NULL, 'w'},
480 {"password", no_argument, NULL, 'W'},
481 {"username", required_argument, NULL, 'U'},
482 {"verbose", no_argument, NULL, 'v'},
483 {"no-privileges", no_argument, NULL, 'x'},
484 {"no-acl", no_argument, NULL, 'x'},
485 {"compress", required_argument, NULL, 'Z'},
486 {"encoding", required_argument, NULL, 'E'},
487 {"help", no_argument, NULL, '?'},
488 {"version", no_argument, NULL, 'V'},
489
490 /*
491 * the following options don't have an equivalent short option letter
492 */
493 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
494 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
495 {"column-inserts", no_argument, &dopt.column_inserts, 1},
496 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
497 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
498 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
499 {"exclude-table-data", required_argument, NULL, 4},
500 {"extra-float-digits", required_argument, NULL, 8},
501 {"if-exists", no_argument, &dopt.if_exists, 1},
502 {"inserts", no_argument, NULL, 9},
503 {"lock-wait-timeout", required_argument, NULL, 2},
504 {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
505 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
506 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
507 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
508 {"role", required_argument, NULL, 3},
509 {"section", required_argument, NULL, 5},
510 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
511 {"snapshot", required_argument, NULL, 6},
512 {"statistics", no_argument, NULL, 22},
513 {"statistics-only", no_argument, NULL, 18},
514 {"strict-names", no_argument, &strict_names, 1},
515 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
516 {"no-comments", no_argument, &dopt.no_comments, 1},
517 {"no-data", no_argument, NULL, 19},
518 {"no-policies", no_argument, &dopt.no_policies, 1},
519 {"no-publications", no_argument, &dopt.no_publications, 1},
520 {"no-schema", no_argument, NULL, 20},
521 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
522 {"no-statistics", no_argument, NULL, 21},
523 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
524 {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
525 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
526 {"no-sync", no_argument, NULL, 7},
527 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
528 {"rows-per-insert", required_argument, NULL, 10},
529 {"include-foreign-data", required_argument, NULL, 11},
530 {"table-and-children", required_argument, NULL, 12},
531 {"exclude-table-and-children", required_argument, NULL, 13},
532 {"exclude-table-data-and-children", required_argument, NULL, 14},
533 {"sync-method", required_argument, NULL, 15},
534 {"filter", required_argument, NULL, 16},
535 {"exclude-extension", required_argument, NULL, 17},
536 {"sequence-data", no_argument, &dopt.sequence_data, 1},
537 {"restrict-key", required_argument, NULL, 25},
538
539 {NULL, 0, NULL, 0}
540 };
541
542 pg_logging_init(argv[0]);
544 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
545
546 /*
547 * Initialize what we need for parallel execution, especially for thread
548 * support on Windows.
549 */
551
552 progname = get_progname(argv[0]);
553
554 if (argc > 1)
555 {
556 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
557 {
558 help(progname);
559 exit_nicely(0);
560 }
561 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
562 {
563 puts("pg_dump (PostgreSQL) " PG_VERSION);
564 exit_nicely(0);
565 }
566 }
567
568 InitDumpOptions(&dopt);
569
570 while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
571 long_options, &optindex)) != -1)
572 {
573 switch (c)
574 {
575 case 'a': /* Dump data only */
576 data_only = true;
577 break;
578
579 case 'b': /* Dump LOs */
580 dopt.outputLOs = true;
581 break;
582
583 case 'B': /* Don't dump LOs */
584 dopt.dontOutputLOs = true;
585 break;
586
587 case 'c': /* clean (i.e., drop) schema prior to create */
588 dopt.outputClean = 1;
589 break;
590
591 case 'C': /* Create DB */
592 dopt.outputCreateDB = 1;
593 break;
594
595 case 'd': /* database name */
597 break;
598
599 case 'e': /* include extension(s) */
601 dopt.include_everything = false;
602 break;
603
604 case 'E': /* Dump encoding */
606 break;
607
608 case 'f':
610 break;
611
612 case 'F':
614 break;
615
616 case 'h': /* server host */
618 break;
619
620 case 'j': /* number of dump jobs */
621 if (!option_parse_int(optarg, "-j/--jobs", 1,
623 &numWorkers))
624 exit_nicely(1);
625 break;
626
627 case 'n': /* include schema(s) */
629 dopt.include_everything = false;
630 break;
631
632 case 'N': /* exclude schema(s) */
634 break;
635
636 case 'O': /* Don't reconnect to match owner */
637 dopt.outputNoOwner = 1;
638 break;
639
640 case 'p': /* server port */
642 break;
643
644 case 'R':
645 /* no-op, still accepted for backwards compatibility */
646 break;
647
648 case 's': /* dump schema only */
649 schema_only = true;
650 break;
651
652 case 'S': /* Username for superuser in plain text output */
654 break;
655
656 case 't': /* include table(s) */
658 dopt.include_everything = false;
659 break;
660
661 case 'T': /* exclude table(s) */
663 break;
664
665 case 'U':
667 break;
668
669 case 'v': /* verbose */
670 g_verbose = true;
672 break;
673
674 case 'w':
676 break;
677
678 case 'W':
680 break;
681
682 case 'x': /* skip ACL dump */
683 dopt.aclsSkip = true;
684 break;
685
686 case 'Z': /* Compression */
690 break;
691
692 case 0:
693 /* This covers the long options. */
694 break;
695
696 case 2: /* lock-wait-timeout */
698 break;
699
700 case 3: /* SET ROLE */
701 use_role = pg_strdup(optarg);
702 break;
703
704 case 4: /* exclude table(s) data */
706 break;
707
708 case 5: /* section */
710 break;
711
712 case 6: /* snapshot */
714 break;
715
716 case 7: /* no-sync */
717 dosync = false;
718 break;
719
720 case 8:
722 if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
724 exit_nicely(1);
725 break;
726
727 case 9: /* inserts */
728
729 /*
730 * dump_inserts also stores --rows-per-insert, careful not to
731 * overwrite that.
732 */
733 if (dopt.dump_inserts == 0)
735 break;
736
737 case 10: /* rows per insert */
738 if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
739 &dopt.dump_inserts))
740 exit_nicely(1);
741 break;
742
743 case 11: /* include foreign data */
745 optarg);
746 break;
747
748 case 12: /* include table(s) and their children */
750 optarg);
751 dopt.include_everything = false;
752 break;
753
754 case 13: /* exclude table(s) and their children */
756 optarg);
757 break;
758
759 case 14: /* exclude data of table(s) and children */
761 optarg);
762 break;
763
764 case 15:
766 exit_nicely(1);
767 break;
768
769 case 16: /* read object filters from file */
771 break;
772
773 case 17: /* exclude extension(s) */
775 optarg);
776 break;
777
778 case 18:
779 statistics_only = true;
780 break;
781
782 case 19:
783 no_data = true;
784 break;
785
786 case 20:
787 no_schema = true;
788 break;
789
790 case 21:
791 no_statistics = true;
792 break;
793
794 case 22:
795 with_statistics = true;
796 break;
797
798 case 25:
800 break;
801
802 default:
803 /* getopt_long already emitted a complaint */
804 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
805 exit_nicely(1);
806 }
807 }
808
809 /*
810 * Non-option argument specifies database name as long as it wasn't
811 * already specified with -d / --dbname
812 */
813 if (optind < argc && dopt.cparams.dbname == NULL)
814 dopt.cparams.dbname = argv[optind++];
815
816 /* Complain if any arguments remain */
817 if (optind < argc)
818 {
819 pg_log_error("too many command-line arguments (first is \"%s\")",
820 argv[optind]);
821 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
822 exit_nicely(1);
823 }
824
825 /* --column-inserts implies --inserts */
826 if (dopt.column_inserts && dopt.dump_inserts == 0)
828
829 /* *-only options are incompatible with each other */
830 check_mut_excl_opts(data_only, "-a/--data-only",
831 schema_only, "-s/--schema-only",
832 statistics_only, "--statistics-only");
833
834 /* --no-* and *-only for same thing are incompatible */
835 check_mut_excl_opts(data_only, "-a/--data-only",
836 no_data, "--no-data");
837 check_mut_excl_opts(schema_only, "-s/--schema-only",
838 no_schema, "--no-schema");
839 check_mut_excl_opts(statistics_only, "--statistics-only",
840 no_statistics, "--no-statistics");
841
842 /* --statistics and --no-statistics are incompatible */
843 check_mut_excl_opts(with_statistics, "--statistics",
844 no_statistics, "--no-statistics");
845
846 /* --statistics is incompatible with *-only (except --statistics-only) */
847 check_mut_excl_opts(with_statistics, "--statistics",
848 data_only, "-a/--data-only",
849 schema_only, "-s/--schema-only");
850
851 /* --include-foreign-data is incompatible with --schema-only */
853 schema_only, "-s/--schema-only");
854
855 if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
856 pg_fatal("option %s is not supported with parallel backup",
857 "--include-foreign-data");
858
859 /* --clean is incompatible with --data-only */
860 check_mut_excl_opts(dopt.outputClean, "-c/--clean",
861 data_only, "-a/--data-only");
862
863 if (dopt.if_exists && !dopt.outputClean)
864 pg_fatal("option %s requires option %s",
865 "--if-exists", "-c/--clean");
866
867 /*
868 * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
869 * "--schema-only --no-schema", will have already caused an error in one
870 * of the checks above.
871 */
872 dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
873 data_only) && !no_data;
874 dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
876 dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
878
879
880 /*
881 * --inserts are already implied above if --column-inserts or
882 * --rows-per-insert were specified.
883 */
884 if (dopt.do_nothing && dopt.dump_inserts == 0)
885 pg_fatal("option %s requires option %s, %s, or %s",
886 "--on-conflict-do-nothing",
887 "--inserts", "--rows-per-insert", "--column-inserts");
888
889 /* Identify archive format to emit */
891
892 /* archiveFormat specific setup */
893 if (archiveFormat == archNull)
894 {
895 plainText = 1;
896
897 /*
898 * If you don't provide a restrict key, one will be appointed for you.
899 */
900 if (!dopt.restrict_key)
902 if (!dopt.restrict_key)
903 pg_fatal("could not generate restrict key");
905 pg_fatal("invalid restrict key");
906 }
907 else if (dopt.restrict_key)
908 pg_fatal("option %s can only be used with %s",
909 "--restrict-key", "--format=plain");
910
911 /*
912 * Custom and directory formats are compressed by default with gzip when
913 * available, not the others. If gzip is not available, no compression is
914 * done by default.
915 */
918 {
919#ifdef HAVE_LIBZ
921#else
923#endif
924 }
925
926 /*
927 * Compression options
928 */
931 pg_fatal("unrecognized compression algorithm: \"%s\"",
933
935 &compression_spec);
937 if (error_detail != NULL)
938 pg_fatal("invalid compression specification: %s",
940
941 error_detail = supports_compression(compression_spec);
942 if (error_detail != NULL)
943 pg_fatal("%s", error_detail);
944
945 /*
946 * Disable support for zstd workers for now - these are based on
947 * threading, and it's unclear how it interacts with parallel dumps on
948 * platforms where that relies on threads too (e.g. Windows).
949 */
950 if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
951 pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
952 "workers");
953
954 /*
955 * If emitting an archive format, we always want to emit a DATABASE item,
956 * in case --create is specified at pg_restore time.
957 */
958 if (!plainText)
959 dopt.outputCreateDB = 1;
960
961 /* Parallel backup only in the directory archive format so far */
962 if (archiveFormat != archDirectory && numWorkers > 1)
963 pg_fatal("parallel backup only supported by the directory format");
964
965 /* Open the output file */
966 fout = CreateArchive(filename, archiveFormat, compression_spec,
968
969 /* Make dump options accessible right away */
970 SetArchiveOptions(fout, &dopt, NULL);
971
972 /* Register the cleanup hook */
974
975 /* Let the archiver know how noisy to be */
976 fout->verbose = g_verbose;
977
978
979 /*
980 * We allow the server to be back to 10, and up to any minor release of
981 * our own major version. (See also version check in pg_dumpall.c.)
982 */
983 fout->minRemoteVersion = 100000;
984 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
985
986 fout->numWorkers = numWorkers;
987
988 /*
989 * Open the database using the Archiver, so it knows about it. Errors mean
990 * death.
991 */
992 ConnectDatabaseAhx(fout, &dopt.cparams, false);
994
995 /*
996 * On hot standbys, never try to dump unlogged table data, since it will
997 * just throw an error.
998 */
999 if (fout->isStandby)
1000 dopt.no_unlogged_table_data = true;
1001
1002 /*
1003 * Find the last built-in OID, if needed (prior to 8.1)
1004 *
1005 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1006 */
1008
1009 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1010
1011 /* Expand schema selection patterns into OID lists */
1013 {
1016 strict_names);
1018 pg_fatal("no matching schemas were found");
1019 }
1022 false);
1023 /* non-matching exclusion patterns aren't an error */
1024
1025 /* Expand table selection patterns into OID lists */
1028 strict_names, false);
1031 strict_names, true);
1035 pg_fatal("no matching tables were found");
1036
1039 false, false);
1042 false, true);
1043
1046 false, false);
1049 false, true);
1050
1053
1054 /* non-matching exclusion patterns aren't an error */
1055
1056 /* Expand extension selection patterns into OID lists */
1058 {
1061 strict_names);
1063 pg_fatal("no matching extensions were found");
1064 }
1067 false);
1068 /* non-matching exclusion patterns aren't an error */
1069
1070 /*
1071 * Dumping LOs is the default for dumps where an inclusion switch is not
1072 * used (an "include everything" dump). -B can be used to exclude LOs
1073 * from those dumps. -b can be used to include LOs even when an inclusion
1074 * switch is used.
1075 *
1076 * -s means "schema only" and LOs are data, not schema, so we never
1077 * include LOs when -s is used.
1078 */
1079 if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1080 dopt.outputLOs = true;
1081
1082 /*
1083 * Collect role names so we can map object owner OIDs to names.
1084 */
1086
1087 /*
1088 * Now scan the database and create DumpableObject structs for all the
1089 * objects we intend to dump.
1090 */
1091 tblinfo = getSchemaData(fout, &numTables);
1092
1093 if (dopt.dumpData)
1094 {
1095 getTableData(&dopt, tblinfo, numTables, 0);
1097 if (!dopt.dumpSchema)
1099 }
1100
1101 if (!dopt.dumpData && dopt.sequence_data)
1102 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1103
1104 /*
1105 * For binary upgrade mode, dump the pg_shdepend rows for large objects
1106 * and maybe even pg_largeobject_metadata (see comment below for details).
1107 * This is faster to restore than the equivalent set of large object
1108 * commands.
1109 */
1110 if (dopt.binary_upgrade)
1111 {
1113
1116
1117 /*
1118 * Only dump large object shdepend rows for this database.
1119 */
1120 shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1121 "AND dbid = (SELECT oid FROM pg_database "
1122 " WHERE datname = current_database())";
1123
1124 /*
1125 * For binary upgrades from v16 and newer versions, we can copy
1126 * pg_largeobject_metadata's files from the old cluster, so we don't
1127 * need to dump its contents. pg_upgrade can't copy/link the files
1128 * from older versions because aclitem (needed by
1129 * pg_largeobject_metadata.lomacl) changed its storage format in v16.
1130 */
1131 if (fout->remoteVersion < 160000)
1132 {
1134
1137 }
1138 }
1139
1140 /*
1141 * In binary-upgrade mode, we do not have to worry about the actual LO
1142 * data or the associated metadata that resides in the pg_largeobject and
1143 * pg_largeobject_metadata tables, respectively.
1144 *
1145 * However, we do need to collect LO information as there may be comments
1146 * or other information on LOs that we do need to dump out.
1147 */
1148 if (dopt.outputLOs || dopt.binary_upgrade)
1149 getLOs(fout);
1150
1151 /*
1152 * Collect dependency data to assist in ordering the objects.
1153 */
1155
1156 /*
1157 * Collect ACLs, comments, and security labels, if wanted.
1158 */
1159 if (!dopt.aclsSkip)
1161 if (!dopt.no_comments)
1163 if (!dopt.no_security_labels)
1165
1166 /* For binary upgrade mode, collect required pg_class information. */
1167 if (dopt.binary_upgrade)
1169
1170 /* Collect sequence information. */
1172
1173 /* Lastly, create dummy objects to represent the section boundaries */
1175
1176 /* Get pointers to all the known DumpableObjects */
1178
1179 /*
1180 * Add dummy dependencies to enforce the dump section ordering.
1181 */
1183
1184 /*
1185 * Sort the objects into a safe dump order (no forward references).
1186 *
1187 * We rely on dependency information to help us determine a safe order, so
1188 * the initial sort is mostly for cosmetic purposes: we sort by name to
1189 * ensure that logically identical schemas will dump identically.
1190 */
1192
1194 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1195
1196 /*
1197 * Create archive TOC entries for all the objects to be dumped, in a safe
1198 * order.
1199 */
1200
1201 /*
1202 * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1203 */
1207
1208 /* The database items are always next, unless we don't want them at all */
1209 if (dopt.outputCreateDB)
1211
1212 /* Now the rearrangeable objects. */
1213 for (i = 0; i < numObjs; i++)
1215
1216 /*
1217 * Set up options info to ensure we dump what we want.
1218 */
1219 ropt = NewRestoreOptions();
1220 ropt->filename = filename;
1221
1222 /* if you change this list, see dumpOptionsFromRestoreOptions */
1223 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1224 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1225 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1228 ropt->dropSchema = dopt.outputClean;
1229 ropt->dumpData = dopt.dumpData;
1230 ropt->dumpSchema = dopt.dumpSchema;
1231 ropt->dumpStatistics = dopt.dumpStatistics;
1232 ropt->if_exists = dopt.if_exists;
1233 ropt->column_inserts = dopt.column_inserts;
1234 ropt->dumpSections = dopt.dumpSections;
1235 ropt->aclsSkip = dopt.aclsSkip;
1236 ropt->superuser = dopt.outputSuperuser;
1237 ropt->createDB = dopt.outputCreateDB;
1238 ropt->noOwner = dopt.outputNoOwner;
1239 ropt->noTableAm = dopt.outputNoTableAm;
1240 ropt->noTablespace = dopt.outputNoTablespaces;
1242 ropt->use_setsessauth = dopt.use_setsessauth;
1244 ropt->dump_inserts = dopt.dump_inserts;
1245 ropt->no_comments = dopt.no_comments;
1246 ropt->no_policies = dopt.no_policies;
1247 ropt->no_publications = dopt.no_publications;
1250 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1253 ropt->sequence_data = dopt.sequence_data;
1254 ropt->binary_upgrade = dopt.binary_upgrade;
1255 ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1256
1257 ropt->compression_spec = compression_spec;
1258
1259 ropt->suppressDumpWarnings = true; /* We've already shown them */
1260
1261 SetArchiveOptions(fout, &dopt, ropt);
1262
1263 /* Mark which entries should be output */
1265
1266 /*
1267 * The archive's TOC entries are now marked as to which ones will actually
1268 * be output, so we can set up their dependency lists properly. This isn't
1269 * necessary for plain-text output, though.
1270 */
1271 if (!plainText)
1273
1274 /*
1275 * And finally we can do the actual output.
1276 *
1277 * Note: for non-plain-text output formats, the output file is written
1278 * inside CloseArchive(). This is, um, bizarre; but not worth changing
1279 * right now.
1280 */
1281 if (plainText)
1283
1285
1286 exit_nicely(0);
1287}
1288
1289
1290static void
1291help(const char *progname)
1292{
1293 printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1294 printf(_("Usage:\n"));
1295 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1296
1297 printf(_("\nGeneral options:\n"));
1298 printf(_(" -f, --file=FILENAME output file or directory name\n"));
1299 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1300 " plain text (default))\n"));
1301 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1302 printf(_(" -v, --verbose verbose mode\n"));
1303 printf(_(" -V, --version output version information, then exit\n"));
1304 printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1305 " compress as specified\n"));
1306 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1307 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1308 printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1309 printf(_(" -?, --help show this help, then exit\n"));
1310
1311 printf(_("\nOptions controlling the output content:\n"));
1312 printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1313 printf(_(" -b, --large-objects include large objects in dump\n"));
1314 printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1315 printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1316 printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1317 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1318 printf(_(" -C, --create include commands to create database in dump\n"));
1319 printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1320 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1321 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1322 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1323 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1324 " plain-text format\n"));
1325 printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1326 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1327 printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1328 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1329 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1330 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1331 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1332 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1333 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1334 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1335 " access to)\n"));
1336 printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1337 printf(_(" --exclude-table-and-children=PATTERN\n"
1338 " do NOT dump the specified table(s), including\n"
1339 " child and partition tables\n"));
1340 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1341 printf(_(" --exclude-table-data-and-children=PATTERN\n"
1342 " do NOT dump data for the specified table(s),\n"
1343 " including child and partition tables\n"));
1344 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1345 printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1346 " based on expressions in FILENAME\n"));
1347 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1348 printf(_(" --include-foreign-data=PATTERN\n"
1349 " include data of foreign tables on foreign\n"
1350 " servers matching PATTERN\n"));
1351 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1352 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1353 printf(_(" --no-comments do not dump comment commands\n"));
1354 printf(_(" --no-data do not dump data\n"));
1355 printf(_(" --no-policies do not dump row security policies\n"));
1356 printf(_(" --no-publications do not dump publications\n"));
1357 printf(_(" --no-schema do not dump schema\n"));
1358 printf(_(" --no-security-labels do not dump security label assignments\n"));
1359 printf(_(" --no-statistics do not dump statistics\n"));
1360 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1361 printf(_(" --no-table-access-method do not dump table access methods\n"));
1362 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1363 printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1364 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1365 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1366 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1367 printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1368 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1369 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1370 printf(_(" --sequence-data include sequence data in dump\n"));
1371 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1372 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1373 printf(_(" --statistics dump the statistics\n"));
1374 printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1375 printf(_(" --strict-names require table and/or schema include patterns to\n"
1376 " match at least one entity each\n"));
1377 printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1378 " child and partition tables\n"));
1379 printf(_(" --use-set-session-authorization\n"
1380 " use SET SESSION AUTHORIZATION commands instead of\n"
1381 " ALTER OWNER commands to set ownership\n"));
1382
1383 printf(_("\nConnection options:\n"));
1384 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1385 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1386 printf(_(" -p, --port=PORT database server port number\n"));
1387 printf(_(" -U, --username=NAME connect as specified database user\n"));
1388 printf(_(" -w, --no-password never prompt for password\n"));
1389 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1390 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1391
1392 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1393 "variable value is used.\n\n"));
1394 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1395 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1396}
1397
1398static void
1400 const char *dumpsnapshot, char *use_role)
1401{
1402 DumpOptions *dopt = AH->dopt;
1403 PGconn *conn = GetConnection(AH);
1404
1406
1407 /*
1408 * Set the client encoding if requested.
1409 */
1410 if (dumpencoding)
1411 {
1413 pg_fatal("invalid client encoding \"%s\" specified",
1414 dumpencoding);
1415 }
1416
1417 /*
1418 * Force standard_conforming_strings on, just in case we are dumping from
1419 * an old server that has it disabled. Without this, literals in views,
1420 * expressions, etc, would be incorrect for modern servers.
1421 */
1422 ExecuteSqlStatement(AH, "SET standard_conforming_strings = on");
1423
1424 /*
1425 * And reflect that to AH->std_strings. You might think that we should
1426 * just delete that variable and the code that checks it, but that would
1427 * be problematic for pg_restore, which at least for now should still cope
1428 * with archives containing the other setting (cf. processStdStringsEntry
1429 * in pg_backup_archiver.c).
1430 */
1431 AH->std_strings = true;
1432
1433 /*
1434 * Get the active encoding, so we know how to escape strings.
1435 */
1438
1439 /*
1440 * Set the role if requested. In a parallel dump worker, we'll be passed
1441 * use_role == NULL, but AH->use_role is already set (if user specified it
1442 * originally) and we should use that.
1443 */
1444 if (!use_role && AH->use_role)
1445 use_role = AH->use_role;
1446
1447 /* Set the role if requested */
1448 if (use_role)
1449 {
1451
1452 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1453 ExecuteSqlStatement(AH, query->data);
1454 destroyPQExpBuffer(query);
1455
1456 /* save it for possible later use by parallel workers */
1457 if (!AH->use_role)
1458 AH->use_role = pg_strdup(use_role);
1459 }
1460
1461 /* Set the datestyle to ISO to ensure the dump's portability */
1462 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1463
1464 /* Likewise, avoid using sql_standard intervalstyle */
1465 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1466
1467 /*
1468 * Use an explicitly specified extra_float_digits if it has been provided.
1469 * Otherwise, set extra_float_digits so that we can dump float data
1470 * exactly (given correctly implemented float I/O code, anyway).
1471 */
1473 {
1475
1476 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1478 ExecuteSqlStatement(AH, q->data);
1480 }
1481 else
1482 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1483
1484 /*
1485 * Disable synchronized scanning, to prevent unpredictable changes in row
1486 * ordering across a dump and reload.
1487 */
1488 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1489
1490 /*
1491 * Disable timeouts if supported.
1492 */
1493 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1494 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1495 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1496 if (AH->remoteVersion >= 170000)
1497 ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1498
1499 /*
1500 * Quote all identifiers, if requested.
1501 */
1503 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1504
1505 /*
1506 * Adjust row-security mode, if supported.
1507 */
1508 if (dopt->enable_row_security)
1509 ExecuteSqlStatement(AH, "SET row_security = on");
1510 else
1511 ExecuteSqlStatement(AH, "SET row_security = off");
1512
1513 /*
1514 * For security reasons, we restrict the expansion of non-system views and
1515 * access to foreign tables during the pg_dump process. This restriction
1516 * is adjusted when dumping foreign table data.
1517 */
1518 set_restrict_relation_kind(AH, "view, foreign-table");
1519
1520 /*
1521 * Initialize prepared-query state to "nothing prepared". We do this here
1522 * so that a parallel dump worker will have its own state.
1523 */
1525
1526 /*
1527 * Start transaction-snapshot mode transaction to dump consistent data.
1528 */
1529 ExecuteSqlStatement(AH, "BEGIN");
1530
1531 /*
1532 * To support the combination of serializable_deferrable with the jobs
1533 * option we use REPEATABLE READ for the worker connections that are
1534 * passed a snapshot. As long as the snapshot is acquired in a
1535 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1536 * REPEATABLE READ transaction provides the appropriate integrity
1537 * guarantees. This is a kluge, but safe for back-patching.
1538 */
1539 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1541 "SET TRANSACTION ISOLATION LEVEL "
1542 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1543 else
1545 "SET TRANSACTION ISOLATION LEVEL "
1546 "REPEATABLE READ, READ ONLY");
1547
1548 /*
1549 * If user specified a snapshot to use, select that. In a parallel dump
1550 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1551 * is already set (if the server can handle it) and we should use that.
1552 */
1553 if (dumpsnapshot)
1555
1556 if (AH->sync_snapshot_id)
1557 {
1559
1560 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1562 ExecuteSqlStatement(AH, query->data);
1563 destroyPQExpBuffer(query);
1564 }
1565 else if (AH->numWorkers > 1)
1567}
1568
1569/* Set up connection for a parallel worker process */
1570static void
1572{
1573 /*
1574 * We want to re-select all the same values the leader connection is
1575 * using. We'll have inherited directly-usable values in
1576 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1577 * inherited encoding value back to a string to pass to setup_connection.
1578 */
1581 NULL,
1582 NULL);
1583}
1584
1585static char *
1587{
1588 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1589 char *result;
1590 PGresult *res;
1591
1592 res = ExecuteSqlQueryForSingleRow(fout, query);
1593 result = pg_strdup(PQgetvalue(res, 0, 0));
1594 PQclear(res);
1595
1596 return result;
1597}
1598
1599static ArchiveFormat
1601{
1603
1605
1606 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1607 {
1608 /* This is used by pg_dumpall, and is not documented */
1611 }
1612 else if (pg_strcasecmp(format, "c") == 0)
1614 else if (pg_strcasecmp(format, "custom") == 0)
1616 else if (pg_strcasecmp(format, "d") == 0)
1618 else if (pg_strcasecmp(format, "directory") == 0)
1620 else if (pg_strcasecmp(format, "p") == 0)
1622 else if (pg_strcasecmp(format, "plain") == 0)
1624 else if (pg_strcasecmp(format, "t") == 0)
1626 else if (pg_strcasecmp(format, "tar") == 0)
1628 else
1629 pg_fatal("invalid output format \"%s\" specified", format);
1630 return archiveFormat;
1631}
1632
1633/*
1634 * Find the OIDs of all schemas matching the given list of patterns,
1635 * and append them to the given OID list.
1636 */
1637static void
1640 SimpleOidList *oids,
1641 bool strict_names)
1642{
1643 PQExpBuffer query;
1644 PGresult *res;
1646 int i;
1647
1648 if (patterns->head == NULL)
1649 return; /* nothing to do */
1650
1651 query = createPQExpBuffer();
1652
1653 /*
1654 * The loop below runs multiple SELECTs might sometimes result in
1655 * duplicate entries in the OID list, but we don't care.
1656 */
1657
1658 for (cell = patterns->head; cell; cell = cell->next)
1659 {
1661 int dotcnt;
1662
1664 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1666 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1667 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1668 &dotcnt);
1669 if (dotcnt > 1)
1670 pg_fatal("improper qualified name (too many dotted names): %s",
1671 cell->val);
1672 else if (dotcnt == 1)
1675
1676 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1677 if (strict_names && PQntuples(res) == 0)
1678 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1679
1680 for (i = 0; i < PQntuples(res); i++)
1681 {
1682 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1683 }
1684
1685 PQclear(res);
1686 resetPQExpBuffer(query);
1687 }
1688
1689 destroyPQExpBuffer(query);
1690}
1691
1692/*
1693 * Find the OIDs of all extensions matching the given list of patterns,
1694 * and append them to the given OID list.
1695 */
1696static void
1699 SimpleOidList *oids,
1700 bool strict_names)
1701{
1702 PQExpBuffer query;
1703 PGresult *res;
1705 int i;
1706
1707 if (patterns->head == NULL)
1708 return; /* nothing to do */
1709
1710 query = createPQExpBuffer();
1711
1712 /*
1713 * The loop below runs multiple SELECTs might sometimes result in
1714 * duplicate entries in the OID list, but we don't care.
1715 */
1716 for (cell = patterns->head; cell; cell = cell->next)
1717 {
1718 int dotcnt;
1719
1721 "SELECT oid FROM pg_catalog.pg_extension e\n");
1722 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1723 false, NULL, "e.extname", NULL, NULL, NULL,
1724 &dotcnt);
1725 if (dotcnt > 0)
1726 pg_fatal("improper qualified name (too many dotted names): %s",
1727 cell->val);
1728
1729 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1730 if (strict_names && PQntuples(res) == 0)
1731 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1732
1733 for (i = 0; i < PQntuples(res); i++)
1734 {
1735 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1736 }
1737
1738 PQclear(res);
1739 resetPQExpBuffer(query);
1740 }
1741
1742 destroyPQExpBuffer(query);
1743}
1744
1745/*
1746 * Find the OIDs of all foreign servers matching the given list of patterns,
1747 * and append them to the given OID list.
1748 */
1749static void
1752 SimpleOidList *oids)
1753{
1754 PQExpBuffer query;
1755 PGresult *res;
1757 int i;
1758
1759 if (patterns->head == NULL)
1760 return; /* nothing to do */
1761
1762 query = createPQExpBuffer();
1763
1764 /*
1765 * The loop below runs multiple SELECTs might sometimes result in
1766 * duplicate entries in the OID list, but we don't care.
1767 */
1768
1769 for (cell = patterns->head; cell; cell = cell->next)
1770 {
1771 int dotcnt;
1772
1774 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1775 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1776 false, NULL, "s.srvname", NULL, NULL, NULL,
1777 &dotcnt);
1778 if (dotcnt > 0)
1779 pg_fatal("improper qualified name (too many dotted names): %s",
1780 cell->val);
1781
1782 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1783 if (PQntuples(res) == 0)
1784 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1785
1786 for (i = 0; i < PQntuples(res); i++)
1787 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1788
1789 PQclear(res);
1790 resetPQExpBuffer(query);
1791 }
1792
1793 destroyPQExpBuffer(query);
1794}
1795
1796/*
1797 * Find the OIDs of all tables matching the given list of patterns,
1798 * and append them to the given OID list. See also expand_dbname_patterns()
1799 * in pg_dumpall.c
1800 */
1801static void
1805{
1806 PQExpBuffer query;
1807 PGresult *res;
1809 int i;
1810
1811 if (patterns->head == NULL)
1812 return; /* nothing to do */
1813
1814 query = createPQExpBuffer();
1815
1816 /*
1817 * this might sometimes result in duplicate entries in the OID list, but
1818 * we don't care.
1819 */
1820
1821 for (cell = patterns->head; cell; cell = cell->next)
1822 {
1824 int dotcnt;
1825
1826 /*
1827 * Query must remain ABSOLUTELY devoid of unqualified names. This
1828 * would be unnecessary given a pg_table_is_visible() variant taking a
1829 * search_path argument.
1830 *
1831 * For with_child_tables, we start with the basic query's results and
1832 * recursively search the inheritance tree to add child tables.
1833 */
1835 {
1836 appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1837 }
1838
1839 appendPQExpBuffer(query,
1840 "SELECT c.oid"
1841 "\nFROM pg_catalog.pg_class c"
1842 "\n LEFT JOIN pg_catalog.pg_namespace n"
1843 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1844 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1845 "\n (array['%c', '%c', '%c', '%c', '%c', '%c', '%c'])\n",
1850 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1851 false, "n.nspname", "c.relname", NULL,
1852 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1853 &dotcnt);
1854 if (dotcnt > 2)
1855 pg_fatal("improper relation name (too many dotted names): %s",
1856 cell->val);
1857 else if (dotcnt == 2)
1860
1862 {
1863 appendPQExpBufferStr(query, "UNION"
1864 "\nSELECT i.inhrelid"
1865 "\nFROM partition_tree p"
1866 "\n JOIN pg_catalog.pg_inherits i"
1867 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1868 "\n)"
1869 "\nSELECT relid FROM partition_tree");
1870 }
1871
1872 ExecuteSqlStatement(fout, "RESET search_path");
1873 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1876 if (strict_names && PQntuples(res) == 0)
1877 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1878
1879 for (i = 0; i < PQntuples(res); i++)
1880 {
1881 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1882 }
1883
1884 PQclear(res);
1885 resetPQExpBuffer(query);
1886 }
1887
1888 destroyPQExpBuffer(query);
1889}
1890
1891/*
1892 * Verifies that the connected database name matches the given database name,
1893 * and if not, dies with an error about the given pattern.
1894 *
1895 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1896 */
1897static void
1898prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1899{
1900 const char *db;
1901
1902 db = PQdb(conn);
1903 if (db == NULL)
1904 pg_fatal("You are currently not connected to a database.");
1905
1906 if (strcmp(db, dbname) != 0)
1907 pg_fatal("cross-database references are not implemented: %s",
1908 pattern);
1909}
1910
1911/*
1912 * checkExtensionMembership
1913 * Determine whether object is an extension member, and if so,
1914 * record an appropriate dependency and set the object's dump flag.
1915 *
1916 * It's important to call this for each object that could be an extension
1917 * member. Generally, we integrate this with determining the object's
1918 * to-be-dumped-ness, since extension membership overrides other rules for that.
1919 *
1920 * Returns true if object is an extension member, else false.
1921 */
1922static bool
1924{
1926
1927 if (ext == NULL)
1928 return false;
1929
1930 dobj->ext_member = true;
1931
1932 /* Record dependency so that getDependencies needn't deal with that */
1933 addObjectDependency(dobj, ext->dobj.dumpId);
1934
1935 /*
1936 * Mark the member object to have any non-initial ACLs dumped. (Any
1937 * initial ACLs will be removed later, using data from pg_init_privs, so
1938 * that we'll dump only the delta from the extension's initial setup.)
1939 *
1940 * In binary upgrades, we still dump all components of the members
1941 * individually, since the idea is to exactly reproduce the database
1942 * contents rather than replace the extension contents with something
1943 * different.
1944 *
1945 * Note: it might be interesting someday to implement storage and delta
1946 * dumping of extension members' RLS policies and/or security labels.
1947 * However there is a pitfall for RLS policies: trying to dump them
1948 * requires getting a lock on their tables, and the calling user might not
1949 * have privileges for that. We need no lock to examine a table's ACLs,
1950 * so the current feature doesn't have a problem of that sort.
1951 */
1952 if (fout->dopt->binary_upgrade)
1953 dobj->dump = ext->dobj.dump;
1954 else
1955 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1956
1957 return true;
1958}
1959
1960/*
1961 * selectDumpableNamespace: policy-setting subroutine
1962 * Mark a namespace as to be dumped or not
1963 */
1964static void
1966{
1967 /*
1968 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1969 * and (for --clean) a DROP SCHEMA statement. (In the absence of
1970 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1971 */
1972 nsinfo->create = true;
1973
1974 /*
1975 * If specific tables are being dumped, do not dump any complete
1976 * namespaces. If specific namespaces are being dumped, dump just those
1977 * namespaces. Otherwise, dump all non-system namespaces.
1978 */
1980 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1981 else if (schema_include_oids.head != NULL)
1982 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1984 nsinfo->dobj.catId.oid) ?
1986 else if (strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1987 {
1988 /*
1989 * We dump out any ACLs defined in pg_catalog, if they are interesting
1990 * (and not the original ACLs which were set at initdb time, see
1991 * pg_init_privs).
1992 */
1993 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1994 }
1995 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1996 strcmp(nsinfo->dobj.name, "information_schema") == 0)
1997 {
1998 /* Other system schemas don't get dumped */
1999 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2000 }
2001 else if (strcmp(nsinfo->dobj.name, "public") == 0)
2002 {
2003 /*
2004 * The public schema is a strange beast that sits in a sort of
2005 * no-mans-land between being a system object and a user object.
2006 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2007 * a comment and an indication of ownership. If the owner is the
2008 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2009 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2010 */
2011 nsinfo->create = false;
2012 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2013 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2014 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2015 nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2016
2017 /*
2018 * Also, make like it has a comment even if it doesn't; this is so
2019 * that we'll emit a command to drop the comment, if appropriate.
2020 * (Without this, we'd not call dumpCommentExtended for it.)
2021 */
2022 nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2023 }
2024 else
2025 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2026
2027 /*
2028 * In any case, a namespace can be excluded by an exclusion switch
2029 */
2030 if (nsinfo->dobj.dump_contains &&
2032 nsinfo->dobj.catId.oid))
2033 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2034
2035 /*
2036 * If the schema belongs to an extension, allow extension membership to
2037 * override the dump decision for the schema itself. However, this does
2038 * not change dump_contains, so this won't change what we do with objects
2039 * within the schema. (If they belong to the extension, they'll get
2040 * suppressed by it, otherwise not.)
2041 */
2043}
2044
2045/*
2046 * selectDumpableTable: policy-setting subroutine
2047 * Mark a table as to be dumped or not
2048 */
2049static void
2051{
2053 return; /* extension membership overrides all else */
2054
2055 /*
2056 * If specific tables are being dumped, dump just those tables; else, dump
2057 * according to the parent namespace's dump flag.
2058 */
2061 tbinfo->dobj.catId.oid) ?
2063 else
2064 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2065
2066 /*
2067 * In any case, a table can be excluded by an exclusion switch
2068 */
2069 if (tbinfo->dobj.dump &&
2071 tbinfo->dobj.catId.oid))
2072 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2073}
2074
2075/*
2076 * selectDumpableType: policy-setting subroutine
2077 * Mark a type as to be dumped or not
2078 *
2079 * If it's a table's rowtype or an autogenerated array type, we also apply a
2080 * special type code to facilitate sorting into the desired order. (We don't
2081 * want to consider those to be ordinary types because that would bring tables
2082 * up into the datatype part of the dump order.) We still set the object's
2083 * dump flag; that's not going to cause the dummy type to be dumped, but we
2084 * need it so that casts involving such types will be dumped correctly -- see
2085 * dumpCast. This means the flag should be set the same as for the underlying
2086 * object (the table or base type).
2087 */
2088static void
2090{
2091 /* skip complex types, except for standalone composite types */
2092 if (OidIsValid(tyinfo->typrelid) &&
2093 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2094 {
2095 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2096
2097 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2098 if (tytable != NULL)
2099 tyinfo->dobj.dump = tytable->dobj.dump;
2100 else
2101 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2102 return;
2103 }
2104
2105 /* skip auto-generated array and multirange types */
2106 if (tyinfo->isArray || tyinfo->isMultirange)
2107 {
2108 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2109
2110 /*
2111 * Fall through to set the dump flag; we assume that the subsequent
2112 * rules will do the same thing as they would for the array's base
2113 * type or multirange's range type. (We cannot reliably look up the
2114 * base type here, since getTypes may not have processed it yet.)
2115 */
2116 }
2117
2119 return; /* extension membership overrides all else */
2120
2121 /* Dump based on if the contents of the namespace are being dumped */
2122 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2123}
2124
2125/*
2126 * selectDumpableDefaultACL: policy-setting subroutine
2127 * Mark a default ACL as to be dumped or not
2128 *
2129 * For per-schema default ACLs, dump if the schema is to be dumped.
2130 * Otherwise dump if we are dumping "everything". Note that dumpSchema
2131 * and aclsSkip are checked separately.
2132 */
2133static void
2135{
2136 /* Default ACLs can't be extension members */
2137
2138 if (dinfo->dobj.namespace)
2139 /* default ACLs are considered part of the namespace */
2140 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2141 else
2142 dinfo->dobj.dump = dopt->include_everything ?
2144}
2145
2146/*
2147 * selectDumpableCast: policy-setting subroutine
2148 * Mark a cast as to be dumped or not
2149 *
2150 * Casts do not belong to any particular namespace (since they haven't got
2151 * names), nor do they have identifiable owners. To distinguish user-defined
2152 * casts from built-in ones, we must resort to checking whether the cast's
2153 * OID is in the range reserved for initdb.
2154 */
2155static void
2157{
2158 if (checkExtensionMembership(&cast->dobj, fout))
2159 return; /* extension membership overrides all else */
2160
2161 /*
2162 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2163 * support ACLs currently.
2164 */
2165 if (cast->dobj.catId.oid <= g_last_builtin_oid)
2166 cast->dobj.dump = DUMP_COMPONENT_NONE;
2167 else
2168 cast->dobj.dump = fout->dopt->include_everything ?
2170}
2171
2172/*
2173 * selectDumpableProcLang: policy-setting subroutine
2174 * Mark a procedural language as to be dumped or not
2175 *
2176 * Procedural languages do not belong to any particular namespace. To
2177 * identify built-in languages, we must resort to checking whether the
2178 * language's OID is in the range reserved for initdb.
2179 */
2180static void
2182{
2183 if (checkExtensionMembership(&plang->dobj, fout))
2184 return; /* extension membership overrides all else */
2185
2186 /*
2187 * Only include procedural languages when we are dumping everything.
2188 *
2189 * For from-initdb procedural languages, only include ACLs, as we do for
2190 * the pg_catalog namespace. We need this because procedural languages do
2191 * not live in any namespace.
2192 */
2193 if (!fout->dopt->include_everything)
2194 plang->dobj.dump = DUMP_COMPONENT_NONE;
2195 else
2196 {
2197 if (plang->dobj.catId.oid <= g_last_builtin_oid)
2198 plang->dobj.dump = DUMP_COMPONENT_ACL;
2199 else
2200 plang->dobj.dump = DUMP_COMPONENT_ALL;
2201 }
2202}
2203
2204/*
2205 * selectDumpableAccessMethod: policy-setting subroutine
2206 * Mark an access method as to be dumped or not
2207 *
2208 * Access methods do not belong to any particular namespace. To identify
2209 * built-in access methods, we must resort to checking whether the
2210 * method's OID is in the range reserved for initdb.
2211 */
2212static void
2214{
2215 if (checkExtensionMembership(&method->dobj, fout))
2216 return; /* extension membership overrides all else */
2217
2218 /*
2219 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2220 * they do not support ACLs currently.
2221 */
2222 if (method->dobj.catId.oid <= g_last_builtin_oid)
2223 method->dobj.dump = DUMP_COMPONENT_NONE;
2224 else
2225 method->dobj.dump = fout->dopt->include_everything ?
2227}
2228
2229/*
2230 * selectDumpableExtension: policy-setting subroutine
2231 * Mark an extension as to be dumped or not
2232 *
2233 * Built-in extensions should be skipped except for checking ACLs, since we
2234 * assume those will already be installed in the target database. We identify
2235 * such extensions by their having OIDs in the range reserved for initdb.
2236 * We dump all user-added extensions by default. No extensions are dumped
2237 * if include_everything is false (i.e., a --schema or --table switch was
2238 * given), except if --extension specifies a list of extensions to dump.
2239 */
2240static void
2242{
2243 /*
2244 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2245 * change permissions on their member objects, if they wish to, and have
2246 * those changes preserved.
2247 */
2248 if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2249 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2250 else
2251 {
2252 /* check if there is a list of extensions to dump */
2254 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2256 extinfo->dobj.catId.oid) ?
2258 else
2259 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2260 dopt->include_everything ?
2262
2263 /* check that the extension is not explicitly excluded */
2264 if (extinfo->dobj.dump &&
2266 extinfo->dobj.catId.oid))
2267 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2268 }
2269}
2270
2271/*
2272 * selectDumpablePublicationObject: policy-setting subroutine
2273 * Mark a publication object as to be dumped or not
2274 *
2275 * A publication can have schemas and tables which have schemas, but those are
2276 * ignored in decision making, because publications are only dumped when we are
2277 * dumping everything.
2278 */
2279static void
2281{
2282 if (checkExtensionMembership(dobj, fout))
2283 return; /* extension membership overrides all else */
2284
2285 dobj->dump = fout->dopt->include_everything ?
2287}
2288
2289/*
2290 * selectDumpableStatisticsObject: policy-setting subroutine
2291 * Mark an extended statistics object as to be dumped or not
2292 *
2293 * We dump an extended statistics object if the schema it's in and the table
2294 * it's for are being dumped. (This'll need more thought if statistics
2295 * objects ever support cross-table stats.)
2296 */
2297static void
2299{
2300 if (checkExtensionMembership(&sobj->dobj, fout))
2301 return; /* extension membership overrides all else */
2302
2303 sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2304 if (sobj->stattable == NULL ||
2305 !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2306 sobj->dobj.dump = DUMP_COMPONENT_NONE;
2307}
2308
2309/*
2310 * selectDumpableObject: policy-setting subroutine
2311 * Mark a generic dumpable object as to be dumped or not
2312 *
2313 * Use this only for object types without a special-case routine above.
2314 */
2315static void
2317{
2318 if (checkExtensionMembership(dobj, fout))
2319 return; /* extension membership overrides all else */
2320
2321 /*
2322 * Default policy is to dump if parent namespace is dumpable, or for
2323 * non-namespace-associated items, dump if we're dumping "everything".
2324 */
2325 if (dobj->namespace)
2326 dobj->dump = dobj->namespace->dobj.dump_contains;
2327 else
2328 dobj->dump = fout->dopt->include_everything ?
2330}
2331
2332/*
2333 * Dump a table's contents for loading using the COPY command
2334 * - this routine is called by the Archiver when it wants the table
2335 * to be dumped.
2336 */
2337static int
2339{
2340 const TableDataInfo *tdinfo = dcontext;
2341 const TableInfo *tbinfo = tdinfo->tdtable;
2342 const char *classname = tbinfo->dobj.name;
2344
2345 /*
2346 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2347 * which uses it already.
2348 */
2351 PGresult *res;
2352 int ret;
2353 char *copybuf;
2354 const char *column_list;
2355
2356 pg_log_info("dumping contents of table \"%s.%s\"",
2357 tbinfo->dobj.namespace->dobj.name, classname);
2358
2359 /*
2360 * Specify the column list explicitly so that we have no possibility of
2361 * retrieving data in the wrong column order. (The default column
2362 * ordering of COPY will not be what we want in certain corner cases
2363 * involving ADD COLUMN and inheritance.)
2364 */
2366
2367 /*
2368 * Use COPY (SELECT ...) TO when dumping a foreign table's data, when a
2369 * filter condition was specified, and when in binary upgrade mode and
2370 * dumping an old pg_largeobject_metadata defined WITH OIDS. For other
2371 * cases a simple COPY suffices.
2372 */
2373 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
2374 (fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
2375 tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
2376 {
2377 /* Temporary allows to access to foreign tables to dump data */
2378 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2380
2381 appendPQExpBufferStr(q, "COPY (SELECT ");
2382 /* klugery to get rid of parens in column list */
2383 if (strlen(column_list) > 2)
2384 {
2386 q->data[q->len - 1] = ' ';
2387 }
2388 else
2389 appendPQExpBufferStr(q, "* ");
2390
2391 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2393 tdinfo->filtercond ? tdinfo->filtercond : "");
2394 }
2395 else
2396 {
2397 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2399 column_list);
2400 }
2402 PQclear(res);
2404
2405 for (;;)
2406 {
2407 ret = PQgetCopyData(conn, &copybuf, 0);
2408
2409 if (ret < 0)
2410 break; /* done or error */
2411
2412 if (copybuf)
2413 {
2414 WriteData(fout, copybuf, ret);
2416 }
2417
2418 /* ----------
2419 * THROTTLE:
2420 *
2421 * There was considerable discussion in late July, 2000 regarding
2422 * slowing down pg_dump when backing up large tables. Users with both
2423 * slow & fast (multi-processor) machines experienced performance
2424 * degradation when doing a backup.
2425 *
2426 * Initial attempts based on sleeping for a number of ms for each ms
2427 * of work were deemed too complex, then a simple 'sleep in each loop'
2428 * implementation was suggested. The latter failed because the loop
2429 * was too tight. Finally, the following was implemented:
2430 *
2431 * If throttle is non-zero, then
2432 * See how long since the last sleep.
2433 * Work out how long to sleep (based on ratio).
2434 * If sleep is more than 100ms, then
2435 * sleep
2436 * reset timer
2437 * EndIf
2438 * EndIf
2439 *
2440 * where the throttle value was the number of ms to sleep per ms of
2441 * work. The calculation was done in each loop.
2442 *
2443 * Most of the hard work is done in the backend, and this solution
2444 * still did not work particularly well: on slow machines, the ratio
2445 * was 50:1, and on medium paced machines, 1:1, and on fast
2446 * multi-processor machines, it had little or no effect, for reasons
2447 * that were unclear.
2448 *
2449 * Further discussion ensued, and the proposal was dropped.
2450 *
2451 * For those people who want this feature, it can be implemented using
2452 * gettimeofday in each loop, calculating the time since last sleep,
2453 * multiplying that by the sleep ratio, then if the result is more
2454 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2455 * function to sleep for a subsecond period ie.
2456 *
2457 * select(0, NULL, NULL, NULL, &tvi);
2458 *
2459 * This will return after the interval specified in the structure tvi.
2460 * Finally, call gettimeofday again to save the 'last sleep time'.
2461 * ----------
2462 */
2463 }
2464 archprintf(fout, "\\.\n\n\n");
2465
2466 if (ret == -2)
2467 {
2468 /* copy data transfer failed */
2469 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2470 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2471 pg_log_error_detail("Command was: %s", q->data);
2472 exit_nicely(1);
2473 }
2474
2475 /* Check command status and return to normal libpq state */
2476 res = PQgetResult(conn);
2477 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2478 {
2479 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2480 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2481 pg_log_error_detail("Command was: %s", q->data);
2482 exit_nicely(1);
2483 }
2484 PQclear(res);
2485
2486 /* Do this to ensure we've pumped libpq back to idle state */
2487 if (PQgetResult(conn) != NULL)
2488 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2489 classname);
2490
2492
2493 /* Revert back the setting */
2494 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2495 set_restrict_relation_kind(fout, "view, foreign-table");
2496
2497 return 1;
2498}
2499
2500/*
2501 * Dump table data using INSERT commands.
2502 *
2503 * Caution: when we restore from an archive file direct to database, the
2504 * INSERT commands emitted by this function have to be parsed by
2505 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2506 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2507 */
2508static int
2510{
2511 const TableDataInfo *tdinfo = dcontext;
2512 const TableInfo *tbinfo = tdinfo->tdtable;
2513 DumpOptions *dopt = fout->dopt;
2516 char *attgenerated;
2517 PGresult *res;
2518 int nfields,
2519 i;
2520 int rows_per_statement = dopt->dump_inserts;
2521 int rows_this_statement = 0;
2522
2523 /* Temporary allows to access to foreign tables to dump data */
2524 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2526
2527 /*
2528 * If we're going to emit INSERTs with column names, the most efficient
2529 * way to deal with generated columns is to exclude them entirely. For
2530 * INSERTs without column names, we have to emit DEFAULT rather than the
2531 * actual column value --- but we can save a few cycles by fetching nulls
2532 * rather than the uninteresting-to-us value.
2533 */
2534 attgenerated = pg_malloc_array(char, tbinfo->numatts);
2535 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2536 nfields = 0;
2537 for (i = 0; i < tbinfo->numatts; i++)
2538 {
2539 if (tbinfo->attisdropped[i])
2540 continue;
2541 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2542 continue;
2543 if (nfields > 0)
2544 appendPQExpBufferStr(q, ", ");
2545 if (tbinfo->attgenerated[i])
2546 appendPQExpBufferStr(q, "NULL");
2547 else
2548 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2549 attgenerated[nfields] = tbinfo->attgenerated[i];
2550 nfields++;
2551 }
2552 /* Servers before 9.4 will complain about zero-column SELECT */
2553 if (nfields == 0)
2554 appendPQExpBufferStr(q, "NULL");
2555 appendPQExpBuffer(q, " FROM ONLY %s",
2557 if (tdinfo->filtercond)
2558 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2559
2561
2562 while (1)
2563 {
2564 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2566
2567 /* cross-check field count, allowing for dummy NULL if any */
2568 if (nfields != PQnfields(res) &&
2569 !(nfields == 0 && PQnfields(res) == 1))
2570 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2571 tbinfo->dobj.name);
2572
2573 /*
2574 * First time through, we build as much of the INSERT statement as
2575 * possible in "insertStmt", which we can then just print for each
2576 * statement. If the table happens to have zero dumpable columns then
2577 * this will be a complete statement, otherwise it will end in
2578 * "VALUES" and be ready to have the row's column values printed.
2579 */
2580 if (insertStmt == NULL)
2581 {
2582 const TableInfo *targettab;
2583
2585
2586 /*
2587 * When load-via-partition-root is set or forced, get the root
2588 * table name for the partition table, so that we can reload data
2589 * through the root table.
2590 */
2591 if (tbinfo->ispartition &&
2592 (dopt->load_via_partition_root ||
2595 else
2596 targettab = tbinfo;
2597
2598 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2600
2601 /* corner case for zero-column table */
2602 if (nfields == 0)
2603 {
2604 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2605 }
2606 else
2607 {
2608 /* append the list of column names if required */
2609 if (dopt->column_inserts)
2610 {
2612 for (int field = 0; field < nfields; field++)
2613 {
2614 if (field > 0)
2617 fmtId(PQfname(res, field)));
2618 }
2620 }
2621
2622 if (tbinfo->needs_override)
2623 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2624
2626 }
2627 }
2628
2629 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2630 {
2631 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2632 if (rows_this_statement == 0)
2633 archputs(insertStmt->data, fout);
2634
2635 /*
2636 * If it is zero-column table then we've already written the
2637 * complete statement, which will mean we've disobeyed
2638 * --rows-per-insert when it's set greater than 1. We do support
2639 * a way to make this multi-row with: SELECT UNION ALL SELECT
2640 * UNION ALL ... but that's non-standard so we should avoid it
2641 * given that using INSERTs is mostly only ever needed for
2642 * cross-database exports.
2643 */
2644 if (nfields == 0)
2645 continue;
2646
2647 /* Emit a row heading */
2648 if (rows_per_statement == 1)
2649 archputs(" (", fout);
2650 else if (rows_this_statement > 0)
2651 archputs(",\n\t(", fout);
2652 else
2653 archputs("\n\t(", fout);
2654
2655 for (int field = 0; field < nfields; field++)
2656 {
2657 if (field > 0)
2658 archputs(", ", fout);
2659 if (attgenerated[field])
2660 {
2661 archputs("DEFAULT", fout);
2662 continue;
2663 }
2664 if (PQgetisnull(res, tuple, field))
2665 {
2666 archputs("NULL", fout);
2667 continue;
2668 }
2669
2670 /* XXX This code is partially duplicated in ruleutils.c */
2671 switch (PQftype(res, field))
2672 {
2673 case INT2OID:
2674 case INT4OID:
2675 case INT8OID:
2676 case OIDOID:
2677 case FLOAT4OID:
2678 case FLOAT8OID:
2679 case NUMERICOID:
2680 {
2681 /*
2682 * These types are printed without quotes unless
2683 * they contain values that aren't accepted by the
2684 * scanner unquoted (e.g., 'NaN'). Note that
2685 * strtod() and friends might accept NaN, so we
2686 * can't use that to test.
2687 *
2688 * In reality we only need to defend against
2689 * infinity and NaN, so we need not get too crazy
2690 * about pattern matching here.
2691 */
2692 const char *s = PQgetvalue(res, tuple, field);
2693
2694 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2695 archputs(s, fout);
2696 else
2697 archprintf(fout, "'%s'", s);
2698 }
2699 break;
2700
2701 case BITOID:
2702 case VARBITOID:
2703 archprintf(fout, "B'%s'",
2704 PQgetvalue(res, tuple, field));
2705 break;
2706
2707 case BOOLOID:
2708 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2709 archputs("true", fout);
2710 else
2711 archputs("false", fout);
2712 break;
2713
2714 default:
2715 /* All other types are printed as string literals. */
2718 PQgetvalue(res, tuple, field),
2719 fout);
2720 archputs(q->data, fout);
2721 break;
2722 }
2723 }
2724
2725 /* Terminate the row ... */
2726 archputs(")", fout);
2727
2728 /* ... and the statement, if the target no. of rows is reached */
2730 {
2731 if (dopt->do_nothing)
2732 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2733 else
2734 archputs(";\n", fout);
2735 /* Reset the row counter */
2737 }
2738 }
2739
2740 if (PQntuples(res) <= 0)
2741 {
2742 PQclear(res);
2743 break;
2744 }
2745 PQclear(res);
2746 }
2747
2748 /* Terminate any statements that didn't make the row count. */
2749 if (rows_this_statement > 0)
2750 {
2751 if (dopt->do_nothing)
2752 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2753 else
2754 archputs(";\n", fout);
2755 }
2756
2757 archputs("\n\n", fout);
2758
2759 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2760
2762 if (insertStmt != NULL)
2764 pg_free(attgenerated);
2765
2766 /* Revert back the setting */
2767 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2768 set_restrict_relation_kind(fout, "view, foreign-table");
2769
2770 return 1;
2771}
2772
2773/*
2774 * getRootTableInfo:
2775 * get the root TableInfo for the given partition table.
2776 */
2777static TableInfo *
2779{
2781
2782 Assert(tbinfo->ispartition);
2783 Assert(tbinfo->numParents == 1);
2784
2785 parentTbinfo = tbinfo->parents[0];
2786 while (parentTbinfo->ispartition)
2787 {
2788 Assert(parentTbinfo->numParents == 1);
2789 parentTbinfo = parentTbinfo->parents[0];
2790 }
2791
2792 return parentTbinfo;
2793}
2794
2795/*
2796 * forcePartitionRootLoad
2797 * Check if we must force load_via_partition_root for this partition.
2798 *
2799 * This is required if any level of ancestral partitioned table has an
2800 * unsafe partitioning scheme.
2801 */
2802static bool
2804{
2806
2807 Assert(tbinfo->ispartition);
2808 Assert(tbinfo->numParents == 1);
2809
2810 parentTbinfo = tbinfo->parents[0];
2811 if (parentTbinfo->unsafe_partitions)
2812 return true;
2813 while (parentTbinfo->ispartition)
2814 {
2815 Assert(parentTbinfo->numParents == 1);
2816 parentTbinfo = parentTbinfo->parents[0];
2817 if (parentTbinfo->unsafe_partitions)
2818 return true;
2819 }
2820
2821 return false;
2822}
2823
2824/*
2825 * dumpTableData -
2826 * dump the contents of a single table
2827 *
2828 * Actually, this just makes an ArchiveEntry for the table contents.
2829 */
2830static void
2832{
2833 DumpOptions *dopt = fout->dopt;
2834 const TableInfo *tbinfo = tdinfo->tdtable;
2837 DataDumperPtr dumpFn;
2838 char *tdDefn = NULL;
2839 char *copyStmt;
2840 const char *copyFrom;
2841
2842 /* We had better have loaded per-column details about this table */
2843 Assert(tbinfo->interesting);
2844
2845 /*
2846 * When load-via-partition-root is set or forced, get the root table name
2847 * for the partition table, so that we can reload data through the root
2848 * table. Then construct a comment to be inserted into the TOC entry's
2849 * defn field, so that such cases can be identified reliably.
2850 */
2851 if (tbinfo->ispartition &&
2852 (dopt->load_via_partition_root ||
2854 {
2855 const TableInfo *parentTbinfo;
2856 char *sanitized;
2857
2861 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2862 sanitized);
2863 free(sanitized);
2864 tdDefn = pg_strdup(copyBuf->data);
2865 }
2866 else
2868
2869 if (dopt->dump_inserts == 0)
2870 {
2871 /* Dump/restore using COPY */
2872 dumpFn = dumpTableData_copy;
2873 /* must use 2 steps here 'cause fmtId is nonreentrant */
2874 printfPQExpBuffer(copyBuf, "COPY %s ",
2875 copyFrom);
2876 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2878 copyStmt = copyBuf->data;
2879 }
2880 else
2881 {
2882 /* Restore using INSERT */
2883 dumpFn = dumpTableData_insert;
2884 copyStmt = NULL;
2885 }
2886
2887 /*
2888 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2889 * dependency on its table as "special" and pass it to ArchiveEntry now.
2890 * See comments for BuildArchiveDependencies.
2891 */
2892 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2893 {
2894 TocEntry *te;
2895
2896 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2897 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2898 .namespace = tbinfo->dobj.namespace->dobj.name,
2899 .owner = tbinfo->rolname,
2900 .description = "TABLE DATA",
2901 .section = SECTION_DATA,
2902 .createStmt = tdDefn,
2903 .copyStmt = copyStmt,
2904 .deps = &(tbinfo->dobj.dumpId),
2905 .nDeps = 1,
2906 .dumpFn = dumpFn,
2907 .dumpArg = tdinfo));
2908
2909 /*
2910 * Set the TocEntry's dataLength in case we are doing a parallel dump
2911 * and want to order dump jobs by table size. We choose to measure
2912 * dataLength in table pages (including TOAST pages) during dump, so
2913 * no scaling is needed.
2914 *
2915 * However, relpages is declared as "integer" in pg_class, and hence
2916 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2917 * Cast so that we get the right interpretation of table sizes
2918 * exceeding INT_MAX pages.
2919 */
2920 te->dataLength = (BlockNumber) tbinfo->relpages;
2921 te->dataLength += (BlockNumber) tbinfo->toastpages;
2922
2923 /*
2924 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2925 * and instead we'd better worry about integer overflow. Clamp to
2926 * INT_MAX if the correct result exceeds that.
2927 */
2928 if (sizeof(te->dataLength) == 4 &&
2929 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2930 te->dataLength < 0))
2931 te->dataLength = INT_MAX;
2932 }
2933
2936}
2937
2938/*
2939 * refreshMatViewData -
2940 * load or refresh the contents of a single materialized view
2941 *
2942 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2943 * statement.
2944 */
2945static void
2947{
2948 TableInfo *tbinfo = tdinfo->tdtable;
2949 PQExpBuffer q;
2950
2951 /* If the materialized view is not flagged as populated, skip this. */
2952 if (!tbinfo->relispopulated)
2953 return;
2954
2955 q = createPQExpBuffer();
2956
2957 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2959
2960 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2962 tdinfo->dobj.catId, /* catalog ID */
2963 tdinfo->dobj.dumpId, /* dump ID */
2964 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2965 .namespace = tbinfo->dobj.namespace->dobj.name,
2966 .owner = tbinfo->rolname,
2967 .description = "MATERIALIZED VIEW DATA",
2968 .section = SECTION_POST_DATA,
2969 .createStmt = q->data,
2970 .deps = tdinfo->dobj.dependencies,
2971 .nDeps = tdinfo->dobj.nDeps));
2972
2974}
2975
2976/*
2977 * getTableData -
2978 * set up dumpable objects representing the contents of tables
2979 */
2980static void
2981getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2982{
2983 int i;
2984
2985 for (i = 0; i < numTables; i++)
2986 {
2987 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2988 (!relkind || tblinfo[i].relkind == relkind))
2989 makeTableDataInfo(dopt, &(tblinfo[i]));
2990 }
2991}
2992
2993/*
2994 * Make a dumpable object for the data of this specific table
2995 *
2996 * Note: we make a TableDataInfo if and only if we are going to dump the
2997 * table data; the "dump" field in such objects isn't very interesting.
2998 */
2999static void
3001{
3003
3004 /*
3005 * Nothing to do if we already decided to dump the table. This will
3006 * happen for "config" tables.
3007 */
3008 if (tbinfo->dataObj != NULL)
3009 return;
3010
3011 /* Skip property graphs (no data to dump) */
3012 if (tbinfo->relkind == RELKIND_PROPGRAPH)
3013 return;
3014 /* Skip VIEWs (no data to dump) */
3015 if (tbinfo->relkind == RELKIND_VIEW)
3016 return;
3017 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3018 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3021 tbinfo->foreign_server)))
3022 return;
3023 /* Skip partitioned tables (data in partitions) */
3024 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3025 return;
3026
3027 /* Don't dump data in unlogged tables, if so requested */
3028 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3030 return;
3031
3032 /* Check that the data is not explicitly excluded */
3034 tbinfo->dobj.catId.oid))
3035 return;
3036
3037 /* OK, let's dump it */
3039
3040 if (tbinfo->relkind == RELKIND_MATVIEW)
3041 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3042 else if (tbinfo->relkind == RELKIND_SEQUENCE)
3043 tdinfo->dobj.objType = DO_SEQUENCE_SET;
3044 else
3045 tdinfo->dobj.objType = DO_TABLE_DATA;
3046
3047 /*
3048 * Note: use tableoid 0 so that this object won't be mistaken for
3049 * something that pg_depend entries apply to.
3050 */
3051 tdinfo->dobj.catId.tableoid = 0;
3052 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3053 AssignDumpId(&tdinfo->dobj);
3054 tdinfo->dobj.name = tbinfo->dobj.name;
3055 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3056 tdinfo->tdtable = tbinfo;
3057 tdinfo->filtercond = NULL; /* might get set later */
3058 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3059
3060 /* A TableDataInfo contains data, of course */
3061 tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3062
3063 tbinfo->dataObj = tdinfo;
3064
3065 /*
3066 * Materialized view statistics must be restored after the data, because
3067 * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3068 *
3069 * The dependency is added here because the statistics objects are created
3070 * first.
3071 */
3072 if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3073 {
3074 tbinfo->stats->section = SECTION_POST_DATA;
3075 addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3076 }
3077
3078 /* Make sure that we'll collect per-column info for this table. */
3079 tbinfo->interesting = true;
3080}
3081
3082/*
3083 * The refresh for a materialized view must be dependent on the refresh for
3084 * any materialized view that this one is dependent on.
3085 *
3086 * This must be called after all the objects are created, but before they are
3087 * sorted.
3088 */
3089static void
3091{
3092 PQExpBuffer query;
3093 PGresult *res;
3094 int ntups,
3095 i;
3096 int i_classid,
3097 i_objid,
3098 i_refobjid;
3099
3100 query = createPQExpBuffer();
3101
3102 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3103 "( "
3104 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3105 "FROM pg_depend d1 "
3106 "JOIN pg_class c1 ON c1.oid = d1.objid "
3107 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3108 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3109 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3110 "AND d2.objid = r1.oid "
3111 "AND d2.refobjid <> d1.objid "
3112 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3113 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3115 "WHERE d1.classid = 'pg_class'::regclass "
3116 "UNION "
3117 "SELECT w.objid, d3.refobjid, c3.relkind "
3118 "FROM w "
3119 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3120 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3121 "AND d3.objid = r3.oid "
3122 "AND d3.refobjid <> w.refobjid "
3123 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3124 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3126 ") "
3127 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3128 "FROM w "
3129 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3130
3131 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3132
3133 ntups = PQntuples(res);
3134
3135 i_classid = PQfnumber(res, "classid");
3136 i_objid = PQfnumber(res, "objid");
3137 i_refobjid = PQfnumber(res, "refobjid");
3138
3139 for (i = 0; i < ntups; i++)
3140 {
3141 CatalogId objId;
3143 DumpableObject *dobj;
3147
3148 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3149 objId.oid = atooid(PQgetvalue(res, i, i_objid));
3150 refobjId.tableoid = objId.tableoid;
3151 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3152
3153 dobj = findObjectByCatalogId(objId);
3154 if (dobj == NULL)
3155 continue;
3156
3157 Assert(dobj->objType == DO_TABLE);
3158 tbinfo = (TableInfo *) dobj;
3159 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3160 dobj = (DumpableObject *) tbinfo->dataObj;
3161 if (dobj == NULL)
3162 continue;
3164
3166 if (refdobj == NULL)
3167 continue;
3168
3169 Assert(refdobj->objType == DO_TABLE);
3171 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3172 refdobj = (DumpableObject *) reftbinfo->dataObj;
3173 if (refdobj == NULL)
3174 continue;
3175 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3176
3178
3179 if (!reftbinfo->relispopulated)
3180 tbinfo->relispopulated = false;
3181 }
3182
3183 PQclear(res);
3184
3185 destroyPQExpBuffer(query);
3186}
3187
3188/*
3189 * getTableDataFKConstraints -
3190 * add dump-order dependencies reflecting foreign key constraints
3191 *
3192 * This code is executed only in a data-only dump --- in schema+data dumps
3193 * we handle foreign key issues by not creating the FK constraints until
3194 * after the data is loaded. In a data-only dump, however, we want to
3195 * order the table data objects in such a way that a table's referenced
3196 * tables are restored first. (In the presence of circular references or
3197 * self-references this may be impossible; we'll detect and complain about
3198 * that during the dependency sorting step.)
3199 */
3200static void
3202{
3204 int numObjs;
3205 int i;
3206
3207 /* Search through all the dumpable objects for FK constraints */
3209 for (i = 0; i < numObjs; i++)
3210 {
3211 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3212 {
3215
3216 /* Not interesting unless both tables are to be dumped */
3217 if (cinfo->contable == NULL ||
3218 cinfo->contable->dataObj == NULL)
3219 continue;
3220 ftable = findTableByOid(cinfo->confrelid);
3221 if (ftable == NULL ||
3222 ftable->dataObj == NULL)
3223 continue;
3224
3225 /*
3226 * Okay, make referencing table's TABLE_DATA object depend on the
3227 * referenced table's TABLE_DATA object.
3228 */
3229 addObjectDependency(&cinfo->contable->dataObj->dobj,
3230 ftable->dataObj->dobj.dumpId);
3231 }
3232 }
3233 free(dobjs);
3234}
3235
3236
3237/*
3238 * dumpDatabase:
3239 * dump the database definition
3240 */
3241static void
3243{
3244 DumpOptions *dopt = fout->dopt;
3250 PGresult *res;
3251 int i_tableoid,
3252 i_oid,
3253 i_datname,
3254 i_datdba,
3255 i_encoding,
3257 i_collate,
3258 i_ctype,
3262 i_minmxid,
3263 i_datacl,
3272 const char *datname,
3273 *dba,
3274 *encoding,
3276 *collate,
3277 *ctype,
3278 *locale,
3279 *icurules,
3281 *datconnlimit,
3282 *tablespace;
3283 uint32 frozenxid,
3284 minmxid;
3285 char *qdatname;
3286
3287 pg_log_info("saving database definition");
3288
3289 /*
3290 * Fetch the database-level properties for this database.
3291 */
3292 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3293 "datdba, "
3294 "pg_encoding_to_char(encoding) AS encoding, "
3295 "datcollate, datctype, datfrozenxid, "
3296 "datacl, acldefault('d', datdba) AS acldefault, "
3297 "datistemplate, datconnlimit, ");
3298 appendPQExpBufferStr(dbQry, "datminmxid, ");
3299 if (fout->remoteVersion >= 170000)
3300 appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3301 else if (fout->remoteVersion >= 150000)
3302 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3303 else
3304 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3305 if (fout->remoteVersion >= 160000)
3306 appendPQExpBufferStr(dbQry, "daticurules, ");
3307 else
3308 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3310 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3311 "shobj_description(oid, 'pg_database') AS description "
3312 "FROM pg_database "
3313 "WHERE datname = current_database()");
3314
3316
3317 i_tableoid = PQfnumber(res, "tableoid");
3318 i_oid = PQfnumber(res, "oid");
3319 i_datname = PQfnumber(res, "datname");
3320 i_datdba = PQfnumber(res, "datdba");
3321 i_encoding = PQfnumber(res, "encoding");
3322 i_datlocprovider = PQfnumber(res, "datlocprovider");
3323 i_collate = PQfnumber(res, "datcollate");
3324 i_ctype = PQfnumber(res, "datctype");
3325 i_datlocale = PQfnumber(res, "datlocale");
3326 i_daticurules = PQfnumber(res, "daticurules");
3327 i_frozenxid = PQfnumber(res, "datfrozenxid");
3328 i_minmxid = PQfnumber(res, "datminmxid");
3329 i_datacl = PQfnumber(res, "datacl");
3330 i_acldefault = PQfnumber(res, "acldefault");
3331 i_datistemplate = PQfnumber(res, "datistemplate");
3332 i_datconnlimit = PQfnumber(res, "datconnlimit");
3333 i_datcollversion = PQfnumber(res, "datcollversion");
3334 i_tablespace = PQfnumber(res, "tablespace");
3335
3336 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3337 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3338 datname = PQgetvalue(res, 0, i_datname);
3339 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3340 encoding = PQgetvalue(res, 0, i_encoding);
3342 collate = PQgetvalue(res, 0, i_collate);
3343 ctype = PQgetvalue(res, 0, i_ctype);
3344 if (!PQgetisnull(res, 0, i_datlocale))
3345 locale = PQgetvalue(res, 0, i_datlocale);
3346 else
3347 locale = NULL;
3348 if (!PQgetisnull(res, 0, i_daticurules))
3350 else
3351 icurules = NULL;
3352 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3353 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3354 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3355 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3359
3361
3362 /*
3363 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3364 * to preserve that), as well as the encoding, locale, and tablespace
3365 * since those can't be altered later. Other DB properties are left to
3366 * the DATABASE PROPERTIES entry, so that they can be applied after
3367 * reconnecting to the target DB.
3368 *
3369 * For binary upgrade, we use the FILE_COPY strategy because testing has
3370 * shown it to be faster. When the server is in binary upgrade mode, it
3371 * will also skip the checkpoints this strategy ordinarily performs.
3372 */
3373 if (dopt->binary_upgrade)
3374 {
3376 "CREATE DATABASE %s WITH TEMPLATE = template0 "
3377 "OID = %u STRATEGY = FILE_COPY",
3378 qdatname, dbCatId.oid);
3379 }
3380 else
3381 {
3382 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3383 qdatname);
3384 }
3385 if (strlen(encoding) > 0)
3386 {
3387 appendPQExpBufferStr(creaQry, " ENCODING = ");
3389 }
3390
3391 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3392 if (datlocprovider[0] == 'b')
3393 appendPQExpBufferStr(creaQry, "builtin");
3394 else if (datlocprovider[0] == 'c')
3396 else if (datlocprovider[0] == 'i')
3398 else
3399 pg_fatal("unrecognized locale provider: %s",
3401
3402 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3403 {
3404 appendPQExpBufferStr(creaQry, " LOCALE = ");
3406 }
3407 else
3408 {
3409 if (strlen(collate) > 0)
3410 {
3411 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3413 }
3414 if (strlen(ctype) > 0)
3415 {
3416 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3418 }
3419 }
3420 if (locale)
3421 {
3422 if (datlocprovider[0] == 'b')
3423 appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3424 else
3425 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3426
3428 }
3429
3430 if (icurules)
3431 {
3432 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3434 }
3435
3436 /*
3437 * For binary upgrade, carry over the collation version. For normal
3438 * dump/restore, omit the version, so that it is computed upon restore.
3439 */
3440 if (dopt->binary_upgrade)
3441 {
3442 if (!PQgetisnull(res, 0, i_datcollversion))
3443 {
3444 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3447 fout);
3448 }
3449 }
3450
3451 /*
3452 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3453 * thing; the decision whether to specify a tablespace should be left till
3454 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3455 * label the DATABASE entry with the tablespace and let the normal
3456 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3457 * attention to default_tablespace, so that won't work.
3458 */
3459 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3460 !dopt->outputNoTablespaces)
3461 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3462 fmtId(tablespace));
3464
3465 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3466 qdatname);
3467
3469
3471 dbCatId, /* catalog ID */
3472 dbDumpId, /* dump ID */
3473 ARCHIVE_OPTS(.tag = datname,
3474 .owner = dba,
3475 .description = "DATABASE",
3476 .section = SECTION_PRE_DATA,
3477 .createStmt = creaQry->data,
3478 .dropStmt = delQry->data));
3479
3480 /* Compute correct tag for archive entry */
3481 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3482
3483 /* Dump DB comment if any */
3484 {
3485 /*
3486 * 8.2 and up keep comments on shared objects in a shared table, so we
3487 * cannot use the dumpComment() code used for other database objects.
3488 * Be careful that the ArchiveEntry parameters match that function.
3489 */
3490 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3491
3492 if (comment && *comment && !dopt->no_comments)
3493 {
3495
3496 /*
3497 * Generates warning when loaded into a differently-named
3498 * database.
3499 */
3500 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3503
3505 ARCHIVE_OPTS(.tag = labelq->data,
3506 .owner = dba,
3507 .description = "COMMENT",
3508 .section = SECTION_NONE,
3509 .createStmt = dbQry->data,
3510 .deps = &dbDumpId,
3511 .nDeps = 1));
3512 }
3513 }
3514
3515 /* Dump DB security label, if enabled */
3516 if (!dopt->no_security_labels)
3517 {
3518 PGresult *shres;
3520
3522
3523 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3527 if (seclabelQry->len > 0)
3529 ARCHIVE_OPTS(.tag = labelq->data,
3530 .owner = dba,
3531 .description = "SECURITY LABEL",
3532 .section = SECTION_NONE,
3533 .createStmt = seclabelQry->data,
3534 .deps = &dbDumpId,
3535 .nDeps = 1));
3537 PQclear(shres);
3538 }
3539
3540 /*
3541 * Dump ACL if any. Note that we do not support initial privileges
3542 * (pg_init_privs) on databases.
3543 */
3544 dbdacl.privtype = 0;
3545 dbdacl.initprivs = NULL;
3546
3547 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3548 qdatname, NULL, NULL,
3549 NULL, dba, &dbdacl);
3550
3551 /*
3552 * Now construct a DATABASE PROPERTIES archive entry to restore any
3553 * non-default database-level properties. (The reason this must be
3554 * separate is that we cannot put any additional commands into the TOC
3555 * entry that has CREATE DATABASE. pg_restore would execute such a group
3556 * in an implicit transaction block, and the backend won't allow CREATE
3557 * DATABASE in that context.)
3558 */
3561
3562 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3563 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3565
3566 if (strcmp(datistemplate, "t") == 0)
3567 {
3568 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3569 qdatname);
3570
3571 /*
3572 * The backend won't accept DROP DATABASE on a template database. We
3573 * can deal with that by removing the template marking before the DROP
3574 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3575 * since no such command is currently supported, fake it with a direct
3576 * UPDATE on pg_database.
3577 */
3578 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3579 "SET datistemplate = false WHERE datname = ");
3582 }
3583
3584 /*
3585 * We do not restore pg_database.dathasloginevt because it is set
3586 * automatically on login event trigger creation.
3587 */
3588
3589 /* Add database-specific SET options */
3591
3592 /*
3593 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3594 * entry, too, for lack of a better place.
3595 */
3596 if (dopt->binary_upgrade)
3597 {
3598 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3599 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3600 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3601 "WHERE datname = ",
3602 frozenxid, minmxid);
3605 }
3606
3607 if (creaQry->len > 0)
3609 ARCHIVE_OPTS(.tag = datname,
3610 .owner = dba,
3611 .description = "DATABASE PROPERTIES",
3612 .section = SECTION_PRE_DATA,
3613 .createStmt = creaQry->data,
3614 .dropStmt = delQry->data,
3615 .deps = &dbDumpId));
3616
3617 /*
3618 * pg_largeobject comes from the old system intact, so set its
3619 * relfrozenxids, relminmxids and relfilenode.
3620 *
3621 * pg_largeobject_metadata also comes from the old system intact for
3622 * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3623 * relfilenode, too. pg_upgrade can't copy/link the files from older
3624 * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3625 * changed its storage format in v16.
3626 */
3627 if (dopt->binary_upgrade)
3628 {
3635 int ii_relfrozenxid,
3637 ii_oid,
3639
3640 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3641 "FROM pg_catalog.pg_class\n"
3642 "WHERE oid IN (%u, %u, %u, %u);\n",
3645
3647
3648 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3649 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3650 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3651 ii_oid = PQfnumber(lo_res, "oid");
3652
3653 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3654 appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3655 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3656 appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3657 for (int i = 0; i < PQntuples(lo_res); ++i)
3658 {
3659 Oid oid;
3660 RelFileNumber relfilenumber;
3663
3664 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3665 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3666
3667 if (oid == LargeObjectRelationId ||
3669 {
3671 outQry = loOutQry;
3672 }
3673 else
3674 {
3676 outQry = lomOutQry;
3677 }
3678
3679 appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3680 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3681 "WHERE oid = %u;\n",
3685
3686 if (oid == LargeObjectRelationId ||
3689 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3690 relfilenumber);
3691 else if (oid == LargeObjectLOidPNIndexId ||
3694 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3695 relfilenumber);
3696 }
3697
3699 "TRUNCATE pg_catalog.pg_largeobject;\n");
3701 "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3702
3705
3707 ARCHIVE_OPTS(.tag = "pg_largeobject",
3708 .description = "pg_largeobject",
3709 .section = SECTION_PRE_DATA,
3710 .createStmt = loOutQry->data));
3711
3712 if (fout->remoteVersion >= 160000)
3714 ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3715 .description = "pg_largeobject_metadata",
3716 .section = SECTION_PRE_DATA,
3717 .createStmt = lomOutQry->data));
3718
3719 PQclear(lo_res);
3720
3726 }
3727
3728 PQclear(res);
3729
3735}
3736
3737/*
3738 * Collect any database-specific or role-and-database-specific SET options
3739 * for this database, and append them to outbuf.
3740 */
3741static void
3743 const char *dbname, Oid dboid)
3744{
3745 PGconn *conn = GetConnection(AH);
3747 PGresult *res;
3748
3749 /* First collect database-specific options */
3750 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3751 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3752 dboid);
3753
3754 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3755
3756 for (int i = 0; i < PQntuples(res); i++)
3758 "DATABASE", dbname, NULL, NULL,
3759 outbuf);
3760
3761 PQclear(res);
3762
3763 /* Now look for role-and-database-specific options */
3764 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3765 "FROM pg_db_role_setting s, pg_roles r "
3766 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3767 dboid);
3768
3769 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3770
3771 for (int i = 0; i < PQntuples(res); i++)
3773 "ROLE", PQgetvalue(res, i, 0),
3774 "DATABASE", dbname,
3775 outbuf);
3776
3777 PQclear(res);
3778
3780}
3781
3782/*
3783 * dumpEncoding: put the correct encoding into the archive
3784 */
3785static void
3787{
3788 const char *encname = pg_encoding_to_char(AH->encoding);
3790
3791 pg_log_info("saving encoding = %s", encname);
3792
3793 appendPQExpBufferStr(qry, "SET client_encoding = ");
3795 appendPQExpBufferStr(qry, ";\n");
3796
3798 ARCHIVE_OPTS(.tag = "ENCODING",
3799 .description = "ENCODING",
3800 .section = SECTION_PRE_DATA,
3801 .createStmt = qry->data));
3802
3803 destroyPQExpBuffer(qry);
3804}
3805
3806
3807/*
3808 * dumpStdStrings: put the correct escape string behavior into the archive
3809 */
3810static void
3812{
3813 const char *stdstrings = AH->std_strings ? "on" : "off";
3815
3816 pg_log_info("saving \"standard_conforming_strings = %s\"",
3817 stdstrings);
3818
3819 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3820 stdstrings);
3821
3823 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3824 .description = "STDSTRINGS",
3825 .section = SECTION_PRE_DATA,
3826 .createStmt = qry->data));
3827
3828 destroyPQExpBuffer(qry);
3829}
3830
3831/*
3832 * dumpSearchPath: record the active search_path in the archive
3833 */
3834static void
3836{
3839 PGresult *res;
3840 char **schemanames = NULL;
3841 int nschemanames = 0;
3842 int i;
3843
3844 /*
3845 * We use the result of current_schemas(), not the search_path GUC,
3846 * because that might contain wildcards such as "$user", which won't
3847 * necessarily have the same value during restore. Also, this way avoids
3848 * listing schemas that may appear in search_path but not actually exist,
3849 * which seems like a prudent exclusion.
3850 */
3852 "SELECT pg_catalog.current_schemas(false)");
3853
3854 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3855 pg_fatal("could not parse result of current_schemas()");
3856
3857 /*
3858 * We use set_config(), not a simple "SET search_path" command, because
3859 * the latter has less-clean behavior if the search path is empty. While
3860 * that's likely to get fixed at some point, it seems like a good idea to
3861 * be as backwards-compatible as possible in what we put into archives.
3862 */
3863 for (i = 0; i < nschemanames; i++)
3864 {
3865 if (i > 0)
3866 appendPQExpBufferStr(path, ", ");
3868 }
3869
3870 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3871 appendStringLiteralAH(qry, path->data, AH);
3872 appendPQExpBufferStr(qry, ", false);\n");
3873
3874 pg_log_info("saving \"search_path = %s\"", path->data);
3875
3877 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3878 .description = "SEARCHPATH",
3879 .section = SECTION_PRE_DATA,
3880 .createStmt = qry->data));
3881
3882 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3883 AH->searchpath = pg_strdup(qry->data);
3884
3886 PQclear(res);
3887 destroyPQExpBuffer(qry);
3888 destroyPQExpBuffer(path);
3889}
3890
3891
3892/*
3893 * getLOs:
3894 * Collect schema-level data about large objects
3895 */
3896static void
3898{
3899 DumpOptions *dopt = fout->dopt;
3901 PGresult *res;
3902 int ntups;
3903 int i;
3904 int n;
3905 int i_oid;
3906 int i_lomowner;
3907 int i_lomacl;
3908 int i_acldefault;
3909
3910 pg_log_info("reading large objects");
3911
3912 /*
3913 * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3914 * with the same owner/ACL appear together.
3915 */
3917 "SELECT oid, lomowner, lomacl, "
3918 "acldefault('L', lomowner) AS acldefault "
3919 "FROM pg_largeobject_metadata ");
3920
3921 /*
3922 * For binary upgrades, we transfer pg_largeobject_metadata via COPY or by
3923 * copying/linking its files from the old cluster. On such upgrades, we
3924 * only need to consider large objects that have comments or security
3925 * labels, since we still restore those objects via COMMENT/SECURITY LABEL
3926 * commands.
3927 */
3928 if (dopt->binary_upgrade)
3930 "WHERE oid IN "
3931 "(SELECT objoid FROM pg_description "
3932 "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
3933 "UNION SELECT objoid FROM pg_seclabel "
3934 "WHERE classoid = " CppAsString2(LargeObjectRelationId) ") ");
3935
3937 "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3938
3940
3941 i_oid = PQfnumber(res, "oid");
3942 i_lomowner = PQfnumber(res, "lomowner");
3943 i_lomacl = PQfnumber(res, "lomacl");
3944 i_acldefault = PQfnumber(res, "acldefault");
3945
3946 ntups = PQntuples(res);
3947
3948 /*
3949 * Group the blobs into suitably-sized groups that have the same owner and
3950 * ACL setting, and build a metadata and a data DumpableObject for each
3951 * group. (If we supported initprivs for blobs, we'd have to insist that
3952 * groups also share initprivs settings, since the DumpableObject only has
3953 * room for one.) i is the index of the first tuple in the current group,
3954 * and n is the number of tuples we include in the group.
3955 */
3956 for (i = 0; i < ntups; i += n)
3957 {
3958 Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3959 char *thisowner = PQgetvalue(res, i, i_lomowner);
3960 char *thisacl = PQgetvalue(res, i, i_lomacl);
3961 LoInfo *loinfo;
3963 char namebuf[64];
3964
3965 /* Scan to find first tuple not to be included in group */
3966 n = 1;
3967 while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3968 {
3969 if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3970 strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3971 break;
3972 n++;
3973 }
3974
3975 /* Build the metadata DumpableObject */
3976 loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3977
3979 loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3980 loinfo->dobj.catId.oid = thisoid;
3981 AssignDumpId(&loinfo->dobj);
3982
3983 if (n > 1)
3984 snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3985 atooid(PQgetvalue(res, i + n - 1, i_oid)));
3986 else
3987 snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3988 loinfo->dobj.name = pg_strdup(namebuf);
3989 loinfo->dacl.acl = pg_strdup(thisacl);
3990 loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3991 loinfo->dacl.privtype = 0;
3992 loinfo->dacl.initprivs = NULL;
3993 loinfo->rolname = getRoleName(thisowner);
3994 loinfo->numlos = n;
3995 loinfo->looids[0] = thisoid;
3996 /* Collect OIDs of the remaining blobs in this group */
3997 for (int k = 1; k < n; k++)
3998 {
4000
4001 loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4002
4003 /* Make sure we can look up loinfo by any of the blobs' OIDs */
4004 extraID.tableoid = LargeObjectRelationId;
4005 extraID.oid = loinfo->looids[k];
4007 }
4008
4009 /* LOs have data */
4010 loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4011
4012 /* Mark whether LO group has a non-empty ACL */
4013 if (!PQgetisnull(res, i, i_lomacl))
4014 loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4015
4016 /*
4017 * In binary upgrade mode, pg_largeobject and pg_largeobject_metadata
4018 * are transferred via COPY or by copying/linking the files from the
4019 * old cluster. Thus, we do not need to dump LO data, definitions, or
4020 * ACLs.
4021 */
4022 if (dopt->binary_upgrade)
4024
4025 /*
4026 * Create a "BLOBS" data item for the group, too. This is just a
4027 * placeholder for sorting; it carries no data now.
4028 */
4030 lodata->objType = DO_LARGE_OBJECT_DATA;
4031 lodata->catId = nilCatalogId;
4033 lodata->name = pg_strdup(namebuf);
4034 lodata->components |= DUMP_COMPONENT_DATA;
4035 /* Set up explicit dependency from data to metadata */
4036 lodata->dependencies = pg_malloc_object(DumpId);
4037 lodata->dependencies[0] = loinfo->dobj.dumpId;
4038 lodata->nDeps = lodata->allocDeps = 1;
4039 }
4040
4041 PQclear(res);
4043}
4044
4045/*
4046 * dumpLO
4047 *
4048 * dump the definition (metadata) of the given large object group
4049 */
4050static void
4052{
4054
4055 /*
4056 * The "definition" is just a newline-separated list of OIDs. We need to
4057 * put something into the dropStmt too, but it can just be a comment.
4058 */
4059 for (int i = 0; i < loinfo->numlos; i++)
4060 appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4061
4062 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4063 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4064 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4065 .owner = loinfo->rolname,
4066 .description = "BLOB METADATA",
4067 .section = SECTION_DATA,
4068 .createStmt = cquery->data,
4069 .dropStmt = "-- dummy"));
4070
4071 /*
4072 * Dump per-blob comments and seclabels if any. We assume these are rare
4073 * enough that it's okay to generate retail TOC entries for them.
4074 */
4075 if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4077 {
4078 for (int i = 0; i < loinfo->numlos; i++)
4079 {
4080 CatalogId catId;
4081 char namebuf[32];
4082
4083 /* Build identifying info for this blob */
4084 catId.tableoid = loinfo->dobj.catId.tableoid;
4085 catId.oid = loinfo->looids[i];
4086 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4087
4088 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4089 dumpComment(fout, "LARGE OBJECT", namebuf,
4090 NULL, loinfo->rolname,
4091 catId, 0, loinfo->dobj.dumpId);
4092
4093 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4094 dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4095 NULL, loinfo->rolname,
4096 catId, 0, loinfo->dobj.dumpId);
4097 }
4098 }
4099
4100 /*
4101 * Dump the ACLs if any (remember that all blobs in the group will have
4102 * the same ACL). If there's just one blob, dump a simple ACL entry; if
4103 * there's more, make a "LARGE OBJECTS" entry that really contains only
4104 * the ACL for the first blob. _printTocEntry() will be cued by the tag
4105 * string to emit a mutated version for each blob.
4106 */
4107 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4108 {
4109 char namebuf[32];
4110
4111 /* Build identifying info for the first blob */
4112 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4113
4114 if (loinfo->numlos > 1)
4115 {
4116 char tagbuf[64];
4117
4118 snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4119 loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4120
4121 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4122 "LARGE OBJECT", namebuf, NULL, NULL,
4123 tagbuf, loinfo->rolname, &loinfo->dacl);
4124 }
4125 else
4126 {
4127 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4128 "LARGE OBJECT", namebuf, NULL, NULL,
4129 NULL, loinfo->rolname, &loinfo->dacl);
4130 }
4131 }
4132
4134}
4135
4136/*
4137 * dumpLOs:
4138 * dump the data contents of the large objects in the given group
4139 */
4140static int
4141dumpLOs(Archive *fout, const void *arg)
4142{
4143 const LoInfo *loinfo = (const LoInfo *) arg;
4145 char buf[LOBBUFSIZE];
4146
4147 pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4148
4149 for (int i = 0; i < loinfo->numlos; i++)
4150 {
4151 Oid loOid = loinfo->looids[i];
4152 int loFd;
4153 int cnt;
4154
4155 /* Open the LO */
4156 loFd = lo_open(conn, loOid, INV_READ);
4157 if (loFd == -1)
4158 pg_fatal("could not open large object %u: %s",
4160
4161 StartLO(fout, loOid);
4162
4163 /* Now read it in chunks, sending data to archive */
4164 do
4165 {
4166 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4167 if (cnt < 0)
4168 pg_fatal("error reading large object %u: %s",
4170
4171 WriteData(fout, buf, cnt);
4172 } while (cnt > 0);
4173
4174 lo_close(conn, loFd);
4175
4176 EndLO(fout, loOid);
4177 }
4178
4179 return 1;
4180}
4181
4182/*
4183 * getPolicies
4184 * get information about all RLS policies on dumpable tables.
4185 */
4186void
4188{
4189 DumpOptions *dopt = fout->dopt;
4190 PQExpBuffer query;
4192 PGresult *res;
4194 int i_oid;
4195 int i_tableoid;
4196 int i_polrelid;
4197 int i_polname;
4198 int i_polcmd;
4199 int i_polpermissive;
4200 int i_polroles;
4201 int i_polqual;
4202 int i_polwithcheck;
4203 int i,
4204 j,
4205 ntups;
4206
4207 /* Skip if --no-policies was specified */
4208 if (dopt->no_policies)
4209 return;
4210
4211 query = createPQExpBuffer();
4213
4214 /*
4215 * Identify tables of interest, and check which ones have RLS enabled.
4216 */
4218 for (i = 0; i < numTables; i++)
4219 {
4220 TableInfo *tbinfo = &tblinfo[i];
4221
4222 /* Ignore row security on tables not to be dumped */
4223 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4224 continue;
4225
4226 /* It can't have RLS or policies if it's not a table */
4227 if (tbinfo->relkind != RELKIND_RELATION &&
4229 continue;
4230
4231 /* Add it to the list of table OIDs to be probed below */
4232 if (tbloids->len > 1) /* do we have more than the '{'? */
4234 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4235
4236 /* Is RLS enabled? (That's separate from whether it has policies) */
4237 if (tbinfo->rowsec)
4238 {
4239 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4240
4241 /*
4242 * We represent RLS being enabled on a table by creating a
4243 * PolicyInfo object with null polname.
4244 *
4245 * Note: use tableoid 0 so that this object won't be mistaken for
4246 * something that pg_depend entries apply to.
4247 */
4249 polinfo->dobj.objType = DO_POLICY;
4250 polinfo->dobj.catId.tableoid = 0;
4251 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4252 AssignDumpId(&polinfo->dobj);
4253 polinfo->dobj.namespace = tbinfo->dobj.namespace;
4254 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4255 polinfo->poltable = tbinfo;
4256 polinfo->polname = NULL;
4257 polinfo->polcmd = '\0';
4258 polinfo->polpermissive = 0;
4259 polinfo->polroles = NULL;
4260 polinfo->polqual = NULL;
4261 polinfo->polwithcheck = NULL;
4262 }
4263 }
4265
4266 /*
4267 * Now, read all RLS policies belonging to the tables of interest, and
4268 * create PolicyInfo objects for them. (Note that we must filter the
4269 * results server-side not locally, because we dare not apply pg_get_expr
4270 * to tables we don't have lock on.)
4271 */
4272 pg_log_info("reading row-level security policies");
4273
4274 printfPQExpBuffer(query,
4275 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4276 appendPQExpBufferStr(query, "pol.polpermissive, ");
4277 appendPQExpBuffer(query,
4278 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4279 " 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, "
4280 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4281 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4282 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4283 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4284 tbloids->data);
4285
4286 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4287
4288 ntups = PQntuples(res);
4289 if (ntups > 0)
4290 {
4291 i_oid = PQfnumber(res, "oid");
4292 i_tableoid = PQfnumber(res, "tableoid");
4293 i_polrelid = PQfnumber(res, "polrelid");
4294 i_polname = PQfnumber(res, "polname");
4295 i_polcmd = PQfnumber(res, "polcmd");
4296 i_polpermissive = PQfnumber(res, "polpermissive");
4297 i_polroles = PQfnumber(res, "polroles");
4298 i_polqual = PQfnumber(res, "polqual");
4299 i_polwithcheck = PQfnumber(res, "polwithcheck");
4300
4302
4303 for (j = 0; j < ntups; j++)
4304 {
4307
4308 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4309
4310 polinfo[j].dobj.objType = DO_POLICY;
4311 polinfo[j].dobj.catId.tableoid =
4312 atooid(PQgetvalue(res, j, i_tableoid));
4313 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4314 AssignDumpId(&polinfo[j].dobj);
4315 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4316 polinfo[j].poltable = tbinfo;
4317 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4318 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4319
4320 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4321 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4322
4323 if (PQgetisnull(res, j, i_polroles))
4324 polinfo[j].polroles = NULL;
4325 else
4326 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4327
4328 if (PQgetisnull(res, j, i_polqual))
4329 polinfo[j].polqual = NULL;
4330 else
4331 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4332
4333 if (PQgetisnull(res, j, i_polwithcheck))
4334 polinfo[j].polwithcheck = NULL;
4335 else
4336 polinfo[j].polwithcheck
4338 }
4339 }
4340
4341 PQclear(res);
4342
4343 destroyPQExpBuffer(query);
4345}
4346
4347/*
4348 * dumpPolicy
4349 * dump the definition of the given policy
4350 */
4351static void
4353{
4354 DumpOptions *dopt = fout->dopt;
4355 TableInfo *tbinfo = polinfo->poltable;
4356 PQExpBuffer query;
4359 char *qtabname;
4360 const char *cmd;
4361 char *tag;
4362
4363 /* Do nothing if not dumping schema */
4364 if (!dopt->dumpSchema)
4365 return;
4366
4367 /*
4368 * If polname is NULL, then this record is just indicating that ROW LEVEL
4369 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4370 * ROW LEVEL SECURITY.
4371 */
4372 if (polinfo->polname == NULL)
4373 {
4374 query = createPQExpBuffer();
4375
4376 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4378
4379 /*
4380 * We must emit the ROW SECURITY object's dependency on its table
4381 * explicitly, because it will not match anything in pg_depend (unlike
4382 * the case for other PolicyInfo objects).
4383 */
4384 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4385 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4386 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4387 .namespace = polinfo->dobj.namespace->dobj.name,
4388 .owner = tbinfo->rolname,
4389 .description = "ROW SECURITY",
4390 .section = SECTION_POST_DATA,
4391 .createStmt = query->data,
4392 .deps = &(tbinfo->dobj.dumpId),
4393 .nDeps = 1));
4394
4395 destroyPQExpBuffer(query);
4396 return;
4397 }
4398
4399 if (polinfo->polcmd == '*')
4400 cmd = "";
4401 else if (polinfo->polcmd == 'r')
4402 cmd = " FOR SELECT";
4403 else if (polinfo->polcmd == 'a')
4404 cmd = " FOR INSERT";
4405 else if (polinfo->polcmd == 'w')
4406 cmd = " FOR UPDATE";
4407 else if (polinfo->polcmd == 'd')
4408 cmd = " FOR DELETE";
4409 else
4410 pg_fatal("unexpected policy command type: %c",
4411 polinfo->polcmd);
4412
4413 query = createPQExpBuffer();
4416
4417 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4418
4419 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4420
4421 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4422 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4423
4424 if (polinfo->polroles != NULL)
4425 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4426
4427 if (polinfo->polqual != NULL)
4428 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4429
4430 if (polinfo->polwithcheck != NULL)
4431 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4432
4433 appendPQExpBufferStr(query, ";\n");
4434
4435 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4437
4438 appendPQExpBuffer(polprefix, "POLICY %s ON",
4439 fmtId(polinfo->polname));
4440
4441 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4442
4443 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4444 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4445 ARCHIVE_OPTS(.tag = tag,
4446 .namespace = polinfo->dobj.namespace->dobj.name,
4447 .owner = tbinfo->rolname,
4448 .description = "POLICY",
4449 .section = SECTION_POST_DATA,
4450 .createStmt = query->data,
4451 .dropStmt = delqry->data));
4452
4453 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4455 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4456 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4457
4458 pfree(tag);
4459 destroyPQExpBuffer(query);
4463}
4464
4465/*
4466 * getPublications
4467 * get information about publications
4468 */
4469void
4471{
4472 DumpOptions *dopt = fout->dopt;
4473 PQExpBuffer query;
4474 PGresult *res;
4476 int i_tableoid;
4477 int i_oid;
4478 int i_pubname;
4479 int i_pubowner;
4480 int i_puballtables;
4482 int i_pubinsert;
4483 int i_pubupdate;
4484 int i_pubdelete;
4485 int i_pubtruncate;
4486 int i_pubviaroot;
4487 int i_pubgencols;
4488 int i,
4489 ntups;
4490
4491 if (dopt->no_publications)
4492 return;
4493
4494 query = createPQExpBuffer();
4495
4496 /* Get the publications. */
4497 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4498 "p.pubowner, p.puballtables, p.pubinsert, "
4499 "p.pubupdate, p.pubdelete, ");
4500
4501 if (fout->remoteVersion >= 110000)
4502 appendPQExpBufferStr(query, "p.pubtruncate, ");
4503 else
4504 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4505
4506 if (fout->remoteVersion >= 130000)
4507 appendPQExpBufferStr(query, "p.pubviaroot, ");
4508 else
4509 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4510
4511 if (fout->remoteVersion >= 180000)
4512 appendPQExpBufferStr(query, "p.pubgencols, ");
4513 else
4514 appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4515
4516 if (fout->remoteVersion >= 190000)
4517 appendPQExpBufferStr(query, "p.puballsequences ");
4518 else
4519 appendPQExpBufferStr(query, "false AS puballsequences ");
4520
4521 appendPQExpBufferStr(query, "FROM pg_publication p");
4522
4523 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4524
4525 ntups = PQntuples(res);
4526
4527 if (ntups == 0)
4528 goto cleanup;
4529
4530 i_tableoid = PQfnumber(res, "tableoid");
4531 i_oid = PQfnumber(res, "oid");
4532 i_pubname = PQfnumber(res, "pubname");
4533 i_pubowner = PQfnumber(res, "pubowner");
4534 i_puballtables = PQfnumber(res, "puballtables");
4535 i_puballsequences = PQfnumber(res, "puballsequences");
4536 i_pubinsert = PQfnumber(res, "pubinsert");
4537 i_pubupdate = PQfnumber(res, "pubupdate");
4538 i_pubdelete = PQfnumber(res, "pubdelete");
4539 i_pubtruncate = PQfnumber(res, "pubtruncate");
4540 i_pubviaroot = PQfnumber(res, "pubviaroot");
4541 i_pubgencols = PQfnumber(res, "pubgencols");
4542
4544
4545 for (i = 0; i < ntups; i++)
4546 {
4547 pubinfo[i].dobj.objType = DO_PUBLICATION;
4548 pubinfo[i].dobj.catId.tableoid =
4549 atooid(PQgetvalue(res, i, i_tableoid));
4550 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4551 AssignDumpId(&pubinfo[i].dobj);
4552 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4553 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4554 pubinfo[i].puballtables =
4555 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4556 pubinfo[i].puballsequences =
4557 (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4558 pubinfo[i].pubinsert =
4559 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4560 pubinfo[i].pubupdate =
4561 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4562 pubinfo[i].pubdelete =
4563 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4564 pubinfo[i].pubtruncate =
4565 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4566 pubinfo[i].pubviaroot =
4567 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4568 pubinfo[i].pubgencols_type =
4569 *(PQgetvalue(res, i, i_pubgencols));
4570 pubinfo[i].except_tables = (SimplePtrList)
4571 {
4572 NULL, NULL
4573 };
4574
4575 /* Decide whether we want to dump it */
4577
4578 /*
4579 * Get the list of tables for publications specified in the EXCEPT
4580 * TABLE clause.
4581 *
4582 * Although individual table entries in EXCEPT list could be stored in
4583 * PublicationRelInfo, dumpPublicationTable cannot be used to emit
4584 * them, because there is no ALTER PUBLICATION ... ADD command to add
4585 * individual table entries to the EXCEPT list.
4586 *
4587 * Therefore, the approach is to dump the complete EXCEPT list in a
4588 * single CREATE PUBLICATION statement. PublicationInfo is used to
4589 * collect this information, which is then emitted by
4590 * dumpPublication().
4591 */
4592 if (fout->remoteVersion >= 190000)
4593 {
4594 int ntbls;
4596
4597 resetPQExpBuffer(query);
4598 appendPQExpBuffer(query,
4599 "SELECT prrelid\n"
4600 "FROM pg_catalog.pg_publication_rel\n"
4601 "WHERE prpubid = %u AND prexcept",
4602 pubinfo[i].dobj.catId.oid);
4603
4605
4607
4608 for (int j = 0; j < ntbls; j++)
4609 {
4610 Oid prrelid;
4612
4614
4616
4617 if (tbinfo != NULL)
4618 simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
4619 }
4620
4622 }
4623 }
4624
4625cleanup:
4626 PQclear(res);
4627
4628 destroyPQExpBuffer(query);
4629}
4630
4631/*
4632 * dumpPublication
4633 * dump the definition of the given publication
4634 */
4635static void
4637{
4638 DumpOptions *dopt = fout->dopt;
4640 PQExpBuffer query;
4641 char *qpubname;
4642 bool first = true;
4643
4644 /* Do nothing if not dumping schema */
4645 if (!dopt->dumpSchema)
4646 return;
4647
4649 query = createPQExpBuffer();
4650
4651 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4652
4653 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4654 qpubname);
4655
4656 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4657 qpubname);
4658
4659 if (pubinfo->puballtables)
4660 {
4661 int n_except = 0;
4662
4663 appendPQExpBufferStr(query, " FOR ALL TABLES");
4664
4665 /* Include EXCEPT (TABLE) clause if there are except_tables. */
4666 for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell = cell->next)
4667 {
4668 TableInfo *tbinfo = (TableInfo *) cell->ptr;
4669
4670 if (++n_except == 1)
4671 appendPQExpBufferStr(query, " EXCEPT (");
4672 else
4673 appendPQExpBufferStr(query, ", ");
4674 appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
4675 }
4676 if (n_except > 0)
4677 appendPQExpBufferChar(query, ')');
4678
4679 if (pubinfo->puballsequences)
4680 appendPQExpBufferStr(query, ", ALL SEQUENCES");
4681 }
4682 else if (pubinfo->puballsequences)
4683 appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4684
4685 appendPQExpBufferStr(query, " WITH (publish = '");
4686 if (pubinfo->pubinsert)
4687 {
4688 appendPQExpBufferStr(query, "insert");
4689 first = false;
4690 }
4691
4692 if (pubinfo->pubupdate)
4693 {
4694 if (!first)
4695 appendPQExpBufferStr(query, ", ");
4696
4697 appendPQExpBufferStr(query, "update");
4698 first = false;
4699 }
4700
4701 if (pubinfo->pubdelete)
4702 {
4703 if (!first)
4704 appendPQExpBufferStr(query, ", ");
4705
4706 appendPQExpBufferStr(query, "delete");
4707 first = false;
4708 }
4709
4710 if (pubinfo->pubtruncate)
4711 {
4712 if (!first)
4713 appendPQExpBufferStr(query, ", ");
4714
4715 appendPQExpBufferStr(query, "truncate");
4716 first = false;
4717 }
4718
4719 appendPQExpBufferChar(query, '\'');
4720
4721 if (pubinfo->pubviaroot)
4722 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4723
4724 if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4725 appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4726
4727 appendPQExpBufferStr(query, ");\n");
4728
4729 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4730 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4731 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4732 .owner = pubinfo->rolname,
4733 .description = "PUBLICATION",
4734 .section = SECTION_POST_DATA,
4735 .createStmt = query->data,
4736 .dropStmt = delq->data));
4737
4738 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4739 dumpComment(fout, "PUBLICATION", qpubname,
4740 NULL, pubinfo->rolname,
4741 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4742
4743 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4744 dumpSecLabel(fout, "PUBLICATION", qpubname,
4745 NULL, pubinfo->rolname,
4746 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4747
4749 destroyPQExpBuffer(query);
4751}
4752
4753/*
4754 * getPublicationNamespaces
4755 * get information about publication membership for dumpable schemas.
4756 */
4757void
4759{
4760 PQExpBuffer query;
4761 PGresult *res;
4763 DumpOptions *dopt = fout->dopt;
4764 int i_tableoid;
4765 int i_oid;
4766 int i_pnpubid;
4767 int i_pnnspid;
4768 int i,
4769 j,
4770 ntups;
4771
4772 if (dopt->no_publications || fout->remoteVersion < 150000)
4773 return;
4774
4775 query = createPQExpBuffer();
4776
4777 /* Collect all publication membership info. */
4779 "SELECT tableoid, oid, pnpubid, pnnspid "
4780 "FROM pg_catalog.pg_publication_namespace");
4781 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4782
4783 ntups = PQntuples(res);
4784
4785 i_tableoid = PQfnumber(res, "tableoid");
4786 i_oid = PQfnumber(res, "oid");
4787 i_pnpubid = PQfnumber(res, "pnpubid");
4788 i_pnnspid = PQfnumber(res, "pnnspid");
4789
4790 /* this allocation may be more than we need */
4792 j = 0;
4793
4794 for (i = 0; i < ntups; i++)
4795 {
4800
4801 /*
4802 * Ignore any entries for which we aren't interested in either the
4803 * publication or the rel.
4804 */
4806 if (pubinfo == NULL)
4807 continue;
4809 if (nspinfo == NULL)
4810 continue;
4811
4812 /* OK, make a DumpableObject for this relationship */
4814 pubsinfo[j].dobj.catId.tableoid =
4815 atooid(PQgetvalue(res, i, i_tableoid));
4816 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4817 AssignDumpId(&pubsinfo[j].dobj);
4818 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4819 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4820 pubsinfo[j].publication = pubinfo;
4821 pubsinfo[j].pubschema = nspinfo;
4822
4823 /* Decide whether we want to dump it */
4825
4826 j++;
4827 }
4828
4829 PQclear(res);
4830 destroyPQExpBuffer(query);
4831}
4832
4833/*
4834 * getPublicationTables
4835 * get information about publication membership for dumpable tables.
4836 */
4837void
4839{
4840 PQExpBuffer query;
4841 PGresult *res;
4843 DumpOptions *dopt = fout->dopt;
4844 int i_tableoid;
4845 int i_oid;
4846 int i_prpubid;
4847 int i_prrelid;
4848 int i_prrelqual;
4849 int i_prattrs;
4850 int i,
4851 j,
4852 ntups;
4853
4854 if (dopt->no_publications)
4855 return;
4856
4857 query = createPQExpBuffer();
4858
4859 /* Collect all publication membership info. */
4860 if (fout->remoteVersion >= 150000)
4861 {
4863 "SELECT tableoid, oid, prpubid, prrelid, "
4864 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4865 "(CASE\n"
4866 " WHEN pr.prattrs IS NOT NULL THEN\n"
4867 " (SELECT array_agg(attname)\n"
4868 " FROM\n"
4869 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4870 " pg_catalog.pg_attribute\n"
4871 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4872 " ELSE NULL END) prattrs "
4873 "FROM pg_catalog.pg_publication_rel pr");
4874 if (fout->remoteVersion >= 190000)
4875 appendPQExpBufferStr(query, " WHERE NOT pr.prexcept");
4876 }
4877 else
4879 "SELECT tableoid, oid, prpubid, prrelid, "
4880 "NULL AS prrelqual, NULL AS prattrs "
4881 "FROM pg_catalog.pg_publication_rel");
4882 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4883
4884 ntups = PQntuples(res);
4885
4886 i_tableoid = PQfnumber(res, "tableoid");
4887 i_oid = PQfnumber(res, "oid");
4888 i_prpubid = PQfnumber(res, "prpubid");
4889 i_prrelid = PQfnumber(res, "prrelid");
4890 i_prrelqual = PQfnumber(res, "prrelqual");
4891 i_prattrs = PQfnumber(res, "prattrs");
4892
4893 /* this allocation may be more than we need */
4895 j = 0;
4896
4897 for (i = 0; i < ntups; i++)
4898 {
4903
4904 /*
4905 * Ignore any entries for which we aren't interested in either the
4906 * publication or the rel.
4907 */
4909 if (pubinfo == NULL)
4910 continue;
4912 if (tbinfo == NULL)
4913 continue;
4914
4915 /* OK, make a DumpableObject for this relationship */
4916 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4917 pubrinfo[j].dobj.catId.tableoid =
4918 atooid(PQgetvalue(res, i, i_tableoid));
4919 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4920 AssignDumpId(&pubrinfo[j].dobj);
4921 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4922 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4923 pubrinfo[j].publication = pubinfo;
4924 pubrinfo[j].pubtable = tbinfo;
4925 if (PQgetisnull(res, i, i_prrelqual))
4926 pubrinfo[j].pubrelqual = NULL;
4927 else
4928 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4929
4930 if (!PQgetisnull(res, i, i_prattrs))
4931 {
4932 char **attnames;
4933 int nattnames;
4935
4936 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4937 &attnames, &nattnames))
4938 pg_fatal("could not parse %s array", "prattrs");
4940 for (int k = 0; k < nattnames; k++)
4941 {
4942 if (k > 0)
4944
4945 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4946 }
4947 pubrinfo[j].pubrattrs = attribs->data;
4948 free(attribs); /* but not attribs->data */
4949 free(attnames);
4950 }
4951 else
4952 pubrinfo[j].pubrattrs = NULL;
4953
4954 /* Decide whether we want to dump it */
4956
4957 j++;
4958 }
4959
4960 PQclear(res);
4961 destroyPQExpBuffer(query);
4962}
4963
4964/*
4965 * dumpPublicationNamespace
4966 * dump the definition of the given publication schema mapping.
4967 */
4968static void
4970{
4971 DumpOptions *dopt = fout->dopt;
4972 NamespaceInfo *schemainfo = pubsinfo->pubschema;
4973 PublicationInfo *pubinfo = pubsinfo->publication;
4974 PQExpBuffer query;
4975 char *tag;
4976
4977 /* Do nothing if not dumping schema */
4978 if (!dopt->dumpSchema)
4979 return;
4980
4981 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4982
4983 query = createPQExpBuffer();
4984
4985 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4986 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4987
4988 /*
4989 * There is no point in creating drop query as the drop is done by schema
4990 * drop.
4991 */
4992 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4993 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4994 ARCHIVE_OPTS(.tag = tag,
4995 .namespace = schemainfo->dobj.name,
4996 .owner = pubinfo->rolname,
4997 .description = "PUBLICATION TABLES IN SCHEMA",
4998 .section = SECTION_POST_DATA,
4999 .createStmt = query->data));
5000
5001 /* These objects can't currently have comments or seclabels */
5002
5003 pfree(tag);
5004 destroyPQExpBuffer(query);
5005}
5006
5007/*
5008 * dumpPublicationTable
5009 * dump the definition of the given publication table mapping
5010 */
5011static void
5013{
5014 DumpOptions *dopt = fout->dopt;
5015 PublicationInfo *pubinfo = pubrinfo->publication;
5016 TableInfo *tbinfo = pubrinfo->pubtable;
5017 PQExpBuffer query;
5018 char *tag;
5019
5020 /* Do nothing if not dumping schema */
5021 if (!dopt->dumpSchema)
5022 return;
5023
5024 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5025
5026 query = createPQExpBuffer();
5027
5028 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5029 fmtId(pubinfo->dobj.name));
5030 appendPQExpBuffer(query, " %s",
5032
5033 if (pubrinfo->pubrattrs)
5034 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5035
5036 if (pubrinfo->pubrelqual)
5037 {
5038 /*
5039 * It's necessary to add parentheses around the expression because
5040 * pg_get_expr won't supply the parentheses for things like WHERE
5041 * TRUE.
5042 */
5043 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5044 }
5045 appendPQExpBufferStr(query, ";\n");
5046
5047 /*
5048 * There is no point in creating a drop query as the drop is done by table
5049 * drop. (If you think to change this, see also _printTocEntry().)
5050 * Although this object doesn't really have ownership as such, set the
5051 * owner field anyway to ensure that the command is run by the correct
5052 * role at restore time.
5053 */
5054 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5055 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5056 ARCHIVE_OPTS(.tag = tag,
5057 .namespace = tbinfo->dobj.namespace->dobj.name,
5058 .owner = pubinfo->rolname,
5059 .description = "PUBLICATION TABLE",
5060 .section = SECTION_POST_DATA,
5061 .createStmt = query->data));
5062
5063 /* These objects can't currently have comments or seclabels */
5064
5065 pfree(tag);
5066 destroyPQExpBuffer(query);
5067}
5068
5069/*
5070 * Is the currently connected user a superuser?
5071 */
5072static bool
5074{
5076 const char *val;
5077
5078 val = PQparameterStatus(AH->connection, "is_superuser");
5079
5080 if (val && strcmp(val, "on") == 0)
5081 return true;
5082
5083 return false;
5084}
5085
5086/*
5087 * Set the given value to restrict_nonsystem_relation_kind value. Since
5088 * restrict_nonsystem_relation_kind is introduced in minor version releases,
5089 * the setting query is effective only where available.
5090 */
5091static void
5093{
5095 PGresult *res;
5096
5097 appendPQExpBuffer(query,
5098 "SELECT set_config(name, '%s', false) "
5099 "FROM pg_settings "
5100 "WHERE name = 'restrict_nonsystem_relation_kind'",
5101 value);
5102 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5103
5104 PQclear(res);
5105 destroyPQExpBuffer(query);
5106}
5107
5108/*
5109 * getSubscriptions
5110 * get information about subscriptions
5111 */
5112void
5114{
5115 DumpOptions *dopt = fout->dopt;
5116 PQExpBuffer query;
5117 PGresult *res;
5118 SubscriptionInfo *subinfo;
5119 int i_tableoid;
5120 int i_oid;
5121 int i_subname;
5122 int i_subowner;
5123 int i_subbinary;
5124 int i_substream;
5128 int i_subrunasowner;
5129 int i_subservername;
5130 int i_subconninfo;
5131 int i_subslotname;
5132 int i_subsynccommit;
5135 int i_suborigin;
5137 int i_subenabled;
5138 int i_subfailover;
5141 int i,
5142 ntups;
5143
5144 if (dopt->no_subscriptions)
5145 return;
5146
5147 if (!is_superuser(fout))
5148 {
5149 int n;
5150
5151 res = ExecuteSqlQuery(fout,
5152 "SELECT count(*) FROM pg_subscription "
5153 "WHERE subdbid = (SELECT oid FROM pg_database"
5154 " WHERE datname = current_database())",
5156 n = atoi(PQgetvalue(res, 0, 0));
5157 if (n > 0)
5158 pg_log_warning("subscriptions not dumped because current user is not a superuser");
5159 PQclear(res);
5160 return;
5161 }
5162
5163 query = createPQExpBuffer();
5164
5165 /* Get the subscriptions in current database. */
5167 "SELECT s.tableoid, s.oid, s.subname,\n"
5168 " s.subowner,\n"
5169 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5170 " s.subpublications,\n");
5171
5172 if (fout->remoteVersion >= 140000)
5173 appendPQExpBufferStr(query, " s.subbinary,\n");
5174 else
5175 appendPQExpBufferStr(query, " false AS subbinary,\n");
5176
5177 if (fout->remoteVersion >= 140000)
5178 appendPQExpBufferStr(query, " s.substream,\n");
5179 else
5180 appendPQExpBufferStr(query, " 'f' AS substream,\n");
5181
5182 if (fout->remoteVersion >= 150000)
5184 " s.subtwophasestate,\n"
5185 " s.subdisableonerr,\n");
5186 else
5187 appendPQExpBuffer(query,
5188 " '%c' AS subtwophasestate,\n"
5189 " false AS subdisableonerr,\n",
5191
5192 if (fout->remoteVersion >= 160000)
5194 " s.subpasswordrequired,\n"
5195 " s.subrunasowner,\n"
5196 " s.suborigin,\n");
5197 else
5198 appendPQExpBuffer(query,
5199 " 't' AS subpasswordrequired,\n"
5200 " 't' AS subrunasowner,\n"
5201 " '%s' AS suborigin,\n",
5203
5204 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5205 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5206 " s.subenabled,\n");
5207 else
5208 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5209 " false AS subenabled,\n");
5210
5211 if (fout->remoteVersion >= 170000)
5213 " s.subfailover,\n");
5214 else
5216 " false AS subfailover,\n");
5217
5218 if (fout->remoteVersion >= 190000)
5220 " s.subretaindeadtuples,\n");
5221 else
5223 " false AS subretaindeadtuples,\n");
5224
5225 if (fout->remoteVersion >= 190000)
5227 " s.submaxretention,\n");
5228 else
5229 appendPQExpBufferStr(query, " 0 AS submaxretention,\n");
5230
5231 if (fout->remoteVersion >= 190000)
5233 " s.subwalrcvtimeout,\n");
5234 else
5236 " '-1' AS subwalrcvtimeout,\n");
5237
5238 if (fout->remoteVersion >= 190000)
5239 appendPQExpBufferStr(query, " fs.srvname AS subservername\n");
5240 else
5241 appendPQExpBufferStr(query, " NULL AS subservername\n");
5242
5244 "FROM pg_subscription s\n");
5245
5246 if (fout->remoteVersion >= 190000)
5248 "LEFT JOIN pg_catalog.pg_foreign_server fs \n"
5249 " ON fs.oid = s.subserver \n");
5250
5251 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5253 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5254 " ON o.external_id = 'pg_' || s.oid::text \n");
5255
5257 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5258 " WHERE datname = current_database())");
5259
5260 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5261
5262 ntups = PQntuples(res);
5263
5264 /*
5265 * Get subscription fields. We don't include subskiplsn in the dump as
5266 * after restoring the dump this value may no longer be relevant.
5267 */
5268 i_tableoid = PQfnumber(res, "tableoid");
5269 i_oid = PQfnumber(res, "oid");
5270 i_subname = PQfnumber(res, "subname");
5271 i_subowner = PQfnumber(res, "subowner");
5272 i_subenabled = PQfnumber(res, "subenabled");
5273 i_subbinary = PQfnumber(res, "subbinary");
5274 i_substream = PQfnumber(res, "substream");
5275 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5276 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5277 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5278 i_subrunasowner = PQfnumber(res, "subrunasowner");
5279 i_subfailover = PQfnumber(res, "subfailover");
5280 i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5281 i_submaxretention = PQfnumber(res, "submaxretention");
5282 i_subservername = PQfnumber(res, "subservername");
5283 i_subconninfo = PQfnumber(res, "subconninfo");
5284 i_subslotname = PQfnumber(res, "subslotname");
5285 i_subsynccommit = PQfnumber(res, "subsynccommit");
5286 i_subwalrcvtimeout = PQfnumber(res, "subwalrcvtimeout");
5287 i_subpublications = PQfnumber(res, "subpublications");
5288 i_suborigin = PQfnumber(res, "suborigin");
5289 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5290
5291 subinfo = pg_malloc_array(SubscriptionInfo, ntups);
5292
5293 for (i = 0; i < ntups; i++)
5294 {
5295 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5296 subinfo[i].dobj.catId.tableoid =
5297 atooid(PQgetvalue(res, i, i_tableoid));
5298 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5299 AssignDumpId(&subinfo[i].dobj);
5300 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5301 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5302
5303 subinfo[i].subenabled =
5304 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5305 if (PQgetisnull(res, i, i_subservername))
5306 subinfo[i].subservername = NULL;
5307 else
5309 subinfo[i].subbinary =
5310 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5311 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5312 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5313 subinfo[i].subdisableonerr =
5314 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5315 subinfo[i].subpasswordrequired =
5316 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5317 subinfo[i].subrunasowner =
5318 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5319 subinfo[i].subfailover =
5320 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5321 subinfo[i].subretaindeadtuples =
5322 (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5323 subinfo[i].submaxretention =
5325 if (PQgetisnull(res, i, i_subconninfo))
5326 subinfo[i].subconninfo = NULL;
5327 else
5328 subinfo[i].subconninfo =
5330 if (PQgetisnull(res, i, i_subslotname))
5331 subinfo[i].subslotname = NULL;
5332 else
5333 subinfo[i].subslotname =
5335 subinfo[i].subsynccommit =
5337 subinfo[i].subwalrcvtimeout =
5339 subinfo[i].subpublications =
5341 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5343 subinfo[i].suboriginremotelsn = NULL;
5344 else
5345 subinfo[i].suboriginremotelsn =
5347
5348 /* Decide whether we want to dump it */
5349 selectDumpableObject(&(subinfo[i].dobj), fout);
5350 }
5351 PQclear(res);
5352
5353 destroyPQExpBuffer(query);
5354}
5355
5356/*
5357 * getSubscriptionRelations
5358 * Get information about subscription membership for dumpable relations. This
5359 * will be used only in binary-upgrade mode for PG17 or later versions.
5360 */
5361void
5363{
5364 DumpOptions *dopt = fout->dopt;
5365 SubscriptionInfo *subinfo = NULL;
5367 PGresult *res;
5368 int i_srsubid;
5369 int i_srrelid;
5370 int i_srsubstate;
5371 int i_srsublsn;
5372 int ntups;
5374
5375 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5376 fout->remoteVersion < 170000)
5377 return;
5378
5379 res = ExecuteSqlQuery(fout,
5380 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5381 "FROM pg_catalog.pg_subscription_rel "
5382 "ORDER BY srsubid",
5384 ntups = PQntuples(res);
5385 if (ntups == 0)
5386 goto cleanup;
5387
5388 /* Get pg_subscription_rel attributes */
5389 i_srsubid = PQfnumber(res, "srsubid");
5390 i_srrelid = PQfnumber(res, "srrelid");
5391 i_srsubstate = PQfnumber(res, "srsubstate");
5392 i_srsublsn = PQfnumber(res, "srsublsn");
5393
5395 for (int i = 0; i < ntups; i++)
5396 {
5398 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5399 TableInfo *tblinfo;
5400
5401 /*
5402 * If we switched to a new subscription, check if the subscription
5403 * exists.
5404 */
5406 {
5408 if (subinfo == NULL)
5409 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5410
5412 }
5413
5414 tblinfo = findTableByOid(relid);
5415 if (tblinfo == NULL)
5416 pg_fatal("failed sanity check, relation with OID %u not found",
5417 relid);
5418
5419 /* OK, make a DumpableObject for this relationship */
5420 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5421 subrinfo[i].dobj.catId.tableoid = relid;
5422 subrinfo[i].dobj.catId.oid = cur_srsubid;
5423 AssignDumpId(&subrinfo[i].dobj);
5424 subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5425 subrinfo[i].dobj.name = tblinfo->dobj.name;
5426 subrinfo[i].subinfo = subinfo;
5427 subrinfo[i].tblinfo = tblinfo;
5428 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5429 if (PQgetisnull(res, i, i_srsublsn))
5430 subrinfo[i].srsublsn = NULL;
5431 else
5432 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5433
5434 /* Decide whether we want to dump it */
5436 }
5437
5438cleanup:
5439 PQclear(res);
5440}
5441
5442/*
5443 * dumpSubscriptionTable
5444 * Dump the definition of the given subscription table mapping. This will be
5445 * used only in binary-upgrade mode for PG17 or later versions.
5446 */
5447static void
5449{
5450 DumpOptions *dopt = fout->dopt;
5451 SubscriptionInfo *subinfo = subrinfo->subinfo;
5452 PQExpBuffer query;
5453 char *tag;
5454
5455 /* Do nothing if not dumping schema */
5456 if (!dopt->dumpSchema)
5457 return;
5458
5459 Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5460
5461 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5462
5463 query = createPQExpBuffer();
5464
5465 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5466 {
5467 /*
5468 * binary_upgrade_add_sub_rel_state will add the subscription relation
5469 * to pg_subscription_rel table. This will be used only in
5470 * binary-upgrade mode.
5471 */
5473 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5475 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5476 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5477 appendPQExpBuffer(query,
5478 ", %u, '%c'",
5479 subrinfo->tblinfo->dobj.catId.oid,
5480 subrinfo->srsubstate);
5481
5482 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5483 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5484 else
5485 appendPQExpBufferStr(query, ", NULL");
5486
5487 appendPQExpBufferStr(query, ");\n");
5488 }
5489
5490 /*
5491 * There is no point in creating a drop query as the drop is done by table
5492 * drop. (If you think to change this, see also _printTocEntry().)
5493 * Although this object doesn't really have ownership as such, set the
5494 * owner field anyway to ensure that the command is run by the correct
5495 * role at restore time.
5496 */
5497 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5498 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5499 ARCHIVE_OPTS(.tag = tag,
5500 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5501 .owner = subinfo->rolname,
5502 .description = "SUBSCRIPTION TABLE",
5503 .section = SECTION_POST_DATA,
5504 .createStmt = query->data));
5505
5506 /* These objects can't currently have comments or seclabels */
5507
5508 pfree(tag);
5509 destroyPQExpBuffer(query);
5510}
5511
5512/*
5513 * dumpSubscription
5514 * dump the definition of the given subscription
5515 */
5516static void
5518{
5519 DumpOptions *dopt = fout->dopt;
5521 PQExpBuffer query;
5522 PQExpBuffer publications;
5523 char *qsubname;
5524 char **pubnames = NULL;
5525 int npubnames = 0;
5526 int i;
5527
5528 /* Do nothing if not dumping schema */
5529 if (!dopt->dumpSchema)
5530 return;
5531
5533 query = createPQExpBuffer();
5534
5535 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5536
5537 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5538 qsubname);
5539
5540 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s ",
5541 qsubname);
5542 if (subinfo->subservername)
5543 {
5544 appendPQExpBuffer(query, "SERVER %s", fmtId(subinfo->subservername));
5545 }
5546 else
5547 {
5548 appendPQExpBufferStr(query, "CONNECTION ");
5549 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5550 }
5551
5552 /* Build list of quoted publications and append them to query. */
5554 pg_fatal("could not parse %s array", "subpublications");
5555
5556 publications = createPQExpBuffer();
5557 for (i = 0; i < npubnames; i++)
5558 {
5559 if (i > 0)
5560 appendPQExpBufferStr(publications, ", ");
5561
5562 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5563 }
5564
5565 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5566 if (subinfo->subslotname)
5567 appendStringLiteralAH(query, subinfo->subslotname, fout);
5568 else
5569 appendPQExpBufferStr(query, "NONE");
5570
5571 if (subinfo->subbinary)
5572 appendPQExpBufferStr(query, ", binary = true");
5573
5574 if (subinfo->substream == LOGICALREP_STREAM_ON)
5575 appendPQExpBufferStr(query, ", streaming = on");
5576 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5577 appendPQExpBufferStr(query, ", streaming = parallel");
5578 else
5579 appendPQExpBufferStr(query, ", streaming = off");
5580
5582 appendPQExpBufferStr(query, ", two_phase = on");
5583
5584 if (subinfo->subdisableonerr)
5585 appendPQExpBufferStr(query, ", disable_on_error = true");
5586
5587 if (!subinfo->subpasswordrequired)
5588 appendPQExpBufferStr(query, ", password_required = false");
5589
5590 if (subinfo->subrunasowner)
5591 appendPQExpBufferStr(query, ", run_as_owner = true");
5592
5593 if (subinfo->subfailover)
5594 appendPQExpBufferStr(query, ", failover = true");
5595
5596 if (subinfo->subretaindeadtuples)
5597 appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5598
5599 if (subinfo->submaxretention)
5600 appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5601
5602 if (strcmp(subinfo->subsynccommit, "off") != 0)
5603 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5604
5605 if (strcmp(subinfo->subwalrcvtimeout, "-1") != 0)
5606 appendPQExpBuffer(query, ", wal_receiver_timeout = %s", fmtId(subinfo->subwalrcvtimeout));
5607
5608 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5609 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5610
5611 appendPQExpBufferStr(query, ");\n");
5612
5613 /*
5614 * In binary-upgrade mode, we allow the replication to continue after the
5615 * upgrade.
5616 */
5617 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5618 {
5619 if (subinfo->suboriginremotelsn)
5620 {
5621 /*
5622 * Preserve the remote_lsn for the subscriber's replication
5623 * origin. This value is required to start the replication from
5624 * the position before the upgrade. This value will be stale if
5625 * the publisher gets upgraded before the subscriber node.
5626 * However, this shouldn't be a problem as the upgrade of the
5627 * publisher ensures that all the transactions were replicated
5628 * before upgrading it.
5629 */
5631 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5633 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5634 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5635 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5636 }
5637
5638 if (subinfo->subenabled)
5639 {
5640 /*
5641 * Enable the subscription to allow the replication to continue
5642 * after the upgrade.
5643 */
5645 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5646 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5647 }
5648 }
5649
5650 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5651 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5652 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5653 .owner = subinfo->rolname,
5654 .description = "SUBSCRIPTION",
5655 .section = SECTION_POST_DATA,
5656 .createStmt = query->data,
5657 .dropStmt = delq->data));
5658
5659 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5660 dumpComment(fout, "SUBSCRIPTION", qsubname,
5661 NULL, subinfo->rolname,
5662 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5663
5664 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5665 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5666 NULL, subinfo->rolname,
5667 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5668
5669 destroyPQExpBuffer(publications);
5670 free(pubnames);
5671
5673 destroyPQExpBuffer(query);
5675}
5676
5677/*
5678 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5679 * the object needs.
5680 */
5681static void
5683 PQExpBuffer create,
5684 const DumpableObject *dobj,
5685 const char *catalog,
5686 const char *keyword,
5687 const char *objname)
5688{
5689 if (dobj->depends_on_ext)
5690 {
5691 char *nm;
5692 PGresult *res;
5693 PQExpBuffer query;
5694 int ntups;
5695 int i_extname;
5696 int i;
5697
5698 /* dodge fmtId() non-reentrancy */
5699 nm = pg_strdup(objname);
5700
5701 query = createPQExpBuffer();
5702 appendPQExpBuffer(query,
5703 "SELECT e.extname "
5704 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5705 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5706 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5707 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5708 catalog,
5709 dobj->catId.oid);
5710 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5711 ntups = PQntuples(res);
5712 i_extname = PQfnumber(res, "extname");
5713 for (i = 0; i < ntups; i++)
5714 {
5715 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5716 keyword, nm,
5717 fmtId(PQgetvalue(res, i, i_extname)));
5718 }
5719
5720 PQclear(res);
5721 destroyPQExpBuffer(query);
5722 pg_free(nm);
5723 }
5724}
5725
5726static Oid
5728{
5729 /*
5730 * If the old version didn't assign an array type, but the new version
5731 * does, we must select an unused type OID to assign. This currently only
5732 * happens for domains, when upgrading pre-v11 to v11 and up.
5733 *
5734 * Note: local state here is kind of ugly, but we must have some, since we
5735 * mustn't choose the same unused OID more than once.
5736 */
5738 PGresult *res;
5739 bool is_dup;
5740
5741 do
5742 {
5745 "SELECT EXISTS(SELECT 1 "
5746 "FROM pg_catalog.pg_type "
5747 "WHERE oid = '%u'::pg_catalog.oid);",
5750 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5751 PQclear(res);
5752 } while (is_dup);
5753
5755}
5756
5757static void
5761 bool force_array_type,
5763{
5765 PGresult *res;
5769 TypeInfo *tinfo;
5770
5771 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5773 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5774 pg_type_oid);
5775
5777 if (tinfo)
5778 pg_type_array_oid = tinfo->typarray;
5779 else
5781
5784
5786 {
5788 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5790 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5792 }
5793
5794 /*
5795 * Pre-set the multirange type oid and its own array type oid.
5796 */
5798 {
5799 if (fout->remoteVersion >= 140000)
5800 {
5802 "SELECT t.oid, t.typarray "
5803 "FROM pg_catalog.pg_type t "
5804 "JOIN pg_catalog.pg_range r "
5805 "ON t.oid = r.rngmultitypid "
5806 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5807 pg_type_oid);
5808
5810
5811 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5812 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5813
5814 PQclear(res);
5815 }
5816 else
5817 {
5820 }
5821
5823 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5825 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5828 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5830 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5832 }
5833
5835}
5836
5837static void
5848
5849/*
5850 * bsearch() comparator for BinaryUpgradeClassOidItem
5851 */
5852static int
5853BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5854{
5857
5858 return pg_cmp_u32(v1.oid, v2.oid);
5859}
5860
5861/*
5862 * collectBinaryUpgradeClassOids
5863 *
5864 * Construct a table of pg_class information required for
5865 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5866 * lookup.
5867 */
5868static void
5870{
5871 PGresult *res;
5872 const char *query;
5873
5874 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5875 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5876 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5877 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5878 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5879 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5880 "ORDER BY c.oid;";
5881
5882 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5883
5887
5888 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5889 {
5897 }
5898
5899 PQclear(res);
5900}
5901
5902static void
5905{
5906 BinaryUpgradeClassOidItem key = {0};
5908
5910
5911 /*
5912 * Preserve the OID and relfilenumber of the table, table's index, table's
5913 * toast table and toast table's index if any.
5914 *
5915 * One complexity is that the current table definition might not require
5916 * the creation of a TOAST table, but the old database might have a TOAST
5917 * table that was created earlier, before some wide columns were dropped.
5918 * By setting the TOAST oid we force creation of the TOAST heap and index
5919 * by the new backend, so we can copy the files during binary upgrade
5920 * without worrying about this case.
5921 */
5922 key.oid = pg_class_oid;
5926
5928 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5929
5930 if (entry->relkind != RELKIND_INDEX &&
5932 {
5934 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5935 pg_class_oid);
5936
5937 /*
5938 * Not every relation has storage. Also, in a pre-v12 database,
5939 * partitioned tables have a relfilenumber, which should not be
5940 * preserved when upgrading.
5941 */
5942 if (RelFileNumberIsValid(entry->relfilenumber) &&
5945 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5946 entry->relfilenumber);
5947
5948 /*
5949 * In a pre-v12 database, partitioned tables might be marked as having
5950 * toast tables, but we should ignore them if so.
5951 */
5952 if (OidIsValid(entry->toast_oid) &&
5954 {
5956 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5957 entry->toast_oid);
5959 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5960 entry->toast_relfilenumber);
5961
5962 /* every toast table has an index */
5964 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5965 entry->toast_index_oid);
5967 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5969 }
5970 }
5971 else
5972 {
5973 /* Preserve the OID and relfilenumber of the index */
5975 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5976 pg_class_oid);
5978 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5979 entry->relfilenumber);
5980 }
5981
5983}
5984
5985/*
5986 * If the DumpableObject is a member of an extension, add a suitable
5987 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5988 *
5989 * For somewhat historical reasons, objname should already be quoted,
5990 * but not objnamespace (if any).
5991 */
5992static void
5994 const DumpableObject *dobj,
5995 const char *objtype,
5996 const char *objname,
5997 const char *objnamespace)
5998{
6000 int i;
6001
6002 if (!dobj->ext_member)
6003 return;
6004
6005 /*
6006 * Find the parent extension. We could avoid this search if we wanted to
6007 * add a link field to DumpableObject, but the space costs of that would
6008 * be considerable. We assume that member objects could only have a
6009 * direct dependency on their own extension, not any others.
6010 */
6011 for (i = 0; i < dobj->nDeps; i++)
6012 {
6014 if (extobj && extobj->objType == DO_EXTENSION)
6015 break;
6016 extobj = NULL;
6017 }
6018 if (extobj == NULL)
6019 pg_fatal("could not find parent extension for %s %s",
6020 objtype, objname);
6021
6023 "\n-- For binary upgrade, handle extension membership the hard way\n");
6024 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6025 fmtId(extobj->name),
6026 objtype);
6027 if (objnamespace && *objnamespace)
6029 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6030}
6031
6032/*
6033 * getNamespaces:
6034 * get information about all namespaces in the system catalogs
6035 */
6036void
6038{
6039 PGresult *res;
6040 int ntups;
6041 int i;
6042 PQExpBuffer query;
6044 int i_tableoid;
6045 int i_oid;
6046 int i_nspname;
6047 int i_nspowner;
6048 int i_nspacl;
6049 int i_acldefault;
6050
6051 query = createPQExpBuffer();
6052
6053 /*
6054 * we fetch all namespaces including system ones, so that every object we
6055 * read in can be linked to a containing namespace.
6056 */
6057 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6058 "n.nspowner, "
6059 "n.nspacl, "
6060 "acldefault('n', n.nspowner) AS acldefault "
6061 "FROM pg_namespace n");
6062
6063 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6064
6065 ntups = PQntuples(res);
6066
6068
6069 i_tableoid = PQfnumber(res, "tableoid");
6070 i_oid = PQfnumber(res, "oid");
6071 i_nspname = PQfnumber(res, "nspname");
6072 i_nspowner = PQfnumber(res, "nspowner");
6073 i_nspacl = PQfnumber(res, "nspacl");
6074 i_acldefault = PQfnumber(res, "acldefault");
6075
6076 for (i = 0; i < ntups; i++)
6077 {
6078 const char *nspowner;
6079
6080 nsinfo[i].dobj.objType = DO_NAMESPACE;
6081 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6082 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6083 AssignDumpId(&nsinfo[i].dobj);
6084 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6085 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6086 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6087 nsinfo[i].dacl.privtype = 0;
6088 nsinfo[i].dacl.initprivs = NULL;
6089 nspowner = PQgetvalue(res, i, i_nspowner);
6090 nsinfo[i].nspowner = atooid(nspowner);
6091 nsinfo[i].rolname = getRoleName(nspowner);
6092
6093 /* Decide whether to dump this namespace */
6095
6096 /* Mark whether namespace has an ACL */
6097 if (!PQgetisnull(res, i, i_nspacl))
6098 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6099
6100 /*
6101 * We ignore any pg_init_privs.initprivs entry for the public schema
6102 * and assume a predetermined default, for several reasons. First,
6103 * dropping and recreating the schema removes its pg_init_privs entry,
6104 * but an empty destination database starts with this ACL nonetheless.
6105 * Second, we support dump/reload of public schema ownership changes.
6106 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6107 * initprivs continues to reflect the initial owner. Hence,
6108 * synthesize the value that nspacl will have after the restore's
6109 * ALTER SCHEMA OWNER. Third, this makes the destination database
6110 * match the source's ACL, even if the latter was an initdb-default
6111 * ACL, which changed in v15. An upgrade pulls in changes to most
6112 * system object ACLs that the DBA had not customized. We've made the
6113 * public schema depart from that, because changing its ACL so easily
6114 * breaks applications.
6115 */
6116 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6117 {
6120
6121 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6132
6133 nsinfo[i].dacl.privtype = 'i';
6134 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6135 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6136
6139 }
6140 }
6141
6142 PQclear(res);
6143 destroyPQExpBuffer(query);
6144}
6145
6146/*
6147 * findNamespace:
6148 * given a namespace OID, look up the info read by getNamespaces
6149 */
6150static NamespaceInfo *
6152{
6154
6156 if (nsinfo == NULL)
6157 pg_fatal("schema with OID %u does not exist", nsoid);
6158 return nsinfo;
6159}
6160
6161/*
6162 * getExtensions:
6163 * read all extensions in the system catalogs and return them in the
6164 * ExtensionInfo* structure
6165 *
6166 * numExtensions is set to the number of extensions read in
6167 */
6170{
6171 DumpOptions *dopt = fout->dopt;
6172 PGresult *res;
6173 int ntups;
6174 int i;
6175 PQExpBuffer query;
6177 int i_tableoid;
6178 int i_oid;
6179 int i_extname;
6180 int i_nspname;
6181 int i_extrelocatable;
6182 int i_extversion;
6183 int i_extconfig;
6184 int i_extcondition;
6185
6186 query = createPQExpBuffer();
6187
6188 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6189 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6190 "FROM pg_extension x "
6191 "JOIN pg_namespace n ON n.oid = x.extnamespace");
6192
6193 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6194
6195 ntups = PQntuples(res);
6196 if (ntups == 0)
6197 goto cleanup;
6198
6200
6201 i_tableoid = PQfnumber(res, "tableoid");
6202 i_oid = PQfnumber(res, "oid");
6203 i_extname = PQfnumber(res, "extname");
6204 i_nspname = PQfnumber(res, "nspname");
6205 i_extrelocatable = PQfnumber(res, "extrelocatable");
6206 i_extversion = PQfnumber(res, "extversion");
6207 i_extconfig = PQfnumber(res, "extconfig");
6208 i_extcondition = PQfnumber(res, "extcondition");
6209
6210 for (i = 0; i < ntups; i++)
6211 {
6212 extinfo[i].dobj.objType = DO_EXTENSION;
6213 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6214 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6215 AssignDumpId(&extinfo[i].dobj);
6216 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6217 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6218 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6219 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6220 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6221 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6222
6223 /* Decide whether we want to dump it */
6225 }
6226
6227cleanup:
6228 PQclear(res);
6229 destroyPQExpBuffer(query);
6230
6231 *numExtensions = ntups;
6232
6233 return extinfo;
6234}
6235
6236/*
6237 * getTypes:
6238 * get information about all types in the system catalogs
6239 *
6240 * NB: this must run after getFuncs() because we assume we can do
6241 * findFuncByOid().
6242 */
6243void
6245{
6246 PGresult *res;
6247 int ntups;
6248 int i;
6252 int i_tableoid;
6253 int i_oid;
6254 int i_typname;
6255 int i_typnamespace;
6256 int i_typacl;
6257 int i_acldefault;
6258 int i_typowner;
6259 int i_typelem;
6260 int i_typrelid;
6261 int i_typrelkind;
6262 int i_typtype;
6263 int i_typisdefined;
6264 int i_isarray;
6265 int i_typarray;
6266
6267 /*
6268 * we include even the built-in types because those may be used as array
6269 * elements by user-defined types
6270 *
6271 * we filter out the built-in types when we dump out the types
6272 *
6273 * same approach for undefined (shell) types and array types
6274 *
6275 * Note: as of 8.3 we can reliably detect whether a type is an
6276 * auto-generated array type by checking the element type's typarray.
6277 * (Before that the test is capable of generating false positives.) We
6278 * still check for name beginning with '_', though, so as to avoid the
6279 * cost of the subselect probe for all standard types. This would have to
6280 * be revisited if the backend ever allows renaming of array types.
6281 */
6282 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6283 "typnamespace, typacl, "
6284 "acldefault('T', typowner) AS acldefault, "
6285 "typowner, "
6286 "typelem, typrelid, typarray, "
6287 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6288 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6289 "typtype, typisdefined, "
6290 "typname[0] = '_' AND typelem != 0 AND "
6291 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6292 "FROM pg_type");
6293
6294 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6295
6296 ntups = PQntuples(res);
6297
6299
6300 i_tableoid = PQfnumber(res, "tableoid");
6301 i_oid = PQfnumber(res, "oid");
6302 i_typname = PQfnumber(res, "typname");
6303 i_typnamespace = PQfnumber(res, "typnamespace");
6304 i_typacl = PQfnumber(res, "typacl");
6305 i_acldefault = PQfnumber(res, "acldefault");
6306 i_typowner = PQfnumber(res, "typowner");
6307 i_typelem = PQfnumber(res, "typelem");
6308 i_typrelid = PQfnumber(res, "typrelid");
6309 i_typrelkind = PQfnumber(res, "typrelkind");
6310 i_typtype = PQfnumber(res, "typtype");
6311 i_typisdefined = PQfnumber(res, "typisdefined");
6312 i_isarray = PQfnumber(res, "isarray");
6313 i_typarray = PQfnumber(res, "typarray");
6314
6315 for (i = 0; i < ntups; i++)
6316 {
6317 tyinfo[i].dobj.objType = DO_TYPE;
6318 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6319 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6320 AssignDumpId(&tyinfo[i].dobj);
6321 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6322 tyinfo[i].dobj.namespace =
6324 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6325 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6326 tyinfo[i].dacl.privtype = 0;
6327 tyinfo[i].dacl.initprivs = NULL;
6328 tyinfo[i].ftypname = NULL; /* may get filled later */
6329 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6330 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6331 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6332 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6333 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6334 tyinfo[i].shellType = NULL;
6335
6336 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6337 tyinfo[i].isDefined = true;
6338 else
6339 tyinfo[i].isDefined = false;
6340
6341 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6342 tyinfo[i].isArray = true;
6343 else
6344 tyinfo[i].isArray = false;
6345
6346 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6347
6348 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6349 tyinfo[i].isMultirange = true;
6350 else
6351 tyinfo[i].isMultirange = false;
6352
6353 /* Decide whether we want to dump it */
6355
6356 /* Mark whether type has an ACL */
6357 if (!PQgetisnull(res, i, i_typacl))
6358 tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6359
6360 /*
6361 * If it's a domain, fetch info about its constraints, if any
6362 */
6363 tyinfo[i].nDomChecks = 0;
6364 tyinfo[i].domChecks = NULL;
6365 tyinfo[i].notnull = NULL;
6366 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6367 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6369
6370 /*
6371 * If it's a base type, make a DumpableObject representing a shell
6372 * definition of the type. We will need to dump that ahead of the I/O
6373 * functions for the type. Similarly, range types need a shell
6374 * definition in case they have a canonicalize function.
6375 *
6376 * Note: the shell type doesn't have a catId. You might think it
6377 * should copy the base type's catId, but then it might capture the
6378 * pg_depend entries for the type, which we don't want.
6379 */
6380 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6381 (tyinfo[i].typtype == TYPTYPE_BASE ||
6382 tyinfo[i].typtype == TYPTYPE_RANGE))
6383 {
6385 stinfo->dobj.objType = DO_SHELL_TYPE;
6386 stinfo->dobj.catId = nilCatalogId;
6387 AssignDumpId(&stinfo->dobj);
6388 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6389 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6390 stinfo->baseType = &(tyinfo[i]);
6391 tyinfo[i].shellType = stinfo;
6392
6393 /*
6394 * Initially mark the shell type as not to be dumped. We'll only
6395 * dump it if the I/O or canonicalize functions need to be dumped;
6396 * this is taken care of while sorting dependencies.
6397 */
6398 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6399 }
6400 }
6401
6402 PQclear(res);
6403
6404 destroyPQExpBuffer(query);
6405}
6406
6407/*
6408 * getOperators:
6409 * get information about all operators in the system catalogs
6410 */
6411void
6413{
6414 PGresult *res;
6415 int ntups;
6416 int i;
6419 int i_tableoid;
6420 int i_oid;
6421 int i_oprname;
6422 int i_oprnamespace;
6423 int i_oprowner;
6424 int i_oprkind;
6425 int i_oprleft;
6426 int i_oprright;
6427 int i_oprcode;
6428
6429 /*
6430 * find all operators, including builtin operators; we filter out
6431 * system-defined operators at dump-out time.
6432 */
6433
6434 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6435 "oprnamespace, "
6436 "oprowner, "
6437 "oprkind, "
6438 "oprleft, "
6439 "oprright, "
6440 "oprcode::oid AS oprcode "
6441 "FROM pg_operator");
6442
6443 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6444
6445 ntups = PQntuples(res);
6446
6448
6449 i_tableoid = PQfnumber(res, "tableoid");
6450 i_oid = PQfnumber(res, "oid");
6451 i_oprname = PQfnumber(res, "oprname");
6452 i_oprnamespace = PQfnumber(res, "oprnamespace");
6453 i_oprowner = PQfnumber(res, "oprowner");
6454 i_oprkind = PQfnumber(res, "oprkind");
6455 i_oprleft = PQfnumber(res, "oprleft");
6456 i_oprright = PQfnumber(res, "oprright");
6457 i_oprcode = PQfnumber(res, "oprcode");
6458
6459 for (i = 0; i < ntups; i++)
6460 {
6461 oprinfo[i].dobj.objType = DO_OPERATOR;
6462 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6463 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6464 AssignDumpId(&oprinfo[i].dobj);
6465 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6466 oprinfo[i].dobj.namespace =
6468 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6469 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6470 oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6471 oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6472 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6473
6474 /* Decide whether we want to dump it */
6476 }
6477
6478 PQclear(res);
6479
6480 destroyPQExpBuffer(query);
6481}
6482
6483/*
6484 * getCollations:
6485 * get information about all collations in the system catalogs
6486 */
6487void
6489{
6490 PGresult *res;
6491 int ntups;
6492 int i;
6493 PQExpBuffer query;
6495 int i_tableoid;
6496 int i_oid;
6497 int i_collname;
6498 int i_collnamespace;
6499 int i_collowner;
6500 int i_collencoding;
6501
6502 query = createPQExpBuffer();
6503
6504 /*
6505 * find all collations, including builtin collations; we filter out
6506 * system-defined collations at dump-out time.
6507 */
6508
6509 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6510 "collnamespace, "
6511 "collowner, "
6512 "collencoding "
6513 "FROM pg_collation");
6514
6515 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6516
6517 ntups = PQntuples(res);
6518
6520
6521 i_tableoid = PQfnumber(res, "tableoid");
6522 i_oid = PQfnumber(res, "oid");
6523 i_collname = PQfnumber(res, "collname");
6524 i_collnamespace = PQfnumber(res, "collnamespace");
6525 i_collowner = PQfnumber(res, "collowner");
6526 i_collencoding = PQfnumber(res, "collencoding");
6527
6528 for (i = 0; i < ntups; i++)
6529 {
6530 collinfo[i].dobj.objType = DO_COLLATION;
6531 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6532 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6533 AssignDumpId(&collinfo[i].dobj);
6534 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6535 collinfo[i].dobj.namespace =
6537 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6538 collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6539
6540 /* Decide whether we want to dump it */
6542 }
6543
6544 PQclear(res);
6545
6546 destroyPQExpBuffer(query);
6547}
6548
6549/*
6550 * getConversions:
6551 * get information about all conversions in the system catalogs
6552 */
6553void
6555{
6556 PGresult *res;
6557 int ntups;
6558 int i;
6559 PQExpBuffer query;
6561 int i_tableoid;
6562 int i_oid;
6563 int i_conname;
6564 int i_connamespace;
6565 int i_conowner;
6566
6567 query = createPQExpBuffer();
6568
6569 /*
6570 * find all conversions, including builtin conversions; we filter out
6571 * system-defined conversions at dump-out time.
6572 */
6573
6574 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6575 "connamespace, "
6576 "conowner "
6577 "FROM pg_conversion");
6578
6579 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6580
6581 ntups = PQntuples(res);
6582
6584
6585 i_tableoid = PQfnumber(res, "tableoid");
6586 i_oid = PQfnumber(res, "oid");
6587 i_conname = PQfnumber(res, "conname");
6588 i_connamespace = PQfnumber(res, "connamespace");
6589 i_conowner = PQfnumber(res, "conowner");
6590
6591 for (i = 0; i < ntups; i++)
6592 {
6593 convinfo[i].dobj.objType = DO_CONVERSION;
6594 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6595 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6596 AssignDumpId(&convinfo[i].dobj);
6597 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6598 convinfo[i].dobj.namespace =
6600 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6601
6602 /* Decide whether we want to dump it */
6604 }
6605
6606 PQclear(res);
6607
6608 destroyPQExpBuffer(query);
6609}
6610
6611/*
6612 * getAccessMethods:
6613 * get information about all user-defined access methods
6614 */
6615void
6617{
6618 PGresult *res;
6619 int ntups;
6620 int i;
6621 PQExpBuffer query;
6623 int i_tableoid;
6624 int i_oid;
6625 int i_amname;
6626 int i_amhandler;
6627 int i_amtype;
6628
6629 query = createPQExpBuffer();
6630
6631 /*
6632 * Select all access methods from pg_am table.
6633 */
6634 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6636 "amtype, "
6637 "amhandler::pg_catalog.regproc AS amhandler ");
6638 appendPQExpBufferStr(query, "FROM pg_am");
6639
6640 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6641
6642 ntups = PQntuples(res);
6643
6645
6646 i_tableoid = PQfnumber(res, "tableoid");
6647 i_oid = PQfnumber(res, "oid");
6648 i_amname = PQfnumber(res, "amname");
6649 i_amhandler = PQfnumber(res, "amhandler");
6650 i_amtype = PQfnumber(res, "amtype");
6651
6652 for (i = 0; i < ntups; i++)
6653 {
6654 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6655 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6656 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6657 AssignDumpId(&aminfo[i].dobj);
6658 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6659 aminfo[i].dobj.namespace = NULL;
6660 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6661 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6662
6663 /* Decide whether we want to dump it */
6665 }
6666
6667 PQclear(res);
6668
6669 destroyPQExpBuffer(query);
6670}
6671
6672
6673/*
6674 * getOpclasses:
6675 * get information about all opclasses in the system catalogs
6676 */
6677void
6679{
6680 PGresult *res;
6681 int ntups;
6682 int i;
6685 int i_tableoid;
6686 int i_oid;
6687 int i_opcmethod;
6688 int i_opcname;
6689 int i_opcnamespace;
6690 int i_opcowner;
6691
6692 /*
6693 * find all opclasses, including builtin opclasses; we filter out
6694 * system-defined opclasses at dump-out time.
6695 */
6696
6697 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6698 "opcnamespace, "
6699 "opcowner "
6700 "FROM pg_opclass");
6701
6702 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6703
6704 ntups = PQntuples(res);
6705
6707
6708 i_tableoid = PQfnumber(res, "tableoid");
6709 i_oid = PQfnumber(res, "oid");
6710 i_opcmethod = PQfnumber(res, "opcmethod");
6711 i_opcname = PQfnumber(res, "opcname");
6712 i_opcnamespace = PQfnumber(res, "opcnamespace");
6713 i_opcowner = PQfnumber(res, "opcowner");
6714
6715 for (i = 0; i < ntups; i++)
6716 {
6717 opcinfo[i].dobj.objType = DO_OPCLASS;
6718 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6719 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6720 AssignDumpId(&opcinfo[i].dobj);
6721 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6722 opcinfo[i].dobj.namespace =
6724 opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6725 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6726
6727 /* Decide whether we want to dump it */
6729 }
6730
6731 PQclear(res);
6732
6733 destroyPQExpBuffer(query);
6734}
6735
6736/*
6737 * getOpfamilies:
6738 * get information about all opfamilies in the system catalogs
6739 */
6740void
6742{
6743 PGresult *res;
6744 int ntups;
6745 int i;
6746 PQExpBuffer query;
6748 int i_tableoid;
6749 int i_oid;
6750 int i_opfmethod;
6751 int i_opfname;
6752 int i_opfnamespace;
6753 int i_opfowner;
6754
6755 query = createPQExpBuffer();
6756
6757 /*
6758 * find all opfamilies, including builtin opfamilies; we filter out
6759 * system-defined opfamilies at dump-out time.
6760 */
6761
6762 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6763 "opfnamespace, "
6764 "opfowner "
6765 "FROM pg_opfamily");
6766
6767 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6768
6769 ntups = PQntuples(res);
6770
6772
6773 i_tableoid = PQfnumber(res, "tableoid");
6774 i_oid = PQfnumber(res, "oid");
6775 i_opfname = PQfnumber(res, "opfname");
6776 i_opfmethod = PQfnumber(res, "opfmethod");
6777 i_opfnamespace = PQfnumber(res, "opfnamespace");
6778 i_opfowner = PQfnumber(res, "opfowner");
6779
6780 for (i = 0; i < ntups; i++)
6781 {
6782 opfinfo[i].dobj.objType = DO_OPFAMILY;
6783 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6784 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6785 AssignDumpId(&opfinfo[i].dobj);
6786 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6787 opfinfo[i].dobj.namespace =
6789 opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6790 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6791
6792 /* Decide whether we want to dump it */
6794 }
6795
6796 PQclear(res);
6797
6798 destroyPQExpBuffer(query);
6799}
6800
6801/*
6802 * getAggregates:
6803 * get information about all user-defined aggregates in the system catalogs
6804 */
6805void
6807{
6808 DumpOptions *dopt = fout->dopt;
6809 PGresult *res;
6810 int ntups;
6811 int i;
6814 int i_tableoid;
6815 int i_oid;
6816 int i_aggname;
6817 int i_aggnamespace;
6818 int i_pronargs;
6819 int i_proargtypes;
6820 int i_proowner;
6821 int i_aggacl;
6822 int i_acldefault;
6823 const char *agg_check;
6824
6825 /*
6826 * Find all interesting aggregates. See comment in getFuncs() for the
6827 * rationale behind the filtering logic.
6828 */
6829 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6830 : "p.proisagg");
6831
6832 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6833 "p.proname AS aggname, "
6834 "p.pronamespace AS aggnamespace, "
6835 "p.pronargs, p.proargtypes, "
6836 "p.proowner, "
6837 "p.proacl AS aggacl, "
6838 "acldefault('f', p.proowner) AS acldefault "
6839 "FROM pg_proc p "
6840 "LEFT JOIN pg_init_privs pip ON "
6841 "(p.oid = pip.objoid "
6842 "AND pip.classoid = 'pg_proc'::regclass "
6843 "AND pip.objsubid = 0) "
6844 "WHERE %s AND ("
6845 "p.pronamespace != "
6846 "(SELECT oid FROM pg_namespace "
6847 "WHERE nspname = 'pg_catalog') OR "
6848 "p.proacl IS DISTINCT FROM pip.initprivs",
6849 agg_check);
6850 if (dopt->binary_upgrade)
6852 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6853 "classid = 'pg_proc'::regclass AND "
6854 "objid = p.oid AND "
6855 "refclassid = 'pg_extension'::regclass AND "
6856 "deptype = 'e')");
6857 appendPQExpBufferChar(query, ')');
6858
6859 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6860
6861 ntups = PQntuples(res);
6862
6864
6865 i_tableoid = PQfnumber(res, "tableoid");
6866 i_oid = PQfnumber(res, "oid");
6867 i_aggname = PQfnumber(res, "aggname");
6868 i_aggnamespace = PQfnumber(res, "aggnamespace");
6869 i_pronargs = PQfnumber(res, "pronargs");
6870 i_proargtypes = PQfnumber(res, "proargtypes");
6871 i_proowner = PQfnumber(res, "proowner");
6872 i_aggacl = PQfnumber(res, "aggacl");
6873 i_acldefault = PQfnumber(res, "acldefault");
6874
6875 for (i = 0; i < ntups; i++)
6876 {
6877 agginfo[i].aggfn.dobj.objType = DO_AGG;
6878 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6879 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6880 AssignDumpId(&agginfo[i].aggfn.dobj);
6881 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6882 agginfo[i].aggfn.dobj.namespace =
6884 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6885 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6886 agginfo[i].aggfn.dacl.privtype = 0;
6887 agginfo[i].aggfn.dacl.initprivs = NULL;
6888 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6889 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6890 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6891 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6892 if (agginfo[i].aggfn.nargs == 0)
6893 agginfo[i].aggfn.argtypes = NULL;
6894 else
6895 {
6896 agginfo[i].aggfn.argtypes = pg_malloc_array(Oid, agginfo[i].aggfn.nargs);
6898 agginfo[i].aggfn.argtypes,
6899 agginfo[i].aggfn.nargs);
6900 }
6901 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6902
6903 /* Decide whether we want to dump it */
6904 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6905
6906 /* Mark whether aggregate has an ACL */
6907 if (!PQgetisnull(res, i, i_aggacl))
6908 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6909 }
6910
6911 PQclear(res);
6912
6913 destroyPQExpBuffer(query);
6914}
6915
6916/*
6917 * getFuncs:
6918 * get information about all user-defined functions in the system catalogs
6919 */
6920void
6922{
6923 DumpOptions *dopt = fout->dopt;
6924 PGresult *res;
6925 int ntups;
6926 int i;
6928 FuncInfo *finfo;
6929 int i_tableoid;
6930 int i_oid;
6931 int i_proname;
6932 int i_pronamespace;
6933 int i_proowner;
6934 int i_prolang;
6935 int i_pronargs;
6936 int i_proargtypes;
6937 int i_prorettype;
6938 int i_proacl;
6939 int i_acldefault;
6940 const char *not_agg_check;
6941
6942 /*
6943 * Find all interesting functions. This is a bit complicated:
6944 *
6945 * 1. Always exclude aggregates; those are handled elsewhere.
6946 *
6947 * 2. Always exclude functions that are internally dependent on something
6948 * else, since presumably those will be created as a result of creating
6949 * the something else. This currently acts only to suppress constructor
6950 * functions for range types. Note this is OK only because the
6951 * constructors don't have any dependencies the range type doesn't have;
6952 * otherwise we might not get creation ordering correct.
6953 *
6954 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6955 * they're members of extensions and we are in binary-upgrade mode then
6956 * include them, since we want to dump extension members individually in
6957 * that mode. Also, if they are used by casts or transforms then we need
6958 * to gather the information about them, though they won't be dumped if
6959 * they are built-in. Also, include functions in pg_catalog if they have
6960 * an ACL different from what's shown in pg_init_privs (so we have to join
6961 * to pg_init_privs; annoying).
6962 */
6963 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6964 : "NOT p.proisagg");
6965
6966 appendPQExpBuffer(query,
6967 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6968 "p.pronargs, p.proargtypes, p.prorettype, "
6969 "p.proacl, "
6970 "acldefault('f', p.proowner) AS acldefault, "
6971 "p.pronamespace, "
6972 "p.proowner "
6973 "FROM pg_proc p "
6974 "LEFT JOIN pg_init_privs pip ON "
6975 "(p.oid = pip.objoid "
6976 "AND pip.classoid = 'pg_proc'::regclass "
6977 "AND pip.objsubid = 0) "
6978 "WHERE %s"
6979 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6980 "WHERE classid = 'pg_proc'::regclass AND "
6981 "objid = p.oid AND deptype = 'i')"
6982 "\n AND ("
6983 "\n pronamespace != "
6984 "(SELECT oid FROM pg_namespace "
6985 "WHERE nspname = 'pg_catalog')"
6986 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6987 "\n WHERE pg_cast.oid > %u "
6988 "\n AND p.oid = pg_cast.castfunc)"
6989 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6990 "\n WHERE pg_transform.oid > %u AND "
6991 "\n (p.oid = pg_transform.trffromsql"
6992 "\n OR p.oid = pg_transform.trftosql))",
6996 if (dopt->binary_upgrade)
6998 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6999 "classid = 'pg_proc'::regclass AND "
7000 "objid = p.oid AND "
7001 "refclassid = 'pg_extension'::regclass AND "
7002 "deptype = 'e')");
7004 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7005 appendPQExpBufferChar(query, ')');
7006
7007 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7008
7009 ntups = PQntuples(res);
7010
7011 finfo = pg_malloc0_array(FuncInfo, ntups);
7012
7013 i_tableoid = PQfnumber(res, "tableoid");
7014 i_oid = PQfnumber(res, "oid");
7015 i_proname = PQfnumber(res, "proname");
7016 i_pronamespace = PQfnumber(res, "pronamespace");
7017 i_proowner = PQfnumber(res, "proowner");
7018 i_prolang = PQfnumber(res, "prolang");
7019 i_pronargs = PQfnumber(res, "pronargs");
7020 i_proargtypes = PQfnumber(res, "proargtypes");
7021 i_prorettype = PQfnumber(res, "prorettype");
7022 i_proacl = PQfnumber(res, "proacl");
7023 i_acldefault = PQfnumber(res, "acldefault");
7024
7025 for (i = 0; i < ntups; i++)
7026 {
7027 finfo[i].dobj.objType = DO_FUNC;
7028 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7029 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7030 AssignDumpId(&finfo[i].dobj);
7031 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7032 finfo[i].dobj.namespace =
7034 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7036 finfo[i].dacl.privtype = 0;
7037 finfo[i].dacl.initprivs = NULL;
7038 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7039 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7040 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7041 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7042 if (finfo[i].nargs == 0)
7043 finfo[i].argtypes = NULL;
7044 else
7045 {
7046 finfo[i].argtypes = pg_malloc_array(Oid, finfo[i].nargs);
7048 finfo[i].argtypes, finfo[i].nargs);
7049 }
7050 finfo[i].postponed_def = false; /* might get set during sort */
7051
7052 /* Decide whether we want to dump it */
7053 selectDumpableObject(&(finfo[i].dobj), fout);
7054
7055 /* Mark whether function has an ACL */
7056 if (!PQgetisnull(res, i, i_proacl))
7058 }
7059
7060 PQclear(res);
7061
7062 destroyPQExpBuffer(query);
7063}
7064
7065/*
7066 * getRelationStatistics
7067 * register the statistics object as a dependent of the relation.
7068 *
7069 * reltuples is passed as a string to avoid complexities in converting from/to
7070 * floating point.
7071 */
7072static RelStatsInfo *
7074 char *reltuples, int32 relallvisible,
7075 int32 relallfrozen, char relkind,
7076 char **indAttNames, int nindAttNames)
7077{
7078 if (!fout->dopt->dumpStatistics)
7079 return NULL;
7080
7081 if ((relkind == RELKIND_RELATION) ||
7082 (relkind == RELKIND_PARTITIONED_TABLE) ||
7083 (relkind == RELKIND_INDEX) ||
7084 (relkind == RELKIND_PARTITIONED_INDEX) ||
7085 (relkind == RELKIND_MATVIEW ||
7086 relkind == RELKIND_FOREIGN_TABLE))
7087 {
7089 DumpableObject *dobj = &info->dobj;
7090
7091 dobj->objType = DO_REL_STATS;
7092 dobj->catId.tableoid = 0;
7093 dobj->catId.oid = 0;
7094 AssignDumpId(dobj);
7096 dobj->dependencies[0] = rel->dumpId;
7097 dobj->nDeps = 1;
7098 dobj->allocDeps = 1;
7100 dobj->name = pg_strdup(rel->name);
7101 dobj->namespace = rel->namespace;
7102 info->relid = rel->catId.oid;
7103 info->relpages = relpages;
7104 info->reltuples = pstrdup(reltuples);
7105 info->relallvisible = relallvisible;
7106 info->relallfrozen = relallfrozen;
7107 info->relkind = relkind;
7108 info->indAttNames = indAttNames;
7109 info->nindAttNames = nindAttNames;
7110
7111 /*
7112 * Ordinarily, stats go in SECTION_DATA for tables and
7113 * SECTION_POST_DATA for indexes.
7114 *
7115 * However, the section may be updated later for materialized view
7116 * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7117 * the stats, so the stats must be restored after the data. Also, the
7118 * materialized view definition may be postponed to SECTION_POST_DATA
7119 * (see repairMatViewBoundaryMultiLoop()).
7120 */
7121 switch (info->relkind)
7122 {
7123 case RELKIND_RELATION:
7125 case RELKIND_MATVIEW:
7127 info->section = SECTION_DATA;
7128 break;
7129 case RELKIND_INDEX:
7131 info->section = SECTION_POST_DATA;
7132 break;
7133 default:
7134 pg_fatal("cannot dump statistics for relation kind \"%c\"",
7135 info->relkind);
7136 }
7137
7138 return info;
7139 }
7140 return NULL;
7141}
7142
7143/*
7144 * getTables
7145 * read all the tables (no indexes) in the system catalogs,
7146 * and return them as an array of TableInfo structures
7147 *
7148 * *numTables is set to the number of tables read in
7149 */
7150TableInfo *
7152{
7153 DumpOptions *dopt = fout->dopt;
7154 PGresult *res;
7155 int ntups;
7156 int i;
7158 TableInfo *tblinfo;
7159 int i_reltableoid;
7160 int i_reloid;
7161 int i_relname;
7162 int i_relnamespace;
7163 int i_relkind;
7164 int i_reltype;
7165 int i_relowner;
7166 int i_relchecks;
7167 int i_relhasindex;
7168 int i_relhasrules;
7169 int i_relpages;
7170 int i_reltuples;
7171 int i_relallvisible;
7172 int i_relallfrozen;
7173 int i_toastpages;
7174 int i_owning_tab;
7175 int i_owning_col;
7176 int i_reltablespace;
7177 int i_relhasoids;
7178 int i_relhastriggers;
7179 int i_relpersistence;
7180 int i_relispopulated;
7181 int i_relreplident;
7182 int i_relrowsec;
7183 int i_relforcerowsec;
7184 int i_relfrozenxid;
7185 int i_toastfrozenxid;
7186 int i_toastoid;
7187 int i_relminmxid;
7188 int i_toastminmxid;
7189 int i_reloptions;
7190 int i_checkoption;
7192 int i_reloftype;
7193 int i_foreignserver;
7194 int i_amname;
7196 int i_relacl;
7197 int i_acldefault;
7198 int i_ispartition;
7199
7200 /*
7201 * Find all the tables and table-like objects.
7202 *
7203 * We must fetch all tables in this phase because otherwise we cannot
7204 * correctly identify inherited columns, owned sequences, etc.
7205 *
7206 * We include system catalogs, so that we can work if a user table is
7207 * defined to inherit from a system catalog (pretty weird, but...)
7208 *
7209 * Note: in this phase we should collect only a minimal amount of
7210 * information about each table, basically just enough to decide if it is
7211 * interesting. In particular, since we do not yet have lock on any user
7212 * table, we MUST NOT invoke any server-side data collection functions
7213 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7214 * wrong answers if any concurrent DDL is happening.
7215 */
7216
7218 "SELECT c.tableoid, c.oid, c.relname, "
7219 "c.relnamespace, c.relkind, c.reltype, "
7220 "c.relowner, "
7221 "c.relchecks, "
7222 "c.relhasindex, c.relhasrules, c.relpages, "
7223 "c.reltuples, c.relallvisible, ");
7224
7225 if (fout->remoteVersion >= 180000)
7226 appendPQExpBufferStr(query, "c.relallfrozen, ");
7227 else
7228 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7229
7231 "c.relhastriggers, c.relpersistence, "
7232 "c.reloftype, "
7233 "c.relacl, "
7234 "acldefault(CASE"
7235 " WHEN c.relkind = " CppAsString2(RELKIND_PROPGRAPH));
7236 /* 19beta1 didn't support acldefault('g'), so we'll fix that below */
7238 fout->remoteVersion >= 200000 ?
7239 " THEN 'g'::\"char\"" :
7240 " THEN NULL");
7242 " WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7243 " THEN 's'::\"char\""
7244 " ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7245 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7246 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7247 "ELSE 0 END AS foreignserver, "
7248 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7249 "tc.oid AS toid, "
7250 "tc.relpages AS toastpages, "
7251 "tc.reloptions AS toast_reloptions, "
7252 "d.refobjid AS owning_tab, "
7253 "d.refobjsubid AS owning_col, "
7254 "tsp.spcname AS reltablespace, ");
7255
7256 if (fout->remoteVersion >= 120000)
7258 "false AS relhasoids, ");
7259 else
7261 "c.relhasoids, ");
7262
7264 "c.relispopulated, ");
7265
7267 "c.relreplident, ");
7268
7270 "c.relrowsecurity, c.relforcerowsecurity, ");
7271
7273 "c.relminmxid, tc.relminmxid AS tminmxid, ");
7274
7276 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7277 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7278 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7279
7281 "am.amname, ");
7282
7284 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7285
7287 "c.relispartition AS ispartition ");
7288
7289 /*
7290 * Left join to pg_depend to pick up dependency info linking sequences to
7291 * their owning column, if any (note this dependency is AUTO except for
7292 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7293 * collect the spcname.
7294 */
7296 "\nFROM pg_class c\n"
7297 "LEFT JOIN pg_depend d ON "
7298 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7299 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7300 "d.objsubid = 0 AND "
7301 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7302 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7303
7304 /*
7305 * Left join to pg_am to pick up the amname.
7306 */
7308 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7309
7310 /*
7311 * We purposefully ignore toast OIDs for partitioned tables; the reason is
7312 * that versions 10 and 11 have them, but later versions do not, so
7313 * emitting them causes the upgrade to fail.
7314 */
7316 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7317 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7318 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7319
7320 /*
7321 * Restrict to interesting relkinds (in particular, not indexes). Not all
7322 * relkinds are possible in older servers, but it's not worth the trouble
7323 * to emit a version-dependent list.
7324 *
7325 * Composite-type table entries won't be dumped as such, but we have to
7326 * make a DumpableObject for them so that we can track dependencies of the
7327 * composite type (pg_depend entries for columns of the composite type
7328 * link to the pg_class entry not the pg_type entry).
7329 */
7331 "WHERE c.relkind IN ("
7340 "ORDER BY c.oid");
7341
7342 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7343
7344 ntups = PQntuples(res);
7345
7346 *numTables = ntups;
7347
7348 /*
7349 * Extract data from result and lock dumpable tables. We do the locking
7350 * before anything else, to minimize the window wherein a table could
7351 * disappear under us.
7352 *
7353 * Note that we have to save info about all tables here, even when dumping
7354 * only one, because we don't yet know which tables might be inheritance
7355 * ancestors of the target table.
7356 */
7357 tblinfo = pg_malloc0_array(TableInfo, ntups);
7358
7359 i_reltableoid = PQfnumber(res, "tableoid");
7360 i_reloid = PQfnumber(res, "oid");
7361 i_relname = PQfnumber(res, "relname");
7362 i_relnamespace = PQfnumber(res, "relnamespace");
7363 i_relkind = PQfnumber(res, "relkind");
7364 i_reltype = PQfnumber(res, "reltype");
7365 i_relowner = PQfnumber(res, "relowner");
7366 i_relchecks = PQfnumber(res, "relchecks");
7367 i_relhasindex = PQfnumber(res, "relhasindex");
7368 i_relhasrules = PQfnumber(res, "relhasrules");
7369 i_relpages = PQfnumber(res, "relpages");
7370 i_reltuples = PQfnumber(res, "reltuples");
7371 i_relallvisible = PQfnumber(res, "relallvisible");
7372 i_relallfrozen = PQfnumber(res, "relallfrozen");
7373 i_toastpages = PQfnumber(res, "toastpages");
7374 i_owning_tab = PQfnumber(res, "owning_tab");
7375 i_owning_col = PQfnumber(res, "owning_col");
7376 i_reltablespace = PQfnumber(res, "reltablespace");
7377 i_relhasoids = PQfnumber(res, "relhasoids");
7378 i_relhastriggers = PQfnumber(res, "relhastriggers");
7379 i_relpersistence = PQfnumber(res, "relpersistence");
7380 i_relispopulated = PQfnumber(res, "relispopulated");
7381 i_relreplident = PQfnumber(res, "relreplident");
7382 i_relrowsec = PQfnumber(res, "relrowsecurity");
7383 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7384 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7385 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7386 i_toastoid = PQfnumber(res, "toid");
7387 i_relminmxid = PQfnumber(res, "relminmxid");
7388 i_toastminmxid = PQfnumber(res, "tminmxid");
7389 i_reloptions = PQfnumber(res, "reloptions");
7390 i_checkoption = PQfnumber(res, "checkoption");
7391 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7392 i_reloftype = PQfnumber(res, "reloftype");
7393 i_foreignserver = PQfnumber(res, "foreignserver");
7394 i_amname = PQfnumber(res, "amname");
7395 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7396 i_relacl = PQfnumber(res, "relacl");
7397 i_acldefault = PQfnumber(res, "acldefault");
7398 i_ispartition = PQfnumber(res, "ispartition");
7399
7400 if (dopt->lockWaitTimeout)
7401 {
7402 /*
7403 * Arrange to fail instead of waiting forever for a table lock.
7404 *
7405 * NB: this coding assumes that the only queries issued within the
7406 * following loop are LOCK TABLEs; else the timeout may be undesirably
7407 * applied to other things too.
7408 */
7409 resetPQExpBuffer(query);
7410 appendPQExpBufferStr(query, "SET statement_timeout = ");
7412 ExecuteSqlStatement(fout, query->data);
7413 }
7414
7415 resetPQExpBuffer(query);
7416
7417 for (i = 0; i < ntups; i++)
7418 {
7419 int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7420 int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7421
7422 tblinfo[i].dobj.objType = DO_TABLE;
7423 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7424 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7425 AssignDumpId(&tblinfo[i].dobj);
7426 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7427 tblinfo[i].dobj.namespace =
7429 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7430 /* acldefault computed below */
7431 tblinfo[i].dacl.privtype = 0;
7432 tblinfo[i].dacl.initprivs = NULL;
7433 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7434 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7435 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7436 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7437 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7438 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7439 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7440 if (PQgetisnull(res, i, i_toastpages))
7441 tblinfo[i].toastpages = 0;
7442 else
7443 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7444 if (PQgetisnull(res, i, i_owning_tab))
7445 {
7446 tblinfo[i].owning_tab = InvalidOid;
7447 tblinfo[i].owning_col = 0;
7448 }
7449 else
7450 {
7451 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7452 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7453 }
7455 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7456 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7457 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7458 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7459 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7460 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7461 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7462 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7464 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7465 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7466 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7467 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7468 if (PQgetisnull(res, i, i_checkoption))
7469 tblinfo[i].checkoption = NULL;
7470 else
7471 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7473 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7475 if (PQgetisnull(res, i, i_amname))
7476 tblinfo[i].amname = NULL;
7477 else
7478 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7479 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7480 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7481
7482 if (tblinfo[i].relkind == RELKIND_PROPGRAPH &&
7483 !(fout->remoteVersion >= 200000))
7484 {
7487
7488 /* Standard ACL as of v19 is {owner=r/owner} */
7490 quoteAclUserName(aclitem, tblinfo[i].rolname);
7492 quoteAclUserName(aclitem, tblinfo[i].rolname);
7495
7496 tblinfo[i].dacl.acldefault = pstrdup(aclarray->data);
7497
7500 }
7501 else
7502 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7503
7504 /* other fields were zeroed above */
7505
7506 /*
7507 * Decide whether we want to dump this table.
7508 */
7509 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7510 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7511 else
7512 selectDumpableTable(&tblinfo[i], fout);
7513
7514 /*
7515 * Now, consider the table "interesting" if we need to dump its
7516 * definition, data or its statistics. Later on, we'll skip a lot of
7517 * data collection for uninteresting tables.
7518 *
7519 * Note: the "interesting" flag will also be set by flagInhTables for
7520 * parents of interesting tables, so that we collect necessary
7521 * inheritance info even when the parents are not themselves being
7522 * dumped. This is the main reason why we need an "interesting" flag
7523 * that's separate from the components-to-dump bitmask.
7524 */
7525 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7529
7530 tblinfo[i].dummy_view = false; /* might get set during sort */
7531 tblinfo[i].postponed_def = false; /* might get set during sort */
7532
7533 /* Tables have data */
7535
7536 /* Mark whether table has an ACL */
7537 if (!PQgetisnull(res, i, i_relacl))
7538 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7539 tblinfo[i].hascolumnACLs = false; /* may get set later */
7540
7541 /* Add statistics */
7542 if (tblinfo[i].interesting)
7543 {
7544 RelStatsInfo *stats;
7545
7546 stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7547 tblinfo[i].relpages,
7548 PQgetvalue(res, i, i_reltuples),
7549 relallvisible, relallfrozen,
7550 tblinfo[i].relkind, NULL, 0);
7551 if (tblinfo[i].relkind == RELKIND_MATVIEW)
7552 tblinfo[i].stats = stats;
7553 }
7554
7555 /*
7556 * Read-lock target tables to make sure they aren't DROPPED or altered
7557 * in schema before we get around to dumping them.
7558 *
7559 * Note that we don't explicitly lock parents of the target tables; we
7560 * assume our lock on the child is enough to prevent schema
7561 * alterations to parent tables.
7562 *
7563 * NOTE: it'd be kinda nice to lock other relations too, not only
7564 * plain or partitioned tables, but the backend doesn't presently
7565 * allow that.
7566 *
7567 * We only need to lock the table for certain components; see
7568 * pg_dump.h
7569 */
7570 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7571 (tblinfo[i].relkind == RELKIND_RELATION ||
7572 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7573 {
7574 /*
7575 * Tables are locked in batches. When dumping from a remote
7576 * server this can save a significant amount of time by reducing
7577 * the number of round trips.
7578 */
7579 if (query->len == 0)
7580 appendPQExpBuffer(query, "LOCK TABLE %s",
7581 fmtQualifiedDumpable(&tblinfo[i]));
7582 else
7583 {
7584 appendPQExpBuffer(query, ", %s",
7585 fmtQualifiedDumpable(&tblinfo[i]));
7586
7587 /* Arbitrarily end a batch when query length reaches 100K. */
7588 if (query->len >= 100000)
7589 {
7590 /* Lock another batch of tables. */
7591 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7592 ExecuteSqlStatement(fout, query->data);
7593 resetPQExpBuffer(query);
7594 }
7595 }
7596 }
7597 }
7598
7599 if (query->len != 0)
7600 {
7601 /* Lock the tables in the last batch. */
7602 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7603 ExecuteSqlStatement(fout, query->data);
7604 }
7605
7606 if (dopt->lockWaitTimeout)
7607 {
7608 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7609 }
7610
7611 PQclear(res);
7612
7613 destroyPQExpBuffer(query);
7614
7615 return tblinfo;
7616}
7617
7618/*
7619 * getOwnedSeqs
7620 * identify owned sequences and mark them as dumpable if owning table is
7621 *
7622 * We used to do this in getTables(), but it's better to do it after the
7623 * index used by findTableByOid() has been set up.
7624 */
7625void
7627{
7628 int i;
7629
7630 /*
7631 * Force sequences that are "owned" by table columns to be dumped whenever
7632 * their owning table is being dumped.
7633 */
7634 for (i = 0; i < numTables; i++)
7635 {
7636 TableInfo *seqinfo = &tblinfo[i];
7637 TableInfo *owning_tab;
7638
7639 if (!OidIsValid(seqinfo->owning_tab))
7640 continue; /* not an owned sequence */
7641
7642 owning_tab = findTableByOid(seqinfo->owning_tab);
7643 if (owning_tab == NULL)
7644 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7645 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7646
7647 /*
7648 * For an identity sequence, dump exactly the same components for the
7649 * sequence as for the owning table. This is important because we
7650 * treat the identity sequence as an integral part of the table. For
7651 * example, there is not any DDL command that allows creation of such
7652 * a sequence independently of the table.
7653 *
7654 * For other owned sequences such as serial sequences, we need to dump
7655 * the components that are being dumped for the table and any
7656 * components that the sequence is explicitly marked with.
7657 *
7658 * We can't simply use the set of components which are being dumped
7659 * for the table as the table might be in an extension (and only the
7660 * non-extension components, eg: ACLs if changed, security labels, and
7661 * policies, are being dumped) while the sequence is not (and
7662 * therefore the definition and other components should also be
7663 * dumped).
7664 *
7665 * If the sequence is part of the extension then it should be properly
7666 * marked by checkExtensionMembership() and this will be a no-op as
7667 * the table will be equivalently marked.
7668 */
7669 if (seqinfo->is_identity_sequence)
7670 seqinfo->dobj.dump = owning_tab->dobj.dump;
7671 else
7672 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7673
7674 /* Make sure that necessary data is available if we're dumping it */
7675 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7676 {
7677 seqinfo->interesting = true;
7678 owning_tab->interesting = true;
7679 }
7680 }
7681}
7682
7683/*
7684 * getInherits
7685 * read all the inheritance information
7686 * from the system catalogs return them in the InhInfo* structure
7687 *
7688 * numInherits is set to the number of pairs read in
7689 */
7690InhInfo *
7692{
7693 PGresult *res;
7694 int ntups;
7695 int i;
7698
7699 int i_inhrelid;
7700 int i_inhparent;
7701
7702 /* find all the inheritance information */
7703 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7704
7705 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7706
7707 ntups = PQntuples(res);
7708
7709 *numInherits = ntups;
7710
7712
7713 i_inhrelid = PQfnumber(res, "inhrelid");
7714 i_inhparent = PQfnumber(res, "inhparent");
7715
7716 for (i = 0; i < ntups; i++)
7717 {
7718 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7719 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7720 }
7721
7722 PQclear(res);
7723
7724 destroyPQExpBuffer(query);
7725
7726 return inhinfo;
7727}
7728
7729/*
7730 * getPartitioningInfo
7731 * get information about partitioning
7732 *
7733 * For the most part, we only collect partitioning info about tables we
7734 * intend to dump. However, this function has to consider all partitioned
7735 * tables in the database, because we need to know about parents of partitions
7736 * we are going to dump even if the parents themselves won't be dumped.
7737 *
7738 * Specifically, what we need to know is whether each partitioned table
7739 * has an "unsafe" partitioning scheme that requires us to force
7740 * load-via-partition-root mode for its children. Currently the only case
7741 * for which we force that is hash partitioning on enum columns, since the
7742 * hash codes depend on enum value OIDs which won't be replicated across
7743 * dump-and-reload. There are other cases in which load-via-partition-root
7744 * might be necessary, but we expect users to cope with them.
7745 */
7746void
7748{
7749 PQExpBuffer query;
7750 PGresult *res;
7751 int ntups;
7752
7753 /* hash partitioning didn't exist before v11 */
7754 if (fout->remoteVersion < 110000)
7755 return;
7756 /* needn't bother if not dumping data */
7757 if (!fout->dopt->dumpData)
7758 return;
7759
7760 query = createPQExpBuffer();
7761
7762 /*
7763 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7764 * appears among the partition opclasses. We needn't check partstrat.
7765 *
7766 * Note that this query may well retrieve info about tables we aren't
7767 * going to dump and hence have no lock on. That's okay since we need not
7768 * invoke any unsafe server-side functions.
7769 */
7771 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7772 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7773 "ON c.opcmethod = a.oid\n"
7774 "WHERE opcname = 'enum_ops' "
7775 "AND opcnamespace = 'pg_catalog'::regnamespace "
7776 "AND amname = 'hash') = ANY(partclass)");
7777
7778 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7779
7780 ntups = PQntuples(res);
7781
7782 for (int i = 0; i < ntups; i++)
7783 {
7784 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7786
7788 if (tbinfo == NULL)
7789 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7790 tabrelid);
7791 tbinfo->unsafe_partitions = true;
7792 }
7793
7794 PQclear(res);
7795
7796 destroyPQExpBuffer(query);
7797}
7798
7799/*
7800 * getIndexes
7801 * get information about every index on a dumpable table
7802 *
7803 * Note: index data is not returned directly to the caller, but it
7804 * does get entered into the DumpableObject tables.
7805 */
7806void
7808{
7811 PGresult *res;
7812 int ntups;
7813 int curtblindx;
7815 int i_tableoid,
7816 i_oid,
7817 i_indrelid,
7819 i_relpages,
7824 i_indexdef,
7826 i_indnatts,
7827 i_indkey,
7831 i_contype,
7832 i_conname,
7837 i_conoid,
7838 i_condef,
7844
7845 /*
7846 * We want to perform just one query against pg_index. However, we
7847 * mustn't try to select every row of the catalog and then sort it out on
7848 * the client side, because some of the server-side functions we need
7849 * would be unsafe to apply to tables we don't have lock on. Hence, we
7850 * build an array of the OIDs of tables we care about (and now have lock
7851 * on!), and use a WHERE clause to constrain which rows are selected.
7852 */
7854 for (int i = 0; i < numTables; i++)
7855 {
7856 TableInfo *tbinfo = &tblinfo[i];
7857
7858 if (!tbinfo->hasindex)
7859 continue;
7860
7861 /*
7862 * We can ignore indexes of uninteresting tables.
7863 */
7864 if (!tbinfo->interesting)
7865 continue;
7866
7867 /* OK, we need info for this table */
7868 if (tbloids->len > 1) /* do we have more than the '{'? */
7870 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7871 }
7873
7875 "SELECT t.tableoid, t.oid, i.indrelid, "
7876 "t.relname AS indexname, "
7877 "t.relpages, t.reltuples, t.relallvisible, ");
7878
7879 if (fout->remoteVersion >= 180000)
7880 appendPQExpBufferStr(query, "t.relallfrozen, ");
7881 else
7882 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7883
7885 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7886 "i.indkey, i.indisclustered, "
7887 "c.contype, c.conname, "
7888 "c.condeferrable, c.condeferred, "
7889 "c.tableoid AS contableoid, "
7890 "c.oid AS conoid, "
7891 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7892 "CASE WHEN i.indexprs IS NOT NULL THEN "
7893 "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7894 " FROM pg_catalog.pg_attribute "
7895 " WHERE attrelid = i.indexrelid) "
7896 "ELSE NULL END AS indattnames, "
7897 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7898 "t.reloptions AS indreloptions, ");
7899
7900
7902 "i.indisreplident, ");
7903
7904 if (fout->remoteVersion >= 110000)
7906 "inh.inhparent AS parentidx, "
7907 "i.indnkeyatts AS indnkeyatts, "
7908 "i.indnatts AS indnatts, "
7909 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7910 " FROM pg_catalog.pg_attribute "
7911 " WHERE attrelid = i.indexrelid AND "
7912 " attstattarget >= 0) AS indstatcols, "
7913 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7914 " FROM pg_catalog.pg_attribute "
7915 " WHERE attrelid = i.indexrelid AND "
7916 " attstattarget >= 0) AS indstatvals, ");
7917 else
7919 "0 AS parentidx, "
7920 "i.indnatts AS indnkeyatts, "
7921 "i.indnatts AS indnatts, "
7922 "'' AS indstatcols, "
7923 "'' AS indstatvals, ");
7924
7925 if (fout->remoteVersion >= 150000)
7927 "i.indnullsnotdistinct, ");
7928 else
7930 "false AS indnullsnotdistinct, ");
7931
7932 if (fout->remoteVersion >= 180000)
7934 "c.conperiod ");
7935 else
7937 "NULL AS conperiod ");
7938
7939 /*
7940 * The point of the messy-looking outer join is to find a constraint that
7941 * is related by an internal dependency link to the index. If we find one,
7942 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7943 * index won't have more than one internal dependency.
7944 *
7945 * Note: the check on conrelid is redundant, but useful because that
7946 * column is indexed while conindid is not.
7947 */
7948 if (fout->remoteVersion >= 110000)
7949 {
7950 appendPQExpBuffer(query,
7951 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7952 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7953 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7954 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7955 "LEFT JOIN pg_catalog.pg_constraint c "
7956 "ON (i.indrelid = c.conrelid AND "
7957 "i.indexrelid = c.conindid AND "
7958 "c.contype IN ('p','u','x')) "
7959 "LEFT JOIN pg_catalog.pg_inherits inh "
7960 "ON (inh.inhrelid = indexrelid) "
7961 "WHERE (i.indisvalid OR t2.relkind = 'p') "
7962 "AND i.indisready "
7963 "ORDER BY i.indrelid, indexname",
7964 tbloids->data);
7965 }
7966 else
7967 {
7968 /*
7969 * the test on indisready is necessary in 9.2, and harmless in
7970 * earlier/later versions
7971 */
7972 appendPQExpBuffer(query,
7973 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7974 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7975 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7976 "LEFT JOIN pg_catalog.pg_constraint c "
7977 "ON (i.indrelid = c.conrelid AND "
7978 "i.indexrelid = c.conindid AND "
7979 "c.contype IN ('p','u','x')) "
7980 "WHERE i.indisvalid AND i.indisready "
7981 "ORDER BY i.indrelid, indexname",
7982 tbloids->data);
7983 }
7984
7985 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7986
7987 ntups = PQntuples(res);
7988
7989 i_tableoid = PQfnumber(res, "tableoid");
7990 i_oid = PQfnumber(res, "oid");
7991 i_indrelid = PQfnumber(res, "indrelid");
7992 i_indexname = PQfnumber(res, "indexname");
7993 i_relpages = PQfnumber(res, "relpages");
7994 i_reltuples = PQfnumber(res, "reltuples");
7995 i_relallvisible = PQfnumber(res, "relallvisible");
7996 i_relallfrozen = PQfnumber(res, "relallfrozen");
7997 i_parentidx = PQfnumber(res, "parentidx");
7998 i_indexdef = PQfnumber(res, "indexdef");
7999 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8000 i_indnatts = PQfnumber(res, "indnatts");
8001 i_indkey = PQfnumber(res, "indkey");
8002 i_indisclustered = PQfnumber(res, "indisclustered");
8003 i_indisreplident = PQfnumber(res, "indisreplident");
8004 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8005 i_contype = PQfnumber(res, "contype");
8006 i_conname = PQfnumber(res, "conname");
8007 i_condeferrable = PQfnumber(res, "condeferrable");
8008 i_condeferred = PQfnumber(res, "condeferred");
8009 i_conperiod = PQfnumber(res, "conperiod");
8010 i_contableoid = PQfnumber(res, "contableoid");
8011 i_conoid = PQfnumber(res, "conoid");
8012 i_condef = PQfnumber(res, "condef");
8013 i_indattnames = PQfnumber(res, "indattnames");
8014 i_tablespace = PQfnumber(res, "tablespace");
8015 i_indreloptions = PQfnumber(res, "indreloptions");
8016 i_indstatcols = PQfnumber(res, "indstatcols");
8017 i_indstatvals = PQfnumber(res, "indstatvals");
8018
8020
8021 /*
8022 * Outer loop iterates once per table, not once per row. Incrementing of
8023 * j is handled by the inner loop.
8024 */
8025 curtblindx = -1;
8026 for (int j = 0; j < ntups;)
8027 {
8030 int numinds;
8031
8032 /* Count rows for this table */
8033 for (numinds = 1; numinds < ntups - j; numinds++)
8034 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8035 break;
8036
8037 /*
8038 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8039 * order.
8040 */
8041 while (++curtblindx < numTables)
8042 {
8043 tbinfo = &tblinfo[curtblindx];
8044 if (tbinfo->dobj.catId.oid == indrelid)
8045 break;
8046 }
8047 if (curtblindx >= numTables)
8048 pg_fatal("unrecognized table OID %u", indrelid);
8049 /* cross-check that we only got requested tables */
8050 if (!tbinfo->hasindex ||
8051 !tbinfo->interesting)
8052 pg_fatal("unexpected index data for table \"%s\"",
8053 tbinfo->dobj.name);
8054
8055 /* Save data for this table */
8056 tbinfo->indexes = indxinfo + j;
8057 tbinfo->numIndexes = numinds;
8058
8059 for (int c = 0; c < numinds; c++, j++)
8060 {
8061 char contype;
8062 char indexkind;
8063 char **indAttNames = NULL;
8064 int nindAttNames = 0;
8066 int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8067 int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8068 int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8069
8070 indxinfo[j].dobj.objType = DO_INDEX;
8071 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8072 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8073 AssignDumpId(&indxinfo[j].dobj);
8074 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8075 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8076 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8077 indxinfo[j].indextable = tbinfo;
8078 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8079 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8080 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8081 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8082 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8083 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8084 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8085 indxinfo[j].indkeys = pg_malloc_array(Oid, indxinfo[j].indnattrs);
8087 indxinfo[j].indkeys, indxinfo[j].indnattrs);
8088 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8089 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8090 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8091 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8092 indxinfo[j].partattaches = (SimplePtrList)
8093 {
8094 NULL, NULL
8095 };
8096
8097 if (indxinfo[j].parentidx == 0)
8099 else
8101
8102 if (!PQgetisnull(res, j, i_indattnames))
8103 {
8105 &indAttNames, &nindAttNames))
8106 pg_fatal("could not parse %s array", "indattnames");
8107 }
8108
8109 relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8110 PQgetvalue(res, j, i_reltuples),
8111 relallvisible, relallfrozen, indexkind,
8112 indAttNames, nindAttNames);
8113
8114 contype = *(PQgetvalue(res, j, i_contype));
8115 if (contype == 'p' || contype == 'u' || contype == 'x')
8116 {
8117 /*
8118 * If we found a constraint matching the index, create an
8119 * entry for it.
8120 */
8122
8124 constrinfo->dobj.objType = DO_CONSTRAINT;
8125 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8126 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8127 AssignDumpId(&constrinfo->dobj);
8128 constrinfo->dobj.dump = tbinfo->dobj.dump;
8129 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8130 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8131 constrinfo->contable = tbinfo;
8132 constrinfo->condomain = NULL;
8133 constrinfo->contype = contype;
8134 if (contype == 'x')
8135 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8136 else
8137 constrinfo->condef = NULL;
8138 constrinfo->confrelid = InvalidOid;
8139 constrinfo->conindex = indxinfo[j].dobj.dumpId;
8140 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8141 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8142 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8143 constrinfo->conislocal = true;
8144 constrinfo->separate = true;
8145
8146 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8147 if (relstats != NULL)
8148 addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8149 }
8150 else
8151 {
8152 /* Plain secondary index */
8153 indxinfo[j].indexconstraint = 0;
8154 }
8155 }
8156 }
8157
8158 PQclear(res);
8159
8160 destroyPQExpBuffer(query);
8162}
8163
8164/*
8165 * getExtendedStatistics
8166 * get information about extended-statistics objects.
8167 *
8168 * Note: extended statistics data is not returned directly to the caller, but
8169 * it does get entered into the DumpableObject tables.
8170 */
8171void
8173{
8174 PQExpBuffer query;
8175 PGresult *res;
8177 int ntups;
8178 int i_tableoid;
8179 int i_oid;
8180 int i_stxname;
8181 int i_stxnamespace;
8182 int i_stxowner;
8183 int i_stxrelid;
8184 int i_stattarget;
8185 int i;
8186
8187 query = createPQExpBuffer();
8188
8189 if (fout->remoteVersion < 130000)
8190 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8191 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8192 "FROM pg_catalog.pg_statistic_ext");
8193 else
8194 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8195 "stxnamespace, stxowner, stxrelid, stxstattarget "
8196 "FROM pg_catalog.pg_statistic_ext");
8197
8198 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8199
8200 ntups = PQntuples(res);
8201
8202 i_tableoid = PQfnumber(res, "tableoid");
8203 i_oid = PQfnumber(res, "oid");
8204 i_stxname = PQfnumber(res, "stxname");
8205 i_stxnamespace = PQfnumber(res, "stxnamespace");
8206 i_stxowner = PQfnumber(res, "stxowner");
8207 i_stxrelid = PQfnumber(res, "stxrelid");
8208 i_stattarget = PQfnumber(res, "stxstattarget");
8209
8211
8212 for (i = 0; i < ntups; i++)
8213 {
8214 statsextinfo[i].dobj.objType = DO_STATSEXT;
8215 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8216 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8217 AssignDumpId(&statsextinfo[i].dobj);
8218 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8219 statsextinfo[i].dobj.namespace =
8221 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8222 statsextinfo[i].stattable =
8224 if (PQgetisnull(res, i, i_stattarget))
8225 statsextinfo[i].stattarget = -1;
8226 else
8227 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8228
8229 /* Decide whether we want to dump it */
8231
8232 if (fout->dopt->dumpStatistics)
8233 statsextinfo[i].dobj.components |= DUMP_COMPONENT_STATISTICS;
8234 }
8235
8236 PQclear(res);
8237 destroyPQExpBuffer(query);
8238}
8239
8240/*
8241 * getConstraints
8242 *
8243 * Get info about constraints on dumpable tables.
8244 *
8245 * Currently handles foreign keys only.
8246 * Unique and primary key constraints are handled with indexes,
8247 * while check constraints are processed in getTableAttrs().
8248 */
8249void
8251{
8254 PGresult *res;
8255 int ntups;
8256 int curtblindx;
8259 int i_contableoid,
8260 i_conoid,
8261 i_conrelid,
8262 i_conname,
8264 i_conindid,
8265 i_condef;
8266
8267 /*
8268 * We want to perform just one query against pg_constraint. However, we
8269 * mustn't try to select every row of the catalog and then sort it out on
8270 * the client side, because some of the server-side functions we need
8271 * would be unsafe to apply to tables we don't have lock on. Hence, we
8272 * build an array of the OIDs of tables we care about (and now have lock
8273 * on!), and use a WHERE clause to constrain which rows are selected.
8274 */
8276 for (int i = 0; i < numTables; i++)
8277 {
8278 TableInfo *tinfo = &tblinfo[i];
8279
8280 if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8281 continue;
8282
8283 /* OK, we need info for this table */
8284 if (tbloids->len > 1) /* do we have more than the '{'? */
8286 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8287 }
8289
8291 "SELECT c.tableoid, c.oid, "
8292 "conrelid, conname, confrelid, ");
8293 if (fout->remoteVersion >= 110000)
8294 appendPQExpBufferStr(query, "conindid, ");
8295 else
8296 appendPQExpBufferStr(query, "0 AS conindid, ");
8297 appendPQExpBuffer(query,
8298 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8299 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8300 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8301 "WHERE contype = 'f' ",
8302 tbloids->data);
8303 if (fout->remoteVersion >= 110000)
8305 "AND conparentid = 0 ");
8307 "ORDER BY conrelid, conname");
8308
8309 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8310
8311 ntups = PQntuples(res);
8312
8313 i_contableoid = PQfnumber(res, "tableoid");
8314 i_conoid = PQfnumber(res, "oid");
8315 i_conrelid = PQfnumber(res, "conrelid");
8316 i_conname = PQfnumber(res, "conname");
8317 i_confrelid = PQfnumber(res, "confrelid");
8318 i_conindid = PQfnumber(res, "conindid");
8319 i_condef = PQfnumber(res, "condef");
8320
8322
8323 curtblindx = -1;
8324 for (int j = 0; j < ntups; j++)
8325 {
8326 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8328
8329 /*
8330 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8331 * order.
8332 */
8333 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8334 {
8335 while (++curtblindx < numTables)
8336 {
8337 tbinfo = &tblinfo[curtblindx];
8338 if (tbinfo->dobj.catId.oid == conrelid)
8339 break;
8340 }
8341 if (curtblindx >= numTables)
8342 pg_fatal("unrecognized table OID %u", conrelid);
8343 }
8344
8345 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8346 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8347 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8348 AssignDumpId(&constrinfo[j].dobj);
8349 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8350 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8351 constrinfo[j].contable = tbinfo;
8352 constrinfo[j].condomain = NULL;
8353 constrinfo[j].contype = 'f';
8354 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8355 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8356 constrinfo[j].conindex = 0;
8357 constrinfo[j].condeferrable = false;
8358 constrinfo[j].condeferred = false;
8359 constrinfo[j].conislocal = true;
8360 constrinfo[j].separate = true;
8361
8362 /*
8363 * Restoring an FK that points to a partitioned table requires that
8364 * all partition indexes have been attached beforehand. Ensure that
8365 * happens by making the constraint depend on each index partition
8366 * attach object.
8367 */
8368 reftable = findTableByOid(constrinfo[j].confrelid);
8369 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8370 {
8371 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8372
8373 if (indexOid != InvalidOid)
8374 {
8375 for (int k = 0; k < reftable->numIndexes; k++)
8376 {
8378
8379 /* not our index? */
8380 if (reftable->indexes[k].dobj.catId.oid != indexOid)
8381 continue;
8382
8383 refidx = &reftable->indexes[k];
8385 break;
8386 }
8387 }
8388 }
8389 }
8390
8391 PQclear(res);
8392
8393 destroyPQExpBuffer(query);
8395}
8396
8397/*
8398 * addConstrChildIdxDeps
8399 *
8400 * Recursive subroutine for getConstraints
8401 *
8402 * Given an object representing a foreign key constraint and an index on the
8403 * partitioned table it references, mark the constraint object as dependent
8404 * on the DO_INDEX_ATTACH object of each index partition, recursively
8405 * drilling down to their partitions if any. This ensures that the FK is not
8406 * restored until the index is fully marked valid.
8407 */
8408static void
8410{
8411 SimplePtrListCell *cell;
8412
8414
8415 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8416 {
8418
8419 addObjectDependency(dobj, attach->dobj.dumpId);
8420
8421 if (attach->partitionIdx->partattaches.head != NULL)
8422 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8423 }
8424}
8425
8426/*
8427 * getDomainConstraints
8428 *
8429 * Get info about constraints on a domain.
8430 */
8431static void
8433{
8436 PGresult *res;
8437 int i_tableoid,
8438 i_oid,
8439 i_conname,
8440 i_consrc,
8442 i_contype;
8443 int ntups;
8444
8445 if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8446 {
8447 /*
8448 * Set up query for constraint-specific details. For servers 17 and
8449 * up, domains have constraints of type 'n' as well as 'c', otherwise
8450 * just the latter.
8451 */
8452 appendPQExpBuffer(query,
8453 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8454 "SELECT tableoid, oid, conname, "
8455 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8456 "convalidated, contype "
8457 "FROM pg_catalog.pg_constraint "
8458 "WHERE contypid = $1 AND contype IN (%s) "
8459 "ORDER BY conname",
8460 fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8461
8462 ExecuteSqlStatement(fout, query->data);
8463
8464 fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8465 }
8466
8467 printfPQExpBuffer(query,
8468 "EXECUTE getDomainConstraints('%u')",
8469 tyinfo->dobj.catId.oid);
8470
8471 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8472
8473 ntups = PQntuples(res);
8474
8475 i_tableoid = PQfnumber(res, "tableoid");
8476 i_oid = PQfnumber(res, "oid");
8477 i_conname = PQfnumber(res, "conname");
8478 i_consrc = PQfnumber(res, "consrc");
8479 i_convalidated = PQfnumber(res, "convalidated");
8480 i_contype = PQfnumber(res, "contype");
8481
8483 tyinfo->domChecks = constrinfo;
8484
8485 /* 'i' tracks result rows; 'j' counts CHECK constraints */
8486 for (int i = 0, j = 0; i < ntups; i++)
8487 {
8488 bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8489 char contype = (PQgetvalue(res, i, i_contype))[0];
8490 ConstraintInfo *constraint;
8491
8492 if (contype == CONSTRAINT_CHECK)
8493 {
8494 constraint = &constrinfo[j++];
8495 tyinfo->nDomChecks++;
8496 }
8497 else
8498 {
8499 Assert(contype == CONSTRAINT_NOTNULL);
8500 Assert(tyinfo->notnull == NULL);
8501 /* use last item in array for the not-null constraint */
8502 tyinfo->notnull = &(constrinfo[ntups - 1]);
8503 constraint = tyinfo->notnull;
8504 }
8505
8506 constraint->dobj.objType = DO_CONSTRAINT;
8507 constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8508 constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8509 AssignDumpId(&(constraint->dobj));
8510 constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8511 constraint->dobj.namespace = tyinfo->dobj.namespace;
8512 constraint->contable = NULL;
8513 constraint->condomain = tyinfo;
8514 constraint->contype = contype;
8515 constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8516 constraint->confrelid = InvalidOid;
8517 constraint->conindex = 0;
8518 constraint->condeferrable = false;
8519 constraint->condeferred = false;
8520 constraint->conislocal = true;
8521
8522 constraint->separate = !validated;
8523
8524 /*
8525 * Make the domain depend on the constraint, ensuring it won't be
8526 * output till any constraint dependencies are OK. If the constraint
8527 * has not been validated, it's going to be dumped after the domain
8528 * anyway, so this doesn't matter.
8529 */
8530 if (validated)
8531 addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8532 }
8533
8534 PQclear(res);
8535
8536 destroyPQExpBuffer(query);
8537}
8538
8539/*
8540 * getRules
8541 * get basic information about every rule in the system
8542 */
8543void
8545{
8546 PGresult *res;
8547 int ntups;
8548 int i;
8551 int i_tableoid;
8552 int i_oid;
8553 int i_rulename;
8554 int i_ruletable;
8555 int i_ev_type;
8556 int i_is_instead;
8557 int i_ev_enabled;
8558
8559 appendPQExpBufferStr(query, "SELECT "
8560 "tableoid, oid, rulename, "
8561 "ev_class AS ruletable, ev_type, is_instead, "
8562 "ev_enabled "
8563 "FROM pg_rewrite "
8564 "ORDER BY oid");
8565
8566 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8567
8568 ntups = PQntuples(res);
8569
8571
8572 i_tableoid = PQfnumber(res, "tableoid");
8573 i_oid = PQfnumber(res, "oid");
8574 i_rulename = PQfnumber(res, "rulename");
8575 i_ruletable = PQfnumber(res, "ruletable");
8576 i_ev_type = PQfnumber(res, "ev_type");
8577 i_is_instead = PQfnumber(res, "is_instead");
8578 i_ev_enabled = PQfnumber(res, "ev_enabled");
8579
8580 for (i = 0; i < ntups; i++)
8581 {
8583
8584 ruleinfo[i].dobj.objType = DO_RULE;
8585 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8586 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8587 AssignDumpId(&ruleinfo[i].dobj);
8588 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8590 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8591 if (ruleinfo[i].ruletable == NULL)
8592 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8593 ruletableoid, ruleinfo[i].dobj.catId.oid);
8594 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8595 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8596 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8597 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8598 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8599 if (ruleinfo[i].ruletable)
8600 {
8601 /*
8602 * If the table is a view or materialized view, force its ON
8603 * SELECT rule to be sorted before the view itself --- this
8604 * ensures that any dependencies for the rule affect the table's
8605 * positioning. Other rules are forced to appear after their
8606 * table.
8607 */
8608 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8609 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8610 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8611 {
8612 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8613 ruleinfo[i].dobj.dumpId);
8614 /* We'll merge the rule into CREATE VIEW, if possible */
8615 ruleinfo[i].separate = false;
8616 }
8617 else
8618 {
8620 ruleinfo[i].ruletable->dobj.dumpId);
8621 ruleinfo[i].separate = true;
8622 }
8623 }
8624 else
8625 ruleinfo[i].separate = true;
8626 }
8627
8628 PQclear(res);
8629
8630 destroyPQExpBuffer(query);
8631}
8632
8633/*
8634 * getTriggers
8635 * get information about every trigger on a dumpable table
8636 *
8637 * Note: trigger data is not returned directly to the caller, but it
8638 * does get entered into the DumpableObject tables.
8639 */
8640void
8642{
8645 PGresult *res;
8646 int ntups;
8647 int curtblindx;
8649 int i_tableoid,
8650 i_oid,
8651 i_tgrelid,
8652 i_tgname,
8655 i_tgdef;
8656
8657 /*
8658 * We want to perform just one query against pg_trigger. However, we
8659 * mustn't try to select every row of the catalog and then sort it out on
8660 * the client side, because some of the server-side functions we need
8661 * would be unsafe to apply to tables we don't have lock on. Hence, we
8662 * build an array of the OIDs of tables we care about (and now have lock
8663 * on!), and use a WHERE clause to constrain which rows are selected.
8664 */
8666 for (int i = 0; i < numTables; i++)
8667 {
8668 TableInfo *tbinfo = &tblinfo[i];
8669
8670 if (!tbinfo->hastriggers ||
8671 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8672 continue;
8673
8674 /* OK, we need info for this table */
8675 if (tbloids->len > 1) /* do we have more than the '{'? */
8677 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8678 }
8680
8681 if (fout->remoteVersion >= 150000)
8682 {
8683 /*
8684 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8685 * result in non-forward-compatible dumps of WHEN clauses due to
8686 * under-parenthesization.
8687 *
8688 * NB: We need to see partition triggers in case the tgenabled flag
8689 * has been changed from the parent.
8690 */
8691 appendPQExpBuffer(query,
8692 "SELECT t.tgrelid, t.tgname, "
8693 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8694 "t.tgenabled, t.tableoid, t.oid, "
8695 "t.tgparentid <> 0 AS tgispartition\n"
8696 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8697 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8698 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8699 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8700 "OR t.tgenabled != u.tgenabled) "
8701 "ORDER BY t.tgrelid, t.tgname",
8702 tbloids->data);
8703 }
8704 else if (fout->remoteVersion >= 130000)
8705 {
8706 /*
8707 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8708 * result in non-forward-compatible dumps of WHEN clauses due to
8709 * under-parenthesization.
8710 *
8711 * NB: We need to see tgisinternal triggers in partitions, in case the
8712 * tgenabled flag has been changed from the parent.
8713 */
8714 appendPQExpBuffer(query,
8715 "SELECT t.tgrelid, t.tgname, "
8716 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8717 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8718 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8719 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8720 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8721 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8722 "ORDER BY t.tgrelid, t.tgname",
8723 tbloids->data);
8724 }
8725 else if (fout->remoteVersion >= 110000)
8726 {
8727 /*
8728 * NB: We need to see tgisinternal triggers in partitions, in case the
8729 * tgenabled flag has been changed from the parent. No tgparentid in
8730 * version 11-12, so we have to match them via pg_depend.
8731 *
8732 * See above about pretty=true in pg_get_triggerdef.
8733 */
8734 appendPQExpBuffer(query,
8735 "SELECT t.tgrelid, t.tgname, "
8736 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8737 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8738 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8739 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8740 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8741 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8742 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8743 " d.objid = t.oid "
8744 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8745 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8746 "ORDER BY t.tgrelid, t.tgname",
8747 tbloids->data);
8748 }
8749 else
8750 {
8751 /* See above about pretty=true in pg_get_triggerdef */
8752 appendPQExpBuffer(query,
8753 "SELECT t.tgrelid, t.tgname, "
8754 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8755 "t.tgenabled, false as tgispartition, "
8756 "t.tableoid, t.oid "
8757 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8758 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8759 "WHERE NOT tgisinternal "
8760 "ORDER BY t.tgrelid, t.tgname",
8761 tbloids->data);
8762 }
8763
8764 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8765
8766 ntups = PQntuples(res);
8767
8768 i_tableoid = PQfnumber(res, "tableoid");
8769 i_oid = PQfnumber(res, "oid");
8770 i_tgrelid = PQfnumber(res, "tgrelid");
8771 i_tgname = PQfnumber(res, "tgname");
8772 i_tgenabled = PQfnumber(res, "tgenabled");
8773 i_tgispartition = PQfnumber(res, "tgispartition");
8774 i_tgdef = PQfnumber(res, "tgdef");
8775
8777
8778 /*
8779 * Outer loop iterates once per table, not once per row. Incrementing of
8780 * j is handled by the inner loop.
8781 */
8782 curtblindx = -1;
8783 for (int j = 0; j < ntups;)
8784 {
8787 int numtrigs;
8788
8789 /* Count rows for this table */
8790 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8791 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8792 break;
8793
8794 /*
8795 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8796 * order.
8797 */
8798 while (++curtblindx < numTables)
8799 {
8800 tbinfo = &tblinfo[curtblindx];
8801 if (tbinfo->dobj.catId.oid == tgrelid)
8802 break;
8803 }
8804 if (curtblindx >= numTables)
8805 pg_fatal("unrecognized table OID %u", tgrelid);
8806
8807 /* Save data for this table */
8808 tbinfo->triggers = tginfo + j;
8809 tbinfo->numTriggers = numtrigs;
8810
8811 for (int c = 0; c < numtrigs; c++, j++)
8812 {
8813 tginfo[j].dobj.objType = DO_TRIGGER;
8814 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8815 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8816 AssignDumpId(&tginfo[j].dobj);
8817 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8818 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8819 tginfo[j].tgtable = tbinfo;
8820 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8821 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8822 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8823 }
8824 }
8825
8826 PQclear(res);
8827
8828 destroyPQExpBuffer(query);
8830}
8831
8832/*
8833 * getEventTriggers
8834 * get information about event triggers
8835 */
8836void
8838{
8839 int i;
8840 PQExpBuffer query;
8841 PGresult *res;
8843 int i_tableoid,
8844 i_oid,
8845 i_evtname,
8846 i_evtevent,
8847 i_evtowner,
8848 i_evttags,
8849 i_evtfname,
8851 int ntups;
8852
8853 query = createPQExpBuffer();
8854
8856 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8857 "evtevent, evtowner, "
8858 "array_to_string(array("
8859 "select quote_literal(x) "
8860 " from unnest(evttags) as t(x)), ', ') as evttags, "
8861 "e.evtfoid::regproc as evtfname "
8862 "FROM pg_event_trigger e "
8863 "ORDER BY e.oid");
8864
8865 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8866
8867 ntups = PQntuples(res);
8868
8870
8871 i_tableoid = PQfnumber(res, "tableoid");
8872 i_oid = PQfnumber(res, "oid");
8873 i_evtname = PQfnumber(res, "evtname");
8874 i_evtevent = PQfnumber(res, "evtevent");
8875 i_evtowner = PQfnumber(res, "evtowner");
8876 i_evttags = PQfnumber(res, "evttags");
8877 i_evtfname = PQfnumber(res, "evtfname");
8878 i_evtenabled = PQfnumber(res, "evtenabled");
8879
8880 for (i = 0; i < ntups; i++)
8881 {
8882 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8883 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8884 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8885 AssignDumpId(&evtinfo[i].dobj);
8886 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8887 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8888 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8889 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8890 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8891 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8892 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8893
8894 /* Decide whether we want to dump it */
8896 }
8897
8898 PQclear(res);
8899
8900 destroyPQExpBuffer(query);
8901}
8902
8903/*
8904 * getProcLangs
8905 * get basic information about every procedural language in the system
8906 *
8907 * NB: this must run after getFuncs() because we assume we can do
8908 * findFuncByOid().
8909 */
8910void
8912{
8913 PGresult *res;
8914 int ntups;
8915 int i;
8918 int i_tableoid;
8919 int i_oid;
8920 int i_lanname;
8921 int i_lanpltrusted;
8922 int i_lanplcallfoid;
8923 int i_laninline;
8924 int i_lanvalidator;
8925 int i_lanacl;
8926 int i_acldefault;
8927 int i_lanowner;
8928
8929 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8930 "lanname, lanpltrusted, lanplcallfoid, "
8931 "laninline, lanvalidator, "
8932 "lanacl, "
8933 "acldefault('l', lanowner) AS acldefault, "
8934 "lanowner "
8935 "FROM pg_language "
8936 "WHERE lanispl "
8937 "ORDER BY oid");
8938
8939 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8940
8941 ntups = PQntuples(res);
8942
8944
8945 i_tableoid = PQfnumber(res, "tableoid");
8946 i_oid = PQfnumber(res, "oid");
8947 i_lanname = PQfnumber(res, "lanname");
8948 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8949 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8950 i_laninline = PQfnumber(res, "laninline");
8951 i_lanvalidator = PQfnumber(res, "lanvalidator");
8952 i_lanacl = PQfnumber(res, "lanacl");
8953 i_acldefault = PQfnumber(res, "acldefault");
8954 i_lanowner = PQfnumber(res, "lanowner");
8955
8956 for (i = 0; i < ntups; i++)
8957 {
8958 planginfo[i].dobj.objType = DO_PROCLANG;
8959 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8960 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8961 AssignDumpId(&planginfo[i].dobj);
8962
8963 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8964 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8965 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8966 planginfo[i].dacl.privtype = 0;
8967 planginfo[i].dacl.initprivs = NULL;
8968 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8969 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8970 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8971 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8972 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8973
8974 /* Decide whether we want to dump it */
8976
8977 /* Mark whether language has an ACL */
8978 if (!PQgetisnull(res, i, i_lanacl))
8979 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8980 }
8981
8982 PQclear(res);
8983
8984 destroyPQExpBuffer(query);
8985}
8986
8987/*
8988 * getCasts
8989 * get basic information about most casts in the system
8990 *
8991 * Skip casts from a range to its multirange, since we'll create those
8992 * automatically.
8993 */
8994void
8996{
8997 PGresult *res;
8998 int ntups;
8999 int i;
9002 int i_tableoid;
9003 int i_oid;
9004 int i_castsource;
9005 int i_casttarget;
9006 int i_castfunc;
9007 int i_castcontext;
9008 int i_castmethod;
9009
9010 if (fout->remoteVersion >= 140000)
9011 {
9012 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9013 "castsource, casttarget, castfunc, castcontext, "
9014 "castmethod "
9015 "FROM pg_cast c "
9016 "WHERE NOT EXISTS ( "
9017 "SELECT 1 FROM pg_range r "
9018 "WHERE c.castsource = r.rngtypid "
9019 "AND c.casttarget = r.rngmultitypid "
9020 ") "
9021 "ORDER BY 3,4");
9022 }
9023 else
9024 {
9025 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9026 "castsource, casttarget, castfunc, castcontext, "
9027 "castmethod "
9028 "FROM pg_cast ORDER BY 3,4");
9029 }
9030
9031 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9032
9033 ntups = PQntuples(res);
9034
9036
9037 i_tableoid = PQfnumber(res, "tableoid");
9038 i_oid = PQfnumber(res, "oid");
9039 i_castsource = PQfnumber(res, "castsource");
9040 i_casttarget = PQfnumber(res, "casttarget");
9041 i_castfunc = PQfnumber(res, "castfunc");
9042 i_castcontext = PQfnumber(res, "castcontext");
9043 i_castmethod = PQfnumber(res, "castmethod");
9044
9045 for (i = 0; i < ntups; i++)
9046 {
9050
9052 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9053 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9054 AssignDumpId(&castinfo[i].dobj);
9055 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9056 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9057 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9058 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9059 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9060
9061 /*
9062 * Try to name cast as concatenation of typnames. This is only used
9063 * for purposes of sorting. If we fail to find either type, the name
9064 * will be an empty string.
9065 */
9067 sTypeInfo = findTypeByOid(castinfo[i].castsource);
9068 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9069 if (sTypeInfo && tTypeInfo)
9070 appendPQExpBuffer(&namebuf, "%s %s",
9071 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9072 castinfo[i].dobj.name = namebuf.data;
9073
9074 /* Decide whether we want to dump it */
9076 }
9077
9078 PQclear(res);
9079
9080 destroyPQExpBuffer(query);
9081}
9082
9083static char *
9085{
9086 PQExpBuffer query;
9087 PGresult *res;
9088 char *lanname;
9089
9090 query = createPQExpBuffer();
9091 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9092 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9093 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9094 destroyPQExpBuffer(query);
9095 PQclear(res);
9096
9097 return lanname;
9098}
9099
9100/*
9101 * getTransforms
9102 * get basic information about every transform in the system
9103 */
9104void
9106{
9107 PGresult *res;
9108 int ntups;
9109 int i;
9110 PQExpBuffer query;
9112 int i_tableoid;
9113 int i_oid;
9114 int i_trftype;
9115 int i_trflang;
9116 int i_trffromsql;
9117 int i_trftosql;
9118
9119 query = createPQExpBuffer();
9120
9121 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9122 "trftype, trflang, trffromsql::oid, trftosql::oid "
9123 "FROM pg_transform "
9124 "ORDER BY 3,4");
9125
9126 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9127
9128 ntups = PQntuples(res);
9129
9131
9132 i_tableoid = PQfnumber(res, "tableoid");
9133 i_oid = PQfnumber(res, "oid");
9134 i_trftype = PQfnumber(res, "trftype");
9135 i_trflang = PQfnumber(res, "trflang");
9136 i_trffromsql = PQfnumber(res, "trffromsql");
9137 i_trftosql = PQfnumber(res, "trftosql");
9138
9139 for (i = 0; i < ntups; i++)
9140 {
9143 char *lanname;
9144
9146 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9147 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9149 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9150 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9151 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9152 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9153
9154 /*
9155 * Try to name transform as concatenation of type and language name.
9156 * This is only used for purposes of sorting. If we fail to find
9157 * either, the name will be an empty string.
9158 */
9162 if (typeInfo && lanname)
9163 appendPQExpBuffer(&namebuf, "%s %s",
9164 typeInfo->dobj.name, lanname);
9165 transforminfo[i].dobj.name = namebuf.data;
9166 free(lanname);
9167
9168 /* Decide whether we want to dump it */
9170 }
9171
9172 PQclear(res);
9173
9174 destroyPQExpBuffer(query);
9175}
9176
9177/*
9178 * getTableAttrs -
9179 * for each interesting table, read info about its attributes
9180 * (names, types, default values, CHECK constraints, etc)
9181 *
9182 * modifies tblinfo
9183 */
9184void
9186{
9187 DumpOptions *dopt = fout->dopt;
9192 PGresult *res;
9193 int ntups;
9194 int curtblindx;
9195 int i_attrelid;
9196 int i_attnum;
9197 int i_attname;
9198 int i_atttypname;
9199 int i_attstattarget;
9200 int i_attstorage;
9201 int i_typstorage;
9202 int i_attidentity;
9203 int i_attgenerated;
9204 int i_attisdropped;
9205 int i_attlen;
9206 int i_attalign;
9207 int i_attislocal;
9208 int i_notnull_name;
9213 int i_attoptions;
9214 int i_attcollation;
9215 int i_attcompression;
9216 int i_attfdwoptions;
9217 int i_attmissingval;
9218 int i_atthasdef;
9219
9220 /*
9221 * We want to perform just one query against pg_attribute, and then just
9222 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9223 * (for CHECK constraints and for NOT NULL constraints). However, we
9224 * mustn't try to select every row of those catalogs and then sort it out
9225 * on the client side, because some of the server-side functions we need
9226 * would be unsafe to apply to tables we don't have lock on. Hence, we
9227 * build an array of the OIDs of tables we care about (and now have lock
9228 * on!), and use a WHERE clause to constrain which rows are selected.
9229 */
9232 for (int i = 0; i < numTables; i++)
9233 {
9234 TableInfo *tbinfo = &tblinfo[i];
9235
9236 /* Don't bother to collect info for sequences */
9237 if (tbinfo->relkind == RELKIND_SEQUENCE)
9238 continue;
9239
9240 /*
9241 * Don't bother with uninteresting tables, either. For binary
9242 * upgrades, this is bypassed for pg_largeobject_metadata and
9243 * pg_shdepend so that the columns names are collected for the
9244 * corresponding COPY commands. Restoring the data for those catalogs
9245 * is faster than restoring the equivalent set of large object
9246 * commands.
9247 */
9248 if (!tbinfo->interesting &&
9249 !(fout->dopt->binary_upgrade &&
9250 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9251 tbinfo->dobj.catId.oid == SharedDependRelationId)))
9252 continue;
9253
9254 /* OK, we need info for this table */
9255 if (tbloids->len > 1) /* do we have more than the '{'? */
9257 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9258
9259 if (tbinfo->ncheck > 0)
9260 {
9261 /* Also make a list of the ones with check constraints */
9262 if (checkoids->len > 1) /* do we have more than the '{'? */
9264 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9265 }
9266 }
9269
9270 /*
9271 * Find all the user attributes and their types.
9272 *
9273 * Since we only want to dump COLLATE clauses for attributes whose
9274 * collation is different from their type's default, we use a CASE here to
9275 * suppress uninteresting attcollations cheaply.
9276 */
9278 "SELECT\n"
9279 "a.attrelid,\n"
9280 "a.attnum,\n"
9281 "a.attname,\n"
9282 "a.attstattarget,\n"
9283 "a.attstorage,\n"
9284 "t.typstorage,\n"
9285 "a.atthasdef,\n"
9286 "a.attisdropped,\n"
9287 "a.attlen,\n"
9288 "a.attalign,\n"
9289 "a.attislocal,\n"
9290 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9291 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9292 "CASE WHEN a.attcollation <> t.typcollation "
9293 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9294 "pg_catalog.array_to_string(ARRAY("
9295 "SELECT pg_catalog.quote_ident(option_name) || "
9296 "' ' || pg_catalog.quote_literal(option_value) "
9297 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9298 "ORDER BY option_name"
9299 "), E',\n ') AS attfdwoptions,\n");
9300
9301 /*
9302 * Find out any NOT NULL markings for each column. In 18 and up we read
9303 * pg_constraint to obtain the constraint name, and for valid constraints
9304 * also pg_description to obtain its comment. notnull_noinherit is set
9305 * according to the NO INHERIT property. For versions prior to 18, we
9306 * store an empty string as the name when a constraint is marked as
9307 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9308 * without a name); also, such cases are never NO INHERIT.
9309 *
9310 * For invalid constraints, we need to store their OIDs for processing
9311 * elsewhere, so we bring the pg_constraint.oid value when the constraint
9312 * is invalid, and NULL otherwise. Their comments are handled not here
9313 * but by collectComments, because they're their own dumpable object.
9314 *
9315 * We track in notnull_islocal whether the constraint was defined directly
9316 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9317 * might modify this later.
9318 */
9319 if (fout->remoteVersion >= 180000)
9321 "co.conname AS notnull_name,\n"
9322 "CASE WHEN co.convalidated THEN pt.description"
9323 " ELSE NULL END AS notnull_comment,\n"
9324 "CASE WHEN NOT co.convalidated THEN co.oid "
9325 "ELSE NULL END AS notnull_invalidoid,\n"
9326 "co.connoinherit AS notnull_noinherit,\n"
9327 "co.conislocal AS notnull_islocal,\n");
9328 else
9330 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9331 "NULL AS notnull_comment,\n"
9332 "NULL AS notnull_invalidoid,\n"
9333 "false AS notnull_noinherit,\n"
9334 "CASE WHEN a.attislocal THEN true\n"
9335 " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9336 " ELSE false\n"
9337 "END AS notnull_islocal,\n");
9338
9339 if (fout->remoteVersion >= 140000)
9341 "a.attcompression AS attcompression,\n");
9342 else
9344 "'' AS attcompression,\n");
9345
9347 "a.attidentity,\n");
9348
9349 if (fout->remoteVersion >= 110000)
9351 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9352 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9353 else
9355 "NULL AS attmissingval,\n");
9356
9357 if (fout->remoteVersion >= 120000)
9359 "a.attgenerated\n");
9360 else
9362 "'' AS attgenerated\n");
9363
9364 /* need left join to pg_type to not fail on dropped columns ... */
9366 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9367 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9368 "LEFT JOIN pg_catalog.pg_type t "
9369 "ON (a.atttypid = t.oid)\n",
9370 tbloids->data);
9371
9372 /*
9373 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9374 * entries and pg_description to get their comments.
9375 */
9376 if (fout->remoteVersion >= 180000)
9378 " LEFT JOIN pg_catalog.pg_constraint co ON "
9379 "(a.attrelid = co.conrelid\n"
9380 " AND co.contype = 'n' AND "
9381 "co.conkey = array[a.attnum])\n"
9382 " LEFT JOIN pg_catalog.pg_description pt ON "
9383 "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9384
9386 "WHERE a.attnum > 0::pg_catalog.int2\n");
9387
9388 /*
9389 * For binary upgrades from <v12, be sure to pick up
9390 * pg_largeobject_metadata's oid column.
9391 */
9392 if (fout->dopt->binary_upgrade && fout->remoteVersion < 120000)
9394 "OR (a.attnum = -2::pg_catalog.int2 AND src.tbloid = "
9396
9398 "ORDER BY a.attrelid, a.attnum");
9399
9401
9402 ntups = PQntuples(res);
9403
9404 i_attrelid = PQfnumber(res, "attrelid");
9405 i_attnum = PQfnumber(res, "attnum");
9406 i_attname = PQfnumber(res, "attname");
9407 i_atttypname = PQfnumber(res, "atttypname");
9408 i_attstattarget = PQfnumber(res, "attstattarget");
9409 i_attstorage = PQfnumber(res, "attstorage");
9410 i_typstorage = PQfnumber(res, "typstorage");
9411 i_attidentity = PQfnumber(res, "attidentity");
9412 i_attgenerated = PQfnumber(res, "attgenerated");
9413 i_attisdropped = PQfnumber(res, "attisdropped");
9414 i_attlen = PQfnumber(res, "attlen");
9415 i_attalign = PQfnumber(res, "attalign");
9416 i_attislocal = PQfnumber(res, "attislocal");
9417 i_notnull_name = PQfnumber(res, "notnull_name");
9418 i_notnull_comment = PQfnumber(res, "notnull_comment");
9419 i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9420 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9421 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9422 i_attoptions = PQfnumber(res, "attoptions");
9423 i_attcollation = PQfnumber(res, "attcollation");
9424 i_attcompression = PQfnumber(res, "attcompression");
9425 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9426 i_attmissingval = PQfnumber(res, "attmissingval");
9427 i_atthasdef = PQfnumber(res, "atthasdef");
9428
9429 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9432
9433 /*
9434 * Outer loop iterates once per table, not once per row. Incrementing of
9435 * r is handled by the inner loop.
9436 */
9437 curtblindx = -1;
9438 for (int r = 0; r < ntups;)
9439 {
9440 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9442 int numatts;
9443 bool hasdefaults;
9444
9445 /* Count rows for this table */
9446 for (numatts = 1; numatts < ntups - r; numatts++)
9447 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9448 break;
9449
9450 /*
9451 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9452 * order.
9453 */
9454 while (++curtblindx < numTables)
9455 {
9456 tbinfo = &tblinfo[curtblindx];
9457 if (tbinfo->dobj.catId.oid == attrelid)
9458 break;
9459 }
9460 if (curtblindx >= numTables)
9461 pg_fatal("unrecognized table OID %u", attrelid);
9462 /* cross-check that we only got requested tables */
9463 if (tbinfo->relkind == RELKIND_SEQUENCE ||
9464 (!tbinfo->interesting &&
9465 !(fout->dopt->binary_upgrade &&
9466 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9467 tbinfo->dobj.catId.oid == SharedDependRelationId))))
9468 pg_fatal("unexpected column data for table \"%s\"",
9469 tbinfo->dobj.name);
9470
9471 /* Save data for this table */
9472 tbinfo->numatts = numatts;
9473 tbinfo->attnames = pg_malloc_array(char *, numatts);
9474 tbinfo->atttypnames = pg_malloc_array(char *, numatts);
9475 tbinfo->attstattarget = pg_malloc_array(int, numatts);
9476 tbinfo->attstorage = pg_malloc_array(char, numatts);
9477 tbinfo->typstorage = pg_malloc_array(char, numatts);
9478 tbinfo->attidentity = pg_malloc_array(char, numatts);
9479 tbinfo->attgenerated = pg_malloc_array(char, numatts);
9480 tbinfo->attisdropped = pg_malloc_array(bool, numatts);
9481 tbinfo->attlen = pg_malloc_array(int, numatts);
9482 tbinfo->attalign = pg_malloc_array(char, numatts);
9483 tbinfo->attislocal = pg_malloc_array(bool, numatts);
9484 tbinfo->attoptions = pg_malloc_array(char *, numatts);
9485 tbinfo->attcollation = pg_malloc_array(Oid, numatts);
9486 tbinfo->attcompression = pg_malloc_array(char, numatts);
9487 tbinfo->attfdwoptions = pg_malloc_array(char *, numatts);
9488 tbinfo->attmissingval = pg_malloc_array(char *, numatts);
9489 tbinfo->notnull_constrs = pg_malloc_array(char *, numatts);
9490 tbinfo->notnull_comment = pg_malloc_array(char *, numatts);
9491 tbinfo->notnull_invalid = pg_malloc_array(bool, numatts);
9492 tbinfo->notnull_noinh = pg_malloc_array(bool, numatts);
9493 tbinfo->notnull_islocal = pg_malloc_array(bool, numatts);
9494 tbinfo->attrdefs = pg_malloc_array(AttrDefInfo *, numatts);
9495 hasdefaults = false;
9496
9497 for (int j = 0; j < numatts; j++, r++)
9498 {
9499 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)) &&
9500 !(fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
9501 tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
9502 pg_fatal("invalid column numbering in table \"%s\"",
9503 tbinfo->dobj.name);
9504 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9505 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9506 if (PQgetisnull(res, r, i_attstattarget))
9507 tbinfo->attstattarget[j] = -1;
9508 else
9509 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9510 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9511 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9512 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9513 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9514 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9515 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9516 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9517 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9518 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9519
9520 /* Handle not-null constraint name and flags */
9522 tbinfo, j,
9529
9530 tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9532 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9533 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9534 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9535 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9536 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9537 tbinfo->attrdefs[j] = NULL; /* fix below */
9538 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9539 hasdefaults = true;
9540 }
9541
9542 if (hasdefaults)
9543 {
9544 /* Collect OIDs of interesting tables that have defaults */
9545 if (tbloids->len > 1) /* do we have more than the '{'? */
9547 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9548 }
9549 }
9550
9551 /* If invalidnotnulloids has any data, finalize it */
9552 if (invalidnotnulloids != NULL)
9554
9555 PQclear(res);
9556
9557 /*
9558 * Now get info about column defaults. This is skipped for a data-only
9559 * dump, as it is only needed for table schemas.
9560 */
9561 if (dopt->dumpSchema && tbloids->len > 1)
9562 {
9563 AttrDefInfo *attrdefs;
9564 int numDefaults;
9566
9567 pg_log_info("finding table default expressions");
9568
9570
9571 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9572 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9573 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9574 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9575 "ORDER BY a.adrelid, a.adnum",
9576 tbloids->data);
9577
9579
9580 numDefaults = PQntuples(res);
9582
9583 curtblindx = -1;
9584 for (int j = 0; j < numDefaults; j++)
9585 {
9586 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9587 Oid adoid = atooid(PQgetvalue(res, j, 1));
9588 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9589 int adnum = atoi(PQgetvalue(res, j, 3));
9590 char *adsrc = PQgetvalue(res, j, 4);
9591
9592 /*
9593 * Locate the associated TableInfo; we rely on tblinfo[] being in
9594 * OID order.
9595 */
9596 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9597 {
9598 while (++curtblindx < numTables)
9599 {
9600 tbinfo = &tblinfo[curtblindx];
9601 if (tbinfo->dobj.catId.oid == adrelid)
9602 break;
9603 }
9604 if (curtblindx >= numTables)
9605 pg_fatal("unrecognized table OID %u", adrelid);
9606 }
9607
9608 if (adnum <= 0 || adnum > tbinfo->numatts)
9609 pg_fatal("invalid adnum value %d for table \"%s\"",
9610 adnum, tbinfo->dobj.name);
9611
9612 /*
9613 * dropped columns shouldn't have defaults, but just in case,
9614 * ignore 'em
9615 */
9616 if (tbinfo->attisdropped[adnum - 1])
9617 continue;
9618
9619 attrdefs[j].dobj.objType = DO_ATTRDEF;
9620 attrdefs[j].dobj.catId.tableoid = adtableoid;
9621 attrdefs[j].dobj.catId.oid = adoid;
9622 AssignDumpId(&attrdefs[j].dobj);
9623 attrdefs[j].adtable = tbinfo;
9624 attrdefs[j].adnum = adnum;
9625 attrdefs[j].adef_expr = pg_strdup(adsrc);
9626
9627 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9628 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9629
9630 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9631
9632 /*
9633 * Figure out whether the default/generation expression should be
9634 * dumped as part of the main CREATE TABLE (or similar) command or
9635 * as a separate ALTER TABLE (or similar) command. The preference
9636 * is to put it into the CREATE command, but in some cases that's
9637 * not possible.
9638 */
9639 if (tbinfo->attgenerated[adnum - 1])
9640 {
9641 /*
9642 * Column generation expressions cannot be dumped separately,
9643 * because there is no syntax for it. By setting separate to
9644 * false here we prevent the "default" from being processed as
9645 * its own dumpable object. Later, flagInhAttrs() will mark
9646 * it as not to be dumped at all, if possible (that is, if it
9647 * can be inherited from a parent).
9648 */
9649 attrdefs[j].separate = false;
9650 }
9651 else if (tbinfo->relkind == RELKIND_VIEW)
9652 {
9653 /*
9654 * Defaults on a VIEW must always be dumped as separate ALTER
9655 * TABLE commands.
9656 */
9657 attrdefs[j].separate = true;
9658 }
9659 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9660 {
9661 /* column will be suppressed, print default separately */
9662 attrdefs[j].separate = true;
9663 }
9664 else
9665 {
9666 attrdefs[j].separate = false;
9667 }
9668
9669 if (!attrdefs[j].separate)
9670 {
9671 /*
9672 * Mark the default as needing to appear before the table, so
9673 * that any dependencies it has must be emitted before the
9674 * CREATE TABLE. If this is not possible, we'll change to
9675 * "separate" mode while sorting dependencies.
9676 */
9678 attrdefs[j].dobj.dumpId);
9679 }
9680
9681 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9682 }
9683
9684 PQclear(res);
9685 }
9686
9687 /*
9688 * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9689 * data-only dump, as it is only needed for table schemas.
9690 */
9691 if (dopt->dumpSchema && invalidnotnulloids)
9692 {
9694 int numConstrs;
9695 int i_tableoid;
9696 int i_oid;
9697 int i_conrelid;
9698 int i_conname;
9699 int i_consrc;
9700 int i_conislocal;
9701
9702 pg_log_info("finding invalid not-null constraints");
9703
9706 "SELECT c.tableoid, c.oid, conrelid, conname, "
9707 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9708 "conislocal, convalidated "
9709 "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9710 "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9711 "ORDER BY c.conrelid, c.conname",
9713
9715
9716 numConstrs = PQntuples(res);
9718
9719 i_tableoid = PQfnumber(res, "tableoid");
9720 i_oid = PQfnumber(res, "oid");
9721 i_conrelid = PQfnumber(res, "conrelid");
9722 i_conname = PQfnumber(res, "conname");
9723 i_consrc = PQfnumber(res, "consrc");
9724 i_conislocal = PQfnumber(res, "conislocal");
9725
9726 /* As above, this loop iterates once per table, not once per row */
9727 curtblindx = -1;
9728 for (int j = 0; j < numConstrs;)
9729 {
9730 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9732 int numcons;
9733
9734 /* Count rows for this table */
9735 for (numcons = 1; numcons < numConstrs - j; numcons++)
9736 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9737 break;
9738
9739 /*
9740 * Locate the associated TableInfo; we rely on tblinfo[] being in
9741 * OID order.
9742 */
9743 while (++curtblindx < numTables)
9744 {
9745 tbinfo = &tblinfo[curtblindx];
9746 if (tbinfo->dobj.catId.oid == conrelid)
9747 break;
9748 }
9749 if (curtblindx >= numTables)
9750 pg_fatal("unrecognized table OID %u", conrelid);
9751
9752 for (int c = 0; c < numcons; c++, j++)
9753 {
9754 constrs[j].dobj.objType = DO_CONSTRAINT;
9755 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9756 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9757 AssignDumpId(&constrs[j].dobj);
9758 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9759 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9760 constrs[j].contable = tbinfo;
9761 constrs[j].condomain = NULL;
9762 constrs[j].contype = 'n';
9763 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9764 constrs[j].confrelid = InvalidOid;
9765 constrs[j].conindex = 0;
9766 constrs[j].condeferrable = false;
9767 constrs[j].condeferred = false;
9768 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9769
9770 /*
9771 * All invalid not-null constraints must be dumped separately,
9772 * because CREATE TABLE would not create them as invalid, and
9773 * also because they must be created after potentially
9774 * violating data has been loaded.
9775 */
9776 constrs[j].separate = true;
9777
9778 constrs[j].dobj.dump = tbinfo->dobj.dump;
9779 }
9780 }
9781 PQclear(res);
9782 }
9783
9784 /*
9785 * Get info about table CHECK constraints. This is skipped for a
9786 * data-only dump, as it is only needed for table schemas.
9787 */
9788 if (dopt->dumpSchema && checkoids->len > 2)
9789 {
9791 int numConstrs;
9792 int i_tableoid;
9793 int i_oid;
9794 int i_conrelid;
9795 int i_conname;
9796 int i_consrc;
9797 int i_conislocal;
9798 int i_convalidated;
9799
9800 pg_log_info("finding table check constraints");
9801
9804 "SELECT c.tableoid, c.oid, conrelid, conname, "
9805 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9806 "conislocal, convalidated "
9807 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9808 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9809 "WHERE contype = 'c' "
9810 "ORDER BY c.conrelid, c.conname",
9811 checkoids->data);
9812
9814
9815 numConstrs = PQntuples(res);
9817
9818 i_tableoid = PQfnumber(res, "tableoid");
9819 i_oid = PQfnumber(res, "oid");
9820 i_conrelid = PQfnumber(res, "conrelid");
9821 i_conname = PQfnumber(res, "conname");
9822 i_consrc = PQfnumber(res, "consrc");
9823 i_conislocal = PQfnumber(res, "conislocal");
9824 i_convalidated = PQfnumber(res, "convalidated");
9825
9826 /* As above, this loop iterates once per table, not once per row */
9827 curtblindx = -1;
9828 for (int j = 0; j < numConstrs;)
9829 {
9830 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9832 int numcons;
9833
9834 /* Count rows for this table */
9835 for (numcons = 1; numcons < numConstrs - j; numcons++)
9836 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9837 break;
9838
9839 /*
9840 * Locate the associated TableInfo; we rely on tblinfo[] being in
9841 * OID order.
9842 */
9843 while (++curtblindx < numTables)
9844 {
9845 tbinfo = &tblinfo[curtblindx];
9846 if (tbinfo->dobj.catId.oid == conrelid)
9847 break;
9848 }
9849 if (curtblindx >= numTables)
9850 pg_fatal("unrecognized table OID %u", conrelid);
9851
9852 if (numcons != tbinfo->ncheck)
9853 {
9854 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9855 "expected %d check constraints on table \"%s\" but found %d",
9856 tbinfo->ncheck),
9857 tbinfo->ncheck, tbinfo->dobj.name, numcons);
9858 pg_log_error_hint("The system catalogs might be corrupted.");
9859 exit_nicely(1);
9860 }
9861
9862 tbinfo->checkexprs = constrs + j;
9863
9864 for (int c = 0; c < numcons; c++, j++)
9865 {
9866 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9867
9868 constrs[j].dobj.objType = DO_CONSTRAINT;
9869 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9870 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9871 AssignDumpId(&constrs[j].dobj);
9872 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9873 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9874 constrs[j].contable = tbinfo;
9875 constrs[j].condomain = NULL;
9876 constrs[j].contype = 'c';
9877 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9878 constrs[j].confrelid = InvalidOid;
9879 constrs[j].conindex = 0;
9880 constrs[j].condeferrable = false;
9881 constrs[j].condeferred = false;
9882 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9883
9884 /*
9885 * An unvalidated constraint needs to be dumped separately, so
9886 * that potentially-violating existing data is loaded before
9887 * the constraint.
9888 */
9889 constrs[j].separate = !validated;
9890
9891 constrs[j].dobj.dump = tbinfo->dobj.dump;
9892
9893 /*
9894 * Mark the constraint as needing to appear before the table
9895 * --- this is so that any other dependencies of the
9896 * constraint will be emitted before we try to create the
9897 * table. If the constraint is to be dumped separately, it
9898 * will be dumped after data is loaded anyway, so don't do it.
9899 * (There's an automatic dependency in the opposite direction
9900 * anyway, so don't need to add one manually here.)
9901 */
9902 if (!constrs[j].separate)
9904 constrs[j].dobj.dumpId);
9905
9906 /*
9907 * We will detect later whether the constraint must be split
9908 * out from the table definition.
9909 */
9910 }
9911 }
9912
9913 PQclear(res);
9914 }
9915
9919}
9920
9921/*
9922 * Based on the getTableAttrs query's row corresponding to one column, set
9923 * the name and flags to handle a not-null constraint for that column in
9924 * the tbinfo struct.
9925 *
9926 * Result row 'r' is for tbinfo's attribute 'j'.
9927 *
9928 * There are four possibilities:
9929 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9930 * (the constraint name) remains NULL.
9931 * 2) The column has a constraint with no name (this is the case when
9932 * constraints come from pre-18 servers). In this case, ->notnull_constrs
9933 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9934 * 3) The column has an invalid not-null constraint. This must be treated
9935 * as a separate object (because it must be created after the table data
9936 * is loaded). So we add its OID to invalidnotnulloids for processing
9937 * elsewhere and do nothing further with it here. We distinguish this
9938 * case because the "notnull_invalidoid" column has been set to a non-NULL
9939 * value, which is the constraint OID. Valid constraints have a null OID.
9940 * 4) The column has a constraint with a known name; in that case
9941 * notnull_constrs carries that name and dumpTableSchema will print
9942 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9943 * (table_column_not_null) and there's no comment on the constraint,
9944 * there's no need to print that name in the dump, so notnull_constrs
9945 * is set to the empty string and it behaves as case 2.
9946 *
9947 * In a child table that inherits from a parent already containing NOT NULL
9948 * constraints and the columns in the child don't have their own NOT NULL
9949 * declarations, we suppress printing constraints in the child: the
9950 * constraints are acquired at the point where the child is attached to the
9951 * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9952 * set not here but in flagInhAttrs. That flag is also used when the
9953 * constraint was validated in a child but all its parent have it as NOT
9954 * VALID.
9955 *
9956 * Any of these constraints might have the NO INHERIT bit. If so we set
9957 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9958 *
9959 * In case 4 above, the name comparison is a bit of a hack; it actually fails
9960 * to do the right thing in all but the trivial case. However, the downside
9961 * of getting it wrong is simply that the name is printed rather than
9962 * suppressed, so it's not a big deal.
9963 *
9964 * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9965 * constraints are found, it is initialized and filled with the array of
9966 * OIDs of such constraints, for later processing.
9967 */
9968static void
9970 TableInfo *tbinfo, int j,
9971 int i_notnull_name,
9977{
9978 DumpOptions *dopt = fout->dopt;
9979
9980 /*
9981 * If this not-null constraint is not valid, list its OID in
9982 * invalidnotnulloids and do nothing further. It'll be processed
9983 * elsewhere later.
9984 *
9985 * Because invalid not-null constraints are rare, we don't want to malloc
9986 * invalidnotnulloids until we're sure we're going it need it, which
9987 * happens here.
9988 */
9989 if (!PQgetisnull(res, r, i_notnull_invalidoid))
9990 {
9991 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
9992
9993 if (*invalidnotnulloids == NULL)
9994 {
9998 }
9999 else
10001
10002 /*
10003 * Track when a parent constraint is invalid for the cases where a
10004 * child constraint has been validated independenly.
10005 */
10006 tbinfo->notnull_invalid[j] = true;
10007
10008 /* nothing else to do */
10009 tbinfo->notnull_constrs[j] = NULL;
10010 return;
10011 }
10012
10013 /*
10014 * notnull_noinh is straight from the query result. notnull_islocal also,
10015 * though flagInhAttrs may change that one later.
10016 */
10017 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10018 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10019 tbinfo->notnull_invalid[j] = false;
10020
10021 /*
10022 * Determine a constraint name to use. If the column is not marked not-
10023 * null, we set NULL which cues ... to do nothing. An empty string says
10024 * to print an unnamed NOT NULL, and anything else is a constraint name to
10025 * use.
10026 */
10027 if (fout->remoteVersion < 180000)
10028 {
10029 /*
10030 * < 18 doesn't have not-null names, so an unnamed constraint is
10031 * sufficient.
10032 */
10033 if (PQgetisnull(res, r, i_notnull_name))
10034 tbinfo->notnull_constrs[j] = NULL;
10035 else
10036 tbinfo->notnull_constrs[j] = "";
10037 }
10038 else
10039 {
10040 if (PQgetisnull(res, r, i_notnull_name))
10041 tbinfo->notnull_constrs[j] = NULL;
10042 else
10043 {
10044 /*
10045 * In binary upgrade of inheritance child tables, must have a
10046 * constraint name that we can UPDATE later; same if there's a
10047 * comment on the constraint.
10048 */
10049 if ((dopt->binary_upgrade &&
10050 !tbinfo->ispartition &&
10051 !tbinfo->notnull_islocal[j]) ||
10053 {
10054 tbinfo->notnull_constrs[j] =
10056 }
10057 else
10058 {
10059 char *default_name;
10060
10061 /* XXX should match ChooseConstraintName better */
10062 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10063 tbinfo->attnames[j]);
10064 if (strcmp(default_name,
10065 PQgetvalue(res, r, i_notnull_name)) == 0)
10066 tbinfo->notnull_constrs[j] = "";
10067 else
10068 {
10069 tbinfo->notnull_constrs[j] =
10071 }
10073 }
10074 }
10075 }
10076}
10077
10078/*
10079 * Test whether a column should be printed as part of table's CREATE TABLE.
10080 * Column number is zero-based.
10081 *
10082 * Normally this is always true, but it's false for dropped columns, as well
10083 * as those that were inherited without any local definition. (If we print
10084 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10085 * For partitions, it's always true, because we want the partitions to be
10086 * created independently and ATTACH PARTITION used afterwards.
10087 *
10088 * In binary_upgrade mode, we must print all columns and fix the attislocal/
10089 * attisdropped state later, so as to keep control of the physical column
10090 * order.
10091 *
10092 * This function exists because there are scattered nonobvious places that
10093 * must be kept in sync with this decision.
10094 */
10095bool
10096shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10097{
10098 if (dopt->binary_upgrade)
10099 return true;
10100 if (tbinfo->attisdropped[colno])
10101 return false;
10102 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10103}
10104
10105
10106/*
10107 * getTSParsers:
10108 * get information about all text search parsers in the system catalogs
10109 */
10110void
10112{
10113 PGresult *res;
10114 int ntups;
10115 int i;
10116 PQExpBuffer query;
10118 int i_tableoid;
10119 int i_oid;
10120 int i_prsname;
10121 int i_prsnamespace;
10122 int i_prsstart;
10123 int i_prstoken;
10124 int i_prsend;
10125 int i_prsheadline;
10126 int i_prslextype;
10127
10128 query = createPQExpBuffer();
10129
10130 /*
10131 * find all text search objects, including builtin ones; we filter out
10132 * system-defined objects at dump-out time.
10133 */
10134
10135 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10136 "prsstart::oid, prstoken::oid, "
10137 "prsend::oid, prsheadline::oid, prslextype::oid "
10138 "FROM pg_ts_parser");
10139
10140 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10141
10142 ntups = PQntuples(res);
10143
10145
10146 i_tableoid = PQfnumber(res, "tableoid");
10147 i_oid = PQfnumber(res, "oid");
10148 i_prsname = PQfnumber(res, "prsname");
10149 i_prsnamespace = PQfnumber(res, "prsnamespace");
10150 i_prsstart = PQfnumber(res, "prsstart");
10151 i_prstoken = PQfnumber(res, "prstoken");
10152 i_prsend = PQfnumber(res, "prsend");
10153 i_prsheadline = PQfnumber(res, "prsheadline");
10154 i_prslextype = PQfnumber(res, "prslextype");
10155
10156 for (i = 0; i < ntups; i++)
10157 {
10158 prsinfo[i].dobj.objType = DO_TSPARSER;
10159 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10160 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10161 AssignDumpId(&prsinfo[i].dobj);
10162 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10163 prsinfo[i].dobj.namespace =
10165 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10166 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10167 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10168 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10169 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10170
10171 /* Decide whether we want to dump it */
10173 }
10174
10175 PQclear(res);
10176
10177 destroyPQExpBuffer(query);
10178}
10179
10180/*
10181 * getTSDictionaries:
10182 * get information about all text search dictionaries in the system catalogs
10183 */
10184void
10186{
10187 PGresult *res;
10188 int ntups;
10189 int i;
10190 PQExpBuffer query;
10192 int i_tableoid;
10193 int i_oid;
10194 int i_dictname;
10195 int i_dictnamespace;
10196 int i_dictowner;
10197 int i_dicttemplate;
10198 int i_dictinitoption;
10199
10200 query = createPQExpBuffer();
10201
10202 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10203 "dictnamespace, dictowner, "
10204 "dicttemplate, dictinitoption "
10205 "FROM pg_ts_dict");
10206
10207 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10208
10209 ntups = PQntuples(res);
10210
10212
10213 i_tableoid = PQfnumber(res, "tableoid");
10214 i_oid = PQfnumber(res, "oid");
10215 i_dictname = PQfnumber(res, "dictname");
10216 i_dictnamespace = PQfnumber(res, "dictnamespace");
10217 i_dictowner = PQfnumber(res, "dictowner");
10218 i_dictinitoption = PQfnumber(res, "dictinitoption");
10219 i_dicttemplate = PQfnumber(res, "dicttemplate");
10220
10221 for (i = 0; i < ntups; i++)
10222 {
10223 dictinfo[i].dobj.objType = DO_TSDICT;
10224 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10225 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10226 AssignDumpId(&dictinfo[i].dobj);
10227 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10228 dictinfo[i].dobj.namespace =
10230 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10231 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10232 if (PQgetisnull(res, i, i_dictinitoption))
10233 dictinfo[i].dictinitoption = NULL;
10234 else
10235 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10236
10237 /* Decide whether we want to dump it */
10239 }
10240
10241 PQclear(res);
10242
10243 destroyPQExpBuffer(query);
10244}
10245
10246/*
10247 * getTSTemplates:
10248 * get information about all text search templates in the system catalogs
10249 */
10250void
10252{
10253 PGresult *res;
10254 int ntups;
10255 int i;
10256 PQExpBuffer query;
10258 int i_tableoid;
10259 int i_oid;
10260 int i_tmplname;
10261 int i_tmplnamespace;
10262 int i_tmplinit;
10263 int i_tmpllexize;
10264
10265 query = createPQExpBuffer();
10266
10267 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10268 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10269 "FROM pg_ts_template");
10270
10271 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10272
10273 ntups = PQntuples(res);
10274
10276
10277 i_tableoid = PQfnumber(res, "tableoid");
10278 i_oid = PQfnumber(res, "oid");
10279 i_tmplname = PQfnumber(res, "tmplname");
10280 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10281 i_tmplinit = PQfnumber(res, "tmplinit");
10282 i_tmpllexize = PQfnumber(res, "tmpllexize");
10283
10284 for (i = 0; i < ntups; i++)
10285 {
10286 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10287 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10288 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10289 AssignDumpId(&tmplinfo[i].dobj);
10290 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10291 tmplinfo[i].dobj.namespace =
10293 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10294 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10295
10296 /* Decide whether we want to dump it */
10298 }
10299
10300 PQclear(res);
10301
10302 destroyPQExpBuffer(query);
10303}
10304
10305/*
10306 * getTSConfigurations:
10307 * get information about all text search configurations
10308 */
10309void
10311{
10312 PGresult *res;
10313 int ntups;
10314 int i;
10315 PQExpBuffer query;
10317 int i_tableoid;
10318 int i_oid;
10319 int i_cfgname;
10320 int i_cfgnamespace;
10321 int i_cfgowner;
10322 int i_cfgparser;
10323
10324 query = createPQExpBuffer();
10325
10326 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10327 "cfgnamespace, cfgowner, cfgparser "
10328 "FROM pg_ts_config");
10329
10330 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10331
10332 ntups = PQntuples(res);
10333
10335
10336 i_tableoid = PQfnumber(res, "tableoid");
10337 i_oid = PQfnumber(res, "oid");
10338 i_cfgname = PQfnumber(res, "cfgname");
10339 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10340 i_cfgowner = PQfnumber(res, "cfgowner");
10341 i_cfgparser = PQfnumber(res, "cfgparser");
10342
10343 for (i = 0; i < ntups; i++)
10344 {
10345 cfginfo[i].dobj.objType = DO_TSCONFIG;
10346 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10347 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10348 AssignDumpId(&cfginfo[i].dobj);
10349 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10350 cfginfo[i].dobj.namespace =
10352 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10353 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10354
10355 /* Decide whether we want to dump it */
10357 }
10358
10359 PQclear(res);
10360
10361 destroyPQExpBuffer(query);
10362}
10363
10364/*
10365 * getForeignDataWrappers:
10366 * get information about all foreign-data wrappers in the system catalogs
10367 */
10368void
10370{
10371 PGresult *res;
10372 int ntups;
10373 int i;
10374 PQExpBuffer query;
10376 int i_tableoid;
10377 int i_oid;
10378 int i_fdwname;
10379 int i_fdwowner;
10380 int i_fdwhandler;
10381 int i_fdwvalidator;
10382 int i_fdwconnection;
10383 int i_fdwacl;
10384 int i_acldefault;
10385 int i_fdwoptions;
10386
10387 query = createPQExpBuffer();
10388
10389 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10390 "fdwowner, "
10391 "fdwhandler::pg_catalog.regproc, "
10392 "fdwvalidator::pg_catalog.regproc, ");
10393
10394 if (fout->remoteVersion >= 190000)
10395 appendPQExpBufferStr(query, "fdwconnection::pg_catalog.regproc, ");
10396 else
10397 appendPQExpBufferStr(query, "'-' AS fdwconnection, ");
10398
10400 "fdwacl, "
10401 "acldefault('F', fdwowner) AS acldefault, "
10402 "array_to_string(ARRAY("
10403 "SELECT quote_ident(option_name) || ' ' || "
10404 "quote_literal(option_value) "
10405 "FROM pg_options_to_table(fdwoptions) "
10406 "ORDER BY option_name"
10407 "), E',\n ') AS fdwoptions "
10408 "FROM pg_foreign_data_wrapper");
10409
10410 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10411
10412 ntups = PQntuples(res);
10413
10415
10416 i_tableoid = PQfnumber(res, "tableoid");
10417 i_oid = PQfnumber(res, "oid");
10418 i_fdwname = PQfnumber(res, "fdwname");
10419 i_fdwowner = PQfnumber(res, "fdwowner");
10420 i_fdwhandler = PQfnumber(res, "fdwhandler");
10421 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10422 i_fdwconnection = PQfnumber(res, "fdwconnection");
10423 i_fdwacl = PQfnumber(res, "fdwacl");
10424 i_acldefault = PQfnumber(res, "acldefault");
10425 i_fdwoptions = PQfnumber(res, "fdwoptions");
10426
10427 for (i = 0; i < ntups; i++)
10428 {
10429 fdwinfo[i].dobj.objType = DO_FDW;
10430 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10431 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10432 AssignDumpId(&fdwinfo[i].dobj);
10433 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10434 fdwinfo[i].dobj.namespace = NULL;
10435 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10436 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10437 fdwinfo[i].dacl.privtype = 0;
10438 fdwinfo[i].dacl.initprivs = NULL;
10439 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10440 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10441 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10442 fdwinfo[i].fdwconnection = pg_strdup(PQgetvalue(res, i, i_fdwconnection));
10443 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10444
10445 /* Decide whether we want to dump it */
10447
10448 /* Mark whether FDW has an ACL */
10449 if (!PQgetisnull(res, i, i_fdwacl))
10450 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10451 }
10452
10453 PQclear(res);
10454
10455 destroyPQExpBuffer(query);
10456}
10457
10458/*
10459 * getForeignServers:
10460 * get information about all foreign servers in the system catalogs
10461 */
10462void
10464{
10465 PGresult *res;
10466 int ntups;
10467 int i;
10468 PQExpBuffer query;
10470 int i_tableoid;
10471 int i_oid;
10472 int i_srvname;
10473 int i_srvowner;
10474 int i_srvfdw;
10475 int i_srvtype;
10476 int i_srvversion;
10477 int i_srvacl;
10478 int i_acldefault;
10479 int i_srvoptions;
10480
10481 query = createPQExpBuffer();
10482
10483 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10484 "srvowner, "
10485 "srvfdw, srvtype, srvversion, srvacl, "
10486 "acldefault('S', srvowner) AS acldefault, "
10487 "array_to_string(ARRAY("
10488 "SELECT quote_ident(option_name) || ' ' || "
10489 "quote_literal(option_value) "
10490 "FROM pg_options_to_table(srvoptions) "
10491 "ORDER BY option_name"
10492 "), E',\n ') AS srvoptions "
10493 "FROM pg_foreign_server");
10494
10495 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10496
10497 ntups = PQntuples(res);
10498
10500
10501 i_tableoid = PQfnumber(res, "tableoid");
10502 i_oid = PQfnumber(res, "oid");
10503 i_srvname = PQfnumber(res, "srvname");
10504 i_srvowner = PQfnumber(res, "srvowner");
10505 i_srvfdw = PQfnumber(res, "srvfdw");
10506 i_srvtype = PQfnumber(res, "srvtype");
10507 i_srvversion = PQfnumber(res, "srvversion");
10508 i_srvacl = PQfnumber(res, "srvacl");
10509 i_acldefault = PQfnumber(res, "acldefault");
10510 i_srvoptions = PQfnumber(res, "srvoptions");
10511
10512 for (i = 0; i < ntups; i++)
10513 {
10514 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10515 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10516 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10517 AssignDumpId(&srvinfo[i].dobj);
10518 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10519 srvinfo[i].dobj.namespace = NULL;
10520 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10521 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10522 srvinfo[i].dacl.privtype = 0;
10523 srvinfo[i].dacl.initprivs = NULL;
10524 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10525 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10526 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10527 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10528 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10529
10530 /* Decide whether we want to dump it */
10532
10533 /* Servers have user mappings */
10534 srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10535
10536 /* Mark whether server has an ACL */
10537 if (!PQgetisnull(res, i, i_srvacl))
10538 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10539 }
10540
10541 PQclear(res);
10542
10543 destroyPQExpBuffer(query);
10544}
10545
10546/*
10547 * getDefaultACLs:
10548 * get information about all default ACL information in the system catalogs
10549 */
10550void
10552{
10553 DumpOptions *dopt = fout->dopt;
10555 PQExpBuffer query;
10556 PGresult *res;
10557 int i_oid;
10558 int i_tableoid;
10559 int i_defaclrole;
10561 int i_defaclobjtype;
10562 int i_defaclacl;
10563 int i_acldefault;
10564 int i,
10565 ntups;
10566
10567 query = createPQExpBuffer();
10568
10569 /*
10570 * Global entries (with defaclnamespace=0) replace the hard-wired default
10571 * ACL for their object type. We should dump them as deltas from the
10572 * default ACL, since that will be used as a starting point for
10573 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10574 * non-global entries can only add privileges not revoke them. We must
10575 * dump those as-is (i.e., as deltas from an empty ACL).
10576 *
10577 * We can use defaclobjtype as the object type for acldefault(), except
10578 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10579 * 's'.
10580 */
10582 "SELECT oid, tableoid, "
10583 "defaclrole, "
10584 "defaclnamespace, "
10585 "defaclobjtype, "
10586 "defaclacl, "
10587 "CASE WHEN defaclnamespace = 0 THEN "
10588 "acldefault(CASE WHEN defaclobjtype = 'S' "
10589 "THEN 's'::\"char\" ELSE defaclobjtype END, "
10590 "defaclrole) ELSE '{}' END AS acldefault "
10591 "FROM pg_default_acl");
10592
10593 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10594
10595 ntups = PQntuples(res);
10596
10598
10599 i_oid = PQfnumber(res, "oid");
10600 i_tableoid = PQfnumber(res, "tableoid");
10601 i_defaclrole = PQfnumber(res, "defaclrole");
10602 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10603 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10604 i_defaclacl = PQfnumber(res, "defaclacl");
10605 i_acldefault = PQfnumber(res, "acldefault");
10606
10607 for (i = 0; i < ntups; i++)
10608 {
10610
10611 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10612 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10613 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10614 AssignDumpId(&daclinfo[i].dobj);
10615 /* cheesy ... is it worth coming up with a better object name? */
10616 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10617
10618 if (nspid != InvalidOid)
10619 daclinfo[i].dobj.namespace = findNamespace(nspid);
10620 else
10621 daclinfo[i].dobj.namespace = NULL;
10622
10623 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10624 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10625 daclinfo[i].dacl.privtype = 0;
10626 daclinfo[i].dacl.initprivs = NULL;
10627 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10628 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10629
10630 /* Default ACLs are ACLs, of course */
10631 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10632
10633 /* Decide whether we want to dump it */
10635 }
10636
10637 PQclear(res);
10638
10639 destroyPQExpBuffer(query);
10640}
10641
10642/*
10643 * getRoleName -- look up the name of a role, given its OID
10644 *
10645 * In current usage, we don't expect failures, so error out for a bad OID.
10646 */
10647static const char *
10649{
10650 Oid roleoid = atooid(roleoid_str);
10651
10652 /*
10653 * Do binary search to find the appropriate item.
10654 */
10655 if (nrolenames > 0)
10656 {
10657 RoleNameItem *low = &rolenames[0];
10658 RoleNameItem *high = &rolenames[nrolenames - 1];
10659
10660 while (low <= high)
10661 {
10662 RoleNameItem *middle = low + (high - low) / 2;
10663
10664 if (roleoid < middle->roleoid)
10665 high = middle - 1;
10666 else if (roleoid > middle->roleoid)
10667 low = middle + 1;
10668 else
10669 return middle->rolename; /* found a match */
10670 }
10671 }
10672
10673 pg_fatal("role with OID %u does not exist", roleoid);
10674 return NULL; /* keep compiler quiet */
10675}
10676
10677/*
10678 * collectRoleNames --
10679 *
10680 * Construct a table of all known roles.
10681 * The table is sorted by OID for speed in lookup.
10682 */
10683static void
10685{
10686 PGresult *res;
10687 const char *query;
10688 int i;
10689
10690 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10691
10692 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10693
10694 nrolenames = PQntuples(res);
10695
10697
10698 for (i = 0; i < nrolenames; i++)
10699 {
10700 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10702 }
10703
10704 PQclear(res);
10705}
10706
10707/*
10708 * getAdditionalACLs
10709 *
10710 * We have now created all the DumpableObjects, and collected the ACL data
10711 * that appears in the directly-associated catalog entries. However, there's
10712 * more ACL-related info to collect. If any of a table's columns have ACLs,
10713 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10714 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10715 * Also, in versions having the pg_init_privs catalog, read that and load the
10716 * information into the relevant DumpableObjects.
10717 */
10718static void
10720{
10722 PGresult *res;
10723 int ntups,
10724 i;
10725
10726 /* Check for per-column ACLs */
10728 "SELECT DISTINCT attrelid FROM pg_attribute "
10729 "WHERE attacl IS NOT NULL");
10730
10731 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10732
10733 ntups = PQntuples(res);
10734 for (i = 0; i < ntups; i++)
10735 {
10736 Oid relid = atooid(PQgetvalue(res, i, 0));
10737 TableInfo *tblinfo;
10738
10739 tblinfo = findTableByOid(relid);
10740 /* OK to ignore tables we haven't got a DumpableObject for */
10741 if (tblinfo)
10742 {
10744 tblinfo->hascolumnACLs = true;
10745 }
10746 }
10747 PQclear(res);
10748
10749 /* Fetch initial-privileges data */
10750 printfPQExpBuffer(query,
10751 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10752 "FROM pg_init_privs");
10753
10754 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10755
10756 ntups = PQntuples(res);
10757 for (i = 0; i < ntups; i++)
10758 {
10759 Oid objoid = atooid(PQgetvalue(res, i, 0));
10760 Oid classoid = atooid(PQgetvalue(res, i, 1));
10761 int objsubid = atoi(PQgetvalue(res, i, 2));
10762 char privtype = *(PQgetvalue(res, i, 3));
10763 char *initprivs = PQgetvalue(res, i, 4);
10764 CatalogId objId;
10765 DumpableObject *dobj;
10766
10767 objId.tableoid = classoid;
10768 objId.oid = objoid;
10769 dobj = findObjectByCatalogId(objId);
10770 /* OK to ignore entries we haven't got a DumpableObject for */
10771 if (dobj)
10772 {
10773 /* Cope with sub-object initprivs */
10774 if (objsubid != 0)
10775 {
10776 if (dobj->objType == DO_TABLE)
10777 {
10778 /* For a column initprivs, set the table's ACL flags */
10780 ((TableInfo *) dobj)->hascolumnACLs = true;
10781 }
10782 else
10783 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10784 classoid, objoid, objsubid);
10785 continue;
10786 }
10787
10788 /*
10789 * We ignore any pg_init_privs.initprivs entry for the public
10790 * schema, as explained in getNamespaces().
10791 */
10792 if (dobj->objType == DO_NAMESPACE &&
10793 strcmp(dobj->name, "public") == 0)
10794 continue;
10795
10796 /* Else it had better be of a type we think has ACLs */
10797 if (dobj->objType == DO_NAMESPACE ||
10798 dobj->objType == DO_TYPE ||
10799 dobj->objType == DO_FUNC ||
10800 dobj->objType == DO_AGG ||
10801 dobj->objType == DO_TABLE ||
10802 dobj->objType == DO_PROCLANG ||
10803 dobj->objType == DO_FDW ||
10804 dobj->objType == DO_FOREIGN_SERVER)
10805 {
10807
10808 daobj->dacl.privtype = privtype;
10809 daobj->dacl.initprivs = pstrdup(initprivs);
10810 }
10811 else
10812 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10813 classoid, objoid, objsubid);
10814 }
10815 }
10816 PQclear(res);
10817
10818 destroyPQExpBuffer(query);
10819}
10820
10821/*
10822 * dumpCommentExtended --
10823 *
10824 * This routine is used to dump any comments associated with the
10825 * object handed to this routine. The routine takes the object type
10826 * and object name (ready to print, except for schema decoration), plus
10827 * the namespace and owner of the object (for labeling the ArchiveEntry),
10828 * plus catalog ID and subid which are the lookup key for pg_description,
10829 * plus the dump ID for the object (for setting a dependency).
10830 * If a matching pg_description entry is found, it is dumped.
10831 *
10832 * Note: in some cases, such as comments for triggers and rules, the "type"
10833 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10834 * but it doesn't seem worth complicating the API for all callers to make
10835 * it cleaner.
10836 *
10837 * Note: although this routine takes a dumpId for dependency purposes,
10838 * that purpose is just to mark the dependency in the emitted dump file
10839 * for possible future use by pg_restore. We do NOT use it for determining
10840 * ordering of the comment in the dump file, because this routine is called
10841 * after dependency sorting occurs. This routine should be called just after
10842 * calling ArchiveEntry() for the specified object.
10843 */
10844static void
10846 const char *name, const char *namespace,
10847 const char *owner, CatalogId catalogId,
10848 int subid, DumpId dumpId,
10849 const char *initdb_comment)
10850{
10851 DumpOptions *dopt = fout->dopt;
10853 int ncomments;
10854
10855 /* do nothing, if --no-comments is supplied */
10856 if (dopt->no_comments)
10857 return;
10858
10859 /* Comments are schema not data ... except LO comments are data */
10860 if (strcmp(type, "LARGE OBJECT") != 0)
10861 {
10862 if (!dopt->dumpSchema)
10863 return;
10864 }
10865 else
10866 {
10867 /* We do dump LO comments in binary-upgrade mode */
10868 if (!dopt->dumpData && !dopt->binary_upgrade)
10869 return;
10870 }
10871
10872 /* Search for comments associated with catalogId, using table */
10873 ncomments = findComments(catalogId.tableoid, catalogId.oid,
10874 &comments);
10875
10876 /* Is there one matching the subid? */
10877 while (ncomments > 0)
10878 {
10879 if (comments->objsubid == subid)
10880 break;
10881 comments++;
10882 ncomments--;
10883 }
10884
10885 if (initdb_comment != NULL)
10886 {
10887 static CommentItem empty_comment = {.descr = ""};
10888
10889 /*
10890 * initdb creates this object with a comment. Skip dumping the
10891 * initdb-provided comment, which would complicate matters for
10892 * non-superuser use of pg_dump. When the DBA has removed initdb's
10893 * comment, replicate that.
10894 */
10895 if (ncomments == 0)
10896 {
10898 ncomments = 1;
10899 }
10900 else if (strcmp(comments->descr, initdb_comment) == 0)
10901 ncomments = 0;
10902 }
10903
10904 /* If a comment exists, build COMMENT ON statement */
10905 if (ncomments > 0)
10906 {
10909
10910 appendPQExpBuffer(query, "COMMENT ON %s ", type);
10911 if (namespace && *namespace)
10912 appendPQExpBuffer(query, "%s.", fmtId(namespace));
10913 appendPQExpBuffer(query, "%s IS ", name);
10915 appendPQExpBufferStr(query, ";\n");
10916
10917 appendPQExpBuffer(tag, "%s %s", type, name);
10918
10919 /*
10920 * We mark comments as SECTION_NONE because they really belong in the
10921 * same section as their parent, whether that is pre-data or
10922 * post-data.
10923 */
10925 ARCHIVE_OPTS(.tag = tag->data,
10926 .namespace = namespace,
10927 .owner = owner,
10928 .description = "COMMENT",
10929 .section = SECTION_NONE,
10930 .createStmt = query->data,
10931 .deps = &dumpId,
10932 .nDeps = 1));
10933
10934 destroyPQExpBuffer(query);
10935 destroyPQExpBuffer(tag);
10936 }
10937}
10938
10939/*
10940 * dumpComment --
10941 *
10942 * Typical simplification of the above function.
10943 */
10944static inline void
10946 const char *name, const char *namespace,
10947 const char *owner, CatalogId catalogId,
10948 int subid, DumpId dumpId)
10949{
10950 dumpCommentExtended(fout, type, name, namespace, owner,
10951 catalogId, subid, dumpId, NULL);
10952}
10953
10954/*
10955 * appendNamedArgument --
10956 *
10957 * Convenience routine for constructing parameters of the form:
10958 * 'paraname', 'value'::type
10959 */
10960static void
10961appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10962 const char *argtype, const char *argval)
10963{
10964 appendPQExpBufferStr(out, ",\n\t");
10965
10966 appendStringLiteralAH(out, argname, fout);
10967 appendPQExpBufferStr(out, ", ");
10968
10970 appendPQExpBuffer(out, "::%s", argtype);
10971}
10972
10973/*
10974 * fetchAttributeStats --
10975 *
10976 * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10977 */
10978static PGresult *
10980{
10982 PQExpBuffer relids = createPQExpBuffer();
10985 int count = 0;
10986 PGresult *res = NULL;
10987 static TocEntry *te;
10988 static bool restarted;
10990
10991 /* If we're just starting, set our TOC pointer. */
10992 if (!te)
10993 te = AH->toc->next;
10994
10995 /*
10996 * We can't easily avoid a second TOC scan for the tar format because it
10997 * writes restore.sql separately, which means we must execute the queries
10998 * twice. This feels risky, but there is no known reason it should
10999 * generate different output than the first pass. Even if it does, the
11000 * worst-case scenario is that restore.sql might have different statistics
11001 * data than the archive.
11002 */
11003 if (!restarted && te == AH->toc && AH->format == archTar)
11004 {
11005 te = AH->toc->next;
11006 restarted = true;
11007 }
11008
11009 appendPQExpBufferChar(relids, '{');
11012
11013 /*
11014 * Scan the TOC for the next set of relevant stats entries. We assume
11015 * that statistics are dumped in the order they are listed in the TOC.
11016 * This is perhaps not the sturdiest assumption, so we verify it matches
11017 * reality in dumpRelationStats_dumper().
11018 */
11019 for (; te != AH->toc && count < max_rels; te = te->next)
11020 {
11021 if ((te->reqs & REQ_STATS) == 0 ||
11022 strcmp(te->desc, "STATISTICS DATA") != 0)
11023 continue;
11024
11025 if (fout->remoteVersion >= 190000)
11026 {
11027 const RelStatsInfo *rsinfo = (const RelStatsInfo *) te->defnDumperArg;
11028 char relid[32];
11029
11030 sprintf(relid, "%u", rsinfo->relid);
11031 appendPGArray(relids, relid);
11032 }
11033 else
11034 {
11035 appendPGArray(nspnames, te->namespace);
11037 }
11038
11039 count++;
11040 }
11041
11042 appendPQExpBufferChar(relids, '}');
11045
11046 /* Execute the query for the next batch of relations. */
11047 if (count > 0)
11048 {
11050
11051 appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11052
11053 if (fout->remoteVersion >= 190000)
11054 {
11055 appendStringLiteralAH(query, relids->data, fout);
11056 appendPQExpBufferStr(query, "::pg_catalog.oid[])");
11057 }
11058 else
11059 {
11061 appendPQExpBufferStr(query, "::pg_catalog.name[],");
11063 appendPQExpBufferStr(query, "::pg_catalog.name[])");
11064 }
11065
11066 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11067 destroyPQExpBuffer(query);
11068 }
11069
11070 destroyPQExpBuffer(relids);
11073 return res;
11074}
11075
11076/*
11077 * dumpRelationStats_dumper --
11078 *
11079 * Generate command to import stats into the relation on the new database.
11080 * This routine is called by the Archiver when it wants the statistics to be
11081 * dumped.
11082 */
11083static char *
11085{
11086 const RelStatsInfo *rsinfo = userArg;
11087 static PGresult *res;
11088 static int rownum;
11089 PQExpBuffer query;
11091 PQExpBuffer out = &out_data;
11092 int i_schemaname;
11093 int i_tablename;
11094 int i_attname;
11095 int i_inherited;
11096 int i_null_frac;
11097 int i_avg_width;
11098 int i_n_distinct;
11102 int i_correlation;
11109 static TocEntry *expected_te;
11110
11111 /*
11112 * fetchAttributeStats() assumes that the statistics are dumped in the
11113 * order they are listed in the TOC. We verify that here for safety.
11114 */
11115 if (!expected_te)
11116 expected_te = ((ArchiveHandle *) fout)->toc;
11117
11119 while ((expected_te->reqs & REQ_STATS) == 0 ||
11120 strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11122
11123 if (te != expected_te)
11124 pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11125 te->dumpId, te->desc, te->tag,
11126 expected_te->dumpId, expected_te->desc, expected_te->tag);
11127
11128 query = createPQExpBuffer();
11129 if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11130 {
11131 if (fout->remoteVersion >= 190000)
11133 "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n");
11134 else
11136 "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n");
11137
11139 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11140 "s.null_frac, s.avg_width, s.n_distinct, "
11141 "s.most_common_vals, s.most_common_freqs, "
11142 "s.histogram_bounds, s.correlation, "
11143 "s.most_common_elems, s.most_common_elem_freqs, "
11144 "s.elem_count_histogram, ");
11145
11146 if (fout->remoteVersion >= 170000)
11148 "s.range_length_histogram, "
11149 "s.range_empty_frac, "
11150 "s.range_bounds_histogram ");
11151 else
11153 "NULL AS range_length_histogram,"
11154 "NULL AS range_empty_frac,"
11155 "NULL AS range_bounds_histogram ");
11156
11157 /*
11158 * The results must be in the order of the relations supplied in the
11159 * parameters to ensure we remain in sync as we walk through the TOC.
11160 *
11161 * For versions before 19, the redundant filter clause on s.tablename
11162 * = ANY(...) seems sufficient to convince the planner to use
11163 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11164 * In newer versions, pg_stats returns the table OIDs, eliminating the
11165 * need for that hack.
11166 */
11167 if (fout->remoteVersion >= 190000)
11169 "FROM pg_catalog.pg_stats s "
11170 "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
11171 "ON s.tableid = u.tableid "
11172 "ORDER BY u.ord, s.attname, s.inherited");
11173 else
11175 "FROM pg_catalog.pg_stats s "
11176 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11177 "ON s.schemaname = u.schemaname "
11178 "AND s.tablename = u.tablename "
11179 "WHERE s.tablename = ANY($2) "
11180 "ORDER BY u.ord, s.attname, s.inherited");
11181
11182 ExecuteSqlStatement(fout, query->data);
11183
11184 fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11185 resetPQExpBuffer(query);
11186 }
11187
11188 initPQExpBuffer(out);
11189
11190 /* restore relation stats */
11191 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11192 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11193 fout->remoteVersion);
11194 appendPQExpBufferStr(out, "\t'schemaname', ");
11195 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11196 appendPQExpBufferStr(out, ",\n");
11197 appendPQExpBufferStr(out, "\t'relname', ");
11198 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11199 appendPQExpBufferStr(out, ",\n");
11200 appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11201
11202 /*
11203 * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11204 * the relation is empty, or it could mean that it hadn't yet been
11205 * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11206 * This ambiguity allegedly can cause the planner to choose inefficient
11207 * plans after restoring to v18 or newer. To deal with this, let's just
11208 * set reltuples to -1 in that case.
11209 */
11210 if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11211 appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11212 else
11213 appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11214
11215 appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11216 rsinfo->relallvisible);
11217
11218 if (fout->remoteVersion >= 180000)
11219 appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11220
11221 appendPQExpBufferStr(out, "\n);\n");
11222
11223 /* Fetch the next batch of attribute statistics if needed. */
11224 if (rownum >= PQntuples(res))
11225 {
11226 PQclear(res);
11228 rownum = 0;
11229 }
11230
11231 i_schemaname = PQfnumber(res, "schemaname");
11232 i_tablename = PQfnumber(res, "tablename");
11233 i_attname = PQfnumber(res, "attname");
11234 i_inherited = PQfnumber(res, "inherited");
11235 i_null_frac = PQfnumber(res, "null_frac");
11236 i_avg_width = PQfnumber(res, "avg_width");
11237 i_n_distinct = PQfnumber(res, "n_distinct");
11238 i_most_common_vals = PQfnumber(res, "most_common_vals");
11239 i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11240 i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11241 i_correlation = PQfnumber(res, "correlation");
11242 i_most_common_elems = PQfnumber(res, "most_common_elems");
11243 i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11244 i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11245 i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11246 i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11247 i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11248
11249 /* restore attribute stats */
11250 for (; rownum < PQntuples(res); rownum++)
11251 {
11252 const char *attname;
11253
11254 /* Stop if the next stat row in our cache isn't for this relation. */
11255 if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11256 strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11257 break;
11258
11259 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11260 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11261 fout->remoteVersion);
11262 appendPQExpBufferStr(out, "\t'schemaname', ");
11263 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11264 appendPQExpBufferStr(out, ",\n\t'relname', ");
11265 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11266
11267 if (PQgetisnull(res, rownum, i_attname))
11268 pg_fatal("unexpected null attname");
11269 attname = PQgetvalue(res, rownum, i_attname);
11270
11271 /*
11272 * Indexes look up attname in indAttNames to derive attnum, all others
11273 * use attname directly. We must specify attnum for indexes, since
11274 * their attnames are not necessarily stable across dump/reload.
11275 */
11276 if (rsinfo->nindAttNames == 0)
11277 {
11278 appendPQExpBufferStr(out, ",\n\t'attname', ");
11280 }
11281 else
11282 {
11283 bool found = false;
11284
11285 for (int i = 0; i < rsinfo->nindAttNames; i++)
11286 {
11287 if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11288 {
11289 appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11290 i + 1);
11291 found = true;
11292 break;
11293 }
11294 }
11295
11296 if (!found)
11297 pg_fatal("could not find index attname \"%s\"", attname);
11298 }
11299
11300 if (!PQgetisnull(res, rownum, i_inherited))
11301 appendNamedArgument(out, fout, "inherited", "boolean",
11302 PQgetvalue(res, rownum, i_inherited));
11303 if (!PQgetisnull(res, rownum, i_null_frac))
11304 appendNamedArgument(out, fout, "null_frac", "real",
11305 PQgetvalue(res, rownum, i_null_frac));
11306 if (!PQgetisnull(res, rownum, i_avg_width))
11307 appendNamedArgument(out, fout, "avg_width", "integer",
11308 PQgetvalue(res, rownum, i_avg_width));
11309 if (!PQgetisnull(res, rownum, i_n_distinct))
11310 appendNamedArgument(out, fout, "n_distinct", "real",
11311 PQgetvalue(res, rownum, i_n_distinct));
11312 if (!PQgetisnull(res, rownum, i_most_common_vals))
11313 appendNamedArgument(out, fout, "most_common_vals", "text",
11314 PQgetvalue(res, rownum, i_most_common_vals));
11315 if (!PQgetisnull(res, rownum, i_most_common_freqs))
11316 appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11317 PQgetvalue(res, rownum, i_most_common_freqs));
11318 if (!PQgetisnull(res, rownum, i_histogram_bounds))
11319 appendNamedArgument(out, fout, "histogram_bounds", "text",
11320 PQgetvalue(res, rownum, i_histogram_bounds));
11321 if (!PQgetisnull(res, rownum, i_correlation))
11322 appendNamedArgument(out, fout, "correlation", "real",
11323 PQgetvalue(res, rownum, i_correlation));
11324 if (!PQgetisnull(res, rownum, i_most_common_elems))
11325 appendNamedArgument(out, fout, "most_common_elems", "text",
11326 PQgetvalue(res, rownum, i_most_common_elems));
11327 if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11328 appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11329 PQgetvalue(res, rownum, i_most_common_elem_freqs));
11330 if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11331 appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11332 PQgetvalue(res, rownum, i_elem_count_histogram));
11333 if (fout->remoteVersion >= 170000)
11334 {
11335 if (!PQgetisnull(res, rownum, i_range_length_histogram))
11336 appendNamedArgument(out, fout, "range_length_histogram", "text",
11337 PQgetvalue(res, rownum, i_range_length_histogram));
11338 if (!PQgetisnull(res, rownum, i_range_empty_frac))
11339 appendNamedArgument(out, fout, "range_empty_frac", "real",
11340 PQgetvalue(res, rownum, i_range_empty_frac));
11341 if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11342 appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11343 PQgetvalue(res, rownum, i_range_bounds_histogram));
11344 }
11345 appendPQExpBufferStr(out, "\n);\n");
11346 }
11347
11348 destroyPQExpBuffer(query);
11349 return out->data;
11350}
11351
11352/*
11353 * dumpRelationStats --
11354 *
11355 * Make an ArchiveEntry for the relation statistics. The Archiver will take
11356 * care of gathering the statistics and generating the restore commands when
11357 * they are needed.
11358 */
11359static void
11361{
11362 const DumpableObject *dobj = &rsinfo->dobj;
11363
11364 /* nothing to do if we are not dumping statistics */
11365 if (!fout->dopt->dumpStatistics)
11366 return;
11367
11369 ARCHIVE_OPTS(.tag = dobj->name,
11370 .namespace = dobj->namespace->dobj.name,
11371 .description = "STATISTICS DATA",
11372 .section = rsinfo->section,
11373 .defnFn = dumpRelationStats_dumper,
11374 .defnArg = rsinfo,
11375 .deps = dobj->dependencies,
11376 .nDeps = dobj->nDeps));
11377}
11378
11379/*
11380 * dumpTableComment --
11381 *
11382 * As above, but dump comments for both the specified table (or view)
11383 * and its columns.
11384 */
11385static void
11387 const char *reltypename)
11388{
11389 DumpOptions *dopt = fout->dopt;
11391 int ncomments;
11392 PQExpBuffer query;
11393 PQExpBuffer tag;
11394
11395 /* do nothing, if --no-comments is supplied */
11396 if (dopt->no_comments)
11397 return;
11398
11399 /* Comments are SCHEMA not data */
11400 if (!dopt->dumpSchema)
11401 return;
11402
11403 /* Search for comments associated with relation, using table */
11404 ncomments = findComments(tbinfo->dobj.catId.tableoid,
11405 tbinfo->dobj.catId.oid,
11406 &comments);
11407
11408 /* If comments exist, build COMMENT ON statements */
11409 if (ncomments <= 0)
11410 return;
11411
11412 query = createPQExpBuffer();
11413 tag = createPQExpBuffer();
11414
11415 while (ncomments > 0)
11416 {
11417 const char *descr = comments->descr;
11418 int objsubid = comments->objsubid;
11419
11420 if (objsubid == 0)
11421 {
11422 resetPQExpBuffer(tag);
11423 appendPQExpBuffer(tag, "%s %s", reltypename,
11424 fmtId(tbinfo->dobj.name));
11425
11426 resetPQExpBuffer(query);
11427 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11429 appendStringLiteralAH(query, descr, fout);
11430 appendPQExpBufferStr(query, ";\n");
11431
11433 ARCHIVE_OPTS(.tag = tag->data,
11434 .namespace = tbinfo->dobj.namespace->dobj.name,
11435 .owner = tbinfo->rolname,
11436 .description = "COMMENT",
11437 .section = SECTION_NONE,
11438 .createStmt = query->data,
11439 .deps = &(tbinfo->dobj.dumpId),
11440 .nDeps = 1));
11441 }
11442 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11443 {
11444 resetPQExpBuffer(tag);
11445 appendPQExpBuffer(tag, "COLUMN %s.",
11446 fmtId(tbinfo->dobj.name));
11447 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11448
11449 resetPQExpBuffer(query);
11450 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11452 appendPQExpBuffer(query, "%s IS ",
11453 fmtId(tbinfo->attnames[objsubid - 1]));
11454 appendStringLiteralAH(query, descr, fout);
11455 appendPQExpBufferStr(query, ";\n");
11456
11458 ARCHIVE_OPTS(.tag = tag->data,
11459 .namespace = tbinfo->dobj.namespace->dobj.name,
11460 .owner = tbinfo->rolname,
11461 .description = "COMMENT",
11462 .section = SECTION_NONE,
11463 .createStmt = query->data,
11464 .deps = &(tbinfo->dobj.dumpId),
11465 .nDeps = 1));
11466 }
11467
11468 comments++;
11469 ncomments--;
11470 }
11471
11472 destroyPQExpBuffer(query);
11473 destroyPQExpBuffer(tag);
11474}
11475
11476/*
11477 * findComments --
11478 *
11479 * Find the comment(s), if any, associated with the given object. All the
11480 * objsubid values associated with the given classoid/objoid are found with
11481 * one search.
11482 */
11483static int
11485{
11487 CommentItem *low;
11488 CommentItem *high;
11489 int nmatch;
11490
11491 /*
11492 * Do binary search to find some item matching the object.
11493 */
11494 low = &comments[0];
11495 high = &comments[ncomments - 1];
11496 while (low <= high)
11497 {
11498 middle = low + (high - low) / 2;
11499
11500 if (classoid < middle->classoid)
11501 high = middle - 1;
11502 else if (classoid > middle->classoid)
11503 low = middle + 1;
11504 else if (objoid < middle->objoid)
11505 high = middle - 1;
11506 else if (objoid > middle->objoid)
11507 low = middle + 1;
11508 else
11509 break; /* found a match */
11510 }
11511
11512 if (low > high) /* no matches */
11513 {
11514 *items = NULL;
11515 return 0;
11516 }
11517
11518 /*
11519 * Now determine how many items match the object. The search loop
11520 * invariant still holds: only items between low and high inclusive could
11521 * match.
11522 */
11523 nmatch = 1;
11524 while (middle > low)
11525 {
11526 if (classoid != middle[-1].classoid ||
11527 objoid != middle[-1].objoid)
11528 break;
11529 middle--;
11530 nmatch++;
11531 }
11532
11533 *items = middle;
11534
11535 middle += nmatch;
11536 while (middle <= high)
11537 {
11538 if (classoid != middle->classoid ||
11539 objoid != middle->objoid)
11540 break;
11541 middle++;
11542 nmatch++;
11543 }
11544
11545 return nmatch;
11546}
11547
11548/*
11549 * collectComments --
11550 *
11551 * Construct a table of all comments available for database objects;
11552 * also set the has-comment component flag for each relevant object.
11553 *
11554 * We used to do per-object queries for the comments, but it's much faster
11555 * to pull them all over at once, and on most databases the memory cost
11556 * isn't high.
11557 *
11558 * The table is sorted by classoid/objid/objsubid for speed in lookup.
11559 */
11560static void
11562{
11563 PGresult *res;
11564 PQExpBuffer query;
11565 int i_description;
11566 int i_classoid;
11567 int i_objoid;
11568 int i_objsubid;
11569 int ntups;
11570 int i;
11571 DumpableObject *dobj;
11572
11573 query = createPQExpBuffer();
11574
11575 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11576 "FROM pg_catalog.pg_description "
11577 "ORDER BY classoid, objoid, objsubid");
11578
11579 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11580
11581 /* Construct lookup table containing OIDs in numeric form */
11582
11583 i_description = PQfnumber(res, "description");
11584 i_classoid = PQfnumber(res, "classoid");
11585 i_objoid = PQfnumber(res, "objoid");
11586 i_objsubid = PQfnumber(res, "objsubid");
11587
11588 ntups = PQntuples(res);
11589
11591 ncomments = 0;
11592 dobj = NULL;
11593
11594 for (i = 0; i < ntups; i++)
11595 {
11596 CatalogId objId;
11597 int subid;
11598
11599 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11600 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11601 subid = atoi(PQgetvalue(res, i, i_objsubid));
11602
11603 /* We needn't remember comments that don't match any dumpable object */
11604 if (dobj == NULL ||
11605 dobj->catId.tableoid != objId.tableoid ||
11606 dobj->catId.oid != objId.oid)
11607 dobj = findObjectByCatalogId(objId);
11608 if (dobj == NULL)
11609 continue;
11610
11611 /*
11612 * Comments on columns of composite types are linked to the type's
11613 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11614 * in the type's own DumpableObject.
11615 */
11616 if (subid != 0 && dobj->objType == DO_TABLE &&
11617 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11618 {
11620
11621 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11622 if (cTypeInfo)
11623 cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11624 }
11625 else
11626 dobj->components |= DUMP_COMPONENT_COMMENT;
11627
11630 comments[ncomments].objoid = objId.oid;
11631 comments[ncomments].objsubid = subid;
11632 ncomments++;
11633 }
11634
11635 PQclear(res);
11636 destroyPQExpBuffer(query);
11637}
11638
11639/*
11640 * dumpDumpableObject
11641 *
11642 * This routine and its subsidiaries are responsible for creating
11643 * ArchiveEntries (TOC objects) for each object to be dumped.
11644 */
11645static void
11647{
11648 /*
11649 * Clear any dump-request bits for components that don't exist for this
11650 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11651 * request for every kind of object.)
11652 */
11653 dobj->dump &= dobj->components;
11654
11655 /* Now, short-circuit if there's nothing to be done here. */
11656 if (dobj->dump == 0)
11657 return;
11658
11659 switch (dobj->objType)
11660 {
11661 case DO_NAMESPACE:
11662 dumpNamespace(fout, (const NamespaceInfo *) dobj);
11663 break;
11664 case DO_EXTENSION:
11665 dumpExtension(fout, (const ExtensionInfo *) dobj);
11666 break;
11667 case DO_TYPE:
11668 dumpType(fout, (const TypeInfo *) dobj);
11669 break;
11670 case DO_SHELL_TYPE:
11671 dumpShellType(fout, (const ShellTypeInfo *) dobj);
11672 break;
11673 case DO_FUNC:
11674 dumpFunc(fout, (const FuncInfo *) dobj);
11675 break;
11676 case DO_AGG:
11677 dumpAgg(fout, (const AggInfo *) dobj);
11678 break;
11679 case DO_OPERATOR:
11680 dumpOpr(fout, (const OprInfo *) dobj);
11681 break;
11682 case DO_ACCESS_METHOD:
11683 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11684 break;
11685 case DO_OPCLASS:
11686 dumpOpclass(fout, (const OpclassInfo *) dobj);
11687 break;
11688 case DO_OPFAMILY:
11689 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11690 break;
11691 case DO_COLLATION:
11692 dumpCollation(fout, (const CollInfo *) dobj);
11693 break;
11694 case DO_CONVERSION:
11695 dumpConversion(fout, (const ConvInfo *) dobj);
11696 break;
11697 case DO_TABLE:
11698 dumpTable(fout, (const TableInfo *) dobj);
11699 break;
11700 case DO_TABLE_ATTACH:
11701 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11702 break;
11703 case DO_ATTRDEF:
11704 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11705 break;
11706 case DO_INDEX:
11707 dumpIndex(fout, (const IndxInfo *) dobj);
11708 break;
11709 case DO_INDEX_ATTACH:
11710 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11711 break;
11712 case DO_STATSEXT:
11713 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11714 dumpStatisticsExtStats(fout, (const StatsExtInfo *) dobj);
11715 break;
11716 case DO_REFRESH_MATVIEW:
11717 refreshMatViewData(fout, (const TableDataInfo *) dobj);
11718 break;
11719 case DO_RULE:
11720 dumpRule(fout, (const RuleInfo *) dobj);
11721 break;
11722 case DO_TRIGGER:
11723 dumpTrigger(fout, (const TriggerInfo *) dobj);
11724 break;
11725 case DO_EVENT_TRIGGER:
11726 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11727 break;
11728 case DO_CONSTRAINT:
11729 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11730 break;
11731 case DO_FK_CONSTRAINT:
11732 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11733 break;
11734 case DO_PROCLANG:
11735 dumpProcLang(fout, (const ProcLangInfo *) dobj);
11736 break;
11737 case DO_CAST:
11738 dumpCast(fout, (const CastInfo *) dobj);
11739 break;
11740 case DO_TRANSFORM:
11741 dumpTransform(fout, (const TransformInfo *) dobj);
11742 break;
11743 case DO_SEQUENCE_SET:
11744 dumpSequenceData(fout, (const TableDataInfo *) dobj);
11745 break;
11746 case DO_TABLE_DATA:
11747 dumpTableData(fout, (const TableDataInfo *) dobj);
11748 break;
11749 case DO_DUMMY_TYPE:
11750 /* table rowtypes and array types are never dumped separately */
11751 break;
11752 case DO_TSPARSER:
11753 dumpTSParser(fout, (const TSParserInfo *) dobj);
11754 break;
11755 case DO_TSDICT:
11756 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11757 break;
11758 case DO_TSTEMPLATE:
11759 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11760 break;
11761 case DO_TSCONFIG:
11762 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11763 break;
11764 case DO_FDW:
11765 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11766 break;
11767 case DO_FOREIGN_SERVER:
11768 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11769 break;
11770 case DO_DEFAULT_ACL:
11771 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11772 break;
11773 case DO_LARGE_OBJECT:
11774 dumpLO(fout, (const LoInfo *) dobj);
11775 break;
11777 if (dobj->dump & DUMP_COMPONENT_DATA)
11778 {
11779 LoInfo *loinfo;
11780 TocEntry *te;
11781
11782 loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11783 if (loinfo == NULL)
11784 pg_fatal("missing metadata for large objects \"%s\"",
11785 dobj->name);
11786
11787 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11788 ARCHIVE_OPTS(.tag = dobj->name,
11789 .owner = loinfo->rolname,
11790 .description = "BLOBS",
11791 .section = SECTION_DATA,
11792 .deps = dobj->dependencies,
11793 .nDeps = dobj->nDeps,
11794 .dumpFn = dumpLOs,
11795 .dumpArg = loinfo));
11796
11797 /*
11798 * Set the TocEntry's dataLength in case we are doing a
11799 * parallel dump and want to order dump jobs by table size.
11800 * (We need some size estimate for every TocEntry with a
11801 * DataDumper function.) We don't currently have any cheap
11802 * way to estimate the size of LOs, but fortunately it doesn't
11803 * matter too much as long as we get large batches of LOs
11804 * processed reasonably early. Assume 8K per blob.
11805 */
11806 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11807 }
11808 break;
11809 case DO_POLICY:
11810 dumpPolicy(fout, (const PolicyInfo *) dobj);
11811 break;
11812 case DO_PUBLICATION:
11813 dumpPublication(fout, (const PublicationInfo *) dobj);
11814 break;
11815 case DO_PUBLICATION_REL:
11817 break;
11820 (const PublicationSchemaInfo *) dobj);
11821 break;
11822 case DO_SUBSCRIPTION:
11823 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11824 break;
11826 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11827 break;
11828 case DO_REL_STATS:
11829 dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11830 break;
11833 /* never dumped, nothing to do */
11834 break;
11835 }
11836}
11837
11838/*
11839 * dumpNamespace
11840 * writes out to fout the queries to recreate a user-defined namespace
11841 */
11842static void
11844{
11845 DumpOptions *dopt = fout->dopt;
11846 PQExpBuffer q;
11848 char *qnspname;
11849
11850 /* Do nothing if not dumping schema */
11851 if (!dopt->dumpSchema)
11852 return;
11853
11854 q = createPQExpBuffer();
11856
11857 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11858
11859 if (nspinfo->create)
11860 {
11861 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11862 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11863 }
11864 else
11865 {
11866 /* see selectDumpableNamespace() */
11868 "-- *not* dropping schema, since initdb creates it\n");
11870 "-- *not* creating schema, since initdb creates it\n");
11871 }
11872
11873 if (dopt->binary_upgrade)
11875 "SCHEMA", qnspname, NULL);
11876
11877 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11878 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11879 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11880 .owner = nspinfo->rolname,
11881 .description = "SCHEMA",
11882 .section = SECTION_PRE_DATA,
11883 .createStmt = q->data,
11884 .dropStmt = delq->data));
11885
11886 /* Dump Schema Comments and Security Labels */
11887 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11888 {
11889 const char *initdb_comment = NULL;
11890
11891 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11892 initdb_comment = "standard public schema";
11894 NULL, nspinfo->rolname,
11895 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11897 }
11898
11899 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11900 dumpSecLabel(fout, "SCHEMA", qnspname,
11901 NULL, nspinfo->rolname,
11902 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11903
11904 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11905 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11906 qnspname, NULL, NULL,
11907 NULL, nspinfo->rolname, &nspinfo->dacl);
11908
11910
11913}
11914
11915/*
11916 * dumpExtension
11917 * writes out to fout the queries to recreate an extension
11918 */
11919static void
11921{
11922 DumpOptions *dopt = fout->dopt;
11923 PQExpBuffer q;
11925 char *qextname;
11926
11927 /* Do nothing if not dumping schema */
11928 if (!dopt->dumpSchema)
11929 return;
11930
11931 q = createPQExpBuffer();
11933
11934 qextname = pg_strdup(fmtId(extinfo->dobj.name));
11935
11936 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11937
11938 if (!dopt->binary_upgrade)
11939 {
11940 /*
11941 * In a regular dump, we simply create the extension, intentionally
11942 * not specifying a version, so that the destination installation's
11943 * default version is used.
11944 *
11945 * Use of IF NOT EXISTS here is unlike our behavior for other object
11946 * types; but there are various scenarios in which it's convenient to
11947 * manually create the desired extension before restoring, so we
11948 * prefer to allow it to exist already.
11949 */
11950 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11951 qextname, fmtId(extinfo->namespace));
11952 }
11953 else
11954 {
11955 /*
11956 * In binary-upgrade mode, it's critical to reproduce the state of the
11957 * database exactly, so our procedure is to create an empty extension,
11958 * restore all the contained objects normally, and add them to the
11959 * extension one by one. This function performs just the first of
11960 * those steps. binary_upgrade_extension_member() takes care of
11961 * adding member objects as they're created.
11962 */
11963 int i;
11964 int n;
11965
11966 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11967
11968 /*
11969 * We unconditionally create the extension, so we must drop it if it
11970 * exists. This could happen if the user deleted 'plpgsql' and then
11971 * readded it, causing its oid to be greater than g_last_builtin_oid.
11972 */
11973 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11974
11976 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11977 appendStringLiteralAH(q, extinfo->dobj.name, fout);
11978 appendPQExpBufferStr(q, ", ");
11979 appendStringLiteralAH(q, extinfo->namespace, fout);
11980 appendPQExpBufferStr(q, ", ");
11981 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11982 appendStringLiteralAH(q, extinfo->extversion, fout);
11983 appendPQExpBufferStr(q, ", ");
11984
11985 /*
11986 * Note that we're pushing extconfig (an OID array) back into
11987 * pg_extension exactly as-is. This is OK because pg_class OIDs are
11988 * preserved in binary upgrade.
11989 */
11990 if (strlen(extinfo->extconfig) > 2)
11991 appendStringLiteralAH(q, extinfo->extconfig, fout);
11992 else
11993 appendPQExpBufferStr(q, "NULL");
11994 appendPQExpBufferStr(q, ", ");
11995 if (strlen(extinfo->extcondition) > 2)
11996 appendStringLiteralAH(q, extinfo->extcondition, fout);
11997 else
11998 appendPQExpBufferStr(q, "NULL");
11999 appendPQExpBufferStr(q, ", ");
12000 appendPQExpBufferStr(q, "ARRAY[");
12001 n = 0;
12002 for (i = 0; i < extinfo->dobj.nDeps; i++)
12003 {
12005
12006 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12007 if (extobj && extobj->objType == DO_EXTENSION)
12008 {
12009 if (n++ > 0)
12010 appendPQExpBufferChar(q, ',');
12012 }
12013 }
12014 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12015 appendPQExpBufferStr(q, ");\n");
12016 }
12017
12018 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12019 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12020 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12021 .description = "EXTENSION",
12022 .section = SECTION_PRE_DATA,
12023 .createStmt = q->data,
12024 .dropStmt = delq->data));
12025
12026 /* Dump Extension Comments */
12027 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12028 dumpComment(fout, "EXTENSION", qextname,
12029 NULL, "",
12030 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12031
12033
12036}
12037
12038/*
12039 * dumpType
12040 * writes out to fout the queries to recreate a user-defined type
12041 */
12042static void
12044{
12045 DumpOptions *dopt = fout->dopt;
12046
12047 /* Do nothing if not dumping schema */
12048 if (!dopt->dumpSchema)
12049 return;
12050
12051 /* Dump out in proper style */
12052 if (tyinfo->typtype == TYPTYPE_BASE)
12054 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12056 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12058 else if (tyinfo->typtype == TYPTYPE_ENUM)
12060 else if (tyinfo->typtype == TYPTYPE_RANGE)
12062 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12064 else
12065 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12066 tyinfo->dobj.name);
12067}
12068
12069/*
12070 * dumpEnumType
12071 * writes out to fout the queries to recreate a user-defined enum type
12072 */
12073static void
12075{
12076 DumpOptions *dopt = fout->dopt;
12080 PGresult *res;
12081 int num,
12082 i;
12083 Oid enum_oid;
12084 char *qtypname;
12085 char *qualtypname;
12086 char *label;
12087 int i_enumlabel;
12088 int i_oid;
12089
12090 if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12091 {
12092 /* Set up query for enum-specific details */
12094 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12095 "SELECT oid, enumlabel "
12096 "FROM pg_catalog.pg_enum "
12097 "WHERE enumtypid = $1 "
12098 "ORDER BY enumsortorder");
12099
12100 ExecuteSqlStatement(fout, query->data);
12101
12102 fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12103 }
12104
12105 printfPQExpBuffer(query,
12106 "EXECUTE dumpEnumType('%u')",
12107 tyinfo->dobj.catId.oid);
12108
12109 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12110
12111 num = PQntuples(res);
12112
12113 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12115
12116 /*
12117 * CASCADE shouldn't be required here as for normal types since the I/O
12118 * functions are generic and do not get dropped.
12119 */
12120 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12121
12122 if (dopt->binary_upgrade)
12124 tyinfo->dobj.catId.oid,
12125 false, false);
12126
12127 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12128 qualtypname);
12129
12130 if (!dopt->binary_upgrade)
12131 {
12132 i_enumlabel = PQfnumber(res, "enumlabel");
12133
12134 /* Labels with server-assigned oids */
12135 for (i = 0; i < num; i++)
12136 {
12137 label = PQgetvalue(res, i, i_enumlabel);
12138 if (i > 0)
12139 appendPQExpBufferChar(q, ',');
12140 appendPQExpBufferStr(q, "\n ");
12142 }
12143 }
12144
12145 appendPQExpBufferStr(q, "\n);\n");
12146
12147 if (dopt->binary_upgrade)
12148 {
12149 i_oid = PQfnumber(res, "oid");
12150 i_enumlabel = PQfnumber(res, "enumlabel");
12151
12152 /* Labels with dump-assigned (preserved) oids */
12153 for (i = 0; i < num; i++)
12154 {
12155 enum_oid = atooid(PQgetvalue(res, i, i_oid));
12156 label = PQgetvalue(res, i, i_enumlabel);
12157
12158 if (i == 0)
12159 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12161 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12162 enum_oid);
12163 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12165 appendPQExpBufferStr(q, ";\n\n");
12166 }
12167 }
12168
12169 if (dopt->binary_upgrade)
12171 "TYPE", qtypname,
12172 tyinfo->dobj.namespace->dobj.name);
12173
12174 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12175 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12176 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12177 .namespace = tyinfo->dobj.namespace->dobj.name,
12178 .owner = tyinfo->rolname,
12179 .description = "TYPE",
12180 .section = SECTION_PRE_DATA,
12181 .createStmt = q->data,
12182 .dropStmt = delq->data));
12183
12184 /* Dump Type Comments and Security Labels */
12185 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12186 dumpComment(fout, "TYPE", qtypname,
12187 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12188 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12189
12190 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12191 dumpSecLabel(fout, "TYPE", qtypname,
12192 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12193 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12194
12195 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12196 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12197 qtypname, NULL,
12198 tyinfo->dobj.namespace->dobj.name,
12199 NULL, tyinfo->rolname, &tyinfo->dacl);
12200
12201 PQclear(res);
12204 destroyPQExpBuffer(query);
12207}
12208
12209/*
12210 * dumpRangeType
12211 * writes out to fout the queries to recreate a user-defined range type
12212 */
12213static void
12215{
12216 DumpOptions *dopt = fout->dopt;
12220 PGresult *res;
12222 char *qtypname;
12223 char *qualtypname;
12224 char *procname;
12225
12226 if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12227 {
12228 /* Set up query for range-specific details */
12230 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12231
12233 "SELECT ");
12234
12235 if (fout->remoteVersion >= 140000)
12237 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12238 else
12240 "NULL AS rngmultitype, ");
12241
12243 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12244 "opc.opcname AS opcname, "
12245 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12246 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12247 "opc.opcdefault, "
12248 "CASE WHEN rngcollation = st.typcollation THEN 0 "
12249 " ELSE rngcollation END AS collation, "
12250 "rngcanonical, rngsubdiff "
12251 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12252 " pg_catalog.pg_opclass opc "
12253 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12254 "rngtypid = $1");
12255
12256 ExecuteSqlStatement(fout, query->data);
12257
12258 fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12259 }
12260
12261 printfPQExpBuffer(query,
12262 "EXECUTE dumpRangeType('%u')",
12263 tyinfo->dobj.catId.oid);
12264
12265 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12266
12267 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12269
12270 /*
12271 * CASCADE shouldn't be required here as for normal types since the I/O
12272 * functions are generic and do not get dropped.
12273 */
12274 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12275
12276 if (dopt->binary_upgrade)
12278 tyinfo->dobj.catId.oid,
12279 false, true);
12280
12281 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12282 qualtypname);
12283
12284 appendPQExpBuffer(q, "\n subtype = %s",
12285 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12286
12287 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12288 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12289 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12290
12291 /* print subtype_opclass only if not default for subtype */
12292 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12293 {
12294 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12295 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12296
12297 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12298 fmtId(nspname));
12300 }
12301
12302 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12304 {
12306
12307 if (coll)
12308 appendPQExpBuffer(q, ",\n collation = %s",
12309 fmtQualifiedDumpable(coll));
12310 }
12311
12312 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12313 if (strcmp(procname, "-") != 0)
12314 appendPQExpBuffer(q, ",\n canonical = %s", procname);
12315
12316 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12317 if (strcmp(procname, "-") != 0)
12318 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12319
12320 appendPQExpBufferStr(q, "\n);\n");
12321
12322 if (dopt->binary_upgrade)
12324 "TYPE", qtypname,
12325 tyinfo->dobj.namespace->dobj.name);
12326
12327 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12328 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12329 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12330 .namespace = tyinfo->dobj.namespace->dobj.name,
12331 .owner = tyinfo->rolname,
12332 .description = "TYPE",
12333 .section = SECTION_PRE_DATA,
12334 .createStmt = q->data,
12335 .dropStmt = delq->data));
12336
12337 /* Dump Type Comments and Security Labels */
12338 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12339 dumpComment(fout, "TYPE", qtypname,
12340 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12341 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12342
12343 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12344 dumpSecLabel(fout, "TYPE", qtypname,
12345 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12346 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12347
12348 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12349 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12350 qtypname, NULL,
12351 tyinfo->dobj.namespace->dobj.name,
12352 NULL, tyinfo->rolname, &tyinfo->dacl);
12353
12354 PQclear(res);
12357 destroyPQExpBuffer(query);
12360}
12361
12362/*
12363 * dumpUndefinedType
12364 * writes out to fout the queries to recreate a !typisdefined type
12365 *
12366 * This is a shell type, but we use different terminology to distinguish
12367 * this case from where we have to emit a shell type definition to break
12368 * circular dependencies. An undefined type shouldn't ever have anything
12369 * depending on it.
12370 */
12371static void
12373{
12374 DumpOptions *dopt = fout->dopt;
12377 char *qtypname;
12378 char *qualtypname;
12379
12380 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12382
12383 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12384
12385 if (dopt->binary_upgrade)
12387 tyinfo->dobj.catId.oid,
12388 false, false);
12389
12390 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12391 qualtypname);
12392
12393 if (dopt->binary_upgrade)
12395 "TYPE", qtypname,
12396 tyinfo->dobj.namespace->dobj.name);
12397
12398 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12399 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12400 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12401 .namespace = tyinfo->dobj.namespace->dobj.name,
12402 .owner = tyinfo->rolname,
12403 .description = "TYPE",
12404 .section = SECTION_PRE_DATA,
12405 .createStmt = q->data,
12406 .dropStmt = delq->data));
12407
12408 /* Dump Type Comments and Security Labels */
12409 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12410 dumpComment(fout, "TYPE", qtypname,
12411 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12412 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12413
12414 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12415 dumpSecLabel(fout, "TYPE", qtypname,
12416 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12417 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12418
12419 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12420 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12421 qtypname, NULL,
12422 tyinfo->dobj.namespace->dobj.name,
12423 NULL, tyinfo->rolname, &tyinfo->dacl);
12424
12429}
12430
12431/*
12432 * dumpBaseType
12433 * writes out to fout the queries to recreate a user-defined base type
12434 */
12435static void
12437{
12438 DumpOptions *dopt = fout->dopt;
12442 PGresult *res;
12443 char *qtypname;
12444 char *qualtypname;
12445 char *typlen;
12446 char *typinput;
12447 char *typoutput;
12448 char *typreceive;
12449 char *typsend;
12450 char *typmodin;
12451 char *typmodout;
12452 char *typanalyze;
12453 char *typsubscript;
12460 char *typcategory;
12461 char *typispreferred;
12462 char *typdelim;
12463 char *typbyval;
12464 char *typalign;
12465 char *typstorage;
12466 char *typcollatable;
12467 char *typdefault;
12468 bool typdefault_is_literal = false;
12469
12470 if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12471 {
12472 /* Set up query for type-specific details */
12474 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12475 "SELECT typlen, "
12476 "typinput, typoutput, typreceive, typsend, "
12477 "typreceive::pg_catalog.oid AS typreceiveoid, "
12478 "typsend::pg_catalog.oid AS typsendoid, "
12479 "typanalyze, "
12480 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12481 "typdelim, typbyval, typalign, typstorage, "
12482 "typmodin, typmodout, "
12483 "typmodin::pg_catalog.oid AS typmodinoid, "
12484 "typmodout::pg_catalog.oid AS typmodoutoid, "
12485 "typcategory, typispreferred, "
12486 "(typcollation <> 0) AS typcollatable, "
12487 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12488
12489 if (fout->remoteVersion >= 140000)
12491 "typsubscript, "
12492 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12493 else
12495 "'-' AS typsubscript, 0 AS typsubscriptoid ");
12496
12497 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12498 "WHERE oid = $1");
12499
12500 ExecuteSqlStatement(fout, query->data);
12501
12502 fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12503 }
12504
12505 printfPQExpBuffer(query,
12506 "EXECUTE dumpBaseType('%u')",
12507 tyinfo->dobj.catId.oid);
12508
12509 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12510
12511 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12512 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12513 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12514 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12515 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12516 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12517 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12518 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12519 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12520 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12521 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12522 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12523 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12524 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12525 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12526 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12527 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12528 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12529 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12530 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12531 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12532 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12533 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12534 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12535 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12536 {
12537 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12538 typdefault_is_literal = true; /* it needs quotes */
12539 }
12540 else
12541 typdefault = NULL;
12542
12543 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12545
12546 /*
12547 * The reason we include CASCADE is that the circular dependency between
12548 * the type and its I/O functions makes it impossible to drop the type any
12549 * other way.
12550 */
12551 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12552
12553 /*
12554 * We might already have a shell type, but setting pg_type_oid is
12555 * harmless, and in any case we'd better set the array type OID.
12556 */
12557 if (dopt->binary_upgrade)
12559 tyinfo->dobj.catId.oid,
12560 false, false);
12561
12563 "CREATE TYPE %s (\n"
12564 " INTERNALLENGTH = %s",
12566 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12567
12568 /* regproc result is sufficiently quoted already */
12569 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12570 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12572 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12574 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12576 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12578 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12580 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12581
12582 if (strcmp(typcollatable, "t") == 0)
12583 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12584
12585 if (typdefault != NULL)
12586 {
12587 appendPQExpBufferStr(q, ",\n DEFAULT = ");
12590 else
12592 }
12593
12595 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12596
12597 if (OidIsValid(tyinfo->typelem))
12598 appendPQExpBuffer(q, ",\n ELEMENT = %s",
12600 zeroIsError));
12601
12602 if (strcmp(typcategory, "U") != 0)
12603 {
12604 appendPQExpBufferStr(q, ",\n CATEGORY = ");
12606 }
12607
12608 if (strcmp(typispreferred, "t") == 0)
12609 appendPQExpBufferStr(q, ",\n PREFERRED = true");
12610
12611 if (typdelim && strcmp(typdelim, ",") != 0)
12612 {
12613 appendPQExpBufferStr(q, ",\n DELIMITER = ");
12614 appendStringLiteralAH(q, typdelim, fout);
12615 }
12616
12617 if (*typalign == TYPALIGN_CHAR)
12618 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12619 else if (*typalign == TYPALIGN_SHORT)
12620 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12621 else if (*typalign == TYPALIGN_INT)
12622 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12623 else if (*typalign == TYPALIGN_DOUBLE)
12624 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12625
12626 if (*typstorage == TYPSTORAGE_PLAIN)
12627 appendPQExpBufferStr(q, ",\n STORAGE = plain");
12628 else if (*typstorage == TYPSTORAGE_EXTERNAL)
12629 appendPQExpBufferStr(q, ",\n STORAGE = external");
12630 else if (*typstorage == TYPSTORAGE_EXTENDED)
12631 appendPQExpBufferStr(q, ",\n STORAGE = extended");
12632 else if (*typstorage == TYPSTORAGE_MAIN)
12633 appendPQExpBufferStr(q, ",\n STORAGE = main");
12634
12635 if (strcmp(typbyval, "t") == 0)
12636 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12637
12638 appendPQExpBufferStr(q, "\n);\n");
12639
12640 if (dopt->binary_upgrade)
12642 "TYPE", qtypname,
12643 tyinfo->dobj.namespace->dobj.name);
12644
12645 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12646 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12647 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12648 .namespace = tyinfo->dobj.namespace->dobj.name,
12649 .owner = tyinfo->rolname,
12650 .description = "TYPE",
12651 .section = SECTION_PRE_DATA,
12652 .createStmt = q->data,
12653 .dropStmt = delq->data));
12654
12655 /* Dump Type Comments and Security Labels */
12656 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12657 dumpComment(fout, "TYPE", qtypname,
12658 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12659 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12660
12661 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12662 dumpSecLabel(fout, "TYPE", qtypname,
12663 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12664 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12665
12666 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12667 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12668 qtypname, NULL,
12669 tyinfo->dobj.namespace->dobj.name,
12670 NULL, tyinfo->rolname, &tyinfo->dacl);
12671
12672 PQclear(res);
12675 destroyPQExpBuffer(query);
12678}
12679
12680/*
12681 * dumpDomain
12682 * writes out to fout the queries to recreate a user-defined domain
12683 */
12684static void
12686{
12687 DumpOptions *dopt = fout->dopt;
12691 PGresult *res;
12692 int i;
12693 char *qtypname;
12694 char *qualtypname;
12695 char *typnotnull;
12696 char *typdefn;
12697 char *typdefault;
12698 Oid typcollation;
12699 bool typdefault_is_literal = false;
12700
12701 if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12702 {
12703 /* Set up query for domain-specific details */
12705 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12706
12707 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12708 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12709 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12710 "t.typdefault, "
12711 "CASE WHEN t.typcollation <> u.typcollation "
12712 "THEN t.typcollation ELSE 0 END AS typcollation "
12713 "FROM pg_catalog.pg_type t "
12714 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12715 "WHERE t.oid = $1");
12716
12717 ExecuteSqlStatement(fout, query->data);
12718
12719 fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12720 }
12721
12722 printfPQExpBuffer(query,
12723 "EXECUTE dumpDomain('%u')",
12724 tyinfo->dobj.catId.oid);
12725
12726 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12727
12728 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12729 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12730 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12731 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12732 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12733 {
12734 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12735 typdefault_is_literal = true; /* it needs quotes */
12736 }
12737 else
12738 typdefault = NULL;
12739 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12740
12741 if (dopt->binary_upgrade)
12743 tyinfo->dobj.catId.oid,
12744 true, /* force array type */
12745 false); /* force multirange type */
12746
12747 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12749
12751 "CREATE DOMAIN %s AS %s",
12753 typdefn);
12754
12755 /* Print collation only if different from base type's collation */
12756 if (OidIsValid(typcollation))
12757 {
12758 CollInfo *coll;
12759
12760 coll = findCollationByOid(typcollation);
12761 if (coll)
12762 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12763 }
12764
12765 /*
12766 * Print a not-null constraint if there's one. In servers older than 17
12767 * these don't have names, so just print it unadorned; in newer ones they
12768 * do, but most of the time it's going to be the standard generated one,
12769 * so omit the name in that case also.
12770 */
12771 if (typnotnull[0] == 't')
12772 {
12773 if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12774 appendPQExpBufferStr(q, " NOT NULL");
12775 else
12776 {
12777 ConstraintInfo *notnull = tyinfo->notnull;
12778
12779 if (!notnull->separate)
12780 {
12781 char *default_name;
12782
12783 /* XXX should match ChooseConstraintName better */
12784 default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12785
12786 if (strcmp(default_name, notnull->dobj.name) == 0)
12787 appendPQExpBufferStr(q, " NOT NULL");
12788 else
12789 appendPQExpBuffer(q, " CONSTRAINT %s %s",
12790 fmtId(notnull->dobj.name), notnull->condef);
12792 }
12793 }
12794 }
12795
12796 if (typdefault != NULL)
12797 {
12798 appendPQExpBufferStr(q, " DEFAULT ");
12801 else
12803 }
12804
12805 PQclear(res);
12806
12807 /*
12808 * Add any CHECK constraints for the domain
12809 */
12810 for (i = 0; i < tyinfo->nDomChecks; i++)
12811 {
12812 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12813
12814 if (!domcheck->separate && domcheck->contype == 'c')
12815 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12816 fmtId(domcheck->dobj.name), domcheck->condef);
12817 }
12818
12819 appendPQExpBufferStr(q, ";\n");
12820
12821 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12822
12823 if (dopt->binary_upgrade)
12825 "DOMAIN", qtypname,
12826 tyinfo->dobj.namespace->dobj.name);
12827
12828 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12829 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12830 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12831 .namespace = tyinfo->dobj.namespace->dobj.name,
12832 .owner = tyinfo->rolname,
12833 .description = "DOMAIN",
12834 .section = SECTION_PRE_DATA,
12835 .createStmt = q->data,
12836 .dropStmt = delq->data));
12837
12838 /* Dump Domain Comments and Security Labels */
12839 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12840 dumpComment(fout, "DOMAIN", qtypname,
12841 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12842 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12843
12844 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12845 dumpSecLabel(fout, "DOMAIN", qtypname,
12846 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12847 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12848
12849 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12850 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12851 qtypname, NULL,
12852 tyinfo->dobj.namespace->dobj.name,
12853 NULL, tyinfo->rolname, &tyinfo->dacl);
12854
12855 /* Dump any per-constraint comments */
12856 for (i = 0; i < tyinfo->nDomChecks; i++)
12857 {
12858 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12860
12861 /* but only if the constraint itself was dumped here */
12862 if (domcheck->separate)
12863 continue;
12864
12866 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12867 fmtId(domcheck->dobj.name));
12868
12869 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12871 tyinfo->dobj.namespace->dobj.name,
12872 tyinfo->rolname,
12873 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12874
12876 }
12877
12878 /*
12879 * And a comment on the not-null constraint, if there's one -- but only if
12880 * the constraint itself was dumped here
12881 */
12882 if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12883 {
12885
12886 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12887 fmtId(tyinfo->notnull->dobj.name));
12888
12889 if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12891 tyinfo->dobj.namespace->dobj.name,
12892 tyinfo->rolname,
12893 tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12895 }
12896
12899 destroyPQExpBuffer(query);
12902}
12903
12904/*
12905 * dumpCompositeType
12906 * writes out to fout the queries to recreate a user-defined stand-alone
12907 * composite type
12908 */
12909static void
12911{
12912 DumpOptions *dopt = fout->dopt;
12914 PQExpBuffer dropped = createPQExpBuffer();
12917 PGresult *res;
12918 char *qtypname;
12919 char *qualtypname;
12920 int ntups;
12921 int i_attname;
12922 int i_atttypdefn;
12923 int i_attlen;
12924 int i_attalign;
12925 int i_attisdropped;
12926 int i_attcollation;
12927 int i;
12928 int actual_atts;
12929
12930 if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12931 {
12932 /*
12933 * Set up query for type-specific details.
12934 *
12935 * Since we only want to dump COLLATE clauses for attributes whose
12936 * collation is different from their type's default, we use a CASE
12937 * here to suppress uninteresting attcollations cheaply. atttypid
12938 * will be 0 for dropped columns; collation does not matter for those.
12939 */
12941 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12942 "SELECT a.attname, a.attnum, "
12943 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12944 "a.attlen, a.attalign, a.attisdropped, "
12945 "CASE WHEN a.attcollation <> at.typcollation "
12946 "THEN a.attcollation ELSE 0 END AS attcollation "
12947 "FROM pg_catalog.pg_type ct "
12948 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12949 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12950 "WHERE ct.oid = $1 "
12951 "ORDER BY a.attnum");
12952
12953 ExecuteSqlStatement(fout, query->data);
12954
12955 fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12956 }
12957
12958 printfPQExpBuffer(query,
12959 "EXECUTE dumpCompositeType('%u')",
12960 tyinfo->dobj.catId.oid);
12961
12962 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12963
12964 ntups = PQntuples(res);
12965
12966 i_attname = PQfnumber(res, "attname");
12967 i_atttypdefn = PQfnumber(res, "atttypdefn");
12968 i_attlen = PQfnumber(res, "attlen");
12969 i_attalign = PQfnumber(res, "attalign");
12970 i_attisdropped = PQfnumber(res, "attisdropped");
12971 i_attcollation = PQfnumber(res, "attcollation");
12972
12973 if (dopt->binary_upgrade)
12974 {
12976 tyinfo->dobj.catId.oid,
12977 false, false);
12979 }
12980
12981 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12983
12984 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12985 qualtypname);
12986
12987 actual_atts = 0;
12988 for (i = 0; i < ntups; i++)
12989 {
12990 char *attname;
12991 char *atttypdefn;
12992 char *attlen;
12993 char *attalign;
12994 bool attisdropped;
12995 Oid attcollation;
12996
12997 attname = PQgetvalue(res, i, i_attname);
12999 attlen = PQgetvalue(res, i, i_attlen);
13001 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13002 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13003
13004 if (attisdropped && !dopt->binary_upgrade)
13005 continue;
13006
13007 /* Format properly if not first attr */
13008 if (actual_atts++ > 0)
13009 appendPQExpBufferChar(q, ',');
13010 appendPQExpBufferStr(q, "\n\t");
13011
13012 if (!attisdropped)
13013 {
13015
13016 /* Add collation if not default for the column type */
13017 if (OidIsValid(attcollation))
13018 {
13019 CollInfo *coll;
13020
13021 coll = findCollationByOid(attcollation);
13022 if (coll)
13023 appendPQExpBuffer(q, " COLLATE %s",
13024 fmtQualifiedDumpable(coll));
13025 }
13026 }
13027 else
13028 {
13029 /*
13030 * This is a dropped attribute and we're in binary_upgrade mode.
13031 * Insert a placeholder for it in the CREATE TYPE command, and set
13032 * length and alignment with direct UPDATE to the catalogs
13033 * afterwards. See similar code in dumpTableSchema().
13034 */
13035 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13036
13037 /* stash separately for insertion after the CREATE TYPE */
13038 appendPQExpBufferStr(dropped,
13039 "\n-- For binary upgrade, recreate dropped column.\n");
13040 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13041 "SET attlen = %s, "
13042 "attalign = '%s', attbyval = false\n"
13043 "WHERE attname = ", attlen, attalign);
13045 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13047 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13048
13049 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13050 qualtypname);
13051 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13052 fmtId(attname));
13053 }
13054 }
13055 appendPQExpBufferStr(q, "\n);\n");
13056 appendPQExpBufferStr(q, dropped->data);
13057
13058 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13059
13060 if (dopt->binary_upgrade)
13062 "TYPE", qtypname,
13063 tyinfo->dobj.namespace->dobj.name);
13064
13065 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13066 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13067 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13068 .namespace = tyinfo->dobj.namespace->dobj.name,
13069 .owner = tyinfo->rolname,
13070 .description = "TYPE",
13071 .section = SECTION_PRE_DATA,
13072 .createStmt = q->data,
13073 .dropStmt = delq->data));
13074
13075
13076 /* Dump Type Comments and Security Labels */
13077 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13078 dumpComment(fout, "TYPE", qtypname,
13079 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13080 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13081
13082 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13083 dumpSecLabel(fout, "TYPE", qtypname,
13084 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13085 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13086
13087 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13088 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13089 qtypname, NULL,
13090 tyinfo->dobj.namespace->dobj.name,
13091 NULL, tyinfo->rolname, &tyinfo->dacl);
13092
13093 /* Dump any per-column comments */
13094 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13096
13097 PQclear(res);
13099 destroyPQExpBuffer(dropped);
13101 destroyPQExpBuffer(query);
13104}
13105
13106/*
13107 * dumpCompositeTypeColComments
13108 * writes out to fout the queries to recreate comments on the columns of
13109 * a user-defined stand-alone composite type.
13110 *
13111 * The caller has already made a query to collect the names and attnums
13112 * of the type's columns, so we just pass that result into here rather
13113 * than reading them again.
13114 */
13115static void
13117 PGresult *res)
13118{
13120 int ncomments;
13121 PQExpBuffer query;
13122 PQExpBuffer target;
13123 int i;
13124 int ntups;
13125 int i_attname;
13126 int i_attnum;
13127 int i_attisdropped;
13128
13129 /* do nothing, if --no-comments is supplied */
13130 if (fout->dopt->no_comments)
13131 return;
13132
13133 /* Search for comments associated with type's pg_class OID */
13135 &comments);
13136
13137 /* If no comments exist, we're done */
13138 if (ncomments <= 0)
13139 return;
13140
13141 /* Build COMMENT ON statements */
13142 query = createPQExpBuffer();
13143 target = createPQExpBuffer();
13144
13145 ntups = PQntuples(res);
13146 i_attnum = PQfnumber(res, "attnum");
13147 i_attname = PQfnumber(res, "attname");
13148 i_attisdropped = PQfnumber(res, "attisdropped");
13149 while (ncomments > 0)
13150 {
13151 const char *attname;
13152
13153 attname = NULL;
13154 for (i = 0; i < ntups; i++)
13155 {
13156 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13157 PQgetvalue(res, i, i_attisdropped)[0] != 't')
13158 {
13159 attname = PQgetvalue(res, i, i_attname);
13160 break;
13161 }
13162 }
13163 if (attname) /* just in case we don't find it */
13164 {
13165 const char *descr = comments->descr;
13166
13167 resetPQExpBuffer(target);
13168 appendPQExpBuffer(target, "COLUMN %s.",
13169 fmtId(tyinfo->dobj.name));
13171
13172 resetPQExpBuffer(query);
13173 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13175 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13176 appendStringLiteralAH(query, descr, fout);
13177 appendPQExpBufferStr(query, ";\n");
13178
13180 ARCHIVE_OPTS(.tag = target->data,
13181 .namespace = tyinfo->dobj.namespace->dobj.name,
13182 .owner = tyinfo->rolname,
13183 .description = "COMMENT",
13184 .section = SECTION_NONE,
13185 .createStmt = query->data,
13186 .deps = &(tyinfo->dobj.dumpId),
13187 .nDeps = 1));
13188 }
13189
13190 comments++;
13191 ncomments--;
13192 }
13193
13194 destroyPQExpBuffer(query);
13195 destroyPQExpBuffer(target);
13196}
13197
13198/*
13199 * dumpShellType
13200 * writes out to fout the queries to create a shell type
13201 *
13202 * We dump a shell definition in advance of the I/O functions for the type.
13203 */
13204static void
13206{
13207 DumpOptions *dopt = fout->dopt;
13208 PQExpBuffer q;
13209
13210 /* Do nothing if not dumping schema */
13211 if (!dopt->dumpSchema)
13212 return;
13213
13214 q = createPQExpBuffer();
13215
13216 /*
13217 * Note the lack of a DROP command for the shell type; any required DROP
13218 * is driven off the base type entry, instead. This interacts with
13219 * _printTocEntry()'s use of the presence of a DROP command to decide
13220 * whether an entry needs an ALTER OWNER command. We don't want to alter
13221 * the shell type's owner immediately on creation; that should happen only
13222 * after it's filled in, otherwise the backend complains.
13223 */
13224
13225 if (dopt->binary_upgrade)
13227 stinfo->baseType->dobj.catId.oid,
13228 false, false);
13229
13230 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13232
13233 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13234 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13235 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13236 .namespace = stinfo->dobj.namespace->dobj.name,
13237 .owner = stinfo->baseType->rolname,
13238 .description = "SHELL TYPE",
13239 .section = SECTION_PRE_DATA,
13240 .createStmt = q->data));
13241
13243}
13244
13245/*
13246 * dumpProcLang
13247 * writes out to fout the queries to recreate a user-defined
13248 * procedural language
13249 */
13250static void
13252{
13253 DumpOptions *dopt = fout->dopt;
13256 bool useParams;
13257 char *qlanname;
13261
13262 /* Do nothing if not dumping schema */
13263 if (!dopt->dumpSchema)
13264 return;
13265
13266 /*
13267 * Try to find the support function(s). It is not an error if we don't
13268 * find them --- if the functions are in the pg_catalog schema, as is
13269 * standard in 8.1 and up, then we won't have loaded them. (In this case
13270 * we will emit a parameterless CREATE LANGUAGE command, which will
13271 * require PL template knowledge in the backend to reload.)
13272 */
13273
13274 funcInfo = findFuncByOid(plang->lanplcallfoid);
13275 if (funcInfo != NULL && !funcInfo->dobj.dump)
13276 funcInfo = NULL; /* treat not-dumped same as not-found */
13277
13278 if (OidIsValid(plang->laninline))
13279 {
13280 inlineInfo = findFuncByOid(plang->laninline);
13281 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13282 inlineInfo = NULL;
13283 }
13284
13285 if (OidIsValid(plang->lanvalidator))
13286 {
13287 validatorInfo = findFuncByOid(plang->lanvalidator);
13288 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13290 }
13291
13292 /*
13293 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13294 * parameters. Otherwise, we'll write a parameterless command, which will
13295 * be interpreted as CREATE EXTENSION.
13296 */
13297 useParams = (funcInfo != NULL &&
13298 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13299 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13300
13303
13304 qlanname = pg_strdup(fmtId(plang->dobj.name));
13305
13306 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13307 qlanname);
13308
13309 if (useParams)
13310 {
13311 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13312 plang->lanpltrusted ? "TRUSTED " : "",
13313 qlanname);
13314 appendPQExpBuffer(defqry, " HANDLER %s",
13316 if (OidIsValid(plang->laninline))
13317 appendPQExpBuffer(defqry, " INLINE %s",
13319 if (OidIsValid(plang->lanvalidator))
13320 appendPQExpBuffer(defqry, " VALIDATOR %s",
13322 }
13323 else
13324 {
13325 /*
13326 * If not dumping parameters, then use CREATE OR REPLACE so that the
13327 * command will not fail if the language is preinstalled in the target
13328 * database.
13329 *
13330 * Modern servers will interpret this as CREATE EXTENSION IF NOT
13331 * EXISTS; perhaps we should emit that instead? But it might just add
13332 * confusion.
13333 */
13334 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13335 qlanname);
13336 }
13338
13339 if (dopt->binary_upgrade)
13341 "LANGUAGE", qlanname, NULL);
13342
13343 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13344 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13345 ARCHIVE_OPTS(.tag = plang->dobj.name,
13346 .owner = plang->lanowner,
13347 .description = "PROCEDURAL LANGUAGE",
13348 .section = SECTION_PRE_DATA,
13349 .createStmt = defqry->data,
13350 .dropStmt = delqry->data,
13351 ));
13352
13353 /* Dump Proc Lang Comments and Security Labels */
13354 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13355 dumpComment(fout, "LANGUAGE", qlanname,
13356 NULL, plang->lanowner,
13357 plang->dobj.catId, 0, plang->dobj.dumpId);
13358
13359 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13360 dumpSecLabel(fout, "LANGUAGE", qlanname,
13361 NULL, plang->lanowner,
13362 plang->dobj.catId, 0, plang->dobj.dumpId);
13363
13364 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13365 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13366 qlanname, NULL, NULL,
13367 NULL, plang->lanowner, &plang->dacl);
13368
13370
13373}
13374
13375/*
13376 * format_function_arguments: generate function name and argument list
13377 *
13378 * This is used when we can rely on pg_get_function_arguments to format
13379 * the argument list. Note, however, that pg_get_function_arguments
13380 * does not special-case zero-argument aggregates.
13381 */
13382static char *
13383format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13384{
13386
13389 if (is_agg && finfo->nargs == 0)
13390 appendPQExpBufferStr(&fn, "(*)");
13391 else
13392 appendPQExpBuffer(&fn, "(%s)", funcargs);
13393 return fn.data;
13394}
13395
13396/*
13397 * format_function_signature: generate function name and argument list
13398 *
13399 * Only a minimal list of input argument types is generated; this is
13400 * sufficient to reference the function, but not to define it.
13401 *
13402 * If honor_quotes is false then the function name is never quoted.
13403 * This is appropriate for use in TOC tags, but not in SQL commands.
13404 */
13405static char *
13407{
13409 int j;
13410
13412 if (honor_quotes)
13413 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13414 else
13415 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13416 for (j = 0; j < finfo->nargs; j++)
13417 {
13418 if (j > 0)
13419 appendPQExpBufferStr(&fn, ", ");
13420
13423 zeroIsError));
13424 }
13426 return fn.data;
13427}
13428
13429
13430/*
13431 * dumpFunc:
13432 * dump out one function
13433 */
13434static void
13436{
13437 DumpOptions *dopt = fout->dopt;
13438 PQExpBuffer query;
13439 PQExpBuffer q;
13442 PGresult *res;
13443 char *funcsig; /* identity signature */
13444 char *funcfullsig = NULL; /* full signature */
13445 char *funcsig_tag;
13446 char *qual_funcsig;
13447 char *proretset;
13448 char *prosrc;
13449 char *probin;
13450 char *prosqlbody;
13451 char *funcargs;
13452 char *funciargs;
13453 char *funcresult;
13454 char *protrftypes;
13455 char *prokind;
13456 char *provolatile;
13457 char *proisstrict;
13458 char *prosecdef;
13459 char *proleakproof;
13460 char *proconfig;
13461 char *procost;
13462 char *prorows;
13463 char *prosupport;
13464 char *proparallel;
13465 char *lanname;
13466 char **configitems = NULL;
13467 int nconfigitems = 0;
13468 const char *keyword;
13469
13470 /* Do nothing if not dumping schema */
13471 if (!dopt->dumpSchema)
13472 return;
13473
13474 query = createPQExpBuffer();
13475 q = createPQExpBuffer();
13478
13479 if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13480 {
13481 /* Set up query for function-specific details */
13483 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13484
13486 "SELECT\n"
13487 "proretset,\n"
13488 "prosrc,\n"
13489 "probin,\n"
13490 "provolatile,\n"
13491 "proisstrict,\n"
13492 "prosecdef,\n"
13493 "lanname,\n"
13494 "proconfig,\n"
13495 "procost,\n"
13496 "prorows,\n"
13497 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13498 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13499 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13500 "proleakproof,\n");
13501
13503 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13504
13506 "proparallel,\n");
13507
13508 if (fout->remoteVersion >= 110000)
13510 "prokind,\n");
13511 else
13513 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13514
13515 if (fout->remoteVersion >= 120000)
13517 "prosupport,\n");
13518 else
13520 "'-' AS prosupport,\n");
13521
13522 if (fout->remoteVersion >= 140000)
13524 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13525 else
13527 "NULL AS prosqlbody\n");
13528
13530 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13531 "WHERE p.oid = $1 "
13532 "AND l.oid = p.prolang");
13533
13534 ExecuteSqlStatement(fout, query->data);
13535
13536 fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13537 }
13538
13539 printfPQExpBuffer(query,
13540 "EXECUTE dumpFunc('%u')",
13541 finfo->dobj.catId.oid);
13542
13543 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13544
13545 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13546 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13547 {
13548 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13549 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13550 prosqlbody = NULL;
13551 }
13552 else
13553 {
13554 prosrc = NULL;
13555 probin = NULL;
13556 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13557 }
13558 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13559 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13560 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13561 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13562 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13563 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13564 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13565 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13566 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13567 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13568 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13569 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13570 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13571 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13572 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13573
13574 /*
13575 * See backend/commands/functioncmds.c for details of how the 'AS' clause
13576 * is used.
13577 */
13578 if (prosqlbody)
13579 {
13581 }
13582 else if (probin[0] != '\0')
13583 {
13586 if (prosrc[0] != '\0')
13587 {
13589
13590 /*
13591 * where we have bin, use dollar quoting if allowed and src
13592 * contains quote or backslash; else use regular quoting.
13593 */
13594 if (dopt->disable_dollar_quoting ||
13595 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13597 else
13599 }
13600 }
13601 else
13602 {
13604 /* with no bin, dollar quote src unconditionally if allowed */
13605 if (dopt->disable_dollar_quoting)
13607 else
13609 }
13610
13611 if (*proconfig)
13612 {
13614 pg_fatal("could not parse %s array", "proconfig");
13615 }
13616 else
13617 {
13618 configitems = NULL;
13619 nconfigitems = 0;
13620 }
13621
13624
13626
13627 qual_funcsig = psprintf("%s.%s",
13628 fmtId(finfo->dobj.namespace->dobj.name),
13629 funcsig);
13630
13631 if (prokind[0] == PROKIND_PROCEDURE)
13632 keyword = "PROCEDURE";
13633 else
13634 keyword = "FUNCTION"; /* works for window functions too */
13635
13636 appendPQExpBuffer(delqry, "DROP %s %s;\n",
13637 keyword, qual_funcsig);
13638
13639 appendPQExpBuffer(q, "CREATE %s %s.%s",
13640 keyword,
13641 fmtId(finfo->dobj.namespace->dobj.name),
13643 funcsig);
13644
13645 if (prokind[0] == PROKIND_PROCEDURE)
13646 /* no result type to output */ ;
13647 else if (funcresult)
13648 appendPQExpBuffer(q, " RETURNS %s", funcresult);
13649 else
13650 appendPQExpBuffer(q, " RETURNS %s%s",
13651 (proretset[0] == 't') ? "SETOF " : "",
13653 zeroIsError));
13654
13655 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13656
13657 if (*protrftypes)
13658 {
13660 int i;
13661
13662 appendPQExpBufferStr(q, " TRANSFORM ");
13664 for (i = 0; typeids[i]; i++)
13665 {
13666 if (i != 0)
13667 appendPQExpBufferStr(q, ", ");
13668 appendPQExpBuffer(q, "FOR TYPE %s",
13670 }
13671
13673 }
13674
13675 if (prokind[0] == PROKIND_WINDOW)
13676 appendPQExpBufferStr(q, " WINDOW");
13677
13679 {
13681 appendPQExpBufferStr(q, " IMMUTABLE");
13682 else if (provolatile[0] == PROVOLATILE_STABLE)
13683 appendPQExpBufferStr(q, " STABLE");
13684 else if (provolatile[0] != PROVOLATILE_VOLATILE)
13685 pg_fatal("unrecognized provolatile value for function \"%s\"",
13686 finfo->dobj.name);
13687 }
13688
13689 if (proisstrict[0] == 't')
13690 appendPQExpBufferStr(q, " STRICT");
13691
13692 if (prosecdef[0] == 't')
13693 appendPQExpBufferStr(q, " SECURITY DEFINER");
13694
13695 if (proleakproof[0] == 't')
13696 appendPQExpBufferStr(q, " LEAKPROOF");
13697
13698 /*
13699 * COST and ROWS are emitted only if present and not default, so as not to
13700 * break backwards-compatibility of the dump without need. Keep this code
13701 * in sync with the defaults in functioncmds.c.
13702 */
13703 if (strcmp(procost, "0") != 0)
13704 {
13705 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13706 {
13707 /* default cost is 1 */
13708 if (strcmp(procost, "1") != 0)
13709 appendPQExpBuffer(q, " COST %s", procost);
13710 }
13711 else
13712 {
13713 /* default cost is 100 */
13714 if (strcmp(procost, "100") != 0)
13715 appendPQExpBuffer(q, " COST %s", procost);
13716 }
13717 }
13718 if (proretset[0] == 't' &&
13719 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13720 appendPQExpBuffer(q, " ROWS %s", prorows);
13721
13722 if (strcmp(prosupport, "-") != 0)
13723 {
13724 /* We rely on regprocout to provide quoting and qualification */
13725 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13726 }
13727
13729 {
13730 if (proparallel[0] == PROPARALLEL_SAFE)
13731 appendPQExpBufferStr(q, " PARALLEL SAFE");
13732 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13733 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13734 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13735 pg_fatal("unrecognized proparallel value for function \"%s\"",
13736 finfo->dobj.name);
13737 }
13738
13739 for (int i = 0; i < nconfigitems; i++)
13740 {
13741 /* we feel free to scribble on configitems[] here */
13742 char *configitem = configitems[i];
13743 char *pos;
13744
13745 pos = strchr(configitem, '=');
13746 if (pos == NULL)
13747 continue;
13748 *pos++ = '\0';
13749 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13750
13751 /*
13752 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13753 * by flatten_set_variable_args() before they were put into the
13754 * proconfig array. However, because the quoting rules used there
13755 * aren't exactly like SQL's, we have to break the list value apart
13756 * and then quote the elements as string literals. (The elements may
13757 * be double-quoted as-is, but we can't just feed them to the SQL
13758 * parser; it would do the wrong thing with elements that are
13759 * zero-length or longer than NAMEDATALEN.) Also, we need a special
13760 * case for empty lists.
13761 *
13762 * Variables that are not so marked should just be emitted as simple
13763 * string literals. If the variable is not known to
13764 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13765 * to use GUC_LIST_QUOTE for extension variables.
13766 */
13768 {
13769 char **namelist;
13770 char **nameptr;
13771
13772 /* Parse string into list of identifiers */
13773 /* this shouldn't fail really */
13774 if (SplitGUCList(pos, ',', &namelist))
13775 {
13776 /* Special case: represent an empty list as NULL */
13777 if (*namelist == NULL)
13778 appendPQExpBufferStr(q, "NULL");
13779 for (nameptr = namelist; *nameptr; nameptr++)
13780 {
13781 if (nameptr != namelist)
13782 appendPQExpBufferStr(q, ", ");
13784 }
13785 }
13787 }
13788 else
13789 appendStringLiteralAH(q, pos, fout);
13790 }
13791
13792 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13793
13795 "pg_catalog.pg_proc", keyword,
13796 qual_funcsig);
13797
13798 if (dopt->binary_upgrade)
13800 keyword, funcsig,
13801 finfo->dobj.namespace->dobj.name);
13802
13803 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13804 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13806 .namespace = finfo->dobj.namespace->dobj.name,
13807 .owner = finfo->rolname,
13808 .description = keyword,
13809 .section = finfo->postponed_def ?
13811 .createStmt = q->data,
13812 .dropStmt = delqry->data));
13813
13814 /* Dump Function Comments and Security Labels */
13815 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13816 dumpComment(fout, keyword, funcsig,
13817 finfo->dobj.namespace->dobj.name, finfo->rolname,
13818 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13819
13820 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13821 dumpSecLabel(fout, keyword, funcsig,
13822 finfo->dobj.namespace->dobj.name, finfo->rolname,
13823 finfo->dobj.catId, 0, finfo->dobj.dumpId);
13824
13825 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13826 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13827 funcsig, NULL,
13828 finfo->dobj.namespace->dobj.name,
13829 NULL, finfo->rolname, &finfo->dacl);
13830
13831 PQclear(res);
13832
13833 destroyPQExpBuffer(query);
13837 free(funcsig);
13842}
13843
13844
13845/*
13846 * Dump a user-defined cast
13847 */
13848static void
13850{
13851 DumpOptions *dopt = fout->dopt;
13857 const char *sourceType;
13858 const char *targetType;
13859
13860 /* Do nothing if not dumping schema */
13861 if (!dopt->dumpSchema)
13862 return;
13863
13864 /* Cannot dump if we don't have the cast function's info */
13865 if (OidIsValid(cast->castfunc))
13866 {
13867 funcInfo = findFuncByOid(cast->castfunc);
13868 if (funcInfo == NULL)
13869 pg_fatal("could not find function definition for function with OID %u",
13870 cast->castfunc);
13871 }
13872
13877
13880 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13882
13883 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13885
13886 switch (cast->castmethod)
13887 {
13889 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13890 break;
13892 appendPQExpBufferStr(defqry, "WITH INOUT");
13893 break;
13895 if (funcInfo)
13896 {
13898
13899 /*
13900 * Always qualify the function name (format_function_signature
13901 * won't qualify it).
13902 */
13903 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13904 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13905 free(fsig);
13906 }
13907 else
13908 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13909 break;
13910 default:
13911 pg_log_warning("bogus value in pg_cast.castmethod field");
13912 }
13913
13914 if (cast->castcontext == 'a')
13915 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13916 else if (cast->castcontext == 'i')
13917 appendPQExpBufferStr(defqry, " AS IMPLICIT");
13919
13920 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13922
13923 appendPQExpBuffer(castargs, "(%s AS %s)",
13925
13926 if (dopt->binary_upgrade)
13928 "CAST", castargs->data, NULL);
13929
13930 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13931 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13932 ARCHIVE_OPTS(.tag = labelq->data,
13933 .description = "CAST",
13934 .section = SECTION_PRE_DATA,
13935 .createStmt = defqry->data,
13936 .dropStmt = delqry->data));
13937
13938 /* Dump Cast Comments */
13939 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13940 dumpComment(fout, "CAST", castargs->data,
13941 NULL, "",
13942 cast->dobj.catId, 0, cast->dobj.dumpId);
13943
13948}
13949
13950/*
13951 * Dump a transform
13952 */
13953static void
13955{
13956 DumpOptions *dopt = fout->dopt;
13963 char *lanname;
13964 const char *transformType;
13965
13966 /* Do nothing if not dumping schema */
13967 if (!dopt->dumpSchema)
13968 return;
13969
13970 /* Cannot dump if we don't have the transform functions' info */
13971 if (OidIsValid(transform->trffromsql))
13972 {
13974 if (fromsqlFuncInfo == NULL)
13975 pg_fatal("could not find function definition for function with OID %u",
13976 transform->trffromsql);
13977 }
13978 if (OidIsValid(transform->trftosql))
13979 {
13980 tosqlFuncInfo = findFuncByOid(transform->trftosql);
13981 if (tosqlFuncInfo == NULL)
13982 pg_fatal("could not find function definition for function with OID %u",
13983 transform->trftosql);
13984 }
13985
13990
13991 lanname = get_language_name(fout, transform->trflang);
13993
13994 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13996
13997 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13999
14000 if (!transform->trffromsql && !transform->trftosql)
14001 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14002
14003 if (transform->trffromsql)
14004 {
14005 if (fromsqlFuncInfo)
14006 {
14008
14009 /*
14010 * Always qualify the function name (format_function_signature
14011 * won't qualify it).
14012 */
14013 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14014 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14015 free(fsig);
14016 }
14017 else
14018 pg_log_warning("bogus value in pg_transform.trffromsql field");
14019 }
14020
14021 if (transform->trftosql)
14022 {
14023 if (transform->trffromsql)
14025
14026 if (tosqlFuncInfo)
14027 {
14029
14030 /*
14031 * Always qualify the function name (format_function_signature
14032 * won't qualify it).
14033 */
14034 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14035 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14036 free(fsig);
14037 }
14038 else
14039 pg_log_warning("bogus value in pg_transform.trftosql field");
14040 }
14041
14043
14044 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14046
14047 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14049
14050 if (dopt->binary_upgrade)
14052 "TRANSFORM", transformargs->data, NULL);
14053
14054 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14055 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14056 ARCHIVE_OPTS(.tag = labelq->data,
14057 .description = "TRANSFORM",
14058 .section = SECTION_PRE_DATA,
14059 .createStmt = defqry->data,
14060 .dropStmt = delqry->data,
14061 .deps = transform->dobj.dependencies,
14062 .nDeps = transform->dobj.nDeps));
14063
14064 /* Dump Transform Comments */
14065 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14066 dumpComment(fout, "TRANSFORM", transformargs->data,
14067 NULL, "",
14068 transform->dobj.catId, 0, transform->dobj.dumpId);
14069
14070 free(lanname);
14075}
14076
14077
14078/*
14079 * dumpOpr
14080 * write out a single operator definition
14081 */
14082static void
14084{
14085 DumpOptions *dopt = fout->dopt;
14086 PQExpBuffer query;
14087 PQExpBuffer q;
14090 PQExpBuffer details;
14091 PGresult *res;
14092 int i_oprkind;
14093 int i_oprcode;
14094 int i_oprleft;
14095 int i_oprright;
14096 int i_oprcom;
14097 int i_oprnegate;
14098 int i_oprrest;
14099 int i_oprjoin;
14100 int i_oprcanmerge;
14101 int i_oprcanhash;
14102 char *oprkind;
14103 char *oprcode;
14104 char *oprleft;
14105 char *oprright;
14106 char *oprcom;
14107 char *oprnegate;
14108 char *oprrest;
14109 char *oprjoin;
14110 char *oprcanmerge;
14111 char *oprcanhash;
14112 char *oprregproc;
14113 char *oprref;
14114
14115 /* Do nothing if not dumping schema */
14116 if (!dopt->dumpSchema)
14117 return;
14118
14119 /*
14120 * some operators are invalid because they were the result of user
14121 * defining operators before commutators exist
14122 */
14123 if (!OidIsValid(oprinfo->oprcode))
14124 return;
14125
14126 query = createPQExpBuffer();
14127 q = createPQExpBuffer();
14130 details = createPQExpBuffer();
14131
14132 if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14133 {
14134 /* Set up query for operator-specific details */
14136 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14137 "SELECT oprkind, "
14138 "oprcode::pg_catalog.regprocedure, "
14139 "oprleft::pg_catalog.regtype, "
14140 "oprright::pg_catalog.regtype, "
14141 "oprcom, "
14142 "oprnegate, "
14143 "oprrest::pg_catalog.regprocedure, "
14144 "oprjoin::pg_catalog.regprocedure, "
14145 "oprcanmerge, oprcanhash "
14146 "FROM pg_catalog.pg_operator "
14147 "WHERE oid = $1");
14148
14149 ExecuteSqlStatement(fout, query->data);
14150
14151 fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14152 }
14153
14154 printfPQExpBuffer(query,
14155 "EXECUTE dumpOpr('%u')",
14156 oprinfo->dobj.catId.oid);
14157
14158 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14159
14160 i_oprkind = PQfnumber(res, "oprkind");
14161 i_oprcode = PQfnumber(res, "oprcode");
14162 i_oprleft = PQfnumber(res, "oprleft");
14163 i_oprright = PQfnumber(res, "oprright");
14164 i_oprcom = PQfnumber(res, "oprcom");
14165 i_oprnegate = PQfnumber(res, "oprnegate");
14166 i_oprrest = PQfnumber(res, "oprrest");
14167 i_oprjoin = PQfnumber(res, "oprjoin");
14168 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14169 i_oprcanhash = PQfnumber(res, "oprcanhash");
14170
14171 oprkind = PQgetvalue(res, 0, i_oprkind);
14172 oprcode = PQgetvalue(res, 0, i_oprcode);
14173 oprleft = PQgetvalue(res, 0, i_oprleft);
14174 oprright = PQgetvalue(res, 0, i_oprright);
14175 oprcom = PQgetvalue(res, 0, i_oprcom);
14176 oprnegate = PQgetvalue(res, 0, i_oprnegate);
14177 oprrest = PQgetvalue(res, 0, i_oprrest);
14178 oprjoin = PQgetvalue(res, 0, i_oprjoin);
14181
14182 /* In PG14 upwards postfix operator support does not exist anymore. */
14183 if (strcmp(oprkind, "r") == 0)
14184 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14185 oprcode);
14186
14188 if (oprregproc)
14189 {
14190 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14192 }
14193
14194 appendPQExpBuffer(oprid, "%s (",
14195 oprinfo->dobj.name);
14196
14197 /*
14198 * right unary means there's a left arg and left unary means there's a
14199 * right arg. (Although the "r" case is dead code for PG14 and later,
14200 * continue to support it in case we're dumping from an old server.)
14201 */
14202 if (strcmp(oprkind, "r") == 0 ||
14203 strcmp(oprkind, "b") == 0)
14204 {
14205 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14206 appendPQExpBufferStr(oprid, oprleft);
14207 }
14208 else
14209 appendPQExpBufferStr(oprid, "NONE");
14210
14211 if (strcmp(oprkind, "l") == 0 ||
14212 strcmp(oprkind, "b") == 0)
14213 {
14214 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14215 appendPQExpBuffer(oprid, ", %s)", oprright);
14216 }
14217 else
14218 appendPQExpBufferStr(oprid, ", NONE)");
14219
14221 if (oprref)
14222 {
14223 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14224 free(oprref);
14225 }
14226
14228 if (oprref)
14229 {
14230 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14231 free(oprref);
14232 }
14233
14234 if (strcmp(oprcanmerge, "t") == 0)
14235 appendPQExpBufferStr(details, ",\n MERGES");
14236
14237 if (strcmp(oprcanhash, "t") == 0)
14238 appendPQExpBufferStr(details, ",\n HASHES");
14239
14241 if (oprregproc)
14242 {
14243 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14245 }
14246
14248 if (oprregproc)
14249 {
14250 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14252 }
14253
14254 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14255 fmtId(oprinfo->dobj.namespace->dobj.name),
14256 oprid->data);
14257
14258 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14259 fmtId(oprinfo->dobj.namespace->dobj.name),
14260 oprinfo->dobj.name, details->data);
14261
14262 if (dopt->binary_upgrade)
14264 "OPERATOR", oprid->data,
14265 oprinfo->dobj.namespace->dobj.name);
14266
14267 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14268 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14269 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14270 .namespace = oprinfo->dobj.namespace->dobj.name,
14271 .owner = oprinfo->rolname,
14272 .description = "OPERATOR",
14273 .section = SECTION_PRE_DATA,
14274 .createStmt = q->data,
14275 .dropStmt = delq->data));
14276
14277 /* Dump Operator Comments */
14278 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14279 dumpComment(fout, "OPERATOR", oprid->data,
14280 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14281 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14282
14283 PQclear(res);
14284
14285 destroyPQExpBuffer(query);
14289 destroyPQExpBuffer(details);
14290}
14291
14292/*
14293 * Convert a function reference obtained from pg_operator
14294 *
14295 * Returns allocated string of what to print, or NULL if function references
14296 * is InvalidOid. Returned string is expected to be free'd by the caller.
14297 *
14298 * The input is a REGPROCEDURE display; we have to strip the argument-types
14299 * part.
14300 */
14301static char *
14303{
14304 char *name;
14305 char *paren;
14306 bool inquote;
14307
14308 /* In all cases "-" means a null reference */
14309 if (strcmp(proc, "-") == 0)
14310 return NULL;
14311
14312 name = pg_strdup(proc);
14313 /* find non-double-quoted left paren */
14314 inquote = false;
14315 for (paren = name; *paren; paren++)
14316 {
14317 if (*paren == '(' && !inquote)
14318 {
14319 *paren = '\0';
14320 break;
14321 }
14322 if (*paren == '"')
14323 inquote = !inquote;
14324 }
14325 return name;
14326}
14327
14328/*
14329 * getFormattedOperatorName - retrieve the operator name for the
14330 * given operator OID (presented in string form).
14331 *
14332 * Returns an allocated string, or NULL if the given OID is invalid.
14333 * Caller is responsible for free'ing result string.
14334 *
14335 * What we produce has the format "OPERATOR(schema.oprname)". This is only
14336 * useful in commands where the operator's argument types can be inferred from
14337 * context. We always schema-qualify the name, though. The predecessor to
14338 * this code tried to skip the schema qualification if possible, but that led
14339 * to wrong results in corner cases, such as if an operator and its negator
14340 * are in different schemas.
14341 */
14342static char *
14344{
14346
14347 /* In all cases "0" means a null reference */
14348 if (strcmp(oproid, "0") == 0)
14349 return NULL;
14350
14352 if (oprInfo == NULL)
14353 {
14354 pg_log_warning("could not find operator with OID %s",
14355 oproid);
14356 return NULL;
14357 }
14358
14359 return psprintf("OPERATOR(%s.%s)",
14360 fmtId(oprInfo->dobj.namespace->dobj.name),
14361 oprInfo->dobj.name);
14362}
14363
14364/*
14365 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14366 *
14367 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14368 * argument lists of these functions are predetermined. Note that the
14369 * caller should ensure we are in the proper schema, because the results
14370 * are search path dependent!
14371 */
14372static char *
14374{
14375 char *result;
14376 char query[128];
14377 PGresult *res;
14378
14379 snprintf(query, sizeof(query),
14380 "SELECT '%u'::pg_catalog.regproc", funcOid);
14381 res = ExecuteSqlQueryForSingleRow(fout, query);
14382
14383 result = pg_strdup(PQgetvalue(res, 0, 0));
14384
14385 PQclear(res);
14386
14387 return result;
14388}
14389
14390/*
14391 * dumpAccessMethod
14392 * write out a single access method definition
14393 */
14394static void
14396{
14397 DumpOptions *dopt = fout->dopt;
14398 PQExpBuffer q;
14400 char *qamname;
14401
14402 /* Do nothing if not dumping schema */
14403 if (!dopt->dumpSchema)
14404 return;
14405
14406 q = createPQExpBuffer();
14408
14409 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14410
14411 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14412
14413 switch (aminfo->amtype)
14414 {
14415 case AMTYPE_INDEX:
14416 appendPQExpBufferStr(q, "TYPE INDEX ");
14417 break;
14418 case AMTYPE_TABLE:
14419 appendPQExpBufferStr(q, "TYPE TABLE ");
14420 break;
14421 default:
14422 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14423 aminfo->amtype, qamname);
14427 return;
14428 }
14429
14430 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14431
14432 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14433 qamname);
14434
14435 if (dopt->binary_upgrade)
14437 "ACCESS METHOD", qamname, NULL);
14438
14439 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14440 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14441 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14442 .description = "ACCESS METHOD",
14443 .section = SECTION_PRE_DATA,
14444 .createStmt = q->data,
14445 .dropStmt = delq->data));
14446
14447 /* Dump Access Method Comments */
14448 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14449 dumpComment(fout, "ACCESS METHOD", qamname,
14450 NULL, "",
14451 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14452
14456}
14457
14458/*
14459 * dumpOpclass
14460 * write out a single operator class definition
14461 */
14462static void
14464{
14465 DumpOptions *dopt = fout->dopt;
14466 PQExpBuffer query;
14467 PQExpBuffer q;
14470 PGresult *res;
14471 int ntups;
14472 int i_opcintype;
14473 int i_opckeytype;
14474 int i_opcdefault;
14475 int i_opcfamily;
14476 int i_opcfamilyname;
14477 int i_opcfamilynsp;
14478 int i_amname;
14479 int i_amopstrategy;
14480 int i_amopopr;
14481 int i_sortfamily;
14482 int i_sortfamilynsp;
14483 int i_amprocnum;
14484 int i_amproc;
14485 int i_amproclefttype;
14487 char *opcintype;
14488 char *opckeytype;
14489 char *opcdefault;
14490 char *opcfamily;
14491 char *opcfamilyname;
14492 char *opcfamilynsp;
14493 char *amname;
14494 char *amopstrategy;
14495 char *amopopr;
14496 char *sortfamily;
14497 char *sortfamilynsp;
14498 char *amprocnum;
14499 char *amproc;
14500 char *amproclefttype;
14501 char *amprocrighttype;
14502 bool needComma;
14503 int i;
14504
14505 /* Do nothing if not dumping schema */
14506 if (!dopt->dumpSchema)
14507 return;
14508
14509 query = createPQExpBuffer();
14510 q = createPQExpBuffer();
14513
14514 /* Get additional fields from the pg_opclass row */
14515 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14516 "opckeytype::pg_catalog.regtype, "
14517 "opcdefault, opcfamily, "
14518 "opfname AS opcfamilyname, "
14519 "nspname AS opcfamilynsp, "
14520 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14521 "FROM pg_catalog.pg_opclass c "
14522 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14523 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14524 "WHERE c.oid = '%u'::pg_catalog.oid",
14525 opcinfo->dobj.catId.oid);
14526
14527 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14528
14529 i_opcintype = PQfnumber(res, "opcintype");
14530 i_opckeytype = PQfnumber(res, "opckeytype");
14531 i_opcdefault = PQfnumber(res, "opcdefault");
14532 i_opcfamily = PQfnumber(res, "opcfamily");
14533 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14534 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14535 i_amname = PQfnumber(res, "amname");
14536
14537 /* opcintype may still be needed after we PQclear res */
14538 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14541 /* opcfamily will still be needed after we PQclear res */
14542 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14545 /* amname will still be needed after we PQclear res */
14546 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14547
14548 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14550 appendPQExpBuffer(delq, " USING %s;\n",
14551 fmtId(amname));
14552
14553 /* Build the fixed portion of the CREATE command */
14554 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14556 if (strcmp(opcdefault, "t") == 0)
14557 appendPQExpBufferStr(q, "DEFAULT ");
14558 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14559 opcintype,
14560 fmtId(amname));
14561 if (strlen(opcfamilyname) > 0)
14562 {
14563 appendPQExpBufferStr(q, " FAMILY ");
14566 }
14567 appendPQExpBufferStr(q, " AS\n ");
14568
14569 needComma = false;
14570
14571 if (strcmp(opckeytype, "-") != 0)
14572 {
14573 appendPQExpBuffer(q, "STORAGE %s",
14574 opckeytype);
14575 needComma = true;
14576 }
14577
14578 PQclear(res);
14579
14580 /*
14581 * Now fetch and print the OPERATOR entries (pg_amop rows).
14582 *
14583 * Print only those opfamily members that are tied to the opclass by
14584 * pg_depend entries.
14585 */
14586 resetPQExpBuffer(query);
14587 appendPQExpBuffer(query, "SELECT amopstrategy, "
14588 "amopopr::pg_catalog.regoperator, "
14589 "opfname AS sortfamily, "
14590 "nspname AS sortfamilynsp "
14591 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14592 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14593 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14594 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14595 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14596 "AND refobjid = '%u'::pg_catalog.oid "
14597 "AND amopfamily = '%s'::pg_catalog.oid "
14598 "ORDER BY amopstrategy",
14599 opcinfo->dobj.catId.oid,
14600 opcfamily);
14601
14602 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14603
14604 ntups = PQntuples(res);
14605
14606 i_amopstrategy = PQfnumber(res, "amopstrategy");
14607 i_amopopr = PQfnumber(res, "amopopr");
14608 i_sortfamily = PQfnumber(res, "sortfamily");
14609 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14610
14611 for (i = 0; i < ntups; i++)
14612 {
14614 amopopr = PQgetvalue(res, i, i_amopopr);
14615 sortfamily = PQgetvalue(res, i, i_sortfamily);
14617
14618 if (needComma)
14619 appendPQExpBufferStr(q, " ,\n ");
14620
14621 appendPQExpBuffer(q, "OPERATOR %s %s",
14623
14624 if (strlen(sortfamily) > 0)
14625 {
14626 appendPQExpBufferStr(q, " FOR ORDER BY ");
14628 appendPQExpBufferStr(q, fmtId(sortfamily));
14629 }
14630
14631 needComma = true;
14632 }
14633
14634 PQclear(res);
14635
14636 /*
14637 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14638 *
14639 * Print only those opfamily members that are tied to the opclass by
14640 * pg_depend entries.
14641 *
14642 * We print the amproclefttype/amprocrighttype even though in most cases
14643 * the backend could deduce the right values, because of the corner case
14644 * of a btree sort support function for a cross-type comparison.
14645 */
14646 resetPQExpBuffer(query);
14647
14648 appendPQExpBuffer(query, "SELECT amprocnum, "
14649 "amproc::pg_catalog.regprocedure, "
14650 "amproclefttype::pg_catalog.regtype, "
14651 "amprocrighttype::pg_catalog.regtype "
14652 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14653 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14654 "AND refobjid = '%u'::pg_catalog.oid "
14655 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14656 "AND objid = ap.oid "
14657 "ORDER BY amprocnum",
14658 opcinfo->dobj.catId.oid);
14659
14660 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14661
14662 ntups = PQntuples(res);
14663
14664 i_amprocnum = PQfnumber(res, "amprocnum");
14665 i_amproc = PQfnumber(res, "amproc");
14666 i_amproclefttype = PQfnumber(res, "amproclefttype");
14667 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14668
14669 for (i = 0; i < ntups; i++)
14670 {
14672 amproc = PQgetvalue(res, i, i_amproc);
14675
14676 if (needComma)
14677 appendPQExpBufferStr(q, " ,\n ");
14678
14679 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14680
14683
14684 appendPQExpBuffer(q, " %s", amproc);
14685
14686 needComma = true;
14687 }
14688
14689 PQclear(res);
14690
14691 /*
14692 * If needComma is still false it means we haven't added anything after
14693 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14694 * clause with the same datatype. This isn't sanctioned by the
14695 * documentation, but actually DefineOpClass will treat it as a no-op.
14696 */
14697 if (!needComma)
14698 appendPQExpBuffer(q, "STORAGE %s", opcintype);
14699
14700 appendPQExpBufferStr(q, ";\n");
14701
14703 appendPQExpBuffer(nameusing, " USING %s",
14704 fmtId(amname));
14705
14706 if (dopt->binary_upgrade)
14708 "OPERATOR CLASS", nameusing->data,
14709 opcinfo->dobj.namespace->dobj.name);
14710
14711 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14712 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14713 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14714 .namespace = opcinfo->dobj.namespace->dobj.name,
14715 .owner = opcinfo->rolname,
14716 .description = "OPERATOR CLASS",
14717 .section = SECTION_PRE_DATA,
14718 .createStmt = q->data,
14719 .dropStmt = delq->data));
14720
14721 /* Dump Operator Class Comments */
14722 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14723 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14724 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14725 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14726
14727 pg_free(opcintype);
14728 pg_free(opcfamily);
14729 pg_free(amname);
14730 destroyPQExpBuffer(query);
14734}
14735
14736/*
14737 * dumpOpfamily
14738 * write out a single operator family definition
14739 *
14740 * Note: this also dumps any "loose" operator members that aren't bound to a
14741 * specific opclass within the opfamily.
14742 */
14743static void
14745{
14746 DumpOptions *dopt = fout->dopt;
14747 PQExpBuffer query;
14748 PQExpBuffer q;
14751 PGresult *res;
14754 int ntups;
14755 int i_amname;
14756 int i_amopstrategy;
14757 int i_amopopr;
14758 int i_sortfamily;
14759 int i_sortfamilynsp;
14760 int i_amprocnum;
14761 int i_amproc;
14762 int i_amproclefttype;
14764 char *amname;
14765 char *amopstrategy;
14766 char *amopopr;
14767 char *sortfamily;
14768 char *sortfamilynsp;
14769 char *amprocnum;
14770 char *amproc;
14771 char *amproclefttype;
14772 char *amprocrighttype;
14773 bool needComma;
14774 int i;
14775
14776 /* Do nothing if not dumping schema */
14777 if (!dopt->dumpSchema)
14778 return;
14779
14780 query = createPQExpBuffer();
14781 q = createPQExpBuffer();
14784
14785 /*
14786 * Fetch only those opfamily members that are tied directly to the
14787 * opfamily by pg_depend entries.
14788 */
14789 appendPQExpBuffer(query, "SELECT amopstrategy, "
14790 "amopopr::pg_catalog.regoperator, "
14791 "opfname AS sortfamily, "
14792 "nspname AS sortfamilynsp "
14793 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14794 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14795 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14796 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14797 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14798 "AND refobjid = '%u'::pg_catalog.oid "
14799 "AND amopfamily = '%u'::pg_catalog.oid "
14800 "ORDER BY amopstrategy",
14801 opfinfo->dobj.catId.oid,
14802 opfinfo->dobj.catId.oid);
14803
14805
14806 resetPQExpBuffer(query);
14807
14808 appendPQExpBuffer(query, "SELECT amprocnum, "
14809 "amproc::pg_catalog.regprocedure, "
14810 "amproclefttype::pg_catalog.regtype, "
14811 "amprocrighttype::pg_catalog.regtype "
14812 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14813 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14814 "AND refobjid = '%u'::pg_catalog.oid "
14815 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14816 "AND objid = ap.oid "
14817 "ORDER BY amprocnum",
14818 opfinfo->dobj.catId.oid);
14819
14821
14822 /* Get additional fields from the pg_opfamily row */
14823 resetPQExpBuffer(query);
14824
14825 appendPQExpBuffer(query, "SELECT "
14826 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14827 "FROM pg_catalog.pg_opfamily "
14828 "WHERE oid = '%u'::pg_catalog.oid",
14829 opfinfo->dobj.catId.oid);
14830
14831 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14832
14833 i_amname = PQfnumber(res, "amname");
14834
14835 /* amname will still be needed after we PQclear res */
14836 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14837
14838 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14840 appendPQExpBuffer(delq, " USING %s;\n",
14841 fmtId(amname));
14842
14843 /* Build the fixed portion of the CREATE command */
14844 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14846 appendPQExpBuffer(q, " USING %s;\n",
14847 fmtId(amname));
14848
14849 PQclear(res);
14850
14851 /* Do we need an ALTER to add loose members? */
14852 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14853 {
14854 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14856 appendPQExpBuffer(q, " USING %s ADD\n ",
14857 fmtId(amname));
14858
14859 needComma = false;
14860
14861 /*
14862 * Now fetch and print the OPERATOR entries (pg_amop rows).
14863 */
14864 ntups = PQntuples(res_ops);
14865
14866 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14867 i_amopopr = PQfnumber(res_ops, "amopopr");
14868 i_sortfamily = PQfnumber(res_ops, "sortfamily");
14869 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14870
14871 for (i = 0; i < ntups; i++)
14872 {
14875 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14877
14878 if (needComma)
14879 appendPQExpBufferStr(q, " ,\n ");
14880
14881 appendPQExpBuffer(q, "OPERATOR %s %s",
14883
14884 if (strlen(sortfamily) > 0)
14885 {
14886 appendPQExpBufferStr(q, " FOR ORDER BY ");
14888 appendPQExpBufferStr(q, fmtId(sortfamily));
14889 }
14890
14891 needComma = true;
14892 }
14893
14894 /*
14895 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14896 */
14897 ntups = PQntuples(res_procs);
14898
14899 i_amprocnum = PQfnumber(res_procs, "amprocnum");
14900 i_amproc = PQfnumber(res_procs, "amproc");
14901 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14902 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14903
14904 for (i = 0; i < ntups; i++)
14905 {
14910
14911 if (needComma)
14912 appendPQExpBufferStr(q, " ,\n ");
14913
14914 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14916 amproc);
14917
14918 needComma = true;
14919 }
14920
14921 appendPQExpBufferStr(q, ";\n");
14922 }
14923
14925 appendPQExpBuffer(nameusing, " USING %s",
14926 fmtId(amname));
14927
14928 if (dopt->binary_upgrade)
14930 "OPERATOR FAMILY", nameusing->data,
14931 opfinfo->dobj.namespace->dobj.name);
14932
14933 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14934 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14935 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14936 .namespace = opfinfo->dobj.namespace->dobj.name,
14937 .owner = opfinfo->rolname,
14938 .description = "OPERATOR FAMILY",
14939 .section = SECTION_PRE_DATA,
14940 .createStmt = q->data,
14941 .dropStmt = delq->data));
14942
14943 /* Dump Operator Family Comments */
14944 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14945 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14946 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14947 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14948
14949 pg_free(amname);
14952 destroyPQExpBuffer(query);
14956}
14957
14958/*
14959 * dumpCollation
14960 * write out a single collation definition
14961 */
14962static void
14964{
14965 DumpOptions *dopt = fout->dopt;
14966 PQExpBuffer query;
14967 PQExpBuffer q;
14969 char *qcollname;
14970 PGresult *res;
14971 int i_collprovider;
14973 int i_collcollate;
14974 int i_collctype;
14975 int i_colllocale;
14976 int i_collicurules;
14977 const char *collprovider;
14978 const char *collcollate;
14979 const char *collctype;
14980 const char *colllocale;
14981 const char *collicurules;
14982
14983 /* Do nothing if not dumping schema */
14984 if (!dopt->dumpSchema)
14985 return;
14986
14987 query = createPQExpBuffer();
14988 q = createPQExpBuffer();
14990
14991 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14992
14993 /* Get collation-specific details */
14994 appendPQExpBufferStr(query, "SELECT ");
14995
14997 "collprovider, "
14998 "collversion, ");
14999
15000 if (fout->remoteVersion >= 120000)
15002 "collisdeterministic, ");
15003 else
15005 "true AS collisdeterministic, ");
15006
15007 if (fout->remoteVersion >= 170000)
15009 "colllocale, ");
15010 else if (fout->remoteVersion >= 150000)
15012 "colliculocale AS colllocale, ");
15013 else
15015 "NULL AS colllocale, ");
15016
15017 if (fout->remoteVersion >= 160000)
15019 "collicurules, ");
15020 else
15022 "NULL AS collicurules, ");
15023
15024 appendPQExpBuffer(query,
15025 "collcollate, "
15026 "collctype "
15027 "FROM pg_catalog.pg_collation c "
15028 "WHERE c.oid = '%u'::pg_catalog.oid",
15029 collinfo->dobj.catId.oid);
15030
15031 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15032
15033 i_collprovider = PQfnumber(res, "collprovider");
15034 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15035 i_collcollate = PQfnumber(res, "collcollate");
15036 i_collctype = PQfnumber(res, "collctype");
15037 i_colllocale = PQfnumber(res, "colllocale");
15038 i_collicurules = PQfnumber(res, "collicurules");
15039
15041
15042 if (!PQgetisnull(res, 0, i_collcollate))
15044 else
15045 collcollate = NULL;
15046
15047 if (!PQgetisnull(res, 0, i_collctype))
15048 collctype = PQgetvalue(res, 0, i_collctype);
15049 else
15050 collctype = NULL;
15051
15052 /*
15053 * Before version 15, collcollate and collctype were of type NAME and
15054 * non-nullable. Treat empty strings as NULL for consistency.
15055 */
15056 if (fout->remoteVersion < 150000)
15057 {
15058 if (collcollate[0] == '\0')
15059 collcollate = NULL;
15060 if (collctype[0] == '\0')
15061 collctype = NULL;
15062 }
15063
15064 if (!PQgetisnull(res, 0, i_colllocale))
15066 else
15067 colllocale = NULL;
15068
15069 if (!PQgetisnull(res, 0, i_collicurules))
15071 else
15073
15074 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15076
15077 appendPQExpBuffer(q, "CREATE COLLATION %s (",
15079
15080 appendPQExpBufferStr(q, "provider = ");
15081 if (collprovider[0] == 'b')
15082 appendPQExpBufferStr(q, "builtin");
15083 else if (collprovider[0] == 'c')
15084 appendPQExpBufferStr(q, "libc");
15085 else if (collprovider[0] == 'i')
15086 appendPQExpBufferStr(q, "icu");
15087 else if (collprovider[0] == 'd')
15088 /* to allow dumping pg_catalog; not accepted on input */
15089 appendPQExpBufferStr(q, "default");
15090 else
15091 pg_fatal("unrecognized collation provider: %s",
15092 collprovider);
15093
15094 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15095 appendPQExpBufferStr(q, ", deterministic = false");
15096
15097 if (collprovider[0] == 'd')
15098 {
15100 pg_log_warning("invalid collation \"%s\"", qcollname);
15101
15102 /* no locale -- the default collation cannot be reloaded anyway */
15103 }
15104 else if (collprovider[0] == 'b')
15105 {
15107 pg_log_warning("invalid collation \"%s\"", qcollname);
15108
15109 appendPQExpBufferStr(q, ", locale = ");
15111 fout);
15112 }
15113 else if (collprovider[0] == 'i')
15114 {
15115 if (fout->remoteVersion >= 150000)
15116 {
15117 if (collcollate || collctype || !colllocale)
15118 pg_log_warning("invalid collation \"%s\"", qcollname);
15119
15120 appendPQExpBufferStr(q, ", locale = ");
15122 fout);
15123 }
15124 else
15125 {
15126 if (!collcollate || !collctype || colllocale ||
15128 pg_log_warning("invalid collation \"%s\"", qcollname);
15129
15130 appendPQExpBufferStr(q, ", locale = ");
15132 }
15133
15134 if (collicurules)
15135 {
15136 appendPQExpBufferStr(q, ", rules = ");
15138 }
15139 }
15140 else if (collprovider[0] == 'c')
15141 {
15143 pg_log_warning("invalid collation \"%s\"", qcollname);
15144
15146 {
15147 appendPQExpBufferStr(q, ", locale = ");
15149 }
15150 else
15151 {
15152 appendPQExpBufferStr(q, ", lc_collate = ");
15154 appendPQExpBufferStr(q, ", lc_ctype = ");
15156 }
15157 }
15158 else
15159 pg_fatal("unrecognized collation provider: %s", collprovider);
15160
15161 /*
15162 * For binary upgrade, carry over the collation version. For normal
15163 * dump/restore, omit the version, so that it is computed upon restore.
15164 */
15165 if (dopt->binary_upgrade)
15166 {
15167 int i_collversion;
15168
15169 i_collversion = PQfnumber(res, "collversion");
15170 if (!PQgetisnull(res, 0, i_collversion))
15171 {
15172 appendPQExpBufferStr(q, ", version = ");
15174 PQgetvalue(res, 0, i_collversion),
15175 fout);
15176 }
15177 }
15178
15179 appendPQExpBufferStr(q, ");\n");
15180
15181 if (dopt->binary_upgrade)
15183 "COLLATION", qcollname,
15184 collinfo->dobj.namespace->dobj.name);
15185
15186 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15187 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15188 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15189 .namespace = collinfo->dobj.namespace->dobj.name,
15190 .owner = collinfo->rolname,
15191 .description = "COLLATION",
15192 .section = SECTION_PRE_DATA,
15193 .createStmt = q->data,
15194 .dropStmt = delq->data));
15195
15196 /* Dump Collation Comments */
15197 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15198 dumpComment(fout, "COLLATION", qcollname,
15199 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15200 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15201
15202 PQclear(res);
15203
15204 destroyPQExpBuffer(query);
15208}
15209
15210/*
15211 * dumpConversion
15212 * write out a single conversion definition
15213 */
15214static void
15216{
15217 DumpOptions *dopt = fout->dopt;
15218 PQExpBuffer query;
15219 PQExpBuffer q;
15221 char *qconvname;
15222 PGresult *res;
15223 int i_conforencoding;
15224 int i_contoencoding;
15225 int i_conproc;
15226 int i_condefault;
15227 const char *conforencoding;
15228 const char *contoencoding;
15229 const char *conproc;
15230 bool condefault;
15231
15232 /* Do nothing if not dumping schema */
15233 if (!dopt->dumpSchema)
15234 return;
15235
15236 query = createPQExpBuffer();
15237 q = createPQExpBuffer();
15239
15240 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15241
15242 /* Get conversion-specific details */
15243 appendPQExpBuffer(query, "SELECT "
15244 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15245 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15246 "conproc, condefault "
15247 "FROM pg_catalog.pg_conversion c "
15248 "WHERE c.oid = '%u'::pg_catalog.oid",
15249 convinfo->dobj.catId.oid);
15250
15251 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15252
15253 i_conforencoding = PQfnumber(res, "conforencoding");
15254 i_contoencoding = PQfnumber(res, "contoencoding");
15255 i_conproc = PQfnumber(res, "conproc");
15256 i_condefault = PQfnumber(res, "condefault");
15257
15260 conproc = PQgetvalue(res, 0, i_conproc);
15261 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15262
15263 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15265
15266 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15267 (condefault) ? "DEFAULT " : "",
15270 appendPQExpBufferStr(q, " TO ");
15272 /* regproc output is already sufficiently quoted */
15273 appendPQExpBuffer(q, " FROM %s;\n", conproc);
15274
15275 if (dopt->binary_upgrade)
15277 "CONVERSION", qconvname,
15278 convinfo->dobj.namespace->dobj.name);
15279
15280 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15281 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15282 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15283 .namespace = convinfo->dobj.namespace->dobj.name,
15284 .owner = convinfo->rolname,
15285 .description = "CONVERSION",
15286 .section = SECTION_PRE_DATA,
15287 .createStmt = q->data,
15288 .dropStmt = delq->data));
15289
15290 /* Dump Conversion Comments */
15291 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15292 dumpComment(fout, "CONVERSION", qconvname,
15293 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15294 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15295
15296 PQclear(res);
15297
15298 destroyPQExpBuffer(query);
15302}
15303
15304/*
15305 * format_aggregate_signature: generate aggregate name and argument list
15306 *
15307 * The argument type names are qualified if needed. The aggregate name
15308 * is never qualified.
15309 */
15310static char *
15312{
15314 int j;
15315
15317 if (honor_quotes)
15318 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15319 else
15320 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15321
15322 if (agginfo->aggfn.nargs == 0)
15323 appendPQExpBufferStr(&buf, "(*)");
15324 else
15325 {
15327 for (j = 0; j < agginfo->aggfn.nargs; j++)
15328 appendPQExpBuffer(&buf, "%s%s",
15329 (j > 0) ? ", " : "",
15331 agginfo->aggfn.argtypes[j],
15332 zeroIsError));
15334 }
15335 return buf.data;
15336}
15337
15338/*
15339 * dumpAgg
15340 * write out a single aggregate definition
15341 */
15342static void
15344{
15345 DumpOptions *dopt = fout->dopt;
15346 PQExpBuffer query;
15347 PQExpBuffer q;
15349 PQExpBuffer details;
15350 char *aggsig; /* identity signature */
15351 char *aggfullsig = NULL; /* full signature */
15352 char *aggsig_tag;
15353 PGresult *res;
15354 int i_agginitval;
15355 int i_aggminitval;
15356 const char *aggtransfn;
15357 const char *aggfinalfn;
15358 const char *aggcombinefn;
15359 const char *aggserialfn;
15360 const char *aggdeserialfn;
15361 const char *aggmtransfn;
15362 const char *aggminvtransfn;
15363 const char *aggmfinalfn;
15364 bool aggfinalextra;
15365 bool aggmfinalextra;
15366 char aggfinalmodify;
15367 char aggmfinalmodify;
15368 const char *aggsortop;
15369 char *aggsortconvop;
15370 char aggkind;
15371 const char *aggtranstype;
15372 const char *aggtransspace;
15373 const char *aggmtranstype;
15374 const char *aggmtransspace;
15375 const char *agginitval;
15376 const char *aggminitval;
15377 const char *proparallel;
15378 char defaultfinalmodify;
15379
15380 /* Do nothing if not dumping schema */
15381 if (!dopt->dumpSchema)
15382 return;
15383
15384 query = createPQExpBuffer();
15385 q = createPQExpBuffer();
15387 details = createPQExpBuffer();
15388
15389 if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15390 {
15391 /* Set up query for aggregate-specific details */
15393 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15394
15396 "SELECT "
15397 "aggtransfn,\n"
15398 "aggfinalfn,\n"
15399 "aggtranstype::pg_catalog.regtype,\n"
15400 "agginitval,\n"
15401 "aggsortop,\n"
15402 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15403 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15404
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
15418 "aggcombinefn,\n"
15419 "aggserialfn,\n"
15420 "aggdeserialfn,\n"
15421 "proparallel,\n");
15422
15423 if (fout->remoteVersion >= 110000)
15425 "aggfinalmodify,\n"
15426 "aggmfinalmodify\n");
15427 else
15429 "'0' AS aggfinalmodify,\n"
15430 "'0' AS aggmfinalmodify\n");
15431
15433 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15434 "WHERE a.aggfnoid = p.oid "
15435 "AND p.oid = $1");
15436
15437 ExecuteSqlStatement(fout, query->data);
15438
15439 fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15440 }
15441
15442 printfPQExpBuffer(query,
15443 "EXECUTE dumpAgg('%u')",
15444 agginfo->aggfn.dobj.catId.oid);
15445
15446 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15447
15448 i_agginitval = PQfnumber(res, "agginitval");
15449 i_aggminitval = PQfnumber(res, "aggminitval");
15450
15451 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15452 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15453 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15454 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15455 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15456 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15457 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15458 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15459 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15460 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15461 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15462 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15463 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15464 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15465 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15466 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15467 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15468 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15471 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15472
15473 {
15474 char *funcargs;
15475 char *funciargs;
15476
15477 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15478 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15481 }
15482
15484
15485 /* identify default modify flag for aggkind (must match DefineAggregate) */
15487 /* replace omitted flags for old versions */
15488 if (aggfinalmodify == '0')
15490 if (aggmfinalmodify == '0')
15492
15493 /* regproc and regtype output is already sufficiently quoted */
15494 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15495 aggtransfn, aggtranstype);
15496
15497 if (strcmp(aggtransspace, "0") != 0)
15498 {
15499 appendPQExpBuffer(details, ",\n SSPACE = %s",
15500 aggtransspace);
15501 }
15502
15503 if (!PQgetisnull(res, 0, i_agginitval))
15504 {
15505 appendPQExpBufferStr(details, ",\n INITCOND = ");
15507 }
15508
15509 if (strcmp(aggfinalfn, "-") != 0)
15510 {
15511 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15512 aggfinalfn);
15513 if (aggfinalextra)
15514 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15516 {
15517 switch (aggfinalmodify)
15518 {
15520 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15521 break;
15523 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15524 break;
15526 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15527 break;
15528 default:
15529 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15530 agginfo->aggfn.dobj.name);
15531 break;
15532 }
15533 }
15534 }
15535
15536 if (strcmp(aggcombinefn, "-") != 0)
15537 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15538
15539 if (strcmp(aggserialfn, "-") != 0)
15540 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15541
15542 if (strcmp(aggdeserialfn, "-") != 0)
15543 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15544
15545 if (strcmp(aggmtransfn, "-") != 0)
15546 {
15547 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15551 }
15552
15553 if (strcmp(aggmtransspace, "0") != 0)
15554 {
15555 appendPQExpBuffer(details, ",\n MSSPACE = %s",
15557 }
15558
15559 if (!PQgetisnull(res, 0, i_aggminitval))
15560 {
15561 appendPQExpBufferStr(details, ",\n MINITCOND = ");
15563 }
15564
15565 if (strcmp(aggmfinalfn, "-") != 0)
15566 {
15567 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15568 aggmfinalfn);
15569 if (aggmfinalextra)
15570 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15572 {
15573 switch (aggmfinalmodify)
15574 {
15576 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15577 break;
15579 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15580 break;
15582 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15583 break;
15584 default:
15585 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15586 agginfo->aggfn.dobj.name);
15587 break;
15588 }
15589 }
15590 }
15591
15593 if (aggsortconvop)
15594 {
15595 appendPQExpBuffer(details, ",\n SORTOP = %s",
15598 }
15599
15601 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15602
15604 {
15605 if (proparallel[0] == PROPARALLEL_SAFE)
15606 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15607 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15608 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15609 else if (proparallel[0] != PROPARALLEL_UNSAFE)
15610 pg_fatal("unrecognized proparallel value for function \"%s\"",
15611 agginfo->aggfn.dobj.name);
15612 }
15613
15614 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15615 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15616 aggsig);
15617
15618 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15619 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15620 aggfullsig ? aggfullsig : aggsig, details->data);
15621
15622 if (dopt->binary_upgrade)
15624 "AGGREGATE", aggsig,
15625 agginfo->aggfn.dobj.namespace->dobj.name);
15626
15627 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15628 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15629 agginfo->aggfn.dobj.dumpId,
15630 ARCHIVE_OPTS(.tag = aggsig_tag,
15631 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15632 .owner = agginfo->aggfn.rolname,
15633 .description = "AGGREGATE",
15634 .section = SECTION_PRE_DATA,
15635 .createStmt = q->data,
15636 .dropStmt = delq->data));
15637
15638 /* Dump Aggregate Comments */
15639 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15640 dumpComment(fout, "AGGREGATE", aggsig,
15641 agginfo->aggfn.dobj.namespace->dobj.name,
15642 agginfo->aggfn.rolname,
15643 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15644
15645 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15646 dumpSecLabel(fout, "AGGREGATE", aggsig,
15647 agginfo->aggfn.dobj.namespace->dobj.name,
15648 agginfo->aggfn.rolname,
15649 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15650
15651 /*
15652 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15653 * command look like a function's GRANT; in particular this affects the
15654 * syntax for zero-argument aggregates and ordered-set aggregates.
15655 */
15656 free(aggsig);
15657
15658 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15659
15660 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15661 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15662 "FUNCTION", aggsig, NULL,
15663 agginfo->aggfn.dobj.namespace->dobj.name,
15664 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15665
15666 free(aggsig);
15669
15670 PQclear(res);
15671
15672 destroyPQExpBuffer(query);
15675 destroyPQExpBuffer(details);
15676}
15677
15678/*
15679 * dumpTSParser
15680 * write out a single text search parser
15681 */
15682static void
15684{
15685 DumpOptions *dopt = fout->dopt;
15686 PQExpBuffer q;
15688 char *qprsname;
15689
15690 /* Do nothing if not dumping schema */
15691 if (!dopt->dumpSchema)
15692 return;
15693
15694 q = createPQExpBuffer();
15696
15697 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15698
15699 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15701
15702 appendPQExpBuffer(q, " START = %s,\n",
15703 convertTSFunction(fout, prsinfo->prsstart));
15704 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15705 convertTSFunction(fout, prsinfo->prstoken));
15706 appendPQExpBuffer(q, " END = %s,\n",
15707 convertTSFunction(fout, prsinfo->prsend));
15708 if (prsinfo->prsheadline != InvalidOid)
15709 appendPQExpBuffer(q, " HEADLINE = %s,\n",
15710 convertTSFunction(fout, prsinfo->prsheadline));
15711 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15712 convertTSFunction(fout, prsinfo->prslextype));
15713
15714 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15716
15717 if (dopt->binary_upgrade)
15719 "TEXT SEARCH PARSER", qprsname,
15720 prsinfo->dobj.namespace->dobj.name);
15721
15722 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15723 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15724 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15725 .namespace = prsinfo->dobj.namespace->dobj.name,
15726 .description = "TEXT SEARCH PARSER",
15727 .section = SECTION_PRE_DATA,
15728 .createStmt = q->data,
15729 .dropStmt = delq->data));
15730
15731 /* Dump Parser Comments */
15732 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15733 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15734 prsinfo->dobj.namespace->dobj.name, "",
15735 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15736
15740}
15741
15742/*
15743 * dumpTSDictionary
15744 * write out a single text search dictionary
15745 */
15746static void
15748{
15749 DumpOptions *dopt = fout->dopt;
15750 PQExpBuffer q;
15752 PQExpBuffer query;
15753 char *qdictname;
15754 PGresult *res;
15755 char *nspname;
15756 char *tmplname;
15757
15758 /* Do nothing if not dumping schema */
15759 if (!dopt->dumpSchema)
15760 return;
15761
15762 q = createPQExpBuffer();
15764 query = createPQExpBuffer();
15765
15766 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15767
15768 /* Fetch name and namespace of the dictionary's template */
15769 appendPQExpBuffer(query, "SELECT nspname, tmplname "
15770 "FROM pg_ts_template p, pg_namespace n "
15771 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15772 dictinfo->dicttemplate);
15773 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15774 nspname = PQgetvalue(res, 0, 0);
15775 tmplname = PQgetvalue(res, 0, 1);
15776
15777 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15779
15780 appendPQExpBufferStr(q, " TEMPLATE = ");
15781 appendPQExpBuffer(q, "%s.", fmtId(nspname));
15783
15784 PQclear(res);
15785
15786 /* the dictinitoption can be dumped straight into the command */
15787 if (dictinfo->dictinitoption)
15788 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15789
15790 appendPQExpBufferStr(q, " );\n");
15791
15792 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15794
15795 if (dopt->binary_upgrade)
15797 "TEXT SEARCH DICTIONARY", qdictname,
15798 dictinfo->dobj.namespace->dobj.name);
15799
15800 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15801 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15802 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15803 .namespace = dictinfo->dobj.namespace->dobj.name,
15804 .owner = dictinfo->rolname,
15805 .description = "TEXT SEARCH DICTIONARY",
15806 .section = SECTION_PRE_DATA,
15807 .createStmt = q->data,
15808 .dropStmt = delq->data));
15809
15810 /* Dump Dictionary Comments */
15811 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15812 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15813 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15814 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15815
15818 destroyPQExpBuffer(query);
15820}
15821
15822/*
15823 * dumpTSTemplate
15824 * write out a single text search template
15825 */
15826static void
15828{
15829 DumpOptions *dopt = fout->dopt;
15830 PQExpBuffer q;
15832 char *qtmplname;
15833
15834 /* Do nothing if not dumping schema */
15835 if (!dopt->dumpSchema)
15836 return;
15837
15838 q = createPQExpBuffer();
15840
15841 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15842
15843 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15845
15846 if (tmplinfo->tmplinit != InvalidOid)
15847 appendPQExpBuffer(q, " INIT = %s,\n",
15848 convertTSFunction(fout, tmplinfo->tmplinit));
15849 appendPQExpBuffer(q, " LEXIZE = %s );\n",
15850 convertTSFunction(fout, tmplinfo->tmpllexize));
15851
15852 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15854
15855 if (dopt->binary_upgrade)
15857 "TEXT SEARCH TEMPLATE", qtmplname,
15858 tmplinfo->dobj.namespace->dobj.name);
15859
15860 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15861 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15862 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15863 .namespace = tmplinfo->dobj.namespace->dobj.name,
15864 .description = "TEXT SEARCH TEMPLATE",
15865 .section = SECTION_PRE_DATA,
15866 .createStmt = q->data,
15867 .dropStmt = delq->data));
15868
15869 /* Dump Template Comments */
15870 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15871 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15872 tmplinfo->dobj.namespace->dobj.name, "",
15873 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15874
15878}
15879
15880/*
15881 * dumpTSConfig
15882 * write out a single text search configuration
15883 */
15884static void
15886{
15887 DumpOptions *dopt = fout->dopt;
15888 PQExpBuffer q;
15890 PQExpBuffer query;
15891 char *qcfgname;
15892 PGresult *res;
15893 char *nspname;
15894 char *prsname;
15895 int ntups,
15896 i;
15897 int i_tokenname;
15898 int i_dictname;
15899
15900 /* Do nothing if not dumping schema */
15901 if (!dopt->dumpSchema)
15902 return;
15903
15904 q = createPQExpBuffer();
15906 query = createPQExpBuffer();
15907
15908 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15909
15910 /* Fetch name and namespace of the config's parser */
15911 appendPQExpBuffer(query, "SELECT nspname, prsname "
15912 "FROM pg_ts_parser p, pg_namespace n "
15913 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15914 cfginfo->cfgparser);
15915 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15916 nspname = PQgetvalue(res, 0, 0);
15917 prsname = PQgetvalue(res, 0, 1);
15918
15919 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15921
15922 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15923 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15924
15925 PQclear(res);
15926
15927 resetPQExpBuffer(query);
15928 appendPQExpBuffer(query,
15929 "SELECT\n"
15930 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15931 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15932 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15933 "FROM pg_catalog.pg_ts_config_map AS m\n"
15934 "WHERE m.mapcfg = '%u'\n"
15935 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15936 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15937
15938 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15939 ntups = PQntuples(res);
15940
15941 i_tokenname = PQfnumber(res, "tokenname");
15942 i_dictname = PQfnumber(res, "dictname");
15943
15944 for (i = 0; i < ntups; i++)
15945 {
15946 char *tokenname = PQgetvalue(res, i, i_tokenname);
15947 char *dictname = PQgetvalue(res, i, i_dictname);
15948
15949 if (i == 0 ||
15950 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15951 {
15952 /* starting a new token type, so start a new command */
15953 if (i > 0)
15954 appendPQExpBufferStr(q, ";\n");
15955 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15957 /* tokenname needs quoting, dictname does NOT */
15958 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15959 fmtId(tokenname), dictname);
15960 }
15961 else
15962 appendPQExpBuffer(q, ", %s", dictname);
15963 }
15964
15965 if (ntups > 0)
15966 appendPQExpBufferStr(q, ";\n");
15967
15968 PQclear(res);
15969
15970 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15972
15973 if (dopt->binary_upgrade)
15975 "TEXT SEARCH CONFIGURATION", qcfgname,
15976 cfginfo->dobj.namespace->dobj.name);
15977
15978 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15979 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15980 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15981 .namespace = cfginfo->dobj.namespace->dobj.name,
15982 .owner = cfginfo->rolname,
15983 .description = "TEXT SEARCH CONFIGURATION",
15984 .section = SECTION_PRE_DATA,
15985 .createStmt = q->data,
15986 .dropStmt = delq->data));
15987
15988 /* Dump Configuration Comments */
15989 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15990 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15991 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15992 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15993
15996 destroyPQExpBuffer(query);
15998}
15999
16000/*
16001 * dumpForeignDataWrapper
16002 * write out a single foreign-data wrapper definition
16003 */
16004static void
16006{
16007 DumpOptions *dopt = fout->dopt;
16008 PQExpBuffer q;
16010 char *qfdwname;
16011
16012 /* Do nothing if not dumping schema */
16013 if (!dopt->dumpSchema)
16014 return;
16015
16016 q = createPQExpBuffer();
16018
16019 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16020
16021 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16022 qfdwname);
16023
16024 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16025 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16026
16027 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16028 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16029
16030 if (strcmp(fdwinfo->fdwconnection, "-") != 0)
16031 appendPQExpBuffer(q, " CONNECTION %s", fdwinfo->fdwconnection);
16032
16033 if (strlen(fdwinfo->fdwoptions) > 0)
16034 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16035
16036 appendPQExpBufferStr(q, ";\n");
16037
16038 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16039 qfdwname);
16040
16041 if (dopt->binary_upgrade)
16043 "FOREIGN DATA WRAPPER", qfdwname,
16044 NULL);
16045
16046 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16047 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16048 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16049 .owner = fdwinfo->rolname,
16050 .description = "FOREIGN DATA WRAPPER",
16051 .section = SECTION_PRE_DATA,
16052 .createStmt = q->data,
16053 .dropStmt = delq->data));
16054
16055 /* Dump Foreign Data Wrapper Comments */
16056 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16057 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16058 NULL, fdwinfo->rolname,
16059 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16060
16061 /* Handle the ACL */
16062 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16063 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16064 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16065 NULL, fdwinfo->rolname, &fdwinfo->dacl);
16066
16068
16071}
16072
16073/*
16074 * dumpForeignServer
16075 * write out a foreign server definition
16076 */
16077static void
16079{
16080 DumpOptions *dopt = fout->dopt;
16081 PQExpBuffer q;
16083 PQExpBuffer query;
16084 PGresult *res;
16085 char *qsrvname;
16086 char *fdwname;
16087
16088 /* Do nothing if not dumping schema */
16089 if (!dopt->dumpSchema)
16090 return;
16091
16092 q = createPQExpBuffer();
16094 query = createPQExpBuffer();
16095
16096 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16097
16098 /* look up the foreign-data wrapper */
16099 appendPQExpBuffer(query, "SELECT fdwname "
16100 "FROM pg_foreign_data_wrapper w "
16101 "WHERE w.oid = '%u'",
16102 srvinfo->srvfdw);
16103 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16104 fdwname = PQgetvalue(res, 0, 0);
16105
16106 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16107 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16108 {
16109 appendPQExpBufferStr(q, " TYPE ");
16110 appendStringLiteralAH(q, srvinfo->srvtype, fout);
16111 }
16112 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16113 {
16114 appendPQExpBufferStr(q, " VERSION ");
16115 appendStringLiteralAH(q, srvinfo->srvversion, fout);
16116 }
16117
16118 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16119 appendPQExpBufferStr(q, fmtId(fdwname));
16120
16121 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16122 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16123
16124 appendPQExpBufferStr(q, ";\n");
16125
16126 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16127 qsrvname);
16128
16129 if (dopt->binary_upgrade)
16131 "SERVER", qsrvname, NULL);
16132
16133 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16134 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16135 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16136 .owner = srvinfo->rolname,
16137 .description = "SERVER",
16138 .section = SECTION_PRE_DATA,
16139 .createStmt = q->data,
16140 .dropStmt = delq->data));
16141
16142 /* Dump Foreign Server Comments */
16143 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16144 dumpComment(fout, "SERVER", qsrvname,
16145 NULL, srvinfo->rolname,
16146 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16147
16148 /* Handle the ACL */
16149 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16150 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16151 "FOREIGN SERVER", qsrvname, NULL, NULL,
16152 NULL, srvinfo->rolname, &srvinfo->dacl);
16153
16154 /* Dump user mappings */
16155 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16157 srvinfo->dobj.name, NULL,
16158 srvinfo->rolname,
16159 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16160
16161 PQclear(res);
16162
16164
16167 destroyPQExpBuffer(query);
16168}
16169
16170/*
16171 * dumpUserMappings
16172 *
16173 * This routine is used to dump any user mappings associated with the
16174 * server handed to this routine. Should be called after ArchiveEntry()
16175 * for the server.
16176 */
16177static void
16179 const char *servername, const char *namespace,
16180 const char *owner,
16181 CatalogId catalogId, DumpId dumpId)
16182{
16183 PQExpBuffer q;
16185 PQExpBuffer query;
16186 PQExpBuffer tag;
16187 PGresult *res;
16188 int ntups;
16189 int i_usename;
16190 int i_umoptions;
16191 int i;
16192
16193 q = createPQExpBuffer();
16194 tag = createPQExpBuffer();
16196 query = createPQExpBuffer();
16197
16198 /*
16199 * We read from the publicly accessible view pg_user_mappings, so as not
16200 * to fail if run by a non-superuser. Note that the view will show
16201 * umoptions as null if the user hasn't got privileges for the associated
16202 * server; this means that pg_dump will dump such a mapping, but with no
16203 * OPTIONS clause. A possible alternative is to skip such mappings
16204 * altogether, but it's not clear that that's an improvement.
16205 */
16206 appendPQExpBuffer(query,
16207 "SELECT usename, "
16208 "array_to_string(ARRAY("
16209 "SELECT quote_ident(option_name) || ' ' || "
16210 "quote_literal(option_value) "
16211 "FROM pg_options_to_table(umoptions) "
16212 "ORDER BY option_name"
16213 "), E',\n ') AS umoptions "
16214 "FROM pg_user_mappings "
16215 "WHERE srvid = '%u' "
16216 "ORDER BY usename",
16217 catalogId.oid);
16218
16219 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16220
16221 ntups = PQntuples(res);
16222 i_usename = PQfnumber(res, "usename");
16223 i_umoptions = PQfnumber(res, "umoptions");
16224
16225 for (i = 0; i < ntups; i++)
16226 {
16227 char *usename;
16228 char *umoptions;
16229
16230 usename = PQgetvalue(res, i, i_usename);
16232
16234 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16235 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16236
16237 if (umoptions && strlen(umoptions) > 0)
16238 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16239
16240 appendPQExpBufferStr(q, ";\n");
16241
16243 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16244 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16245
16246 resetPQExpBuffer(tag);
16247 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16248 usename, servername);
16249
16251 ARCHIVE_OPTS(.tag = tag->data,
16252 .namespace = namespace,
16253 .owner = owner,
16254 .description = "USER MAPPING",
16255 .section = SECTION_PRE_DATA,
16256 .createStmt = q->data,
16257 .dropStmt = delq->data));
16258 }
16259
16260 PQclear(res);
16261
16262 destroyPQExpBuffer(query);
16264 destroyPQExpBuffer(tag);
16266}
16267
16268/*
16269 * Write out default privileges information
16270 */
16271static void
16273{
16274 DumpOptions *dopt = fout->dopt;
16275 PQExpBuffer q;
16276 PQExpBuffer tag;
16277 const char *type;
16278
16279 /* Do nothing if not dumping schema, or if we're skipping ACLs */
16280 if (!dopt->dumpSchema || dopt->aclsSkip)
16281 return;
16282
16283 q = createPQExpBuffer();
16284 tag = createPQExpBuffer();
16285
16286 switch (daclinfo->defaclobjtype)
16287 {
16288 case DEFACLOBJ_RELATION:
16289 type = "TABLES";
16290 break;
16291 case DEFACLOBJ_SEQUENCE:
16292 type = "SEQUENCES";
16293 break;
16294 case DEFACLOBJ_FUNCTION:
16295 type = "FUNCTIONS";
16296 break;
16297 case DEFACLOBJ_TYPE:
16298 type = "TYPES";
16299 break;
16301 type = "SCHEMAS";
16302 break;
16304 type = "LARGE OBJECTS";
16305 break;
16306 default:
16307 /* shouldn't get here */
16308 pg_fatal("unrecognized object type in default privileges: %d",
16309 (int) daclinfo->defaclobjtype);
16310 type = ""; /* keep compiler quiet */
16311 }
16312
16313 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16314
16315 /* build the actual command(s) for this tuple */
16317 daclinfo->dobj.namespace != NULL ?
16318 daclinfo->dobj.namespace->dobj.name : NULL,
16319 daclinfo->dacl.acl,
16320 daclinfo->dacl.acldefault,
16321 daclinfo->defaclrole,
16322 fout->remoteVersion,
16323 q))
16324 pg_fatal("could not parse default ACL list (%s)",
16325 daclinfo->dacl.acl);
16326
16327 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16328 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16329 ARCHIVE_OPTS(.tag = tag->data,
16330 .namespace = daclinfo->dobj.namespace ?
16331 daclinfo->dobj.namespace->dobj.name : NULL,
16332 .owner = daclinfo->defaclrole,
16333 .description = "DEFAULT ACL",
16334 .section = SECTION_POST_DATA,
16335 .createStmt = q->data));
16336
16337 destroyPQExpBuffer(tag);
16339}
16340
16341/*----------
16342 * Write out grant/revoke information
16343 *
16344 * 'objDumpId' is the dump ID of the underlying object.
16345 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16346 * or InvalidDumpId if there is no need for a second dependency.
16347 * 'type' must be one of
16348 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16349 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16350 * 'name' is the formatted name of the object. Must be quoted etc. already.
16351 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16352 * (Currently we assume that subname is only provided for table columns.)
16353 * 'nspname' is the namespace the object is in (NULL if none).
16354 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16355 * to use the default for the object type.
16356 * 'owner' is the owner, NULL if there is no owner (for languages).
16357 * 'dacl' is the DumpableAcl struct for the object.
16358 *
16359 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16360 * no ACL entry was created.
16361 *----------
16362 */
16363static DumpId
16365 const char *type, const char *name, const char *subname,
16366 const char *nspname, const char *tag, const char *owner,
16367 const DumpableAcl *dacl)
16368{
16370 DumpOptions *dopt = fout->dopt;
16371 const char *acls = dacl->acl;
16372 const char *acldefault = dacl->acldefault;
16373 char privtype = dacl->privtype;
16374 const char *initprivs = dacl->initprivs;
16375 const char *baseacls;
16376 PQExpBuffer sql;
16377
16378 /* Do nothing if ACL dump is not enabled */
16379 if (dopt->aclsSkip)
16380 return InvalidDumpId;
16381
16382 /* --data-only skips ACLs *except* large object ACLs */
16383 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16384 return InvalidDumpId;
16385
16386 sql = createPQExpBuffer();
16387
16388 /*
16389 * In binary upgrade mode, we don't run an extension's script but instead
16390 * dump out the objects independently and then recreate them. To preserve
16391 * any initial privileges which were set on extension objects, we need to
16392 * compute the set of GRANT and REVOKE commands necessary to get from the
16393 * default privileges of an object to its initial privileges as recorded
16394 * in pg_init_privs.
16395 *
16396 * At restore time, we apply these commands after having called
16397 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16398 * copy the results into pg_init_privs. This is how we preserve the
16399 * contents of that catalog across binary upgrades.
16400 */
16401 if (dopt->binary_upgrade && privtype == 'e' &&
16402 initprivs && *initprivs != '\0')
16403 {
16404 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16405 if (!buildACLCommands(name, subname, nspname, type,
16406 initprivs, acldefault, owner,
16407 "", fout->remoteVersion, sql))
16408 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16409 initprivs, acldefault, name, type);
16410 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16411 }
16412
16413 /*
16414 * Now figure the GRANT and REVOKE commands needed to get to the object's
16415 * actual current ACL, starting from the initprivs if given, else from the
16416 * object-type-specific default. Also, while buildACLCommands will assume
16417 * that a NULL/empty acls string means it needn't do anything, what that
16418 * actually represents is the object-type-specific default; so we need to
16419 * substitute the acldefault string to get the right results in that case.
16420 */
16421 if (initprivs && *initprivs != '\0')
16422 {
16423 baseacls = initprivs;
16424 if (acls == NULL || *acls == '\0')
16425 acls = acldefault;
16426 }
16427 else
16429
16430 if (!buildACLCommands(name, subname, nspname, type,
16431 acls, baseacls, owner,
16432 "", fout->remoteVersion, sql))
16433 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16434 acls, baseacls, name, type);
16435
16436 if (sql->len > 0)
16437 {
16439 DumpId aclDeps[2];
16440 int nDeps = 0;
16441
16442 if (tag)
16444 else if (subname)
16445 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16446 else
16447 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16448
16449 aclDeps[nDeps++] = objDumpId;
16450 if (altDumpId != InvalidDumpId)
16451 aclDeps[nDeps++] = altDumpId;
16452
16454
16456 ARCHIVE_OPTS(.tag = tagbuf->data,
16457 .namespace = nspname,
16458 .owner = owner,
16459 .description = "ACL",
16460 .section = SECTION_NONE,
16461 .createStmt = sql->data,
16462 .deps = aclDeps,
16463 .nDeps = nDeps));
16464
16466 }
16467
16468 destroyPQExpBuffer(sql);
16469
16470 return aclDumpId;
16471}
16472
16473/*
16474 * dumpSecLabel
16475 *
16476 * This routine is used to dump any security labels associated with the
16477 * object handed to this routine. The routine takes the object type
16478 * and object name (ready to print, except for schema decoration), plus
16479 * the namespace and owner of the object (for labeling the ArchiveEntry),
16480 * plus catalog ID and subid which are the lookup key for pg_seclabel,
16481 * plus the dump ID for the object (for setting a dependency).
16482 * If a matching pg_seclabel entry is found, it is dumped.
16483 *
16484 * Note: although this routine takes a dumpId for dependency purposes,
16485 * that purpose is just to mark the dependency in the emitted dump file
16486 * for possible future use by pg_restore. We do NOT use it for determining
16487 * ordering of the label in the dump file, because this routine is called
16488 * after dependency sorting occurs. This routine should be called just after
16489 * calling ArchiveEntry() for the specified object.
16490 */
16491static void
16492dumpSecLabel(Archive *fout, const char *type, const char *name,
16493 const char *namespace, const char *owner,
16494 CatalogId catalogId, int subid, DumpId dumpId)
16495{
16496 DumpOptions *dopt = fout->dopt;
16497 SecLabelItem *labels;
16498 int nlabels;
16499 int i;
16500 PQExpBuffer query;
16501
16502 /* do nothing, if --no-security-labels is supplied */
16503 if (dopt->no_security_labels)
16504 return;
16505
16506 /*
16507 * Security labels are schema not data ... except large object labels are
16508 * data
16509 */
16510 if (strcmp(type, "LARGE OBJECT") != 0)
16511 {
16512 if (!dopt->dumpSchema)
16513 return;
16514 }
16515 else
16516 {
16517 /* We do dump large object security labels in binary-upgrade mode */
16518 if (!dopt->dumpData && !dopt->binary_upgrade)
16519 return;
16520 }
16521
16522 /* Search for security labels associated with catalogId, using table */
16523 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16524
16525 query = createPQExpBuffer();
16526
16527 for (i = 0; i < nlabels; i++)
16528 {
16529 /*
16530 * Ignore label entries for which the subid doesn't match.
16531 */
16532 if (labels[i].objsubid != subid)
16533 continue;
16534
16535 appendPQExpBuffer(query,
16536 "SECURITY LABEL FOR %s ON %s ",
16537 fmtId(labels[i].provider), type);
16538 if (namespace && *namespace)
16539 appendPQExpBuffer(query, "%s.", fmtId(namespace));
16540 appendPQExpBuffer(query, "%s IS ", name);
16541 appendStringLiteralAH(query, labels[i].label, fout);
16542 appendPQExpBufferStr(query, ";\n");
16543 }
16544
16545 if (query->len > 0)
16546 {
16548
16549 appendPQExpBuffer(tag, "%s %s", type, name);
16551 ARCHIVE_OPTS(.tag = tag->data,
16552 .namespace = namespace,
16553 .owner = owner,
16554 .description = "SECURITY LABEL",
16555 .section = SECTION_NONE,
16556 .createStmt = query->data,
16557 .deps = &dumpId,
16558 .nDeps = 1));
16559 destroyPQExpBuffer(tag);
16560 }
16561
16562 destroyPQExpBuffer(query);
16563}
16564
16565/*
16566 * dumpTableSecLabel
16567 *
16568 * As above, but dump security label for both the specified table (or view)
16569 * and its columns.
16570 */
16571static void
16573{
16574 DumpOptions *dopt = fout->dopt;
16575 SecLabelItem *labels;
16576 int nlabels;
16577 int i;
16578 PQExpBuffer query;
16579 PQExpBuffer target;
16580
16581 /* do nothing, if --no-security-labels is supplied */
16582 if (dopt->no_security_labels)
16583 return;
16584
16585 /* SecLabel are SCHEMA not data */
16586 if (!dopt->dumpSchema)
16587 return;
16588
16589 /* Search for comments associated with relation, using table */
16590 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16591 tbinfo->dobj.catId.oid,
16592 &labels);
16593
16594 /* If security labels exist, build SECURITY LABEL statements */
16595 if (nlabels <= 0)
16596 return;
16597
16598 query = createPQExpBuffer();
16599 target = createPQExpBuffer();
16600
16601 for (i = 0; i < nlabels; i++)
16602 {
16603 const char *colname;
16604 const char *provider = labels[i].provider;
16605 const char *label = labels[i].label;
16606 int objsubid = labels[i].objsubid;
16607
16608 resetPQExpBuffer(target);
16609 if (objsubid == 0)
16610 {
16611 appendPQExpBuffer(target, "%s %s", reltypename,
16613 }
16614 else
16615 {
16616 colname = getAttrName(objsubid, tbinfo);
16617 /* first fmtXXX result must be consumed before calling again */
16618 appendPQExpBuffer(target, "COLUMN %s",
16620 appendPQExpBuffer(target, ".%s", fmtId(colname));
16621 }
16622 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16623 fmtId(provider), target->data);
16625 appendPQExpBufferStr(query, ";\n");
16626 }
16627 if (query->len > 0)
16628 {
16629 resetPQExpBuffer(target);
16630 appendPQExpBuffer(target, "%s %s", reltypename,
16631 fmtId(tbinfo->dobj.name));
16633 ARCHIVE_OPTS(.tag = target->data,
16634 .namespace = tbinfo->dobj.namespace->dobj.name,
16635 .owner = tbinfo->rolname,
16636 .description = "SECURITY LABEL",
16637 .section = SECTION_NONE,
16638 .createStmt = query->data,
16639 .deps = &(tbinfo->dobj.dumpId),
16640 .nDeps = 1));
16641 }
16642 destroyPQExpBuffer(query);
16643 destroyPQExpBuffer(target);
16644}
16645
16646/*
16647 * findSecLabels
16648 *
16649 * Find the security label(s), if any, associated with the given object.
16650 * All the objsubid values associated with the given classoid/objoid are
16651 * found with one search.
16652 */
16653static int
16655{
16657 SecLabelItem *low;
16658 SecLabelItem *high;
16659 int nmatch;
16660
16661 if (nseclabels <= 0) /* no labels, so no match is possible */
16662 {
16663 *items = NULL;
16664 return 0;
16665 }
16666
16667 /*
16668 * Do binary search to find some item matching the object.
16669 */
16670 low = &seclabels[0];
16671 high = &seclabels[nseclabels - 1];
16672 while (low <= high)
16673 {
16674 middle = low + (high - low) / 2;
16675
16676 if (classoid < middle->classoid)
16677 high = middle - 1;
16678 else if (classoid > middle->classoid)
16679 low = middle + 1;
16680 else if (objoid < middle->objoid)
16681 high = middle - 1;
16682 else if (objoid > middle->objoid)
16683 low = middle + 1;
16684 else
16685 break; /* found a match */
16686 }
16687
16688 if (low > high) /* no matches */
16689 {
16690 *items = NULL;
16691 return 0;
16692 }
16693
16694 /*
16695 * Now determine how many items match the object. The search loop
16696 * invariant still holds: only items between low and high inclusive could
16697 * match.
16698 */
16699 nmatch = 1;
16700 while (middle > low)
16701 {
16702 if (classoid != middle[-1].classoid ||
16703 objoid != middle[-1].objoid)
16704 break;
16705 middle--;
16706 nmatch++;
16707 }
16708
16709 *items = middle;
16710
16711 middle += nmatch;
16712 while (middle <= high)
16713 {
16714 if (classoid != middle->classoid ||
16715 objoid != middle->objoid)
16716 break;
16717 middle++;
16718 nmatch++;
16719 }
16720
16721 return nmatch;
16722}
16723
16724/*
16725 * collectSecLabels
16726 *
16727 * Construct a table of all security labels available for database objects;
16728 * also set the has-seclabel component flag for each relevant object.
16729 *
16730 * The table is sorted by classoid/objid/objsubid for speed in lookup.
16731 */
16732static void
16734{
16735 PGresult *res;
16736 PQExpBuffer query;
16737 int i_label;
16738 int i_provider;
16739 int i_classoid;
16740 int i_objoid;
16741 int i_objsubid;
16742 int ntups;
16743 int i;
16744 DumpableObject *dobj;
16745
16746 query = createPQExpBuffer();
16747
16749 "SELECT label, provider, classoid, objoid, objsubid "
16750 "FROM pg_catalog.pg_seclabels "
16751 "ORDER BY classoid, objoid, objsubid");
16752
16753 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16754
16755 /* Construct lookup table containing OIDs in numeric form */
16756 i_label = PQfnumber(res, "label");
16757 i_provider = PQfnumber(res, "provider");
16758 i_classoid = PQfnumber(res, "classoid");
16759 i_objoid = PQfnumber(res, "objoid");
16760 i_objsubid = PQfnumber(res, "objsubid");
16761
16762 ntups = PQntuples(res);
16763
16765 nseclabels = 0;
16766 dobj = NULL;
16767
16768 for (i = 0; i < ntups; i++)
16769 {
16770 CatalogId objId;
16771 int subid;
16772
16773 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16774 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16775 subid = atoi(PQgetvalue(res, i, i_objsubid));
16776
16777 /* We needn't remember labels that don't match any dumpable object */
16778 if (dobj == NULL ||
16779 dobj->catId.tableoid != objId.tableoid ||
16780 dobj->catId.oid != objId.oid)
16781 dobj = findObjectByCatalogId(objId);
16782 if (dobj == NULL)
16783 continue;
16784
16785 /*
16786 * Labels on columns of composite types are linked to the type's
16787 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16788 * in the type's own DumpableObject.
16789 */
16790 if (subid != 0 && dobj->objType == DO_TABLE &&
16791 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16792 {
16794
16795 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16796 if (cTypeInfo)
16797 cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16798 }
16799 else
16800 dobj->components |= DUMP_COMPONENT_SECLABEL;
16801
16805 seclabels[nseclabels].objoid = objId.oid;
16806 seclabels[nseclabels].objsubid = subid;
16807 nseclabels++;
16808 }
16809
16810 PQclear(res);
16811 destroyPQExpBuffer(query);
16812}
16813
16814/*
16815 * dumpTable
16816 * write out to fout the declarations (not data) of a user-defined table
16817 */
16818static void
16820{
16821 DumpOptions *dopt = fout->dopt;
16823 char *namecopy;
16824
16825 /* Do nothing if not dumping schema */
16826 if (!dopt->dumpSchema)
16827 return;
16828
16829 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16830 {
16831 if (tbinfo->relkind == RELKIND_SEQUENCE)
16833 else
16835 }
16836
16837 /* Handle the ACL here */
16838 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16839 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16840 {
16841 const char *objtype;
16842
16843 switch (tbinfo->relkind)
16844 {
16845 case RELKIND_SEQUENCE:
16846 objtype = "SEQUENCE";
16847 break;
16848 case RELKIND_PROPGRAPH:
16849 objtype = "PROPERTY GRAPH";
16850 break;
16851 default:
16852 objtype = "TABLE";
16853 break;
16854 }
16855
16857 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16858 objtype, namecopy, NULL,
16859 tbinfo->dobj.namespace->dobj.name,
16860 NULL, tbinfo->rolname, &tbinfo->dacl);
16861 }
16862
16863 /*
16864 * Handle column ACLs, if any. Note: we pull these with a separate query
16865 * rather than trying to fetch them during getTableAttrs, so that we won't
16866 * miss ACLs on system columns. Doing it this way also allows us to dump
16867 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16868 */
16869 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16870 {
16872 PGresult *res;
16873 int i;
16874
16875 if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16876 {
16877 /* Set up query for column ACLs */
16879 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16880
16881 /*
16882 * In principle we should call acldefault('c', relowner) to get
16883 * the default ACL for a column. However, we don't currently
16884 * store the numeric OID of the relowner in TableInfo. We could
16885 * convert the owner name using regrole, but that creates a risk
16886 * of failure due to concurrent role renames. Given that the
16887 * default ACL for columns is empty and is likely to stay that
16888 * way, it's not worth extra cycles and risk to avoid hard-wiring
16889 * that knowledge here.
16890 */
16892 "SELECT at.attname, "
16893 "at.attacl, "
16894 "'{}' AS acldefault, "
16895 "pip.privtype, pip.initprivs "
16896 "FROM pg_catalog.pg_attribute at "
16897 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16898 "(at.attrelid = pip.objoid "
16899 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16900 "AND at.attnum = pip.objsubid) "
16901 "WHERE at.attrelid = $1 AND "
16902 "NOT at.attisdropped "
16903 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16904 "ORDER BY at.attnum");
16905
16906 ExecuteSqlStatement(fout, query->data);
16907
16908 fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16909 }
16910
16911 printfPQExpBuffer(query,
16912 "EXECUTE getColumnACLs('%u')",
16913 tbinfo->dobj.catId.oid);
16914
16915 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16916
16917 for (i = 0; i < PQntuples(res); i++)
16918 {
16919 char *attname = PQgetvalue(res, i, 0);
16920 char *attacl = PQgetvalue(res, i, 1);
16921 char *acldefault = PQgetvalue(res, i, 2);
16922 char privtype = *(PQgetvalue(res, i, 3));
16923 char *initprivs = PQgetvalue(res, i, 4);
16925 char *attnamecopy;
16926
16927 coldacl.acl = attacl;
16928 coldacl.acldefault = acldefault;
16929 coldacl.privtype = privtype;
16930 coldacl.initprivs = initprivs;
16932
16933 /*
16934 * Column's GRANT type is always TABLE. Each column ACL depends
16935 * on the table-level ACL, since we can restore column ACLs in
16936 * parallel but the table-level ACL has to be done first.
16937 */
16938 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16939 "TABLE", namecopy, attnamecopy,
16940 tbinfo->dobj.namespace->dobj.name,
16941 NULL, tbinfo->rolname, &coldacl);
16943 }
16944 PQclear(res);
16945 destroyPQExpBuffer(query);
16946 }
16947
16949}
16950
16951/*
16952 * Create the AS clause for a view or materialized view. The semicolon is
16953 * stripped because a materialized view must add a WITH NO DATA clause.
16954 *
16955 * This returns a new buffer which must be freed by the caller.
16956 */
16957static PQExpBuffer
16959{
16962 PGresult *res;
16963 int len;
16964
16965 /* Fetch the view definition */
16966 appendPQExpBuffer(query,
16967 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16968 tbinfo->dobj.catId.oid);
16969
16970 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16971
16972 if (PQntuples(res) != 1)
16973 {
16974 if (PQntuples(res) < 1)
16975 pg_fatal("query to obtain definition of view \"%s\" returned no data",
16976 tbinfo->dobj.name);
16977 else
16978 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16979 tbinfo->dobj.name);
16980 }
16981
16982 len = PQgetlength(res, 0, 0);
16983
16984 if (len == 0)
16985 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16986 tbinfo->dobj.name);
16987
16988 /* Strip off the trailing semicolon so that other things may follow. */
16989 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16990 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16991
16992 PQclear(res);
16993 destroyPQExpBuffer(query);
16994
16995 return result;
16996}
16997
16998/*
16999 * Create a dummy AS clause for a view. This is used when the real view
17000 * definition has to be postponed because of circular dependencies.
17001 * We must duplicate the view's external properties -- column names and types
17002 * (including collation) -- so that it works for subsequent references.
17003 *
17004 * This returns a new buffer which must be freed by the caller.
17005 */
17006static PQExpBuffer
17008{
17010 int j;
17011
17012 appendPQExpBufferStr(result, "SELECT");
17013
17014 for (j = 0; j < tbinfo->numatts; j++)
17015 {
17016 if (j > 0)
17019
17020 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17021
17022 /*
17023 * Must add collation if not default for the type, because CREATE OR
17024 * REPLACE VIEW won't change it
17025 */
17026 if (OidIsValid(tbinfo->attcollation[j]))
17027 {
17028 CollInfo *coll;
17029
17030 coll = findCollationByOid(tbinfo->attcollation[j]);
17031 if (coll)
17032 appendPQExpBuffer(result, " COLLATE %s",
17033 fmtQualifiedDumpable(coll));
17034 }
17035
17036 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17037 }
17038
17039 return result;
17040}
17041
17042/*
17043 * dumpTableSchema
17044 * write the declaration (not data) of one user-defined table or view
17045 */
17046static void
17048{
17049 DumpOptions *dopt = fout->dopt;
17053 char *qrelname;
17054 char *qualrelname;
17055 int numParents;
17056 TableInfo **parents;
17057 int actual_atts; /* number of attrs in this CREATE statement */
17058 const char *reltypename;
17059 char *storage;
17060 int j,
17061 k;
17062
17063 /* We had better have loaded per-column details about this table */
17064 Assert(tbinfo->interesting);
17065
17066 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17068
17069 if (tbinfo->hasoids)
17070 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17071 qrelname);
17072
17073 if (dopt->binary_upgrade)
17075
17076 /* Is it a table or a view? */
17077 if (tbinfo->relkind == RELKIND_VIEW)
17078 {
17080
17081 /*
17082 * Note: keep this code in sync with the is_view case in dumpRule()
17083 */
17084
17085 reltypename = "VIEW";
17086
17087 if (dopt->binary_upgrade)
17089 tbinfo->dobj.catId.oid);
17090
17091 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17092
17093 if (tbinfo->dummy_view)
17095 else
17096 {
17097 if (nonemptyReloptions(tbinfo->reloptions))
17098 {
17099 appendPQExpBufferStr(q, " WITH (");
17100 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17101 appendPQExpBufferChar(q, ')');
17102 }
17104 }
17105 appendPQExpBuffer(q, " AS\n%s", result->data);
17107
17108 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17109 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17110 appendPQExpBufferStr(q, ";\n");
17111 }
17112 else if (tbinfo->relkind == RELKIND_PROPGRAPH)
17113 {
17115 PGresult *res;
17116 int len;
17117
17118 reltypename = "PROPERTY GRAPH";
17119
17120 if (dopt->binary_upgrade)
17122 tbinfo->dobj.catId.oid);
17123
17124 appendPQExpBuffer(query,
17125 "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
17126 tbinfo->dobj.catId.oid);
17127
17128 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17129
17130 if (PQntuples(res) != 1)
17131 {
17132 if (PQntuples(res) < 1)
17133 pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
17134 tbinfo->dobj.name);
17135 else
17136 pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
17137 tbinfo->dobj.name);
17138 }
17139
17140 len = PQgetlength(res, 0, 0);
17141
17142 if (len == 0)
17143 pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
17144 tbinfo->dobj.name);
17145
17146 appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
17147
17148 PQclear(res);
17149 destroyPQExpBuffer(query);
17150
17151 appendPQExpBufferStr(q, ";\n");
17152 }
17153 else
17154 {
17155 char *partkeydef = NULL;
17156 char *ftoptions = NULL;
17157 char *srvname = NULL;
17158 const char *foreign = "";
17159
17160 /*
17161 * Set reltypename, and collect any relkind-specific data that we
17162 * didn't fetch during getTables().
17163 */
17164 switch (tbinfo->relkind)
17165 {
17167 {
17169 PGresult *res;
17170
17171 reltypename = "TABLE";
17172
17173 /* retrieve partition key definition */
17174 appendPQExpBuffer(query,
17175 "SELECT pg_get_partkeydef('%u')",
17176 tbinfo->dobj.catId.oid);
17177 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17178 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17179 PQclear(res);
17180 destroyPQExpBuffer(query);
17181 break;
17182 }
17184 {
17186 PGresult *res;
17187 int i_srvname;
17188 int i_ftoptions;
17189
17190 reltypename = "FOREIGN TABLE";
17191
17192 /* retrieve name of foreign server and generic options */
17193 appendPQExpBuffer(query,
17194 "SELECT fs.srvname, "
17195 "pg_catalog.array_to_string(ARRAY("
17196 "SELECT pg_catalog.quote_ident(option_name) || "
17197 "' ' || pg_catalog.quote_literal(option_value) "
17198 "FROM pg_catalog.pg_options_to_table(ftoptions) "
17199 "ORDER BY option_name"
17200 "), E',\n ') AS ftoptions "
17201 "FROM pg_catalog.pg_foreign_table ft "
17202 "JOIN pg_catalog.pg_foreign_server fs "
17203 "ON (fs.oid = ft.ftserver) "
17204 "WHERE ft.ftrelid = '%u'",
17205 tbinfo->dobj.catId.oid);
17206 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17207 i_srvname = PQfnumber(res, "srvname");
17208 i_ftoptions = PQfnumber(res, "ftoptions");
17211 PQclear(res);
17212 destroyPQExpBuffer(query);
17213
17214 foreign = "FOREIGN ";
17215 break;
17216 }
17217 case RELKIND_MATVIEW:
17218 reltypename = "MATERIALIZED VIEW";
17219 break;
17220 default:
17221 reltypename = "TABLE";
17222 break;
17223 }
17224
17225 numParents = tbinfo->numParents;
17226 parents = tbinfo->parents;
17227
17228 if (dopt->binary_upgrade)
17230 tbinfo->dobj.catId.oid);
17231
17232 /*
17233 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17234 * ignore it when dumping if it was set in this case.
17235 */
17236 appendPQExpBuffer(q, "CREATE %s%s %s",
17237 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17238 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17239 "UNLOGGED " : "",
17241 qualrelname);
17242
17243 /*
17244 * Attach to type, if reloftype; except in case of a binary upgrade,
17245 * we dump the table normally and attach it to the type afterward.
17246 */
17247 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17248 appendPQExpBuffer(q, " OF %s",
17249 getFormattedTypeName(fout, tbinfo->reloftype,
17250 zeroIsError));
17251
17252 if (tbinfo->relkind != RELKIND_MATVIEW)
17253 {
17254 /* Dump the attributes */
17255 actual_atts = 0;
17256 for (j = 0; j < tbinfo->numatts; j++)
17257 {
17258 /*
17259 * Normally, dump if it's locally defined in this table, and
17260 * not dropped. But for binary upgrade, we'll dump all the
17261 * columns, and then fix up the dropped and nonlocal cases
17262 * below.
17263 */
17264 if (shouldPrintColumn(dopt, tbinfo, j))
17265 {
17266 bool print_default;
17267 bool print_notnull;
17268
17269 /*
17270 * Default value --- suppress if to be printed separately
17271 * or not at all.
17272 */
17273 print_default = (tbinfo->attrdefs[j] != NULL &&
17274 tbinfo->attrdefs[j]->dobj.dump &&
17275 !tbinfo->attrdefs[j]->separate);
17276
17277 /*
17278 * Not Null constraint --- print it if it is locally
17279 * defined, or if binary upgrade. (In the latter case, we
17280 * reset conislocal below.)
17281 */
17282 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17283 (tbinfo->notnull_islocal[j] ||
17284 dopt->binary_upgrade ||
17285 tbinfo->ispartition));
17286
17287 /*
17288 * Skip column if fully defined by reloftype, except in
17289 * binary upgrade
17290 */
17291 if (OidIsValid(tbinfo->reloftype) &&
17293 !dopt->binary_upgrade)
17294 continue;
17295
17296 /* Format properly if not first attr */
17297 if (actual_atts == 0)
17298 appendPQExpBufferStr(q, " (");
17299 else
17300 appendPQExpBufferChar(q, ',');
17301 appendPQExpBufferStr(q, "\n ");
17302 actual_atts++;
17303
17304 /* Attribute name */
17305 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17306
17307 if (tbinfo->attisdropped[j])
17308 {
17309 /*
17310 * ALTER TABLE DROP COLUMN clears
17311 * pg_attribute.atttypid, so we will not have gotten a
17312 * valid type name; insert INTEGER as a stopgap. We'll
17313 * clean things up later.
17314 */
17315 appendPQExpBufferStr(q, " INTEGER /* dummy */");
17316 /* and skip to the next column */
17317 continue;
17318 }
17319
17320 /*
17321 * Attribute type; print it except when creating a typed
17322 * table ('OF type_name'), but in binary-upgrade mode,
17323 * print it in that case too.
17324 */
17325 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17326 {
17327 appendPQExpBuffer(q, " %s",
17328 tbinfo->atttypnames[j]);
17329 }
17330
17331 if (print_default)
17332 {
17333 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17334 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17335 tbinfo->attrdefs[j]->adef_expr);
17336 else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17337 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17338 tbinfo->attrdefs[j]->adef_expr);
17339 else
17340 appendPQExpBuffer(q, " DEFAULT %s",
17341 tbinfo->attrdefs[j]->adef_expr);
17342 }
17343
17344 if (print_notnull)
17345 {
17346 if (tbinfo->notnull_constrs[j][0] == '\0')
17347 appendPQExpBufferStr(q, " NOT NULL");
17348 else
17349 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17350 fmtId(tbinfo->notnull_constrs[j]));
17351
17352 if (tbinfo->notnull_noinh[j])
17353 appendPQExpBufferStr(q, " NO INHERIT");
17354 }
17355
17356 /* Add collation if not default for the type */
17357 if (OidIsValid(tbinfo->attcollation[j]))
17358 {
17359 CollInfo *coll;
17360
17361 coll = findCollationByOid(tbinfo->attcollation[j]);
17362 if (coll)
17363 appendPQExpBuffer(q, " COLLATE %s",
17364 fmtQualifiedDumpable(coll));
17365 }
17366 }
17367
17368 /*
17369 * On the other hand, if we choose not to print a column
17370 * (likely because it is created by inheritance), but the
17371 * column has a locally-defined not-null constraint, we need
17372 * to dump the constraint as a standalone object.
17373 *
17374 * This syntax isn't SQL-conforming, but if you wanted
17375 * standard output you wouldn't be creating non-standard
17376 * objects to begin with.
17377 */
17378 if (!shouldPrintColumn(dopt, tbinfo, j) &&
17379 !tbinfo->attisdropped[j] &&
17380 tbinfo->notnull_constrs[j] != NULL &&
17381 tbinfo->notnull_islocal[j])
17382 {
17383 /* Format properly if not first attr */
17384 if (actual_atts == 0)
17385 appendPQExpBufferStr(q, " (");
17386 else
17387 appendPQExpBufferChar(q, ',');
17388 appendPQExpBufferStr(q, "\n ");
17389 actual_atts++;
17390
17391 if (tbinfo->notnull_constrs[j][0] == '\0')
17392 appendPQExpBuffer(q, "NOT NULL %s",
17393 fmtId(tbinfo->attnames[j]));
17394 else
17395 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17396 tbinfo->notnull_constrs[j],
17397 fmtId(tbinfo->attnames[j]));
17398
17399 if (tbinfo->notnull_noinh[j])
17400 appendPQExpBufferStr(q, " NO INHERIT");
17401 }
17402 }
17403
17404 /*
17405 * Add non-inherited CHECK constraints, if any.
17406 *
17407 * For partitions, we need to include check constraints even if
17408 * they're not defined locally, because the ALTER TABLE ATTACH
17409 * PARTITION that we'll emit later expects the constraint to be
17410 * there. (No need to fix conislocal: ATTACH PARTITION does that)
17411 */
17412 for (j = 0; j < tbinfo->ncheck; j++)
17413 {
17414 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17415
17416 if (constr->separate ||
17417 (!constr->conislocal && !tbinfo->ispartition))
17418 continue;
17419
17420 if (actual_atts == 0)
17421 appendPQExpBufferStr(q, " (\n ");
17422 else
17423 appendPQExpBufferStr(q, ",\n ");
17424
17425 appendPQExpBuffer(q, "CONSTRAINT %s ",
17426 fmtId(constr->dobj.name));
17427 appendPQExpBufferStr(q, constr->condef);
17428
17429 actual_atts++;
17430 }
17431
17432 if (actual_atts)
17433 appendPQExpBufferStr(q, "\n)");
17434 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17435 {
17436 /*
17437 * No attributes? we must have a parenthesized attribute list,
17438 * even though empty, when not using the OF TYPE syntax.
17439 */
17440 appendPQExpBufferStr(q, " (\n)");
17441 }
17442
17443 /*
17444 * Emit the INHERITS clause (not for partitions), except in
17445 * binary-upgrade mode.
17446 */
17447 if (numParents > 0 && !tbinfo->ispartition &&
17448 !dopt->binary_upgrade)
17449 {
17450 appendPQExpBufferStr(q, "\nINHERITS (");
17451 for (k = 0; k < numParents; k++)
17452 {
17453 TableInfo *parentRel = parents[k];
17454
17455 if (k > 0)
17456 appendPQExpBufferStr(q, ", ");
17458 }
17459 appendPQExpBufferChar(q, ')');
17460 }
17461
17462 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17463 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17464
17465 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17466 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17467 }
17468
17469 if (nonemptyReloptions(tbinfo->reloptions) ||
17470 nonemptyReloptions(tbinfo->toast_reloptions))
17471 {
17472 bool addcomma = false;
17473
17474 appendPQExpBufferStr(q, "\nWITH (");
17475 if (nonemptyReloptions(tbinfo->reloptions))
17476 {
17477 addcomma = true;
17478 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17479 }
17480 if (nonemptyReloptions(tbinfo->toast_reloptions))
17481 {
17482 if (addcomma)
17483 appendPQExpBufferStr(q, ", ");
17484 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17485 fout);
17486 }
17487 appendPQExpBufferChar(q, ')');
17488 }
17489
17490 /* Dump generic options if any */
17491 if (ftoptions && ftoptions[0])
17492 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17493
17494 /*
17495 * For materialized views, create the AS clause just like a view. At
17496 * this point, we always mark the view as not populated.
17497 */
17498 if (tbinfo->relkind == RELKIND_MATVIEW)
17499 {
17501
17503 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17504 result->data);
17506 }
17507 else
17508 appendPQExpBufferStr(q, ";\n");
17509
17510 /* Materialized views can depend on extensions */
17511 if (tbinfo->relkind == RELKIND_MATVIEW)
17513 "pg_catalog.pg_class",
17514 "MATERIALIZED VIEW",
17515 qualrelname);
17516
17517 /*
17518 * in binary upgrade mode, update the catalog with any missing values
17519 * that might be present.
17520 */
17521 if (dopt->binary_upgrade)
17522 {
17523 for (j = 0; j < tbinfo->numatts; j++)
17524 {
17525 if (tbinfo->attmissingval[j][0] != '\0')
17526 {
17527 appendPQExpBufferStr(q, "\n-- set missing value.\n");
17529 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17531 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17532 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17533 appendPQExpBufferChar(q, ',');
17534 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17535 appendPQExpBufferStr(q, ");\n\n");
17536 }
17537 }
17538 }
17539
17540 /*
17541 * To create binary-compatible heap files, we have to ensure the same
17542 * physical column order, including dropped columns, as in the
17543 * original. Therefore, we create dropped columns above and drop them
17544 * here, also updating their attlen/attalign values so that the
17545 * dropped column can be skipped properly. (We do not bother with
17546 * restoring the original attbyval setting.) Also, inheritance
17547 * relationships are set up by doing ALTER TABLE INHERIT rather than
17548 * using an INHERITS clause --- the latter would possibly mess up the
17549 * column order. That also means we have to take care about setting
17550 * attislocal correctly, plus fix up any inherited CHECK constraints.
17551 * Analogously, we set up typed tables using ALTER TABLE / OF here.
17552 *
17553 * We process foreign and partitioned tables here, even though they
17554 * lack heap storage, because they can participate in inheritance
17555 * relationships and we want this stuff to be consistent across the
17556 * inheritance tree. We can exclude indexes, toast tables, sequences
17557 * and matviews, even though they have storage, because we don't
17558 * support altering or dropping columns in them, nor can they be part
17559 * of inheritance trees.
17560 */
17561 if (dopt->binary_upgrade &&
17562 (tbinfo->relkind == RELKIND_RELATION ||
17563 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17564 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17565 {
17566 bool firstitem;
17567 bool firstitem_extra;
17568
17569 /*
17570 * Drop any dropped columns. Merge the pg_attribute manipulations
17571 * into a single SQL command, so that we don't cause repeated
17572 * relcache flushes on the target table. Otherwise we risk O(N^2)
17573 * relcache bloat while dropping N columns.
17574 */
17575 resetPQExpBuffer(extra);
17576 firstitem = true;
17577 for (j = 0; j < tbinfo->numatts; j++)
17578 {
17579 if (tbinfo->attisdropped[j])
17580 {
17581 if (firstitem)
17582 {
17583 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17584 "UPDATE pg_catalog.pg_attribute\n"
17585 "SET attlen = v.dlen, "
17586 "attalign = v.dalign, "
17587 "attbyval = false\n"
17588 "FROM (VALUES ");
17589 firstitem = false;
17590 }
17591 else
17592 appendPQExpBufferStr(q, ",\n ");
17593 appendPQExpBufferChar(q, '(');
17594 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17595 appendPQExpBuffer(q, ", %d, '%c')",
17596 tbinfo->attlen[j],
17597 tbinfo->attalign[j]);
17598 /* The ALTER ... DROP COLUMN commands must come after */
17599 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17601 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17602 fmtId(tbinfo->attnames[j]));
17603 }
17604 }
17605 if (!firstitem)
17606 {
17607 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17608 "WHERE attrelid = ");
17610 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17611 " AND attname = v.dname;\n");
17612 /* Now we can issue the actual DROP COLUMN commands */
17613 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17614 }
17615
17616 /*
17617 * Fix up inherited columns. As above, do the pg_attribute
17618 * manipulations in a single SQL command.
17619 */
17620 firstitem = true;
17621 for (j = 0; j < tbinfo->numatts; j++)
17622 {
17623 if (!tbinfo->attisdropped[j] &&
17624 !tbinfo->attislocal[j])
17625 {
17626 if (firstitem)
17627 {
17628 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17629 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17630 "SET attislocal = false\n"
17631 "WHERE attrelid = ");
17633 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17634 " AND attname IN (");
17635 firstitem = false;
17636 }
17637 else
17638 appendPQExpBufferStr(q, ", ");
17639 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17640 }
17641 }
17642 if (!firstitem)
17643 appendPQExpBufferStr(q, ");\n");
17644
17645 /*
17646 * Fix up not-null constraints that come from inheritance. As
17647 * above, do the pg_constraint manipulations in a single SQL
17648 * command. (Actually, two in special cases, if we're doing an
17649 * upgrade from < 18).
17650 */
17651 firstitem = true;
17652 firstitem_extra = true;
17653 resetPQExpBuffer(extra);
17654 for (j = 0; j < tbinfo->numatts; j++)
17655 {
17656 /*
17657 * If a not-null constraint comes from inheritance, reset
17658 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17659 * below. Special hack: in versions < 18, columns with no
17660 * local definition need their constraint to be matched by
17661 * column number in conkeys instead of by constraint name,
17662 * because the latter is not available. (We distinguish the
17663 * case because the constraint name is the empty string.)
17664 */
17665 if (tbinfo->notnull_constrs[j] != NULL &&
17666 !tbinfo->notnull_islocal[j])
17667 {
17668 if (tbinfo->notnull_constrs[j][0] != '\0')
17669 {
17670 if (firstitem)
17671 {
17672 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17673 "SET conislocal = false\n"
17674 "WHERE contype = 'n' AND conrelid = ");
17676 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17677 "conname IN (");
17678 firstitem = false;
17679 }
17680 else
17681 appendPQExpBufferStr(q, ", ");
17682 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17683 }
17684 else
17685 {
17686 if (firstitem_extra)
17687 {
17688 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17689 "SET conislocal = false\n"
17690 "WHERE contype = 'n' AND conrelid = ");
17692 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17693 "conkey IN (");
17694 firstitem_extra = false;
17695 }
17696 else
17697 appendPQExpBufferStr(extra, ", ");
17698 appendPQExpBuffer(extra, "'{%d}'", j + 1);
17699 }
17700 }
17701 }
17702 if (!firstitem)
17703 appendPQExpBufferStr(q, ");\n");
17704 if (!firstitem_extra)
17705 appendPQExpBufferStr(extra, ");\n");
17706
17707 if (extra->len > 0)
17708 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17709
17710 /*
17711 * Add inherited CHECK constraints, if any.
17712 *
17713 * For partitions, they were already dumped, and conislocal
17714 * doesn't need fixing.
17715 *
17716 * As above, issue only one direct manipulation of pg_constraint.
17717 * Although it is tempting to merge the ALTER ADD CONSTRAINT
17718 * commands into one as well, refrain for now due to concern about
17719 * possible backend memory bloat if there are many such
17720 * constraints.
17721 */
17722 resetPQExpBuffer(extra);
17723 firstitem = true;
17724 for (k = 0; k < tbinfo->ncheck; k++)
17725 {
17726 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17727
17728 if (constr->separate || constr->conislocal || tbinfo->ispartition)
17729 continue;
17730
17731 if (firstitem)
17732 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17733 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17735 fmtId(constr->dobj.name),
17736 constr->condef);
17737 /* Update pg_constraint after all the ALTER TABLEs */
17738 if (firstitem)
17739 {
17740 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17741 "SET conislocal = false\n"
17742 "WHERE contype = 'c' AND conrelid = ");
17744 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17745 appendPQExpBufferStr(extra, " AND conname IN (");
17746 firstitem = false;
17747 }
17748 else
17749 appendPQExpBufferStr(extra, ", ");
17750 appendStringLiteralAH(extra, constr->dobj.name, fout);
17751 }
17752 if (!firstitem)
17753 {
17754 appendPQExpBufferStr(extra, ");\n");
17755 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17756 }
17757
17758 if (numParents > 0 && !tbinfo->ispartition)
17759 {
17760 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17761 for (k = 0; k < numParents; k++)
17762 {
17763 TableInfo *parentRel = parents[k];
17764
17765 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17768 }
17769 }
17770
17771 if (OidIsValid(tbinfo->reloftype))
17772 {
17773 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17774 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17776 getFormattedTypeName(fout, tbinfo->reloftype,
17777 zeroIsError));
17778 }
17779 }
17780
17781 /*
17782 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17783 * relminmxid of all vacuumable relations. (While vacuum.c processes
17784 * TOAST tables semi-independently, here we see them only as children
17785 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17786 * child toast table is handled below.)
17787 */
17788 if (dopt->binary_upgrade &&
17789 (tbinfo->relkind == RELKIND_RELATION ||
17790 tbinfo->relkind == RELKIND_MATVIEW))
17791 {
17792 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17793 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17794 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17795 "WHERE oid = ",
17796 tbinfo->frozenxid, tbinfo->minmxid);
17798 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17799
17800 if (tbinfo->toast_oid)
17801 {
17802 /*
17803 * The toast table will have the same OID at restore, so we
17804 * can safely target it by OID.
17805 */
17806 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17807 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17808 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17809 "WHERE oid = '%u';\n",
17810 tbinfo->toast_frozenxid,
17811 tbinfo->toast_minmxid, tbinfo->toast_oid);
17812 }
17813 }
17814
17815 /*
17816 * In binary_upgrade mode, restore matviews' populated status by
17817 * poking pg_class directly. This is pretty ugly, but we can't use
17818 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17819 * matview is not populated even though this matview is; in any case,
17820 * we want to transfer the matview's heap storage, not run REFRESH.
17821 */
17822 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17823 tbinfo->relispopulated)
17824 {
17825 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17826 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17827 "SET relispopulated = 't'\n"
17828 "WHERE oid = ");
17830 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17831 }
17832
17833 /*
17834 * Dump additional per-column properties that we can't handle in the
17835 * main CREATE TABLE command.
17836 */
17837 for (j = 0; j < tbinfo->numatts; j++)
17838 {
17839 /* None of this applies to dropped columns */
17840 if (tbinfo->attisdropped[j])
17841 continue;
17842
17843 /*
17844 * Dump per-column statistics information. We only issue an ALTER
17845 * TABLE statement if the attstattarget entry for this column is
17846 * not the default value.
17847 */
17848 if (tbinfo->attstattarget[j] >= 0)
17849 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17851 fmtId(tbinfo->attnames[j]),
17852 tbinfo->attstattarget[j]);
17853
17854 /*
17855 * Dump per-column storage information. The statement is only
17856 * dumped if the storage has been changed from the type's default.
17857 */
17858 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17859 {
17860 switch (tbinfo->attstorage[j])
17861 {
17862 case TYPSTORAGE_PLAIN:
17863 storage = "PLAIN";
17864 break;
17866 storage = "EXTERNAL";
17867 break;
17869 storage = "EXTENDED";
17870 break;
17871 case TYPSTORAGE_MAIN:
17872 storage = "MAIN";
17873 break;
17874 default:
17875 storage = NULL;
17876 }
17877
17878 /*
17879 * Only dump the statement if it's a storage type we recognize
17880 */
17881 if (storage != NULL)
17882 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17884 fmtId(tbinfo->attnames[j]),
17885 storage);
17886 }
17887
17888 /*
17889 * Dump per-column compression, if it's been set.
17890 */
17891 if (!dopt->no_toast_compression)
17892 {
17893 const char *cmname;
17894
17895 switch (tbinfo->attcompression[j])
17896 {
17897 case 'p':
17898 cmname = "pglz";
17899 break;
17900 case 'l':
17901 cmname = "lz4";
17902 break;
17903 default:
17904 cmname = NULL;
17905 break;
17906 }
17907
17908 if (cmname != NULL)
17909 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17911 fmtId(tbinfo->attnames[j]),
17912 cmname);
17913 }
17914
17915 /*
17916 * Dump per-column attributes.
17917 */
17918 if (tbinfo->attoptions[j][0] != '\0')
17919 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17921 fmtId(tbinfo->attnames[j]),
17922 tbinfo->attoptions[j]);
17923
17924 /*
17925 * Dump per-column fdw options.
17926 */
17927 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17928 tbinfo->attfdwoptions[j][0] != '\0')
17930 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17931 " %s\n"
17932 ");\n",
17934 fmtId(tbinfo->attnames[j]),
17935 tbinfo->attfdwoptions[j]);
17936 } /* end loop over columns */
17937
17941 }
17942
17943 /*
17944 * dump properties we only have ALTER TABLE syntax for
17945 */
17946 if ((tbinfo->relkind == RELKIND_RELATION ||
17947 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17948 tbinfo->relkind == RELKIND_MATVIEW) &&
17949 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17950 {
17951 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17952 {
17953 /* nothing to do, will be set when the index is dumped */
17954 }
17955 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17956 {
17957 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17958 qualrelname);
17959 }
17960 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17961 {
17962 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17963 qualrelname);
17964 }
17965 }
17966
17967 if (tbinfo->forcerowsec)
17968 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17969 qualrelname);
17970
17971 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17972
17973 if (dopt->binary_upgrade)
17976 tbinfo->dobj.namespace->dobj.name);
17977
17978 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17979 {
17980 char *tablespace = NULL;
17981 char *tableam = NULL;
17982
17983 /*
17984 * _selectTablespace() relies on tablespace-enabled objects in the
17985 * default tablespace to have a tablespace of "" (empty string) versus
17986 * non-tablespace-enabled objects to have a tablespace of NULL.
17987 * getTables() sets tbinfo->reltablespace to "" for the default
17988 * tablespace (not NULL).
17989 */
17990 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17991 tablespace = tbinfo->reltablespace;
17992
17993 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17995 tableam = tbinfo->amname;
17996
17997 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17998 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17999 .namespace = tbinfo->dobj.namespace->dobj.name,
18000 .tablespace = tablespace,
18001 .tableam = tableam,
18002 .relkind = tbinfo->relkind,
18003 .owner = tbinfo->rolname,
18004 .description = reltypename,
18005 .section = tbinfo->postponed_def ?
18007 .createStmt = q->data,
18008 .dropStmt = delq->data));
18009 }
18010
18011 /* Dump Table Comments */
18012 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18014
18015 /* Dump Table Security Labels */
18016 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18018
18019 /*
18020 * Dump comments for not-null constraints that aren't to be dumped
18021 * separately (those are processed by collectComments/dumpComment).
18022 */
18023 if (!fout->dopt->no_comments && dopt->dumpSchema &&
18024 fout->remoteVersion >= 180000)
18025 {
18027 PQExpBuffer tag = NULL;
18028
18029 for (j = 0; j < tbinfo->numatts; j++)
18030 {
18031 if (tbinfo->notnull_constrs[j] != NULL &&
18032 tbinfo->notnull_comment[j] != NULL)
18033 {
18034 if (comment == NULL)
18035 {
18037 tag = createPQExpBuffer();
18038 }
18039 else
18040 {
18042 resetPQExpBuffer(tag);
18043 }
18044
18045 appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18046 fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18047 appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18049
18050 appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18051 fmtId(tbinfo->notnull_constrs[j]), qrelname);
18052
18054 ARCHIVE_OPTS(.tag = tag->data,
18055 .namespace = tbinfo->dobj.namespace->dobj.name,
18056 .owner = tbinfo->rolname,
18057 .description = "COMMENT",
18058 .section = SECTION_NONE,
18059 .createStmt = comment->data,
18060 .deps = &(tbinfo->dobj.dumpId),
18061 .nDeps = 1));
18062 }
18063 }
18064
18066 destroyPQExpBuffer(tag);
18067 }
18068
18069 /* Dump comments on inlined table constraints */
18070 for (j = 0; j < tbinfo->ncheck; j++)
18071 {
18072 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18073
18074 if (constr->separate || !constr->conislocal)
18075 continue;
18076
18077 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18079 }
18080
18083 destroyPQExpBuffer(extra);
18086}
18087
18088/*
18089 * dumpTableAttach
18090 * write to fout the commands to attach a child partition
18091 *
18092 * Child partitions are always made by creating them separately
18093 * and then using ATTACH PARTITION, rather than using
18094 * CREATE TABLE ... PARTITION OF. This is important for preserving
18095 * any possible discrepancy in column layout, to allow assigning the
18096 * correct tablespace if different, and so that it's possible to restore
18097 * a partition without restoring its parent. (You'll get an error from
18098 * the ATTACH PARTITION command, but that can be ignored, or skipped
18099 * using "pg_restore -L" if you prefer.) The last point motivates
18100 * treating ATTACH PARTITION as a completely separate ArchiveEntry
18101 * rather than emitting it within the child partition's ArchiveEntry.
18102 */
18103static void
18105{
18106 DumpOptions *dopt = fout->dopt;
18107 PQExpBuffer q;
18108 PGresult *res;
18109 char *partbound;
18110
18111 /* Do nothing if not dumping schema */
18112 if (!dopt->dumpSchema)
18113 return;
18114
18115 q = createPQExpBuffer();
18116
18117 if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18118 {
18119 /* Set up query for partbound details */
18121 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18122
18124 "SELECT pg_get_expr(c.relpartbound, c.oid) "
18125 "FROM pg_class c "
18126 "WHERE c.oid = $1");
18127
18129
18130 fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18131 }
18132
18134 "EXECUTE dumpTableAttach('%u')",
18135 attachinfo->partitionTbl->dobj.catId.oid);
18136
18138 partbound = PQgetvalue(res, 0, 0);
18139
18140 /* Perform ALTER TABLE on the parent */
18142 "ALTER TABLE ONLY %s ",
18143 fmtQualifiedDumpable(attachinfo->parentTbl));
18145 "ATTACH PARTITION %s %s;\n",
18146 fmtQualifiedDumpable(attachinfo->partitionTbl),
18147 partbound);
18148
18149 /*
18150 * There is no point in creating a drop query as the drop is done by table
18151 * drop. (If you think to change this, see also _printTocEntry().)
18152 * Although this object doesn't really have ownership as such, set the
18153 * owner field anyway to ensure that the command is run by the correct
18154 * role at restore time.
18155 */
18156 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18157 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18158 .namespace = attachinfo->dobj.namespace->dobj.name,
18159 .owner = attachinfo->partitionTbl->rolname,
18160 .description = "TABLE ATTACH",
18161 .section = SECTION_PRE_DATA,
18162 .createStmt = q->data));
18163
18164 PQclear(res);
18166}
18167
18168/*
18169 * dumpAttrDef --- dump an attribute's default-value declaration
18170 */
18171static void
18173{
18174 DumpOptions *dopt = fout->dopt;
18175 TableInfo *tbinfo = adinfo->adtable;
18176 int adnum = adinfo->adnum;
18177 PQExpBuffer q;
18179 char *qualrelname;
18180 char *tag;
18181 char *foreign;
18182
18183 /* Do nothing if not dumping schema */
18184 if (!dopt->dumpSchema)
18185 return;
18186
18187 /* Skip if not "separate"; it was dumped in the table's definition */
18188 if (!adinfo->separate)
18189 return;
18190
18191 q = createPQExpBuffer();
18193
18195
18196 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18197
18199 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18200 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18201 adinfo->adef_expr);
18202
18203 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18205 fmtId(tbinfo->attnames[adnum - 1]));
18206
18207 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18208
18209 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18210 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18211 ARCHIVE_OPTS(.tag = tag,
18212 .namespace = tbinfo->dobj.namespace->dobj.name,
18213 .owner = tbinfo->rolname,
18214 .description = "DEFAULT",
18215 .section = SECTION_PRE_DATA,
18216 .createStmt = q->data,
18217 .dropStmt = delq->data));
18218
18219 pfree(tag);
18223}
18224
18225/*
18226 * getAttrName: extract the correct name for an attribute
18227 *
18228 * The array tblInfo->attnames[] only provides names of user attributes;
18229 * if a system attribute number is supplied, we have to fake it.
18230 * We also do a little bit of bounds checking for safety's sake.
18231 */
18232static const char *
18233getAttrName(int attrnum, const TableInfo *tblInfo)
18234{
18235 if (attrnum > 0 && attrnum <= tblInfo->numatts)
18236 return tblInfo->attnames[attrnum - 1];
18237 switch (attrnum)
18238 {
18240 return "ctid";
18242 return "xmin";
18244 return "cmin";
18246 return "xmax";
18248 return "cmax";
18250 return "tableoid";
18251 }
18252 pg_fatal("invalid column number %d for table \"%s\"",
18253 attrnum, tblInfo->dobj.name);
18254 return NULL; /* keep compiler quiet */
18255}
18256
18257/*
18258 * dumpIndex
18259 * write out to fout a user-defined index
18260 */
18261static void
18263{
18264 DumpOptions *dopt = fout->dopt;
18265 TableInfo *tbinfo = indxinfo->indextable;
18266 bool is_constraint = (indxinfo->indexconstraint != 0);
18267 PQExpBuffer q;
18269 char *qindxname;
18270 char *qqindxname;
18271
18272 /* Do nothing if not dumping schema */
18273 if (!dopt->dumpSchema)
18274 return;
18275
18276 q = createPQExpBuffer();
18278
18279 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18281
18282 /*
18283 * If there's an associated constraint, don't dump the index per se, but
18284 * do dump any comment for it. (This is safe because dependency ordering
18285 * will have ensured the constraint is emitted first.) Note that the
18286 * emitted comment has to be shown as depending on the constraint, not the
18287 * index, in such cases.
18288 */
18289 if (!is_constraint)
18290 {
18291 char *indstatcols = indxinfo->indstatcols;
18292 char *indstatvals = indxinfo->indstatvals;
18293 char **indstatcolsarray = NULL;
18294 char **indstatvalsarray = NULL;
18295 int nstatcols = 0;
18296 int nstatvals = 0;
18297
18298 if (dopt->binary_upgrade)
18300 indxinfo->dobj.catId.oid);
18301
18302 /* Plain secondary index */
18303 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18304
18305 /*
18306 * Append ALTER TABLE commands as needed to set properties that we
18307 * only have ALTER TABLE syntax for. Keep this in sync with the
18308 * similar code in dumpConstraint!
18309 */
18310
18311 /* If the index is clustered, we need to record that. */
18312 if (indxinfo->indisclustered)
18313 {
18314 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18316 /* index name is not qualified in this syntax */
18317 appendPQExpBuffer(q, " ON %s;\n",
18318 qindxname);
18319 }
18320
18321 /*
18322 * If the index has any statistics on some of its columns, generate
18323 * the associated ALTER INDEX queries.
18324 */
18325 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18326 {
18327 int j;
18328
18329 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18330 pg_fatal("could not parse index statistic columns");
18331 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18332 pg_fatal("could not parse index statistic values");
18333 if (nstatcols != nstatvals)
18334 pg_fatal("mismatched number of columns and values for index statistics");
18335
18336 for (j = 0; j < nstatcols; j++)
18337 {
18338 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18339
18340 /*
18341 * Note that this is a column number, so no quotes should be
18342 * used.
18343 */
18344 appendPQExpBuffer(q, "ALTER COLUMN %s ",
18346 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18348 }
18349 }
18350
18351 /* Indexes can depend on extensions */
18353 "pg_catalog.pg_class",
18354 "INDEX", qqindxname);
18355
18356 /* If the index defines identity, we need to record that. */
18357 if (indxinfo->indisreplident)
18358 {
18359 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18361 /* index name is not qualified in this syntax */
18362 appendPQExpBuffer(q, " INDEX %s;\n",
18363 qindxname);
18364 }
18365
18366 /*
18367 * If this index is a member of a partitioned index, the backend will
18368 * not allow us to drop it separately, so don't try. It will go away
18369 * automatically when we drop either the index's table or the
18370 * partitioned index. (If, in a selective restore with --clean, we
18371 * drop neither of those, then this index will not be dropped either.
18372 * But that's fine, and even if you think it's not, the backend won't
18373 * let us do differently.)
18374 */
18375 if (indxinfo->parentidx == 0)
18376 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18377
18378 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18379 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18380 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18381 .namespace = tbinfo->dobj.namespace->dobj.name,
18382 .tablespace = indxinfo->tablespace,
18383 .owner = tbinfo->rolname,
18384 .description = "INDEX",
18385 .section = SECTION_POST_DATA,
18386 .createStmt = q->data,
18387 .dropStmt = delq->data));
18388
18391 }
18392
18393 /* Dump Index Comments */
18394 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18395 dumpComment(fout, "INDEX", qindxname,
18396 tbinfo->dobj.namespace->dobj.name,
18397 tbinfo->rolname,
18398 indxinfo->dobj.catId, 0,
18399 is_constraint ? indxinfo->indexconstraint :
18400 indxinfo->dobj.dumpId);
18401
18406}
18407
18408/*
18409 * dumpIndexAttach
18410 * write out to fout a partitioned-index attachment clause
18411 */
18412static void
18414{
18415 /* Do nothing if not dumping schema */
18416 if (!fout->dopt->dumpSchema)
18417 return;
18418
18419 if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18420 {
18422
18423 appendPQExpBuffer(q, "ALTER INDEX %s ",
18424 fmtQualifiedDumpable(attachinfo->parentIdx));
18425 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18426 fmtQualifiedDumpable(attachinfo->partitionIdx));
18427
18428 /*
18429 * There is no need for a dropStmt since the drop is done implicitly
18430 * when we drop either the index's table or the partitioned index.
18431 * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18432 * there's no way to do it anyway. (If you think to change this,
18433 * consider also what to do with --if-exists.)
18434 *
18435 * Although this object doesn't really have ownership as such, set the
18436 * owner field anyway to ensure that the command is run by the correct
18437 * role at restore time.
18438 */
18439 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18440 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18441 .namespace = attachinfo->dobj.namespace->dobj.name,
18442 .owner = attachinfo->parentIdx->indextable->rolname,
18443 .description = "INDEX ATTACH",
18444 .section = SECTION_POST_DATA,
18445 .createStmt = q->data));
18446
18448 }
18449}
18450
18451/*
18452 * dumpStatisticsExt
18453 * write out to fout an extended statistics object
18454 */
18455static void
18457{
18458 DumpOptions *dopt = fout->dopt;
18459 PQExpBuffer q;
18461 PQExpBuffer query;
18462 char *qstatsextname;
18463 PGresult *res;
18464 char *stxdef;
18465
18466 /* Do nothing if not dumping schema */
18467 if (!dopt->dumpSchema)
18468 return;
18469
18470 q = createPQExpBuffer();
18472 query = createPQExpBuffer();
18473
18475
18476 appendPQExpBuffer(query, "SELECT "
18477 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18478 statsextinfo->dobj.catId.oid);
18479
18480 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18481
18482 stxdef = PQgetvalue(res, 0, 0);
18483
18484 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18485 appendPQExpBuffer(q, "%s;\n", stxdef);
18486
18487 /*
18488 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18489 * for this statistics object is not the default value.
18490 */
18491 if (statsextinfo->stattarget >= 0)
18492 {
18493 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18495 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18496 statsextinfo->stattarget);
18497 }
18498
18499 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18501
18502 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18503 ArchiveEntry(fout, statsextinfo->dobj.catId,
18504 statsextinfo->dobj.dumpId,
18505 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18506 .namespace = statsextinfo->dobj.namespace->dobj.name,
18507 .owner = statsextinfo->rolname,
18508 .description = "STATISTICS",
18509 .section = SECTION_POST_DATA,
18510 .createStmt = q->data,
18511 .dropStmt = delq->data));
18512
18513 /* Dump Statistics Comments */
18514 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18515 dumpComment(fout, "STATISTICS", qstatsextname,
18516 statsextinfo->dobj.namespace->dobj.name,
18517 statsextinfo->rolname,
18518 statsextinfo->dobj.catId, 0,
18519 statsextinfo->dobj.dumpId);
18520
18521 PQclear(res);
18524 destroyPQExpBuffer(query);
18526}
18527
18528/*
18529 * dumpStatisticsExtStats
18530 * write out to fout the stats for an extended statistics object
18531 */
18532static void
18534{
18535 DumpOptions *dopt = fout->dopt;
18536 PQExpBuffer query;
18537 PGresult *res;
18538 int nstats;
18539
18540 /* Do nothing if not dumping statistics */
18541 if (!dopt->dumpStatistics)
18542 return;
18543
18544 if (!fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS])
18545 {
18547
18548 /*---------
18549 * Set up query for details about extended statistics objects.
18550 *
18551 * The query depends on the backend version:
18552 * - In v19 and newer versions, query directly the pg_stats_ext*
18553 * catalogs.
18554 * - In v18 and older versions, ndistinct and dependencies have a
18555 * different format that needs translation.
18556 * - In v14 and older versions, inherited does not exist.
18557 * - In v11 and older versions, there is no pg_stats_ext, hence
18558 * the logic joins pg_statistic_ext and pg_namespace.
18559 *---------
18560 */
18561
18563 "PREPARE getExtStatsStats(pg_catalog.name, pg_catalog.name) AS\n"
18564 "SELECT ");
18565
18566 /*
18567 * Versions 15 and newer have inherited stats.
18568 *
18569 * Create this column in all versions because we need to order by it
18570 * later.
18571 */
18572 if (fout->remoteVersion >= 150000)
18573 appendPQExpBufferStr(pq, "e.inherited, ");
18574 else
18575 appendPQExpBufferStr(pq, "false AS inherited, ");
18576
18577 /*--------
18578 * The ndistinct and dependencies formats changed in v19, so
18579 * everything before that needs to be translated.
18580 *
18581 * The ndistinct translation converts this kind of data:
18582 * {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
18583 *
18584 * to this:
18585 * [ {"attributes": [3,4], "ndistinct": 11},
18586 * {"attributes": [3,6], "ndistinct": 11},
18587 * {"attributes": [4,6], "ndistinct": 11},
18588 * {"attributes": [3,4,6], "ndistinct": 11} ]
18589 *
18590 * The dependencies translation converts this kind of data:
18591 * {"3 => 4": 1.000000, "3 => 6": 1.000000,
18592 * "4 => 6": 1.000000, "3, 4 => 6": 1.000000,
18593 * "3, 6 => 4": 1.000000}
18594 *
18595 * to this:
18596 * [ {"attributes": [3], "dependency": 4, "degree": 1.000000},
18597 * {"attributes": [3], "dependency": 6, "degree": 1.000000},
18598 * {"attributes": [4], "dependency": 6, "degree": 1.000000},
18599 * {"attributes": [3,4], "dependency": 6, "degree": 1.000000},
18600 * {"attributes": [3,6], "dependency": 4, "degree": 1.000000} ]
18601 *--------
18602 */
18603 if (fout->remoteVersion >= 190000)
18604 appendPQExpBufferStr(pq, "e.n_distinct, e.dependencies, ");
18605 else
18607 "( "
18608 "SELECT json_agg( "
18609 " json_build_object( "
18611 " string_to_array(kv.key, ', ')::integer[], "
18613 " kv.value::bigint )) "
18614 "FROM json_each_text(e.n_distinct::text::json) AS kv"
18615 ") AS n_distinct, "
18616 "( "
18617 "SELECT json_agg( "
18618 " json_build_object( "
18620 " string_to_array( "
18621 " split_part(kv.key, ' => ', 1), "
18622 " ', ')::integer[], "
18624 " split_part(kv.key, ' => ', 2)::integer, "
18626 " kv.value::double precision )) "
18627 "FROM json_each_text(e.dependencies::text::json) AS kv "
18628 ") AS dependencies, ");
18629
18630 /* MCV was introduced v13 */
18631 if (fout->remoteVersion >= 130000)
18633 "e.most_common_vals, e.most_common_freqs, "
18634 "e.most_common_base_freqs, ");
18635 else
18637 "NULL AS most_common_vals, NULL AS most_common_freqs, "
18638 "NULL AS most_common_base_freqs, ");
18639
18640 /* Expressions were introduced in v14 */
18641 if (fout->remoteVersion >= 140000)
18642 {
18643 /*
18644 * There is no ordering column in pg_stats_ext_exprs. However, we
18645 * can rely on the unnesting of pg_statistic_ext_data.stxdexpr to
18646 * maintain the desired order of expression elements.
18647 */
18649 "( "
18650 "SELECT jsonb_pretty(jsonb_agg("
18651 "nullif(j.obj, '{}'::jsonb))) "
18652 "FROM pg_stats_ext_exprs AS ee "
18653 "CROSS JOIN LATERAL jsonb_strip_nulls("
18654 " jsonb_build_object( "
18655 " 'null_frac', ee.null_frac::text, "
18656 " 'avg_width', ee.avg_width::text, "
18657 " 'n_distinct', ee.n_distinct::text, "
18658 " 'most_common_vals', ee.most_common_vals::text, "
18659 " 'most_common_freqs', ee.most_common_freqs::text, "
18660 " 'histogram_bounds', ee.histogram_bounds::text, "
18661 " 'correlation', ee.correlation::text, "
18662 " 'most_common_elems', ee.most_common_elems::text, "
18663 " 'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
18664 " 'elem_count_histogram', ee.elem_count_histogram::text");
18665
18666 /* These three have been added to pg_stats_ext_exprs in v19. */
18667 if (fout->remoteVersion >= 190000)
18669 ", "
18670 " 'range_length_histogram', ee.range_length_histogram::text, "
18671 " 'range_empty_frac', ee.range_empty_frac::text, "
18672 " 'range_bounds_histogram', ee.range_bounds_histogram::text");
18673
18675 " )) AS j(obj)"
18676 "WHERE ee.statistics_schemaname = $1 "
18677 "AND ee.statistics_name = $2 ");
18678 /* Inherited expressions introduced in v15 */
18679 if (fout->remoteVersion >= 150000)
18680 appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
18681
18682 appendPQExpBufferStr(pq, ") AS exprs ");
18683 }
18684 else
18685 appendPQExpBufferStr(pq, "NULL AS exprs ");
18686
18687 /* pg_stats_ext introduced in v12 */
18688 if (fout->remoteVersion >= 120000)
18690 "FROM pg_catalog.pg_stats_ext AS e "
18691 "WHERE e.statistics_schemaname = $1 "
18692 "AND e.statistics_name = $2 ");
18693 else
18695 "FROM ( "
18696 "SELECT s.stxndistinct AS n_distinct, "
18697 " s.stxdependencies AS dependencies "
18698 "FROM pg_catalog.pg_statistic_ext AS s "
18699 "JOIN pg_catalog.pg_namespace AS n "
18700 "ON n.oid = s.stxnamespace "
18701 "WHERE n.nspname = $1 "
18702 "AND s.stxname = $2 "
18703 ") AS e ");
18704
18705 /* we always have an inherited column, but it may be a constant */
18706 appendPQExpBufferStr(pq, "ORDER BY inherited");
18707
18708 ExecuteSqlStatement(fout, pq->data);
18709
18710 fout->is_prepared[PREPQUERY_DUMPEXTSTATSOBJSTATS] = true;
18711
18713 }
18714
18715 query = createPQExpBuffer();
18716
18717 appendPQExpBufferStr(query, "EXECUTE getExtStatsStats(");
18718 appendStringLiteralAH(query, statsextinfo->dobj.namespace->dobj.name, fout);
18719 appendPQExpBufferStr(query, "::pg_catalog.name, ");
18720 appendStringLiteralAH(query, statsextinfo->dobj.name, fout);
18721 appendPQExpBufferStr(query, "::pg_catalog.name)");
18722
18723 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18724
18725 destroyPQExpBuffer(query);
18726
18727 nstats = PQntuples(res);
18728
18729 if (nstats > 0)
18730 {
18732
18733 int i_inherited = PQfnumber(res, "inherited");
18734 int i_ndistinct = PQfnumber(res, "n_distinct");
18735 int i_dependencies = PQfnumber(res, "dependencies");
18736 int i_mcv = PQfnumber(res, "most_common_vals");
18737 int i_mcf = PQfnumber(res, "most_common_freqs");
18738 int i_mcbf = PQfnumber(res, "most_common_base_freqs");
18739 int i_exprs = PQfnumber(res, "exprs");
18740
18741 for (int i = 0; i < nstats; i++)
18742 {
18743 TableInfo *tbinfo = statsextinfo->stattable;
18744
18745 if (PQgetisnull(res, i, i_inherited))
18746 pg_fatal("inherited cannot be NULL");
18747
18749 "SELECT * FROM pg_catalog.pg_restore_extended_stats(\n");
18750 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
18751 fout->remoteVersion);
18752
18753 /* Relation information */
18754 appendPQExpBufferStr(out, "\t'schemaname', ");
18755 appendStringLiteralAH(out, tbinfo->dobj.namespace->dobj.name, fout);
18756 appendPQExpBufferStr(out, ",\n\t'relname', ");
18757 appendStringLiteralAH(out, tbinfo->dobj.name, fout);
18758
18759 /* Extended statistics information */
18760 appendPQExpBufferStr(out, ",\n\t'statistics_schemaname', ");
18761 appendStringLiteralAH(out, statsextinfo->dobj.namespace->dobj.name, fout);
18762 appendPQExpBufferStr(out, ",\n\t'statistics_name', ");
18763 appendStringLiteralAH(out, statsextinfo->dobj.name, fout);
18764 appendNamedArgument(out, fout, "inherited", "boolean",
18765 PQgetvalue(res, i, i_inherited));
18766
18767 if (!PQgetisnull(res, i, i_ndistinct))
18768 appendNamedArgument(out, fout, "n_distinct", "pg_ndistinct",
18769 PQgetvalue(res, i, i_ndistinct));
18770
18771 if (!PQgetisnull(res, i, i_dependencies))
18772 appendNamedArgument(out, fout, "dependencies", "pg_dependencies",
18773 PQgetvalue(res, i, i_dependencies));
18774
18775 if (!PQgetisnull(res, i, i_mcv))
18776 appendNamedArgument(out, fout, "most_common_vals", "text[]",
18777 PQgetvalue(res, i, i_mcv));
18778
18779 if (!PQgetisnull(res, i, i_mcf))
18780 appendNamedArgument(out, fout, "most_common_freqs", "double precision[]",
18781 PQgetvalue(res, i, i_mcf));
18782
18783 if (!PQgetisnull(res, i, i_mcbf))
18784 appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
18785 PQgetvalue(res, i, i_mcbf));
18786
18787 if (!PQgetisnull(res, i, i_exprs))
18788 appendNamedArgument(out, fout, "exprs", "jsonb",
18789 PQgetvalue(res, i, i_exprs));
18790
18791 appendPQExpBufferStr(out, "\n);\n");
18792 }
18793
18795 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18796 .namespace = statsextinfo->dobj.namespace->dobj.name,
18797 .owner = statsextinfo->rolname,
18798 .description = "EXTENDED STATISTICS DATA",
18799 .section = SECTION_POST_DATA,
18800 .createStmt = out->data,
18801 .deps = &statsextinfo->dobj.dumpId,
18802 .nDeps = 1));
18803 destroyPQExpBuffer(out);
18804 }
18805 PQclear(res);
18806}
18807
18808/*
18809 * dumpConstraint
18810 * write out to fout a user-defined constraint
18811 */
18812static void
18814{
18815 DumpOptions *dopt = fout->dopt;
18816 TableInfo *tbinfo = coninfo->contable;
18817 PQExpBuffer q;
18819 char *tag = NULL;
18820 char *foreign;
18821
18822 /* Do nothing if not dumping schema */
18823 if (!dopt->dumpSchema)
18824 return;
18825
18826 q = createPQExpBuffer();
18828
18829 foreign = tbinfo &&
18830 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18831
18832 if (coninfo->contype == 'p' ||
18833 coninfo->contype == 'u' ||
18834 coninfo->contype == 'x')
18835 {
18836 /* Index-related constraint */
18838 int k;
18839
18841
18842 if (indxinfo == NULL)
18843 pg_fatal("missing index for constraint \"%s\"",
18844 coninfo->dobj.name);
18845
18846 if (dopt->binary_upgrade)
18848 indxinfo->dobj.catId.oid);
18849
18850 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18852 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18853 fmtId(coninfo->dobj.name));
18854
18855 if (coninfo->condef)
18856 {
18857 /* pg_get_constraintdef should have provided everything */
18858 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18859 }
18860 else
18861 {
18863 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18864
18865 /*
18866 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18867 * indexes. Being able to create this was fixed, but we need to
18868 * make the index distinct in order to be able to restore the
18869 * dump.
18870 */
18871 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18872 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18873 appendPQExpBufferStr(q, " (");
18874 for (k = 0; k < indxinfo->indnkeyattrs; k++)
18875 {
18876 int indkey = (int) indxinfo->indkeys[k];
18877 const char *attname;
18878
18880 break;
18882
18883 appendPQExpBuffer(q, "%s%s",
18884 (k == 0) ? "" : ", ",
18885 fmtId(attname));
18886 }
18887 if (coninfo->conperiod)
18888 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18889
18890 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18891 appendPQExpBufferStr(q, ") INCLUDE (");
18892
18893 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18894 {
18895 int indkey = (int) indxinfo->indkeys[k];
18896 const char *attname;
18897
18899 break;
18901
18902 appendPQExpBuffer(q, "%s%s",
18903 (k == indxinfo->indnkeyattrs) ? "" : ", ",
18904 fmtId(attname));
18905 }
18906
18907 appendPQExpBufferChar(q, ')');
18908
18909 if (nonemptyReloptions(indxinfo->indreloptions))
18910 {
18911 appendPQExpBufferStr(q, " WITH (");
18912 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18913 appendPQExpBufferChar(q, ')');
18914 }
18915
18916 if (coninfo->condeferrable)
18917 {
18918 appendPQExpBufferStr(q, " DEFERRABLE");
18919 if (coninfo->condeferred)
18920 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18921 }
18922
18923 appendPQExpBufferStr(q, ";\n");
18924 }
18925
18926 /*
18927 * Append ALTER TABLE commands as needed to set properties that we
18928 * only have ALTER TABLE syntax for. Keep this in sync with the
18929 * similar code in dumpIndex!
18930 */
18931
18932 /* If the index is clustered, we need to record that. */
18933 if (indxinfo->indisclustered)
18934 {
18935 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18937 /* index name is not qualified in this syntax */
18938 appendPQExpBuffer(q, " ON %s;\n",
18939 fmtId(indxinfo->dobj.name));
18940 }
18941
18942 /* If the index defines identity, we need to record that. */
18943 if (indxinfo->indisreplident)
18944 {
18945 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18947 /* index name is not qualified in this syntax */
18948 appendPQExpBuffer(q, " INDEX %s;\n",
18949 fmtId(indxinfo->dobj.name));
18950 }
18951
18952 /* Indexes can depend on extensions */
18954 "pg_catalog.pg_class", "INDEX",
18956
18957 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18959 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18960 fmtId(coninfo->dobj.name));
18961
18962 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18963
18964 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18965 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18966 ARCHIVE_OPTS(.tag = tag,
18967 .namespace = tbinfo->dobj.namespace->dobj.name,
18968 .tablespace = indxinfo->tablespace,
18969 .owner = tbinfo->rolname,
18970 .description = "CONSTRAINT",
18971 .section = SECTION_POST_DATA,
18972 .createStmt = q->data,
18973 .dropStmt = delq->data));
18974 }
18975 else if (coninfo->contype == 'f')
18976 {
18977 char *only;
18978
18979 /*
18980 * Foreign keys on partitioned tables are always declared as
18981 * inheriting to partitions; for all other cases, emit them as
18982 * applying ONLY directly to the named table, because that's how they
18983 * work for regular inherited tables.
18984 */
18985 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18986
18987 /*
18988 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18989 * current table data is not processed
18990 */
18991 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18993 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18994 fmtId(coninfo->dobj.name),
18995 coninfo->condef);
18996
18997 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18999 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19000 fmtId(coninfo->dobj.name));
19001
19002 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19003
19004 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19005 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19006 ARCHIVE_OPTS(.tag = tag,
19007 .namespace = tbinfo->dobj.namespace->dobj.name,
19008 .owner = tbinfo->rolname,
19009 .description = "FK CONSTRAINT",
19010 .section = SECTION_POST_DATA,
19011 .createStmt = q->data,
19012 .dropStmt = delq->data));
19013 }
19014 else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
19015 {
19016 /* CHECK or invalid not-null constraint on a table */
19017
19018 /* Ignore if not to be dumped separately, or if it was inherited */
19019 if (coninfo->separate && coninfo->conislocal)
19020 {
19021 const char *keyword;
19022
19023 if (coninfo->contype == 'c')
19024 keyword = "CHECK CONSTRAINT";
19025 else
19026 keyword = "CONSTRAINT";
19027
19028 /* not ONLY since we want it to propagate to children */
19029 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
19031 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19032 fmtId(coninfo->dobj.name),
19033 coninfo->condef);
19034
19035 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
19037 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19038 fmtId(coninfo->dobj.name));
19039
19040 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19041
19042 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19043 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19044 ARCHIVE_OPTS(.tag = tag,
19045 .namespace = tbinfo->dobj.namespace->dobj.name,
19046 .owner = tbinfo->rolname,
19047 .description = keyword,
19048 .section = SECTION_POST_DATA,
19049 .createStmt = q->data,
19050 .dropStmt = delq->data));
19051 }
19052 }
19053 else if (tbinfo == NULL)
19054 {
19055 /* CHECK, NOT NULL constraint on a domain */
19056 TypeInfo *tyinfo = coninfo->condomain;
19057
19058 Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
19059
19060 /* Ignore if not to be dumped separately */
19061 if (coninfo->separate)
19062 {
19063 const char *keyword;
19064
19065 if (coninfo->contype == 'c')
19066 keyword = "CHECK CONSTRAINT";
19067 else
19068 keyword = "CONSTRAINT";
19069
19070 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
19072 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19073 fmtId(coninfo->dobj.name),
19074 coninfo->condef);
19075
19076 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
19078 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19079 fmtId(coninfo->dobj.name));
19080
19081 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
19082
19083 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19084 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19085 ARCHIVE_OPTS(.tag = tag,
19086 .namespace = tyinfo->dobj.namespace->dobj.name,
19087 .owner = tyinfo->rolname,
19088 .description = keyword,
19089 .section = SECTION_POST_DATA,
19090 .createStmt = q->data,
19091 .dropStmt = delq->data));
19092
19093 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19094 {
19096 char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
19097
19098 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
19099 fmtId(coninfo->dobj.name));
19100
19102 tyinfo->dobj.namespace->dobj.name,
19103 tyinfo->rolname,
19104 coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
19107 }
19108 }
19109 }
19110 else
19111 {
19112 pg_fatal("unrecognized constraint type: %c",
19113 coninfo->contype);
19114 }
19115
19116 /* Dump Constraint Comments --- only works for table constraints */
19117 if (tbinfo && coninfo->separate &&
19118 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19120
19121 pfree(tag);
19124}
19125
19126/*
19127 * dumpTableConstraintComment --- dump a constraint's comment if any
19128 *
19129 * This is split out because we need the function in two different places
19130 * depending on whether the constraint is dumped as part of CREATE TABLE
19131 * or as a separate ALTER command.
19132 */
19133static void
19135{
19136 TableInfo *tbinfo = coninfo->contable;
19138 char *qtabname;
19139
19140 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19141
19142 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
19143 fmtId(coninfo->dobj.name));
19144
19145 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19147 tbinfo->dobj.namespace->dobj.name,
19148 tbinfo->rolname,
19149 coninfo->dobj.catId, 0,
19150 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
19151
19154}
19155
19156static inline SeqType
19158{
19159 for (size_t i = 0; i < lengthof(SeqTypeNames); i++)
19160 {
19161 if (strcmp(SeqTypeNames[i], name) == 0)
19162 return (SeqType) i;
19163 }
19164
19165 pg_fatal("unrecognized sequence type: %s", name);
19166 return (SeqType) 0; /* keep compiler quiet */
19167}
19168
19169/*
19170 * bsearch() comparator for SequenceItem
19171 */
19172static int
19173SequenceItemCmp(const void *p1, const void *p2)
19174{
19175 SequenceItem v1 = *((const SequenceItem *) p1);
19176 SequenceItem v2 = *((const SequenceItem *) p2);
19177
19178 return pg_cmp_u32(v1.oid, v2.oid);
19179}
19180
19181/*
19182 * collectSequences
19183 *
19184 * Construct a table of sequence information. This table is sorted by OID for
19185 * speed in lookup.
19186 */
19187static void
19189{
19190 PGresult *res;
19191 const char *query;
19192
19193 /*
19194 * Since version 18, we can gather the sequence data in this query with
19195 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19196 */
19197 if (fout->remoteVersion < 180000 ||
19198 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
19199 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19200 "seqstart, seqincrement, "
19201 "seqmax, seqmin, "
19202 "seqcache, seqcycle, "
19203 "NULL, 'f' "
19204 "FROM pg_catalog.pg_sequence "
19205 "ORDER BY seqrelid";
19206 else
19207 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19208 "seqstart, seqincrement, "
19209 "seqmax, seqmin, "
19210 "seqcache, seqcycle, "
19211 "last_value, is_called "
19212 "FROM pg_catalog.pg_sequence, "
19213 "pg_get_sequence_data(seqrelid) "
19214 "ORDER BY seqrelid;";
19215
19216 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19217
19218 nsequences = PQntuples(res);
19220
19221 for (int i = 0; i < nsequences; i++)
19222 {
19223 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19225 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19226 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19227 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19228 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19229 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19230 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19231 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19232 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19233 sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19234 }
19235
19236 PQclear(res);
19237}
19238
19239/*
19240 * dumpSequence
19241 * write the declaration (not data) of one user-defined sequence
19242 */
19243static void
19245{
19246 DumpOptions *dopt = fout->dopt;
19248 bool is_ascending;
19253 char *qseqname;
19254 TableInfo *owning_tab = NULL;
19255 SequenceItem key = {0};
19256
19257 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
19258
19259 /*
19260 * The sequence information is gathered in a sorted table before any calls
19261 * to dumpSequence(). See collectSequences() for more information.
19262 */
19264
19265 key.oid = tbinfo->dobj.catId.oid;
19267 sizeof(SequenceItem), SequenceItemCmp);
19268
19269 /* Calculate default limits for a sequence of this type */
19270 is_ascending = (seq->incby >= 0);
19271 if (seq->seqtype == SEQTYPE_SMALLINT)
19272 {
19275 }
19276 else if (seq->seqtype == SEQTYPE_INTEGER)
19277 {
19280 }
19281 else if (seq->seqtype == SEQTYPE_BIGINT)
19282 {
19285 }
19286 else
19287 {
19288 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19289 default_minv = default_maxv = 0; /* keep compiler quiet */
19290 }
19291
19292 /*
19293 * Identity sequences are not to be dropped separately.
19294 */
19295 if (!tbinfo->is_identity_sequence)
19296 {
19297 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19299 }
19300
19301 resetPQExpBuffer(query);
19302
19303 if (dopt->binary_upgrade)
19304 {
19306 tbinfo->dobj.catId.oid);
19307
19308 /*
19309 * In older PG versions a sequence will have a pg_type entry, but v14
19310 * and up don't use that, so don't attempt to preserve the type OID.
19311 */
19312 }
19313
19314 if (tbinfo->is_identity_sequence)
19315 {
19316 owning_tab = findTableByOid(tbinfo->owning_tab);
19317
19318 appendPQExpBuffer(query,
19319 "ALTER TABLE %s ",
19320 fmtQualifiedDumpable(owning_tab));
19321 appendPQExpBuffer(query,
19322 "ALTER COLUMN %s ADD GENERATED ",
19323 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19324 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19325 appendPQExpBufferStr(query, "ALWAYS");
19326 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19327 appendPQExpBufferStr(query, "BY DEFAULT");
19328 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19330
19331 /*
19332 * Emit persistence option only if it's different from the owning
19333 * table's. This avoids using this new syntax unnecessarily.
19334 */
19335 if (tbinfo->relpersistence != owning_tab->relpersistence)
19336 appendPQExpBuffer(query, " %s\n",
19337 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19338 "UNLOGGED" : "LOGGED");
19339 }
19340 else
19341 {
19342 appendPQExpBuffer(query,
19343 "CREATE %sSEQUENCE %s\n",
19344 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19345 "UNLOGGED " : "",
19347
19348 if (seq->seqtype != SEQTYPE_BIGINT)
19349 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19350 }
19351
19352 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19353
19354 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19355
19356 if (seq->minv != default_minv)
19357 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19358 else
19359 appendPQExpBufferStr(query, " NO MINVALUE\n");
19360
19361 if (seq->maxv != default_maxv)
19362 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19363 else
19364 appendPQExpBufferStr(query, " NO MAXVALUE\n");
19365
19366 appendPQExpBuffer(query,
19367 " CACHE " INT64_FORMAT "%s",
19368 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19369
19370 if (tbinfo->is_identity_sequence)
19371 appendPQExpBufferStr(query, "\n);\n");
19372 else
19373 appendPQExpBufferStr(query, ";\n");
19374
19375 /* binary_upgrade: no need to clear TOAST table oid */
19376
19377 if (dopt->binary_upgrade)
19379 "SEQUENCE", qseqname,
19380 tbinfo->dobj.namespace->dobj.name);
19381
19382 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19383 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19384 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19385 .namespace = tbinfo->dobj.namespace->dobj.name,
19386 .owner = tbinfo->rolname,
19387 .description = "SEQUENCE",
19388 .section = SECTION_PRE_DATA,
19389 .createStmt = query->data,
19390 .dropStmt = delqry->data));
19391
19392 /*
19393 * If the sequence is owned by a table column, emit the ALTER for it as a
19394 * separate TOC entry immediately following the sequence's own entry. It's
19395 * OK to do this rather than using full sorting logic, because the
19396 * dependency that tells us it's owned will have forced the table to be
19397 * created first. We can't just include the ALTER in the TOC entry
19398 * because it will fail if we haven't reassigned the sequence owner to
19399 * match the table's owner.
19400 *
19401 * We need not schema-qualify the table reference because both sequence
19402 * and table must be in the same schema.
19403 */
19404 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19405 {
19406 owning_tab = findTableByOid(tbinfo->owning_tab);
19407
19408 if (owning_tab == NULL)
19409 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19410 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19411
19412 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19413 {
19414 resetPQExpBuffer(query);
19415 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19417 appendPQExpBuffer(query, " OWNED BY %s",
19418 fmtQualifiedDumpable(owning_tab));
19419 appendPQExpBuffer(query, ".%s;\n",
19420 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19421
19422 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19424 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19425 .namespace = tbinfo->dobj.namespace->dobj.name,
19426 .owner = tbinfo->rolname,
19427 .description = "SEQUENCE OWNED BY",
19428 .section = SECTION_PRE_DATA,
19429 .createStmt = query->data,
19430 .deps = &(tbinfo->dobj.dumpId),
19431 .nDeps = 1));
19432 }
19433 }
19434
19435 /* Dump Sequence Comments and Security Labels */
19436 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19437 dumpComment(fout, "SEQUENCE", qseqname,
19438 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19439 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19440
19441 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19442 dumpSecLabel(fout, "SEQUENCE", qseqname,
19443 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19444 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19445
19446 destroyPQExpBuffer(query);
19449}
19450
19451/*
19452 * dumpSequenceData
19453 * write the data of one user-defined sequence
19454 */
19455static void
19457{
19458 TableInfo *tbinfo = tdinfo->tdtable;
19459 int64 last;
19460 bool called;
19461 PQExpBuffer query;
19462
19463 /* needn't bother if not dumping sequence data */
19464 if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19465 return;
19466
19467 query = createPQExpBuffer();
19468
19469 /*
19470 * For versions >= 18, the sequence information is gathered in the sorted
19471 * array before any calls to dumpSequenceData(). See collectSequences()
19472 * for more information.
19473 *
19474 * For older versions, we have to query the sequence relations
19475 * individually.
19476 */
19477 if (fout->remoteVersion < 180000)
19478 {
19479 PGresult *res;
19480
19481 appendPQExpBuffer(query,
19482 "SELECT last_value, is_called FROM %s",
19484
19485 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19486
19487 if (PQntuples(res) != 1)
19488 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19489 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19490 PQntuples(res)),
19491 tbinfo->dobj.name, PQntuples(res));
19492
19493 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19494 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19495
19496 PQclear(res);
19497 }
19498 else
19499 {
19500 SequenceItem key = {0};
19501 SequenceItem *entry;
19502
19504 Assert(tbinfo->dobj.catId.oid);
19505
19506 key.oid = tbinfo->dobj.catId.oid;
19507 entry = bsearch(&key, sequences, nsequences,
19508 sizeof(SequenceItem), SequenceItemCmp);
19509
19510 if (entry->null_seqtuple)
19511 pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19512 "SELECT privilege on the sequence or the sequence may "
19513 "have been concurrently dropped",
19514 tbinfo->dobj.name);
19515
19516 last = entry->last_value;
19517 called = entry->is_called;
19518 }
19519
19520 resetPQExpBuffer(query);
19521 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19523 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19524 last, (called ? "true" : "false"));
19525
19526 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19528 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19529 .namespace = tbinfo->dobj.namespace->dobj.name,
19530 .owner = tbinfo->rolname,
19531 .description = "SEQUENCE SET",
19532 .section = SECTION_DATA,
19533 .createStmt = query->data,
19534 .deps = &(tbinfo->dobj.dumpId),
19535 .nDeps = 1));
19536
19537 destroyPQExpBuffer(query);
19538}
19539
19540/*
19541 * dumpTrigger
19542 * write the declaration of one user-defined table trigger
19543 */
19544static void
19546{
19547 DumpOptions *dopt = fout->dopt;
19548 TableInfo *tbinfo = tginfo->tgtable;
19549 PQExpBuffer query;
19553 char *qtabname;
19554 char *tag;
19555
19556 /* Do nothing if not dumping schema */
19557 if (!dopt->dumpSchema)
19558 return;
19559
19560 query = createPQExpBuffer();
19564
19565 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19566
19567 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19569
19570 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19571 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19572
19573 /* Triggers can depend on extensions */
19575 "pg_catalog.pg_trigger", "TRIGGER",
19577
19578 if (tginfo->tgispartition)
19579 {
19580 Assert(tbinfo->ispartition);
19581
19582 /*
19583 * Partition triggers only appear here because their 'tgenabled' flag
19584 * differs from its parent's. The trigger is created already, so
19585 * remove the CREATE and replace it with an ALTER. (Clear out the
19586 * DROP query too, so that pg_dump --create does not cause errors.)
19587 */
19588 resetPQExpBuffer(query);
19590 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19591 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19593 switch (tginfo->tgenabled)
19594 {
19595 case 'f':
19596 case 'D':
19597 appendPQExpBufferStr(query, "DISABLE");
19598 break;
19599 case 't':
19600 case 'O':
19601 appendPQExpBufferStr(query, "ENABLE");
19602 break;
19603 case 'R':
19604 appendPQExpBufferStr(query, "ENABLE REPLICA");
19605 break;
19606 case 'A':
19607 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19608 break;
19609 }
19610 appendPQExpBuffer(query, " TRIGGER %s;\n",
19611 fmtId(tginfo->dobj.name));
19612 }
19613 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19614 {
19615 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19616 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19618 switch (tginfo->tgenabled)
19619 {
19620 case 'D':
19621 case 'f':
19622 appendPQExpBufferStr(query, "DISABLE");
19623 break;
19624 case 'A':
19625 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19626 break;
19627 case 'R':
19628 appendPQExpBufferStr(query, "ENABLE REPLICA");
19629 break;
19630 default:
19631 appendPQExpBufferStr(query, "ENABLE");
19632 break;
19633 }
19634 appendPQExpBuffer(query, " TRIGGER %s;\n",
19635 fmtId(tginfo->dobj.name));
19636 }
19637
19638 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19639 fmtId(tginfo->dobj.name));
19640
19641 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19642
19643 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19644 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19645 ARCHIVE_OPTS(.tag = tag,
19646 .namespace = tbinfo->dobj.namespace->dobj.name,
19647 .owner = tbinfo->rolname,
19648 .description = "TRIGGER",
19649 .section = SECTION_POST_DATA,
19650 .createStmt = query->data,
19651 .dropStmt = delqry->data));
19652
19653 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19655 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19656 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19657
19658 pfree(tag);
19659 destroyPQExpBuffer(query);
19664}
19665
19666/*
19667 * dumpEventTrigger
19668 * write the declaration of one user-defined event trigger
19669 */
19670static void
19672{
19673 DumpOptions *dopt = fout->dopt;
19674 PQExpBuffer query;
19676 char *qevtname;
19677
19678 /* Do nothing if not dumping schema */
19679 if (!dopt->dumpSchema)
19680 return;
19681
19682 query = createPQExpBuffer();
19684
19685 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19686
19687 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19689 appendPQExpBufferStr(query, " ON ");
19690 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19691
19692 if (strcmp("", evtinfo->evttags) != 0)
19693 {
19694 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19695 appendPQExpBufferStr(query, evtinfo->evttags);
19696 appendPQExpBufferChar(query, ')');
19697 }
19698
19699 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19700 appendPQExpBufferStr(query, evtinfo->evtfname);
19701 appendPQExpBufferStr(query, "();\n");
19702
19703 if (evtinfo->evtenabled != 'O')
19704 {
19705 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19706 qevtname);
19707 switch (evtinfo->evtenabled)
19708 {
19709 case 'D':
19710 appendPQExpBufferStr(query, "DISABLE");
19711 break;
19712 case 'A':
19713 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19714 break;
19715 case 'R':
19716 appendPQExpBufferStr(query, "ENABLE REPLICA");
19717 break;
19718 default:
19719 appendPQExpBufferStr(query, "ENABLE");
19720 break;
19721 }
19722 appendPQExpBufferStr(query, ";\n");
19723 }
19724
19725 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19726 qevtname);
19727
19728 if (dopt->binary_upgrade)
19730 "EVENT TRIGGER", qevtname, NULL);
19731
19732 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19733 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19734 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19735 .owner = evtinfo->evtowner,
19736 .description = "EVENT TRIGGER",
19737 .section = SECTION_POST_DATA,
19738 .createStmt = query->data,
19739 .dropStmt = delqry->data));
19740
19741 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19742 dumpComment(fout, "EVENT TRIGGER", qevtname,
19743 NULL, evtinfo->evtowner,
19744 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19745
19746 if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19747 dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19748 NULL, evtinfo->evtowner,
19749 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19750
19751 destroyPQExpBuffer(query);
19754}
19755
19756/*
19757 * dumpRule
19758 * Dump a rule
19759 */
19760static void
19762{
19763 DumpOptions *dopt = fout->dopt;
19764 TableInfo *tbinfo = rinfo->ruletable;
19765 bool is_view;
19766 PQExpBuffer query;
19767 PQExpBuffer cmd;
19770 char *qtabname;
19771 PGresult *res;
19772 char *tag;
19773
19774 /* Do nothing if not dumping schema */
19775 if (!dopt->dumpSchema)
19776 return;
19777
19778 /*
19779 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19780 * we do not want to dump it as a separate object.
19781 */
19782 if (!rinfo->separate)
19783 return;
19784
19785 /*
19786 * If it's an ON SELECT rule, we want to print it as a view definition,
19787 * instead of a rule.
19788 */
19789 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19790
19791 query = createPQExpBuffer();
19792 cmd = createPQExpBuffer();
19795
19796 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19797
19798 if (is_view)
19799 {
19801
19802 /*
19803 * We need OR REPLACE here because we'll be replacing a dummy view.
19804 * Otherwise this should look largely like the regular view dump code.
19805 */
19806 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19808 if (nonemptyReloptions(tbinfo->reloptions))
19809 {
19810 appendPQExpBufferStr(cmd, " WITH (");
19811 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19812 appendPQExpBufferChar(cmd, ')');
19813 }
19815 appendPQExpBuffer(cmd, " AS\n%s", result->data);
19817 if (tbinfo->checkoption != NULL)
19818 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19819 tbinfo->checkoption);
19820 appendPQExpBufferStr(cmd, ";\n");
19821 }
19822 else
19823 {
19824 /* In the rule case, just print pg_get_ruledef's result verbatim */
19825 appendPQExpBuffer(query,
19826 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19827 rinfo->dobj.catId.oid);
19828
19829 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19830
19831 if (PQntuples(res) != 1)
19832 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19833 rinfo->dobj.name, tbinfo->dobj.name);
19834
19835 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19836
19837 PQclear(res);
19838 }
19839
19840 /*
19841 * Add the command to alter the rules replication firing semantics if it
19842 * differs from the default.
19843 */
19844 if (rinfo->ev_enabled != 'O')
19845 {
19846 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19847 switch (rinfo->ev_enabled)
19848 {
19849 case 'A':
19850 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19851 fmtId(rinfo->dobj.name));
19852 break;
19853 case 'R':
19854 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19855 fmtId(rinfo->dobj.name));
19856 break;
19857 case 'D':
19858 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19859 fmtId(rinfo->dobj.name));
19860 break;
19861 }
19862 }
19863
19864 if (is_view)
19865 {
19866 /*
19867 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19868 * REPLACE VIEW to replace the rule with something with minimal
19869 * dependencies.
19870 */
19872
19873 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19876 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19878 }
19879 else
19880 {
19881 appendPQExpBuffer(delcmd, "DROP RULE %s ",
19882 fmtId(rinfo->dobj.name));
19883 appendPQExpBuffer(delcmd, "ON %s;\n",
19885 }
19886
19887 appendPQExpBuffer(ruleprefix, "RULE %s ON",
19888 fmtId(rinfo->dobj.name));
19889
19890 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19891
19892 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19893 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19894 ARCHIVE_OPTS(.tag = tag,
19895 .namespace = tbinfo->dobj.namespace->dobj.name,
19896 .owner = tbinfo->rolname,
19897 .description = "RULE",
19898 .section = SECTION_POST_DATA,
19899 .createStmt = cmd->data,
19900 .dropStmt = delcmd->data));
19901
19902 /* Dump rule comments */
19903 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19905 tbinfo->dobj.namespace->dobj.name,
19906 tbinfo->rolname,
19907 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19908
19909 pfree(tag);
19910 destroyPQExpBuffer(query);
19911 destroyPQExpBuffer(cmd);
19915}
19916
19917/*
19918 * getExtensionMembership --- obtain extension membership data
19919 *
19920 * We need to identify objects that are extension members as soon as they're
19921 * loaded, so that we can correctly determine whether they need to be dumped.
19922 * Generally speaking, extension member objects will get marked as *not* to
19923 * be dumped, as they will be recreated by the single CREATE EXTENSION
19924 * command. However, in binary upgrade mode we still need to dump the members
19925 * individually.
19926 */
19927void
19929 int numExtensions)
19930{
19931 PQExpBuffer query;
19932 PGresult *res;
19933 int ntups,
19934 i;
19935 int i_classid,
19936 i_objid,
19937 i_refobjid;
19938 ExtensionInfo *ext;
19939
19940 /* Nothing to do if no extensions */
19941 if (numExtensions == 0)
19942 return;
19943
19944 query = createPQExpBuffer();
19945
19946 /* refclassid constraint is redundant but may speed the search */
19947 appendPQExpBufferStr(query, "SELECT "
19948 "classid, objid, refobjid "
19949 "FROM pg_depend "
19950 "WHERE refclassid = 'pg_extension'::regclass "
19951 "AND deptype = 'e' "
19952 "ORDER BY 3");
19953
19954 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19955
19956 ntups = PQntuples(res);
19957
19958 i_classid = PQfnumber(res, "classid");
19959 i_objid = PQfnumber(res, "objid");
19960 i_refobjid = PQfnumber(res, "refobjid");
19961
19962 /*
19963 * Since we ordered the SELECT by referenced ID, we can expect that
19964 * multiple entries for the same extension will appear together; this
19965 * saves on searches.
19966 */
19967 ext = NULL;
19968
19969 for (i = 0; i < ntups; i++)
19970 {
19971 CatalogId objId;
19972 Oid extId;
19973
19974 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19975 objId.oid = atooid(PQgetvalue(res, i, i_objid));
19977
19978 if (ext == NULL ||
19979 ext->dobj.catId.oid != extId)
19981
19982 if (ext == NULL)
19983 {
19984 /* shouldn't happen */
19985 pg_log_warning("could not find referenced extension %u", extId);
19986 continue;
19987 }
19988
19989 recordExtensionMembership(objId, ext);
19990 }
19991
19992 PQclear(res);
19993
19994 destroyPQExpBuffer(query);
19995}
19996
19997/*
19998 * processExtensionTables --- deal with extension configuration tables
19999 *
20000 * There are two parts to this process:
20001 *
20002 * 1. Identify and create dump records for extension configuration tables.
20003 *
20004 * Extensions can mark tables as "configuration", which means that the user
20005 * is able and expected to modify those tables after the extension has been
20006 * loaded. For these tables, we dump out only the data- the structure is
20007 * expected to be handled at CREATE EXTENSION time, including any indexes or
20008 * foreign keys, which brings us to-
20009 *
20010 * 2. Record FK dependencies between configuration tables.
20011 *
20012 * Due to the FKs being created at CREATE EXTENSION time and therefore before
20013 * the data is loaded, we have to work out what the best order for reloading
20014 * the data is, to avoid FK violations when the tables are restored. This is
20015 * not perfect- we can't handle circular dependencies and if any exist they
20016 * will cause an invalid dump to be produced (though at least all of the data
20017 * is included for a user to manually restore). This is currently documented
20018 * but perhaps we can provide a better solution in the future.
20019 */
20020void
20022 int numExtensions)
20023{
20024 DumpOptions *dopt = fout->dopt;
20025 PQExpBuffer query;
20026 PGresult *res;
20027 int ntups,
20028 i;
20029 int i_conrelid,
20031
20032 /* Nothing to do if no extensions */
20033 if (numExtensions == 0)
20034 return;
20035
20036 /*
20037 * Identify extension configuration tables and create TableDataInfo
20038 * objects for them, ensuring their data will be dumped even though the
20039 * tables themselves won't be.
20040 *
20041 * Note that we create TableDataInfo objects even in schema-only mode, ie,
20042 * user data in a configuration table is treated like schema data. This
20043 * seems appropriate since system data in a config table would get
20044 * reloaded by CREATE EXTENSION. If the extension is not listed in the
20045 * list of extensions to be included, none of its data is dumped.
20046 */
20047 for (i = 0; i < numExtensions; i++)
20048 {
20050 char *extconfig = curext->extconfig;
20051 char *extcondition = curext->extcondition;
20052 char **extconfigarray = NULL;
20053 char **extconditionarray = NULL;
20054 int nconfigitems = 0;
20055 int nconditionitems = 0;
20056
20057 /*
20058 * Check if this extension is listed as to include in the dump. If
20059 * not, any table data associated with it is discarded.
20060 */
20063 curext->dobj.catId.oid))
20064 continue;
20065
20066 /*
20067 * Check if this extension is listed as to exclude in the dump. If
20068 * yes, any table data associated with it is discarded.
20069 */
20072 curext->dobj.catId.oid))
20073 continue;
20074
20075 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
20076 {
20077 int j;
20078
20079 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
20080 pg_fatal("could not parse %s array", "extconfig");
20081 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
20082 pg_fatal("could not parse %s array", "extcondition");
20084 pg_fatal("mismatched number of configurations and conditions for extension");
20085
20086 for (j = 0; j < nconfigitems; j++)
20087 {
20090 bool dumpobj =
20091 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
20092
20094 if (configtbl == NULL)
20095 continue;
20096
20097 /*
20098 * Tables of not-to-be-dumped extensions shouldn't be dumped
20099 * unless the table or its schema is explicitly included
20100 */
20101 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
20102 {
20103 /* check table explicitly requested */
20104 if (table_include_oids.head != NULL &&
20106 configtbloid))
20107 dumpobj = true;
20108
20109 /* check table's schema explicitly requested */
20110 if (configtbl->dobj.namespace->dobj.dump &
20112 dumpobj = true;
20113 }
20114
20115 /* check table excluded by an exclusion switch */
20116 if (table_exclude_oids.head != NULL &&
20118 configtbloid))
20119 dumpobj = false;
20120
20121 /* check schema excluded by an exclusion switch */
20123 configtbl->dobj.namespace->dobj.catId.oid))
20124 dumpobj = false;
20125
20126 if (dumpobj)
20127 {
20129 if (configtbl->dataObj != NULL)
20130 {
20131 if (strlen(extconditionarray[j]) > 0)
20132 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
20133 }
20134 }
20135 }
20136 }
20137 if (extconfigarray)
20141 }
20142
20143 /*
20144 * Now that all the TableDataInfo objects have been created for all the
20145 * extensions, check their FK dependencies and register them to try and
20146 * dump the data out in an order that they can be restored in.
20147 *
20148 * Note that this is not a problem for user tables as their FKs are
20149 * recreated after the data has been loaded.
20150 */
20151
20152 query = createPQExpBuffer();
20153
20154 printfPQExpBuffer(query,
20155 "SELECT conrelid, confrelid "
20156 "FROM pg_constraint "
20157 "JOIN pg_depend ON (objid = confrelid) "
20158 "WHERE contype = 'f' "
20159 "AND refclassid = 'pg_extension'::regclass "
20160 "AND classid = 'pg_class'::regclass;");
20161
20162 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20163 ntups = PQntuples(res);
20164
20165 i_conrelid = PQfnumber(res, "conrelid");
20166 i_confrelid = PQfnumber(res, "confrelid");
20167
20168 /* Now get the dependencies and register them */
20169 for (i = 0; i < ntups; i++)
20170 {
20171 Oid conrelid,
20172 confrelid;
20174 *contable;
20175
20176 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
20177 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
20178 contable = findTableByOid(conrelid);
20179 reftable = findTableByOid(confrelid);
20180
20181 if (reftable == NULL ||
20182 reftable->dataObj == NULL ||
20183 contable == NULL ||
20184 contable->dataObj == NULL)
20185 continue;
20186
20187 /*
20188 * Make referencing TABLE_DATA object depend on the referenced table's
20189 * TABLE_DATA object.
20190 */
20191 addObjectDependency(&contable->dataObj->dobj,
20192 reftable->dataObj->dobj.dumpId);
20193 }
20194 PQclear(res);
20195 destroyPQExpBuffer(query);
20196}
20197
20198/*
20199 * getDependencies --- obtain available dependency data
20200 */
20201static void
20203{
20204 PQExpBuffer query;
20205 PGresult *res;
20206 int ntups,
20207 i;
20208 int i_classid,
20209 i_objid,
20211 i_refobjid,
20212 i_deptype;
20213 DumpableObject *dobj,
20214 *refdobj;
20215
20216 pg_log_info("reading dependency data");
20217
20218 query = createPQExpBuffer();
20219
20220 /*
20221 * Messy query to collect the dependency data we need. Note that we
20222 * ignore the sub-object column, so that dependencies of or on a column
20223 * look the same as dependencies of or on a whole table.
20224 *
20225 * PIN dependencies aren't interesting, and EXTENSION dependencies were
20226 * already processed by getExtensionMembership.
20227 */
20228 appendPQExpBufferStr(query, "SELECT "
20229 "classid, objid, refclassid, refobjid, deptype "
20230 "FROM pg_depend "
20231 "WHERE deptype != 'p' AND deptype != 'e'\n");
20232
20233 /*
20234 * Since we don't treat pg_amop entries as separate DumpableObjects, we
20235 * have to translate their dependencies into dependencies of their parent
20236 * opfamily. Ignore internal dependencies though, as those will point to
20237 * their parent opclass, which we needn't consider here (and if we did,
20238 * it'd just result in circular dependencies). Also, "loose" opfamily
20239 * entries will have dependencies on their parent opfamily, which we
20240 * should drop since they'd likewise become useless self-dependencies.
20241 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20242 */
20243 appendPQExpBufferStr(query, "UNION ALL\n"
20244 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20245 "FROM pg_depend d, pg_amop o "
20246 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20247 "classid = 'pg_amop'::regclass AND objid = o.oid "
20248 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20249
20250 /* Likewise for pg_amproc entries */
20251 appendPQExpBufferStr(query, "UNION ALL\n"
20252 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20253 "FROM pg_depend d, pg_amproc p "
20254 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20255 "classid = 'pg_amproc'::regclass AND objid = p.oid "
20256 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20257
20258 /*
20259 * Translate dependencies of pg_propgraph_element entries into
20260 * dependencies of their parent pg_class entry.
20261 */
20262 if (fout->remoteVersion >= 190000)
20263 appendPQExpBufferStr(query, "UNION ALL\n"
20264 "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
20265 "FROM pg_depend d, pg_propgraph_element pge "
20266 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20267 "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
20268
20269 /* Sort the output for efficiency below */
20270 appendPQExpBufferStr(query, "ORDER BY 1,2");
20271
20272 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20273
20274 ntups = PQntuples(res);
20275
20276 i_classid = PQfnumber(res, "classid");
20277 i_objid = PQfnumber(res, "objid");
20278 i_refclassid = PQfnumber(res, "refclassid");
20279 i_refobjid = PQfnumber(res, "refobjid");
20280 i_deptype = PQfnumber(res, "deptype");
20281
20282 /*
20283 * Since we ordered the SELECT by referencing ID, we can expect that
20284 * multiple entries for the same object will appear together; this saves
20285 * on searches.
20286 */
20287 dobj = NULL;
20288
20289 for (i = 0; i < ntups; i++)
20290 {
20291 CatalogId objId;
20293 char deptype;
20294
20295 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20296 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20297 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20298 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20299 deptype = *(PQgetvalue(res, i, i_deptype));
20300
20301 if (dobj == NULL ||
20302 dobj->catId.tableoid != objId.tableoid ||
20303 dobj->catId.oid != objId.oid)
20304 dobj = findObjectByCatalogId(objId);
20305
20306 /*
20307 * Failure to find objects mentioned in pg_depend is not unexpected,
20308 * since for example we don't collect info about TOAST tables.
20309 */
20310 if (dobj == NULL)
20311 {
20312#ifdef NOT_USED
20313 pg_log_warning("no referencing object %u %u",
20314 objId.tableoid, objId.oid);
20315#endif
20316 continue;
20317 }
20318
20320
20321 if (refdobj == NULL)
20322 {
20323#ifdef NOT_USED
20324 pg_log_warning("no referenced object %u %u",
20325 refobjId.tableoid, refobjId.oid);
20326#endif
20327 continue;
20328 }
20329
20330 /*
20331 * For 'x' dependencies, mark the object for later; we still add the
20332 * normal dependency, for possible ordering purposes. Currently
20333 * pg_dump_sort.c knows to put extensions ahead of all object types
20334 * that could possibly depend on them, but this is safer.
20335 */
20336 if (deptype == 'x')
20337 dobj->depends_on_ext = true;
20338
20339 /*
20340 * Ordinarily, table rowtypes have implicit dependencies on their
20341 * tables. However, for a composite type the implicit dependency goes
20342 * the other way in pg_depend; which is the right thing for DROP but
20343 * it doesn't produce the dependency ordering we need. So in that one
20344 * case, we reverse the direction of the dependency.
20345 */
20346 if (deptype == 'i' &&
20347 dobj->objType == DO_TABLE &&
20348 refdobj->objType == DO_TYPE)
20350 else
20351 /* normal case */
20353 }
20354
20355 PQclear(res);
20356
20357 destroyPQExpBuffer(query);
20358}
20359
20360
20361/*
20362 * createBoundaryObjects - create dummy DumpableObjects to represent
20363 * dump section boundaries.
20364 */
20365static DumpableObject *
20367{
20369
20371
20372 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20373 dobjs[0].catId = nilCatalogId;
20374 AssignDumpId(dobjs + 0);
20375 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20376
20377 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20378 dobjs[1].catId = nilCatalogId;
20379 AssignDumpId(dobjs + 1);
20380 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20381
20382 return dobjs;
20383}
20384
20385/*
20386 * addBoundaryDependencies - add dependencies as needed to enforce the dump
20387 * section boundaries.
20388 */
20389static void
20392{
20395 int i;
20396
20397 for (i = 0; i < numObjs; i++)
20398 {
20399 DumpableObject *dobj = dobjs[i];
20400
20401 /*
20402 * The classification of object types here must match the SECTION_xxx
20403 * values assigned during subsequent ArchiveEntry calls!
20404 */
20405 switch (dobj->objType)
20406 {
20407 case DO_NAMESPACE:
20408 case DO_EXTENSION:
20409 case DO_TYPE:
20410 case DO_SHELL_TYPE:
20411 case DO_FUNC:
20412 case DO_AGG:
20413 case DO_OPERATOR:
20414 case DO_ACCESS_METHOD:
20415 case DO_OPCLASS:
20416 case DO_OPFAMILY:
20417 case DO_COLLATION:
20418 case DO_CONVERSION:
20419 case DO_TABLE:
20420 case DO_TABLE_ATTACH:
20421 case DO_ATTRDEF:
20422 case DO_PROCLANG:
20423 case DO_CAST:
20424 case DO_DUMMY_TYPE:
20425 case DO_TSPARSER:
20426 case DO_TSDICT:
20427 case DO_TSTEMPLATE:
20428 case DO_TSCONFIG:
20429 case DO_FDW:
20430 case DO_FOREIGN_SERVER:
20431 case DO_TRANSFORM:
20432 /* Pre-data objects: must come before the pre-data boundary */
20434 break;
20435 case DO_TABLE_DATA:
20436 case DO_SEQUENCE_SET:
20437 case DO_LARGE_OBJECT:
20439 /* Data objects: must come between the boundaries */
20442 break;
20443 case DO_INDEX:
20444 case DO_INDEX_ATTACH:
20445 case DO_STATSEXT:
20446 case DO_REFRESH_MATVIEW:
20447 case DO_TRIGGER:
20448 case DO_EVENT_TRIGGER:
20449 case DO_DEFAULT_ACL:
20450 case DO_POLICY:
20451 case DO_PUBLICATION:
20452 case DO_PUBLICATION_REL:
20454 case DO_SUBSCRIPTION:
20456 /* Post-data objects: must come after the post-data boundary */
20458 break;
20459 case DO_RULE:
20460 /* Rules are post-data, but only if dumped separately */
20461 if (((RuleInfo *) dobj)->separate)
20462 addObjectDependency(dobj, postDataBound->dumpId);
20463 break;
20464 case DO_CONSTRAINT:
20465 case DO_FK_CONSTRAINT:
20466 /* Constraints are post-data, but only if dumped separately */
20467 if (((ConstraintInfo *) dobj)->separate)
20468 addObjectDependency(dobj, postDataBound->dumpId);
20469 break;
20471 /* nothing to do */
20472 break;
20474 /* must come after the pre-data boundary */
20475 addObjectDependency(dobj, preDataBound->dumpId);
20476 break;
20477 case DO_REL_STATS:
20478 /* stats section varies by parent object type, DATA or POST */
20479 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20480 {
20481 addObjectDependency(dobj, preDataBound->dumpId);
20482 addObjectDependency(postDataBound, dobj->dumpId);
20483 }
20484 else
20485 addObjectDependency(dobj, postDataBound->dumpId);
20486 break;
20487 }
20488 }
20489}
20490
20491
20492/*
20493 * BuildArchiveDependencies - create dependency data for archive TOC entries
20494 *
20495 * The raw dependency data obtained by getDependencies() is not terribly
20496 * useful in an archive dump, because in many cases there are dependency
20497 * chains linking through objects that don't appear explicitly in the dump.
20498 * For example, a view will depend on its _RETURN rule while the _RETURN rule
20499 * will depend on other objects --- but the rule will not appear as a separate
20500 * object in the dump. We need to adjust the view's dependencies to include
20501 * whatever the rule depends on that is included in the dump.
20502 *
20503 * Just to make things more complicated, there are also "special" dependencies
20504 * such as the dependency of a TABLE DATA item on its TABLE, which we must
20505 * not rearrange because pg_restore knows that TABLE DATA only depends on
20506 * its table. In these cases we must leave the dependencies strictly as-is
20507 * even if they refer to not-to-be-dumped objects.
20508 *
20509 * To handle this, the convention is that "special" dependencies are created
20510 * during ArchiveEntry calls, and an archive TOC item that has any such
20511 * entries will not be touched here. Otherwise, we recursively search the
20512 * DumpableObject data structures to build the correct dependencies for each
20513 * archive TOC item.
20514 */
20515static void
20517{
20519 TocEntry *te;
20520
20521 /* Scan all TOC entries in the archive */
20522 for (te = AH->toc->next; te != AH->toc; te = te->next)
20523 {
20524 DumpableObject *dobj;
20525 DumpId *dependencies;
20526 int nDeps;
20527 int allocDeps;
20528
20529 /* No need to process entries that will not be dumped */
20530 if (te->reqs == 0)
20531 continue;
20532 /* Ignore entries that already have "special" dependencies */
20533 if (te->nDeps > 0)
20534 continue;
20535 /* Otherwise, look up the item's original DumpableObject, if any */
20536 dobj = findObjectByDumpId(te->dumpId);
20537 if (dobj == NULL)
20538 continue;
20539 /* No work if it has no dependencies */
20540 if (dobj->nDeps <= 0)
20541 continue;
20542 /* Set up work array */
20543 allocDeps = 64;
20544 dependencies = pg_malloc_array(DumpId, allocDeps);
20545 nDeps = 0;
20546 /* Recursively find all dumpable dependencies */
20547 findDumpableDependencies(AH, dobj,
20548 &dependencies, &nDeps, &allocDeps);
20549 /* And save 'em ... */
20550 if (nDeps > 0)
20551 {
20552 dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20553 te->dependencies = dependencies;
20554 te->nDeps = nDeps;
20555 }
20556 else
20557 pg_free(dependencies);
20558 }
20559}
20560
20561/* Recursive search subroutine for BuildArchiveDependencies */
20562static void
20564 DumpId **dependencies, int *nDeps, int *allocDeps)
20565{
20566 int i;
20567
20568 /*
20569 * Ignore section boundary objects: if we search through them, we'll
20570 * report lots of bogus dependencies.
20571 */
20572 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20574 return;
20575
20576 for (i = 0; i < dobj->nDeps; i++)
20577 {
20578 DumpId depid = dobj->dependencies[i];
20579
20580 if (TocIDRequired(AH, depid) != 0)
20581 {
20582 /* Object will be dumped, so just reference it as a dependency */
20583 if (*nDeps >= *allocDeps)
20584 {
20585 *allocDeps *= 2;
20586 *dependencies = pg_realloc_array(*dependencies, DumpId, *allocDeps);
20587 }
20588 (*dependencies)[*nDeps] = depid;
20589 (*nDeps)++;
20590 }
20591 else
20592 {
20593 /*
20594 * Object will not be dumped, so recursively consider its deps. We
20595 * rely on the assumption that sortDumpableObjects already broke
20596 * any dependency loops, else we might recurse infinitely.
20597 */
20599
20600 if (otherdobj)
20602 dependencies, nDeps, allocDeps);
20603 }
20604 }
20605}
20606
20607
20608/*
20609 * getFormattedTypeName - retrieve a nicely-formatted type name for the
20610 * given type OID.
20611 *
20612 * This does not guarantee to schema-qualify the output, so it should not
20613 * be used to create the target object name for CREATE or ALTER commands.
20614 *
20615 * Note that the result is cached and must not be freed by the caller.
20616 */
20617static const char *
20619{
20621 char *result;
20622 PQExpBuffer query;
20623 PGresult *res;
20624
20625 if (oid == 0)
20626 {
20627 if ((opts & zeroAsStar) != 0)
20628 return "*";
20629 else if ((opts & zeroAsNone) != 0)
20630 return "NONE";
20631 }
20632
20633 /* see if we have the result cached in the type's TypeInfo record */
20634 typeInfo = findTypeByOid(oid);
20635 if (typeInfo && typeInfo->ftypname)
20636 return typeInfo->ftypname;
20637
20638 query = createPQExpBuffer();
20639 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20640 oid);
20641
20642 res = ExecuteSqlQueryForSingleRow(fout, query->data);
20643
20644 /* result of format_type is already quoted */
20645 result = pg_strdup(PQgetvalue(res, 0, 0));
20646
20647 PQclear(res);
20648 destroyPQExpBuffer(query);
20649
20650 /*
20651 * Cache the result for re-use in later requests, if possible. If we
20652 * don't have a TypeInfo for the type, the string will be leaked once the
20653 * caller is done with it ... but that case really should not happen, so
20654 * leaking if it does seems acceptable.
20655 */
20656 if (typeInfo)
20657 typeInfo->ftypname = result;
20658
20659 return result;
20660}
20661
20662/*
20663 * Return a column list clause for the given relation.
20664 *
20665 * Special case: if there are no undropped columns in the relation, return
20666 * "", not an invalid "()" column list.
20667 */
20668static const char *
20670{
20671 int numatts = ti->numatts;
20672 char **attnames = ti->attnames;
20673 bool *attisdropped = ti->attisdropped;
20674 char *attgenerated = ti->attgenerated;
20675 bool needComma;
20676 int i;
20677
20678 appendPQExpBufferChar(buffer, '(');
20679 needComma = false;
20680 for (i = 0; i < numatts; i++)
20681 {
20682 if (attisdropped[i])
20683 continue;
20684 if (attgenerated[i])
20685 continue;
20686 if (needComma)
20687 appendPQExpBufferStr(buffer, ", ");
20688 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20689 needComma = true;
20690 }
20691
20692 if (!needComma)
20693 return ""; /* no undropped columns */
20694
20695 appendPQExpBufferChar(buffer, ')');
20696 return buffer->data;
20697}
20698
20699/*
20700 * Check if a reloptions array is nonempty.
20701 */
20702static bool
20703nonemptyReloptions(const char *reloptions)
20704{
20705 /* Don't want to print it if it's just "{}" */
20706 return (reloptions != NULL && strlen(reloptions) > 2);
20707}
20708
20709/*
20710 * Format a reloptions array and append it to the given buffer.
20711 *
20712 * "prefix" is prepended to the option names; typically it's "" or "toast.".
20713 */
20714static void
20715appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20716 const char *prefix, Archive *fout)
20717{
20718 bool res;
20719
20720 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20721 fout->std_strings);
20722 if (!res)
20723 pg_log_warning("could not parse %s array", "reloptions");
20724}
20725
20726/*
20727 * read_dump_filters - retrieve object identifier patterns from file
20728 *
20729 * Parse the specified filter file for include and exclude patterns, and add
20730 * them to the relevant lists. If the filename is "-" then filters will be
20731 * read from STDIN rather than a file.
20732 */
20733static void
20735{
20737 char *objname;
20739 FilterObjectType objtype;
20740
20742
20743 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20744 {
20746 {
20747 switch (objtype)
20748 {
20750 break;
20757 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20758 "include",
20759 filter_object_type_name(objtype));
20760 exit_nicely(1);
20761 break; /* unreachable */
20762
20765 break;
20768 break;
20771 dopt->include_everything = false;
20772 break;
20775 dopt->include_everything = false;
20776 break;
20779 objname);
20780 dopt->include_everything = false;
20781 break;
20782 }
20783 }
20785 {
20786 switch (objtype)
20787 {
20789 break;
20795 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20796 "exclude",
20797 filter_object_type_name(objtype));
20798 exit_nicely(1);
20799 break;
20800
20803 break;
20806 objname);
20807 break;
20810 objname);
20811 break;
20814 break;
20817 break;
20820 objname);
20821 break;
20822 }
20823 }
20824 else
20825 {
20827 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20828 }
20829
20830 if (objname)
20831 free(objname);
20832 }
20833
20835}
Acl * acldefault(ObjectType objtype, Oid ownerId)
Definition acl.c:827
#define InvalidAttrNumber
Definition attnum.h:23
int lo_read(int fd, char *buf, int len)
Definition be-fsstubs.c:154
static void help(void)
Definition pg_config.c:71
void recordAdditionalCatalogID(CatalogId catId, DumpableObject *dobj)
Definition common.c:722
void recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
Definition common.c:1066
FuncInfo * findFuncByOid(Oid oid)
Definition common.c:921
TableInfo * findTableByOid(Oid oid)
Definition common.c:866
ExtensionInfo * findExtensionByOid(Oid oid)
Definition common.c:1011
CollInfo * findCollationByOid(Oid oid)
Definition common.c:975
SubscriptionInfo * findSubscriptionByOid(Oid oid)
Definition common.c:1047
OprInfo * findOprByOid(Oid oid)
Definition common.c:939
NamespaceInfo * findNamespaceByOid(Oid oid)
Definition common.c:993
void addObjectDependency(DumpableObject *dobj, DumpId refId)
Definition common.c:821
DumpableObject * findObjectByDumpId(DumpId dumpId)
Definition common.c:768
void parseOidArray(const char *str, Oid *array, int arraysize)
Definition common.c:1114
ExtensionInfo * findOwningExtension(CatalogId catalogId)
Definition common.c:1090
TableInfo * getSchemaData(Archive *fout, int *numTablesPtr)
Definition common.c:98
TypeInfo * findTypeByOid(Oid oid)
Definition common.c:902
DumpId createDumpId(void)
Definition common.c:748
DumpableObject * findObjectByCatalogId(CatalogId catalogId)
Definition common.c:781
void AssignDumpId(DumpableObject *dobj)
Definition common.c:660
void getDumpableObjects(DumpableObject ***objs, int *numObjs)
Definition common.c:800
PublicationInfo * findPublicationByOid(Oid oid)
Definition common.c:1029
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
bool is_superuser(void)
Definition common.c:2522
uint32 BlockNumber
Definition block.h:31
static void cleanup(void)
Definition bootstrap.c:886
static const gbtree_vinfo tinfo
Definition btree_bit.c:136
#define PG_INT32_MAX
Definition c.h:732
#define ngettext(s, p, n)
Definition c.h:1310
#define INT64_FORMAT
Definition c.h:693
#define Assert(condition)
Definition c.h:1002
#define PG_TEXTDOMAIN(domain)
Definition c.h:1343
int64_t int64
Definition c.h:680
#define PG_INT16_MIN
Definition c.h:728
#define CppAsString2(x)
Definition c.h:565
int32_t int32
Definition c.h:679
#define PG_INT64_MAX
Definition c.h:735
#define PG_INT64_MIN
Definition c.h:734
uint32_t uint32
Definition c.h:683
#define lengthof(array)
Definition c.h:932
#define PG_INT32_MIN
Definition c.h:731
#define StaticAssertDecl(condition, errmessage)
Definition c.h:1067
#define PG_INT16_MAX
Definition c.h:729
#define OidIsValid(objectId)
Definition c.h:917
uint32 result
int nspid
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition exec.c:430
int main(void)
char * supports_compression(const pg_compress_specification compression_spec)
Definition compress_io.c:87
char * validate_compress_specification(pg_compress_specification *spec)
bool parse_compress_algorithm(char *name, pg_compress_algorithm *algorithm)
Definition compression.c:79
void parse_compress_specification(pg_compress_algorithm algorithm, char *specification, pg_compress_specification *result)
#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
char * generate_restrict_key(void)
Definition dumputils.c:976
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:1000
void buildShSecLabelQuery(const char *catalog_name, Oid objectId, PQExpBuffer sql)
Definition dumputils.c:681
void makeAlterConfigCommand(PGconn *conn, const char *configitem, const char *type, const char *name, const char *type2, const char *name2, PQExpBuffer buf)
Definition dumputils.c:868
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:733
void quoteAclUserName(PQExpBuffer output, const char *input)
Definition dumputils.c:588
void emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, const char *objtype, const char *objname)
Definition dumputils.c:699
Datum arg
Definition elog.c:1323
#define _(x)
Definition elog.c:96
char * PQdb(const PGconn *conn)
const char * PQparameterStatus(const PGconn *conn, const char *paramName)
int PQclientEncoding(const PGconn *conn)
char * PQerrorMessage(const PGconn *conn)
int PQsetClientEncoding(PGconn *conn, const char *encoding)
void PQfreemem(void *ptr)
Definition fe-exec.c:4068
Oid PQftype(const PGresult *res, int field_num)
Definition fe-exec.c:3750
int PQfnumber(const PGresult *res, const char *field_name)
Definition fe-exec.c:3620
int PQgetCopyData(PGconn *conn, char **buffer, int async)
Definition fe-exec.c:2833
int lo_close(PGconn *conn, int fd)
Definition fe-lobj.c:96
int lo_open(PGconn *conn, Oid lobjId, int mode)
Definition fe-lobj.c:57
void * pg_malloc(size_t size)
Definition fe_memutils.c:53
char * pg_strdup(const char *in)
Definition fe_memutils.c:91
void pg_free(void *ptr)
#define pg_realloc_array(pointer, type, count)
Definition fe_memutils.h:74
#define pg_malloc_array(type, count)
Definition fe_memutils.h:66
#define pg_malloc0_object(type)
Definition fe_memutils.h:61
#define pg_malloc_object(type)
Definition fe_memutils.h:60
#define pg_malloc0_array(type, count)
Definition fe_memutils.h:67
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
#define comment
#define storage
long val
Definition informix.c:689
static struct @175 value
static char * encoding
Definition initdb.c:139
static DataDirSyncMethod sync_method
Definition initdb.c:170
static int pg_cmp_u32(uint32 a, uint32 b)
Definition int.h:719
int j
Definition isn.c:78
int i
Definition isn.c:77
#define PQgetvalue
#define PQgetResult
#define PQgetlength
#define PQclear
#define PQnfields
#define PQresultStatus
#define PQgetisnull
#define PQfname
#define PQntuples
@ PGRES_COMMAND_OK
Definition libpq-fe.h:131
@ PGRES_COPY_OUT
Definition libpq-fe.h:137
@ PGRES_TUPLES_OK
Definition libpq-fe.h:134
#define INV_READ
Definition libpq-fs.h:22
void pg_logging_increase_verbosity(void)
Definition logging.c:187
void pg_logging_init(const char *argv0)
Definition logging.c:85
void pg_logging_set_level(enum pg_log_level new_level)
Definition logging.c:178
#define pg_log_error(...)
Definition logging.h:108
#define pg_log_error_hint(...)
Definition logging.h:114
#define pg_log_info(...)
Definition logging.h:126
@ PG_LOG_WARNING
Definition logging.h:38
#define pg_log_error_detail(...)
Definition logging.h:111
const char * progname
Definition main.c:44
char * pstrdup(const char *in)
Definition mcxt.c:1910
void pfree(void *pointer)
Definition mcxt.c:1619
bool option_parse_int(const char *optarg, const char *optname, int min_range, int max_range, int *result)
bool parse_sync_method(const char *optarg, DataDirSyncMethod *sync_method)
#define check_mut_excl_opts(set, opt,...)
Oid oprid(Operator op)
Definition parse_oper.c:241
static AmcheckOptions opts
Definition pg_amcheck.c:112
NameData attname
char attalign
int16 attlen
NameData rolname
Definition pg_authid.h:36
@ 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:285
int EndLO(Archive *AHX, Oid oid)
void ProcessArchiveRestoreOptions(Archive *AHX)
RestoreOptions * NewRestoreOptions(void)
#define InvalidDumpId
Definition pg_backup.h:287
#define appendStringLiteralAH(buf, str, AH)
Definition pg_backup.h:344
int StartLO(Archive *AHX, Oid oid)
enum _archiveFormat ArchiveFormat
void ConnectDatabaseAhx(Archive *AHX, const ConnParams *cparams, bool isReconnect)
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:72
@ PREPQUERY_DUMPTABLEATTACH
Definition pg_backup.h:75
@ PREPQUERY_DUMPBASETYPE
Definition pg_backup.h:67
@ PREPQUERY_DUMPRANGETYPE
Definition pg_backup.h:74
@ PREPQUERY_DUMPOPR
Definition pg_backup.h:73
@ PREPQUERY_DUMPEXTSTATSOBJSTATS
Definition pg_backup.h:71
@ PREPQUERY_GETATTRIBUTESTATS
Definition pg_backup.h:76
@ 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:77
@ PREPQUERY_GETDOMAINCONSTRAINTS
Definition pg_backup.h:78
@ 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:81
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)
PGresult * ExecuteSqlQuery(Archive *AHX, const char *query, ExecStatusType status)
PGresult * ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
void exit_nicely(int code)
void set_dump_section(const char *arg, int *dumpSections)
#define pg_fatal(...)
static char format
static char * label
static PgChecksumMode mode
#define FUNC_MAX_ARGS
const void size_t len
char datlocprovider
Definition pg_database.h:46
NameData datname
Definition pg_database.h:37
bool datistemplate
Definition pg_database.h:49
int32 datconnlimit
Definition pg_database.h:61
static void expand_schema_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition pg_dump.c:1638
static const CatalogId nilCatalogId
Definition pg_dump.c:191
static void dumpEncoding(Archive *AH)
Definition pg_dump.c:3786
void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:8250
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:16364
static SimpleStringList schema_include_patterns
Definition pg_dump.c:167
static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
Definition pg_dump.c:18172
ExtensionInfo * getExtensions(Archive *fout, int *numExtensions)
Definition pg_dump.c:6169
static void selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
Definition pg_dump.c:2181
static void collectBinaryUpgradeClassOids(Archive *fout)
Definition pg_dump.c:5869
static PQExpBuffer createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17007
static void dumpUserMappings(Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
Definition pg_dump.c:16178
static void dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
Definition pg_dump.c:4969
static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
Definition pg_dump.c:20390
void getPublicationNamespaces(Archive *fout)
Definition pg_dump.c:4758
static void dumpSearchPath(Archive *AH)
Definition pg_dump.c:3835
static int ncomments
Definition pg_dump.c:203
static void selectDumpableTable(TableInfo *tbinfo, Archive *fout)
Definition pg_dump.c:2050
static DumpableObject * createBoundaryObjects(void)
Definition pg_dump.c:20366
static char * convertTSFunction(Archive *fout, Oid funcOid)
Definition pg_dump.c:14373
static void dumpDatabase(Archive *fout)
Definition pg_dump.c:3242
static SimpleStringList table_include_patterns
Definition pg_dump.c:172
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:5682
static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
Definition pg_dump.c:5727
static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
Definition pg_dump.c:11843
static bool forcePartitionRootLoad(const TableInfo *tbinfo)
Definition pg_dump.c:2803
static void dumpCast(Archive *fout, const CastInfo *cast)
Definition pg_dump.c:13849
static SimpleOidList schema_exclude_oids
Definition pg_dump.c:170
static bool have_extra_float_digits
Definition pg_dump.c:194
static void dumpIndex(Archive *fout, const IndxInfo *indxinfo)
Definition pg_dump.c:18262
void getPartitioningInfo(Archive *fout)
Definition pg_dump.c:7747
static int nbinaryUpgradeClassOids
Definition pg_dump.c:211
static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12436
OidOptions
Definition pg_dump.c:145
@ zeroIsError
Definition pg_dump.c:146
@ zeroAsStar
Definition pg_dump.c:147
@ zeroAsNone
Definition pg_dump.c:148
static char * dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
Definition pg_dump.c:11084
static SimpleOidList extension_include_oids
Definition pg_dump.c:186
static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
Definition pg_dump.c:15747
static void dumpAgg(Archive *fout, const AggInfo *agginfo)
Definition pg_dump.c:15343
static int extra_float_digits
Definition pg_dump.c:195
static int SequenceItemCmp(const void *p1, const void *p2)
Definition pg_dump.c:19173
static void dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
Definition pg_dump.c:11360
static void dumpTableComment(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition pg_dump.c:11386
static SimpleStringList extension_include_patterns
Definition pg_dump.c:185
static void selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
Definition pg_dump.c:1965
InhInfo * getInherits(Archive *fout, int *numInherits)
Definition pg_dump.c:7691
void getForeignDataWrappers(Archive *fout)
Definition pg_dump.c:10369
static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
Definition pg_dump.c:19545
static void binary_upgrade_set_type_oids_by_rel(Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
Definition pg_dump.c:5838
static void dumpTable(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:16819
static SimpleOidList extension_exclude_oids
Definition pg_dump.c:189
static SimpleStringList table_exclude_patterns
Definition pg_dump.c:175
static PQExpBuffer createViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:16958
static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
Definition pg_dump.c:18456
void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:4187
static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12214
void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition pg_dump.c:19928
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:10945
static char * getFormattedOperatorName(const char *oproid)
Definition pg_dump.c:14343
static char * format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
Definition pg_dump.c:13406
static int nseclabels
Definition pg_dump.c:207
static pg_compress_algorithm compression_algorithm
Definition pg_dump.c:159
static void dumpStdStrings(Archive *AH)
Definition pg_dump.c:3811
static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
Definition pg_dump.c:18813
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12043
static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
Definition pg_dump.c:18104
void getTypes(Archive *fout)
Definition pg_dump.c:6244
static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
Definition pg_dump.c:14395
static void dumpOpr(Archive *fout, const OprInfo *oprinfo)
Definition pg_dump.c:14083
static void selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
Definition pg_dump.c:2298
static void selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:2280
static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:19456
static void dumpFunc(Archive *fout, const FuncInfo *finfo)
Definition pg_dump.c:13435
static void selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
Definition pg_dump.c:2134
static void BuildArchiveDependencies(Archive *fout)
Definition pg_dump.c:20516
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:7073
static const char *const SeqTypeNames[]
Definition pg_dump.c:119
void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:7626
static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
Definition pg_dump.c:3000
static int nsequences
Definition pg_dump.c:215
static const char * getAttrName(int attrnum, const TableInfo *tblInfo)
Definition pg_dump.c:18233
static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
Definition pg_dump.c:16078
static RoleNameItem * rolenames
Definition pg_dump.c:198
static void collectRoleNames(Archive *fout)
Definition pg_dump.c:10684
static PGresult * fetchAttributeStats(Archive *fout)
Definition pg_dump.c:10979
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
Definition pg_dump.c:20715
void getOpclasses(Archive *fout)
Definition pg_dump.c:6678
void getForeignServers(Archive *fout)
Definition pg_dump.c:10463
void getFuncs(Archive *fout)
Definition pg_dump.c:6921
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:2831
static void prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
Definition pg_dump.c:1898
static bool dosync
Definition pg_dump.c:152
static int dumpTableData_copy(Archive *fout, const void *dcontext)
Definition pg_dump.c:2338
#define MAX_BLOBS_PER_ARCHIVE_ENTRY
Definition pg_dump.c:231
static const char * getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
Definition pg_dump.c:20618
static void getDependencies(Archive *fout)
Definition pg_dump.c:20202
static void buildMatViewRefreshDependencies(Archive *fout)
Definition pg_dump.c:3090
void getTSDictionaries(Archive *fout)
Definition pg_dump.c:10185
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:5758
#define DUMP_DEFAULT_ROWS_PER_INSERT
Definition pg_dump.c:224
void getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:4838
static SeqType parse_sequence_type(const char *name)
Definition pg_dump.c:19157
static const char * getRoleName(const char *roleoid_str)
Definition pg_dump.c:10648
static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
Definition pg_dump.c:13205
static SequenceItem * sequences
Definition pg_dump.c:214
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:2946
static int findComments(Oid classoid, Oid objoid, CommentItem **items)
Definition pg_dump.c:11484
static SimpleStringList foreign_servers_include_patterns
Definition pg_dump.c:182
static void selectDumpableCast(CastInfo *cast, Archive *fout)
Definition pg_dump.c:2156
void getCasts(Archive *fout)
Definition pg_dump.c:8995
static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
Definition pg_dump.c:4636
static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
Definition pg_dump.c:4352
void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:7807
static void setupDumpWorker(Archive *AH)
Definition pg_dump.c:1571
static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
Definition pg_dump.c:8409
void getTSConfigurations(Archive *fout)
Definition pg_dump.c:10310
static int nrolenames
Definition pg_dump.c:199
static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
Definition pg_dump.c:16654
static SimpleStringList table_include_patterns_and_children
Definition pg_dump.c:173
static char * convertRegProcReference(const char *proc)
Definition pg_dump.c:14302
static void getAdditionalACLs(Archive *fout)
Definition pg_dump.c:10719
static void getTableDataFKConstraints(void)
Definition pg_dump.c:3201
static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
Definition pg_dump.c:2981
static SimpleOidList table_exclude_oids
Definition pg_dump.c:177
SeqType
Definition pg_dump.c:113
@ SEQTYPE_BIGINT
Definition pg_dump.c:116
@ SEQTYPE_INTEGER
Definition pg_dump.c:115
@ SEQTYPE_SMALLINT
Definition pg_dump.c:114
void getAccessMethods(Archive *fout)
Definition pg_dump.c:6616
void getConversions(Archive *fout)
Definition pg_dump.c:6554
void getRules(Archive *fout)
Definition pg_dump.c:8544
static void dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12685
void getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
Definition pg_dump.c:9185
static void collectComments(Archive *fout)
Definition pg_dump.c:11561
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
Definition pg_dump.c:8432
static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
Definition pg_dump.c:14744
static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
Definition pg_dump.c:16272
static void selectDumpableObject(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:2316
static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
Definition pg_dump.c:15827
static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
Definition pg_dump.c:18413
static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
Definition pg_dump.c:5448
void getCollations(Archive *fout)
Definition pg_dump.c:6488
static char * format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
Definition pg_dump.c:13383
static int strict_names
Definition pg_dump.c:157
static void dumpTransform(Archive *fout, const TransformInfo *transform)
Definition pg_dump.c:13954
void getAggregates(Archive *fout)
Definition pg_dump.c:6806
static void dumpLO(Archive *fout, const LoInfo *loinfo)
Definition pg_dump.c:4051
void getNamespaces(Archive *fout)
Definition pg_dump.c:6037
static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
Definition pg_dump.c:5012
void getPublications(Archive *fout)
Definition pg_dump.c:4470
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:5993
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
Definition pg_dump.c:11646
static void getLOs(Archive *fout)
Definition pg_dump.c:3897
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, const char *dbname, Oid dboid)
Definition pg_dump.c:3742
void getTSParsers(Archive *fout)
Definition pg_dump.c:10111
static void setup_connection(Archive *AH, const char *dumpencoding, const char *dumpsnapshot, char *use_role)
Definition pg_dump.c:1399
static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
Definition pg_dump.c:19134
static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12372
static void selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
Definition pg_dump.c:2213
static const char * fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
Definition pg_dump.c:20669
static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names, bool with_child_tables)
Definition pg_dump.c:1802
static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, DumpId **dependencies, int *nDeps, int *allocDeps)
Definition pg_dump.c:20563
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:9969
static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17047
static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
Definition pg_dump.c:15683
static void expand_foreign_server_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids)
Definition pg_dump.c:1750
TableInfo * getTables(Archive *fout, int *numTables)
Definition pg_dump.c:7151
static void dumpRule(Archive *fout, const RuleInfo *rinfo)
Definition pg_dump.c:19761
static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12910
static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12074
static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
Definition pg_dump.c:11920
#define fmtQualifiedDumpable(obj)
Definition pg_dump.c:236
static bool nonemptyReloptions(const char *reloptions)
Definition pg_dump.c:20703
static SimpleStringList extension_exclude_patterns
Definition pg_dump.c:188
static BinaryUpgradeClassOidItem * binaryUpgradeClassOids
Definition pg_dump.c:210
static SimpleOidList table_include_oids
Definition pg_dump.c:174
static void dumpStatisticsExtStats(Archive *fout, const StatsExtInfo *statsextinfo)
Definition pg_dump.c:18533
void getExtendedStatistics(Archive *fout)
Definition pg_dump.c:8172
static NamespaceInfo * findNamespace(Oid nsoid)
Definition pg_dump.c:6151
static char * get_synchronized_snapshot(Archive *fout)
Definition pg_dump.c:1586
static int dumpLOs(Archive *fout, const void *arg)
Definition pg_dump.c:4141
static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
Definition pg_dump.c:5517
static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
Definition pg_dump.c:10961
void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition pg_dump.c:20021
static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
Definition pg_dump.c:19671
static int BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
Definition pg_dump.c:5853
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:10845
void getDefaultACLs(Archive *fout)
Definition pg_dump.c:10551
static SimpleStringList tabledata_exclude_patterns
Definition pg_dump.c:178
static void dumpConversion(Archive *fout, const ConvInfo *convinfo)
Definition pg_dump.c:15215
static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
Definition pg_dump.c:16005
static void dumpProcLang(Archive *fout, const ProcLangInfo *plang)
Definition pg_dump.c:13251
static void dumpSecLabel(Archive *fout, const char *type, const char *name, const char *namespace, const char *owner, CatalogId catalogId, int subid, DumpId dumpId)
Definition pg_dump.c:16492
void getSubscriptions(Archive *fout)
Definition pg_dump.c:5113
static void collectSecLabels(Archive *fout)
Definition pg_dump.c:16733
static void selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
Definition pg_dump.c:2241
static void collectSequences(Archive *fout)
Definition pg_dump.c:19188
static Oid g_last_builtin_oid
Definition pg_dump.c:154
#define MAX_ATTR_STATS_RELS
Definition pg_dump.c:218
void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:8641
void getTransforms(Archive *fout)
Definition pg_dump.c:9105
void getEventTriggers(Archive *fout)
Definition pg_dump.c:8837
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode)
Definition pg_dump.c:1600
static void read_dump_filters(const char *filename, DumpOptions *dopt)
Definition pg_dump.c:20734
static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
Definition pg_dump.c:15885
static SecLabelItem * seclabels
Definition pg_dump.c:206
static SimpleStringList tabledata_exclude_patterns_and_children
Definition pg_dump.c:179
static char * get_language_name(Archive *fout, Oid langid)
Definition pg_dump.c:9084
static bool checkExtensionMembership(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:1923
static CommentItem * comments
Definition pg_dump.c:202
static int dumpTableData_insert(Archive *fout, const void *dcontext)
Definition pg_dump.c:2509
static SimpleOidList tabledata_exclude_oids
Definition pg_dump.c:180
static SimpleStringList table_exclude_patterns_and_children
Definition pg_dump.c:176
static void binary_upgrade_set_pg_class_oids(Archive *fout, PQExpBuffer upgrade_buffer, Oid pg_class_oid)
Definition pg_dump.c:5903
void getTSTemplates(Archive *fout)
Definition pg_dump.c:10251
static void set_restrict_relation_kind(Archive *AH, const char *value)
Definition pg_dump.c:5092
static char * format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
Definition pg_dump.c:15311
void getProcLangs(Archive *fout)
Definition pg_dump.c:8911
static void dumpSequence(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:19244
bool shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
Definition pg_dump.c:10096
static TableInfo * getRootTableInfo(const TableInfo *tbinfo)
Definition pg_dump.c:2778
void getSubscriptionRelations(Archive *fout)
Definition pg_dump.c:5362
void getOperators(Archive *fout)
Definition pg_dump.c:6412
static SimpleOidList foreign_servers_include_oids
Definition pg_dump.c:183
static void dumpCollation(Archive *fout, const CollInfo *collinfo)
Definition pg_dump.c:14963
static void dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition pg_dump.c:16572
static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res)
Definition pg_dump.c:13116
static void expand_extension_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition pg_dump.c:1697
void getOpfamilies(Archive *fout)
Definition pg_dump.c:6741
static void selectDumpableType(TypeInfo *tyinfo, Archive *fout)
Definition pg_dump.c:2089
static SimpleOidList schema_include_oids
Definition pg_dump.c:168
static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
Definition pg_dump.c:14463
static SimpleStringList schema_exclude_patterns
Definition pg_dump.c:169
#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)
#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)
#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:47
PGDLLIMPORT char * optarg
Definition getopt.c:49
NameData subname
static char buf[DEFAULT_XLOG_SEG_SIZE]
char typalign
Definition pg_type.h:178
#define pg_encoding_to_char
Definition pg_wchar.h:483
static char * tablespace
Definition pgbench.c:217
#define pg_log_warning(...)
Definition pgfnames.c:24
int pg_strcasecmp(const char *s1, const char *s2)
#define sprintf
Definition port.h:263
#define snprintf
Definition port.h:261
const char * get_progname(const char *argv0)
Definition path.c:669
#define printf(...)
Definition port.h:267
off_t pgoff_t
Definition port.h:422
#define InvalidOid
unsigned int Oid
#define atooid(x)
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
PQExpBuffer createPQExpBuffer(void)
Definition pqexpbuffer.c:72
void initPQExpBuffer(PQExpBuffer str)
Definition pqexpbuffer.c:90
void resetPQExpBuffer(PQExpBuffer str)
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
void destroyPQExpBuffer(PQExpBuffer str)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
void termPQExpBuffer(PQExpBuffer str)
char * c
static int fb(int x)
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:344
void simple_string_list_append(SimpleStringList *list, const char *val)
Definition simple_list.c:63
void simple_ptr_list_append(SimplePtrList *list, void *ptr)
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
#define free(a)
#define PG_DEPENDENCIES_KEY_ATTRIBUTES
#define PG_DEPENDENCIES_KEY_DEGREE
#define PG_DEPENDENCIES_KEY_DEPENDENCY
#define PG_NDISTINCT_KEY_ATTRIBUTES
#define PG_NDISTINCT_KEY_NDISTINCT
PGconn * GetConnection(void)
Definition streamutil.c:60
char * dbname
Definition streamutil.c:49
PGconn * conn
Definition streamutil.c:52
const char * fmtId(const char *rawid)
void setFmtEncoding(int encoding)
void appendStringLiteralConn(PQExpBuffer buf, const char *str, PGconn *conn)
void appendPGArray(PQExpBuffer buffer, const char *value)
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)
bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions, const char *prefix, int encoding, bool std_strings)
void appendStringLiteralDQ(PQExpBuffer buf, const char *str, const char *dqprefix)
int remoteVersion
Definition pg_backup.h:234
DumpOptions * dopt
Definition pg_backup.h:229
bool * is_prepared
Definition pg_backup.h:256
char * searchpath
Definition pg_backup.h:248
bool std_strings
Definition pg_backup.h:245
int numWorkers
Definition pg_backup.h:240
int encoding
Definition pg_backup.h:244
char * use_role
Definition pg_backup.h:249
char * sync_snapshot_id
Definition pg_backup.h:241
RelFileNumber toast_index_relfilenumber
Definition pg_dump.c:108
RelFileNumber toast_relfilenumber
Definition pg_dump.c:106
RelFileNumber relfilenumber
Definition pg_dump.c:104
Oid tableoid
Definition pg_backup.h:281
Oid classoid
Definition pg_dump.c:86
Oid objoid
Definition pg_dump.c:87
int objsubid
Definition pg_dump.c:88
const char * descr
Definition pg_dump.c:85
const char * rolename
Definition pg_dump.c:80
Oid roleoid
Definition pg_dump.c:79
const char * provider
Definition pg_dump.c:93
Oid classoid
Definition pg_dump.c:95
int objsubid
Definition pg_dump.c:97
const char * label
Definition pg_dump.c:94
int64 minv
Definition pg_dump.c:134
int64 cache
Definition pg_dump.c:138
int64 startv
Definition pg_dump.c:136
int64 maxv
Definition pg_dump.c:135
bool is_called
Definition pg_dump.c:140
int64 incby
Definition pg_dump.c:137
int64 last_value
Definition pg_dump.c:139
SeqType seqtype
Definition pg_dump.c:132
bool cycled
Definition pg_dump.c:133
bool null_seqtuple
Definition pg_dump.c:141
SimpleOidListCell * head
Definition simple_list.h:28
struct SimplePtrListCell * next
Definition simple_list.h:48
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
char * suboriginremotelsn
Definition pg_dump.h:732
bool subpasswordrequired
Definition pg_dump.h:720
const char * rolname
Definition pg_dump.h:714
char * subservername
Definition pg_dump.h:725
char * subsynccommit
Definition pg_dump.h:728
char * subpublications
Definition pg_dump.h:730
char * subwalrcvtimeout
Definition pg_dump.h:729
char * subslotname
Definition pg_dump.h:727
char subtwophasestate
Definition pg_dump.h:718
bool subretaindeadtuples
Definition pg_dump.h:723
char * subconninfo
Definition pg_dump.h:726
DumpableObject dobj
Definition pg_dump.h:713
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 * pgport
Definition pg_backup.h:88
char * pghost
Definition pg_backup.h:89
trivalue promptPassword
Definition pg_backup.h:91
char * username
Definition pg_backup.h:90
char * dbname
Definition pg_backup.h:87
TypeInfo * condomain
Definition pg_dump.h:520
TableInfo * contable
Definition pg_dump.h:519
DumpableObject dobj
Definition pg_dump.h:518
DumpId conindex
Definition pg_dump.h:524
bool condeferrable
Definition pg_dump.h:525
char * condef
Definition pg_dump.h:522
int no_toast_compression
Definition pg_backup.h:192
char * restrict_key
Definition pg_backup.h:220
int column_inserts
Definition pg_backup.h:185
bool dontOutputLOs
Definition pg_backup.h:208
int use_setsessauth
Definition pg_backup.h:198
int outputCreateDB
Definition pg_backup.h:206
bool include_everything
Definition pg_backup.h:203
int sequence_data
Definition pg_backup.h:212
int disable_dollar_quoting
Definition pg_backup.h:184
bool dumpSchema
Definition pg_backup.h:216
int serializable_deferrable
Definition pg_backup.h:194
int outputNoTableAm
Definition pg_backup.h:196
int enable_row_security
Definition pg_backup.h:199
char * outputSuperuser
Definition pg_backup.h:210
int no_security_labels
Definition pg_backup.h:190
int no_unlogged_table_data
Definition pg_backup.h:193
bool dumpStatistics
Definition pg_backup.h:218
int no_publications
Definition pg_backup.h:189
ConnParams cparams
Definition pg_backup.h:173
const char * lockWaitTimeout
Definition pg_backup.h:180
int no_subscriptions
Definition pg_backup.h:191
int load_via_partition_root
Definition pg_backup.h:200
int outputNoTablespaces
Definition pg_backup.h:197
int disable_triggers
Definition pg_backup.h:195
int outputNoOwner
Definition pg_backup.h:209
int binary_upgrade
Definition pg_backup.h:175
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
DumpComponents dump
Definition pg_dump.h:153
DumpId * dependencies
Definition pg_dump.h:159
DumpId dumpId
Definition pg_dump.h:151
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
DumpableObject dobj
Definition pg_dump.h:195
char * extconfig
Definition pg_dump.h:199
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
const char * rolname
Definition pg_dump.h:190
int32 nindAttNames
Definition pg_dump.h:463
char ** indAttNames
Definition pg_dump.h:462
int32 relpages
Definition pg_dump.h:452
int32 relallfrozen
Definition pg_dump.h:455
char * reltuples
Definition pg_dump.h:453
teSection section
Definition pg_dump.h:464
int32 relallvisible
Definition pg_dump.h:454
DumpableObject dobj
Definition pg_dump.h:450
int suppressDumpWarnings
Definition pg_backup.h:152
ConnParams cparams
Definition pg_backup.h:146
pg_compress_specification compression_spec
Definition pg_backup.h:150
int disable_dollar_quoting
Definition pg_backup.h:110
char * restrict_key
Definition pg_backup.h:168
const char * filename
Definition pg_backup.h:121
const char * lockWaitTimeout
Definition pg_backup.h:125
int enable_row_security
Definition pg_backup.h:159
DumpableObject dobj
Definition pg_dump.h:477
bool separate
Definition pg_dump.h:482
char ev_enabled
Definition pg_dump.h:481
bool is_instead
Definition pg_dump.h:480
TableInfo * ruletable
Definition pg_dump.h:478
char ev_type
Definition pg_dump.h:479
DumpableObject dobj
Definition pg_dump.h:413
char * attidentity
Definition pg_dump.h:361
char * reltablespace
Definition pg_dump.h:314
struct _relStatsInfo * stats
Definition pg_dump.h:381
int ncheck
Definition pg_dump.h:330
bool ispartition
Definition pg_dump.h:344
DumpableObject dobj
Definition pg_dump.h:307
bool is_identity_sequence
Definition pg_dump.h:337
Oid reloftype
Definition pg_dump.h:332
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
Oid reltype
Definition pg_dump.h:331
bool hasoids
Definition pg_dump.h:324
Oid toast_oid
Definition pg_dump.h:327
Oid foreign_server
Definition pg_dump.h:333
bool hasrules
Definition pg_dump.h:319
uint32 frozenxid
Definition pg_dump.h:325
int owning_col
Definition pg_dump.h:336
char * checkoption
Definition pg_dump.h:316
bool hastriggers
Definition pg_dump.h:320
const char * rolname
Definition pg_dump.h:309
char relreplident
Definition pg_dump.h:313
uint32 minmxid
Definition pg_dump.h:326
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 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
char * reloptions
Definition pg_dump.h:315
uint32 toast_frozenxid
Definition pg_dump.h:328
uint32 toast_minmxid
Definition pg_dump.h:329
bool postponed_def
Definition pg_dump.h:343
bool rowsec
Definition pg_dump.h:322
struct _tocEntry * next
const void * defnDumperArg
DumpId * dependencies
DumpableObject dobj
Definition pg_dump.h:555
DumpableObject dobj
Definition pg_dump.h:205
char data[NAMEDATALEN]
Definition c.h:890
#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:129
static ItemArray items
static void * fn(void *arg)
#define FirstNormalObjectId
Definition transam.h:197
@ TRI_YES
Definition vacuumlo.c:38
@ TRI_NO
Definition vacuumlo.c:37
static char * error_detail
Definition validator.c:43
bool SplitGUCList(char *rawstring, char separator, List **namelist)
Definition varlena.c:3063
const char * description
const char * type
const char * name
ArchiveMode
Definition xlog.h:66