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 */
977
978
979 /*
980 * We allow the server to be back to 9.2, and up to any minor release of
981 * our own major version. (See also version check in pg_dumpall.c.)
982 */
983 fout->minRemoteVersion = 90200;
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)
1282 RestoreArchive(fout, false);
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 if (AH->remoteVersion >= 90300)
1495 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1496 if (AH->remoteVersion >= 90600)
1497 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1498 if (AH->remoteVersion >= 170000)
1499 ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1500
1501 /*
1502 * Quote all identifiers, if requested.
1503 */
1505 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1506
1507 /*
1508 * Adjust row-security mode, if supported.
1509 */
1510 if (AH->remoteVersion >= 90500)
1511 {
1512 if (dopt->enable_row_security)
1513 ExecuteSqlStatement(AH, "SET row_security = on");
1514 else
1515 ExecuteSqlStatement(AH, "SET row_security = off");
1516 }
1517
1518 /*
1519 * For security reasons, we restrict the expansion of non-system views and
1520 * access to foreign tables during the pg_dump process. This restriction
1521 * is adjusted when dumping foreign table data.
1522 */
1523 set_restrict_relation_kind(AH, "view, foreign-table");
1524
1525 /*
1526 * Initialize prepared-query state to "nothing prepared". We do this here
1527 * so that a parallel dump worker will have its own state.
1528 */
1530
1531 /*
1532 * Start transaction-snapshot mode transaction to dump consistent data.
1533 */
1534 ExecuteSqlStatement(AH, "BEGIN");
1535
1536 /*
1537 * To support the combination of serializable_deferrable with the jobs
1538 * option we use REPEATABLE READ for the worker connections that are
1539 * passed a snapshot. As long as the snapshot is acquired in a
1540 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1541 * REPEATABLE READ transaction provides the appropriate integrity
1542 * guarantees. This is a kluge, but safe for back-patching.
1543 */
1544 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1546 "SET TRANSACTION ISOLATION LEVEL "
1547 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1548 else
1550 "SET TRANSACTION ISOLATION LEVEL "
1551 "REPEATABLE READ, READ ONLY");
1552
1553 /*
1554 * If user specified a snapshot to use, select that. In a parallel dump
1555 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1556 * is already set (if the server can handle it) and we should use that.
1557 */
1558 if (dumpsnapshot)
1560
1561 if (AH->sync_snapshot_id)
1562 {
1564
1565 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1567 ExecuteSqlStatement(AH, query->data);
1568 destroyPQExpBuffer(query);
1569 }
1570 else if (AH->numWorkers > 1)
1571 {
1572 if (AH->isStandby && AH->remoteVersion < 100000)
1573 pg_fatal("parallel dumps from standby servers are not supported by this server version");
1575 }
1576}
1577
1578/* Set up connection for a parallel worker process */
1579static void
1581{
1582 /*
1583 * We want to re-select all the same values the leader connection is
1584 * using. We'll have inherited directly-usable values in
1585 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1586 * inherited encoding value back to a string to pass to setup_connection.
1587 */
1590 NULL,
1591 NULL);
1592}
1593
1594static char *
1596{
1597 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1598 char *result;
1599 PGresult *res;
1600
1601 res = ExecuteSqlQueryForSingleRow(fout, query);
1602 result = pg_strdup(PQgetvalue(res, 0, 0));
1603 PQclear(res);
1604
1605 return result;
1606}
1607
1608static ArchiveFormat
1610{
1612
1614
1615 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1616 {
1617 /* This is used by pg_dumpall, and is not documented */
1620 }
1621 else if (pg_strcasecmp(format, "c") == 0)
1623 else if (pg_strcasecmp(format, "custom") == 0)
1625 else if (pg_strcasecmp(format, "d") == 0)
1627 else if (pg_strcasecmp(format, "directory") == 0)
1629 else if (pg_strcasecmp(format, "p") == 0)
1631 else if (pg_strcasecmp(format, "plain") == 0)
1633 else if (pg_strcasecmp(format, "t") == 0)
1635 else if (pg_strcasecmp(format, "tar") == 0)
1637 else
1638 pg_fatal("invalid output format \"%s\" specified", format);
1639 return archiveFormat;
1640}
1641
1642/*
1643 * Find the OIDs of all schemas matching the given list of patterns,
1644 * and append them to the given OID list.
1645 */
1646static void
1649 SimpleOidList *oids,
1650 bool strict_names)
1651{
1652 PQExpBuffer query;
1653 PGresult *res;
1655 int i;
1656
1657 if (patterns->head == NULL)
1658 return; /* nothing to do */
1659
1660 query = createPQExpBuffer();
1661
1662 /*
1663 * The loop below runs multiple SELECTs might sometimes result in
1664 * duplicate entries in the OID list, but we don't care.
1665 */
1666
1667 for (cell = patterns->head; cell; cell = cell->next)
1668 {
1670 int dotcnt;
1671
1673 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1675 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1676 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1677 &dotcnt);
1678 if (dotcnt > 1)
1679 pg_fatal("improper qualified name (too many dotted names): %s",
1680 cell->val);
1681 else if (dotcnt == 1)
1684
1685 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1686 if (strict_names && PQntuples(res) == 0)
1687 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1688
1689 for (i = 0; i < PQntuples(res); i++)
1690 {
1691 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1692 }
1693
1694 PQclear(res);
1695 resetPQExpBuffer(query);
1696 }
1697
1698 destroyPQExpBuffer(query);
1699}
1700
1701/*
1702 * Find the OIDs of all extensions matching the given list of patterns,
1703 * and append them to the given OID list.
1704 */
1705static void
1708 SimpleOidList *oids,
1709 bool strict_names)
1710{
1711 PQExpBuffer query;
1712 PGresult *res;
1714 int i;
1715
1716 if (patterns->head == NULL)
1717 return; /* nothing to do */
1718
1719 query = createPQExpBuffer();
1720
1721 /*
1722 * The loop below runs multiple SELECTs might sometimes result in
1723 * duplicate entries in the OID list, but we don't care.
1724 */
1725 for (cell = patterns->head; cell; cell = cell->next)
1726 {
1727 int dotcnt;
1728
1730 "SELECT oid FROM pg_catalog.pg_extension e\n");
1731 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1732 false, NULL, "e.extname", NULL, NULL, NULL,
1733 &dotcnt);
1734 if (dotcnt > 0)
1735 pg_fatal("improper qualified name (too many dotted names): %s",
1736 cell->val);
1737
1738 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1739 if (strict_names && PQntuples(res) == 0)
1740 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1741
1742 for (i = 0; i < PQntuples(res); i++)
1743 {
1744 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1745 }
1746
1747 PQclear(res);
1748 resetPQExpBuffer(query);
1749 }
1750
1751 destroyPQExpBuffer(query);
1752}
1753
1754/*
1755 * Find the OIDs of all foreign servers matching the given list of patterns,
1756 * and append them to the given OID list.
1757 */
1758static void
1761 SimpleOidList *oids)
1762{
1763 PQExpBuffer query;
1764 PGresult *res;
1766 int i;
1767
1768 if (patterns->head == NULL)
1769 return; /* nothing to do */
1770
1771 query = createPQExpBuffer();
1772
1773 /*
1774 * The loop below runs multiple SELECTs might sometimes result in
1775 * duplicate entries in the OID list, but we don't care.
1776 */
1777
1778 for (cell = patterns->head; cell; cell = cell->next)
1779 {
1780 int dotcnt;
1781
1783 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1784 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1785 false, NULL, "s.srvname", NULL, NULL, NULL,
1786 &dotcnt);
1787 if (dotcnt > 0)
1788 pg_fatal("improper qualified name (too many dotted names): %s",
1789 cell->val);
1790
1791 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1792 if (PQntuples(res) == 0)
1793 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1794
1795 for (i = 0; i < PQntuples(res); i++)
1796 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1797
1798 PQclear(res);
1799 resetPQExpBuffer(query);
1800 }
1801
1802 destroyPQExpBuffer(query);
1803}
1804
1805/*
1806 * Find the OIDs of all tables matching the given list of patterns,
1807 * and append them to the given OID list. See also expand_dbname_patterns()
1808 * in pg_dumpall.c
1809 */
1810static void
1814{
1815 PQExpBuffer query;
1816 PGresult *res;
1818 int i;
1819
1820 if (patterns->head == NULL)
1821 return; /* nothing to do */
1822
1823 query = createPQExpBuffer();
1824
1825 /*
1826 * this might sometimes result in duplicate entries in the OID list, but
1827 * we don't care.
1828 */
1829
1830 for (cell = patterns->head; cell; cell = cell->next)
1831 {
1833 int dotcnt;
1834
1835 /*
1836 * Query must remain ABSOLUTELY devoid of unqualified names. This
1837 * would be unnecessary given a pg_table_is_visible() variant taking a
1838 * search_path argument.
1839 *
1840 * For with_child_tables, we start with the basic query's results and
1841 * recursively search the inheritance tree to add child tables.
1842 */
1844 {
1845 appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1846 }
1847
1848 appendPQExpBuffer(query,
1849 "SELECT c.oid"
1850 "\nFROM pg_catalog.pg_class c"
1851 "\n LEFT JOIN pg_catalog.pg_namespace n"
1852 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1853 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1854 "\n (array['%c', '%c', '%c', '%c', '%c', '%c', '%c'])\n",
1859 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1860 false, "n.nspname", "c.relname", NULL,
1861 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1862 &dotcnt);
1863 if (dotcnt > 2)
1864 pg_fatal("improper relation name (too many dotted names): %s",
1865 cell->val);
1866 else if (dotcnt == 2)
1869
1871 {
1872 appendPQExpBufferStr(query, "UNION"
1873 "\nSELECT i.inhrelid"
1874 "\nFROM partition_tree p"
1875 "\n JOIN pg_catalog.pg_inherits i"
1876 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1877 "\n)"
1878 "\nSELECT relid FROM partition_tree");
1879 }
1880
1881 ExecuteSqlStatement(fout, "RESET search_path");
1882 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1885 if (strict_names && PQntuples(res) == 0)
1886 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1887
1888 for (i = 0; i < PQntuples(res); i++)
1889 {
1890 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1891 }
1892
1893 PQclear(res);
1894 resetPQExpBuffer(query);
1895 }
1896
1897 destroyPQExpBuffer(query);
1898}
1899
1900/*
1901 * Verifies that the connected database name matches the given database name,
1902 * and if not, dies with an error about the given pattern.
1903 *
1904 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1905 */
1906static void
1907prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1908{
1909 const char *db;
1910
1911 db = PQdb(conn);
1912 if (db == NULL)
1913 pg_fatal("You are currently not connected to a database.");
1914
1915 if (strcmp(db, dbname) != 0)
1916 pg_fatal("cross-database references are not implemented: %s",
1917 pattern);
1918}
1919
1920/*
1921 * checkExtensionMembership
1922 * Determine whether object is an extension member, and if so,
1923 * record an appropriate dependency and set the object's dump flag.
1924 *
1925 * It's important to call this for each object that could be an extension
1926 * member. Generally, we integrate this with determining the object's
1927 * to-be-dumped-ness, since extension membership overrides other rules for that.
1928 *
1929 * Returns true if object is an extension member, else false.
1930 */
1931static bool
1933{
1935
1936 if (ext == NULL)
1937 return false;
1938
1939 dobj->ext_member = true;
1940
1941 /* Record dependency so that getDependencies needn't deal with that */
1942 addObjectDependency(dobj, ext->dobj.dumpId);
1943
1944 /*
1945 * In 9.6 and above, mark the member object to have any non-initial ACLs
1946 * dumped. (Any initial ACLs will be removed later, using data from
1947 * pg_init_privs, so that we'll dump only the delta from the extension's
1948 * initial setup.)
1949 *
1950 * Prior to 9.6, we do not include any extension member components.
1951 *
1952 * In binary upgrades, we still dump all components of the members
1953 * individually, since the idea is to exactly reproduce the database
1954 * contents rather than replace the extension contents with something
1955 * different.
1956 *
1957 * Note: it might be interesting someday to implement storage and delta
1958 * dumping of extension members' RLS policies and/or security labels.
1959 * However there is a pitfall for RLS policies: trying to dump them
1960 * requires getting a lock on their tables, and the calling user might not
1961 * have privileges for that. We need no lock to examine a table's ACLs,
1962 * so the current feature doesn't have a problem of that sort.
1963 */
1964 if (fout->dopt->binary_upgrade)
1965 dobj->dump = ext->dobj.dump;
1966 else
1967 {
1968 if (fout->remoteVersion < 90600)
1969 dobj->dump = DUMP_COMPONENT_NONE;
1970 else
1971 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1972 }
1973
1974 return true;
1975}
1976
1977/*
1978 * selectDumpableNamespace: policy-setting subroutine
1979 * Mark a namespace as to be dumped or not
1980 */
1981static void
1983{
1984 /*
1985 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1986 * and (for --clean) a DROP SCHEMA statement. (In the absence of
1987 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1988 */
1989 nsinfo->create = true;
1990
1991 /*
1992 * If specific tables are being dumped, do not dump any complete
1993 * namespaces. If specific namespaces are being dumped, dump just those
1994 * namespaces. Otherwise, dump all non-system namespaces.
1995 */
1997 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1998 else if (schema_include_oids.head != NULL)
1999 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2001 nsinfo->dobj.catId.oid) ?
2003 else if (fout->remoteVersion >= 90600 &&
2004 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2005 {
2006 /*
2007 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2008 * they are interesting (and not the original ACLs which were set at
2009 * initdb time, see pg_init_privs).
2010 */
2011 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2012 }
2013 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2014 strcmp(nsinfo->dobj.name, "information_schema") == 0)
2015 {
2016 /* Other system schemas don't get dumped */
2017 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2018 }
2019 else if (strcmp(nsinfo->dobj.name, "public") == 0)
2020 {
2021 /*
2022 * The public schema is a strange beast that sits in a sort of
2023 * no-mans-land between being a system object and a user object.
2024 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2025 * a comment and an indication of ownership. If the owner is the
2026 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2027 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2028 */
2029 nsinfo->create = false;
2030 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2031 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2032 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2033 nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2034
2035 /*
2036 * Also, make like it has a comment even if it doesn't; this is so
2037 * that we'll emit a command to drop the comment, if appropriate.
2038 * (Without this, we'd not call dumpCommentExtended for it.)
2039 */
2040 nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2041 }
2042 else
2043 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2044
2045 /*
2046 * In any case, a namespace can be excluded by an exclusion switch
2047 */
2048 if (nsinfo->dobj.dump_contains &&
2050 nsinfo->dobj.catId.oid))
2051 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2052
2053 /*
2054 * If the schema belongs to an extension, allow extension membership to
2055 * override the dump decision for the schema itself. However, this does
2056 * not change dump_contains, so this won't change what we do with objects
2057 * within the schema. (If they belong to the extension, they'll get
2058 * suppressed by it, otherwise not.)
2059 */
2061}
2062
2063/*
2064 * selectDumpableTable: policy-setting subroutine
2065 * Mark a table as to be dumped or not
2066 */
2067static void
2069{
2071 return; /* extension membership overrides all else */
2072
2073 /*
2074 * If specific tables are being dumped, dump just those tables; else, dump
2075 * according to the parent namespace's dump flag.
2076 */
2079 tbinfo->dobj.catId.oid) ?
2081 else
2082 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2083
2084 /*
2085 * In any case, a table can be excluded by an exclusion switch
2086 */
2087 if (tbinfo->dobj.dump &&
2089 tbinfo->dobj.catId.oid))
2090 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2091}
2092
2093/*
2094 * selectDumpableType: policy-setting subroutine
2095 * Mark a type as to be dumped or not
2096 *
2097 * If it's a table's rowtype or an autogenerated array type, we also apply a
2098 * special type code to facilitate sorting into the desired order. (We don't
2099 * want to consider those to be ordinary types because that would bring tables
2100 * up into the datatype part of the dump order.) We still set the object's
2101 * dump flag; that's not going to cause the dummy type to be dumped, but we
2102 * need it so that casts involving such types will be dumped correctly -- see
2103 * dumpCast. This means the flag should be set the same as for the underlying
2104 * object (the table or base type).
2105 */
2106static void
2108{
2109 /* skip complex types, except for standalone composite types */
2110 if (OidIsValid(tyinfo->typrelid) &&
2111 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2112 {
2113 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2114
2115 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2116 if (tytable != NULL)
2117 tyinfo->dobj.dump = tytable->dobj.dump;
2118 else
2119 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2120 return;
2121 }
2122
2123 /* skip auto-generated array and multirange types */
2124 if (tyinfo->isArray || tyinfo->isMultirange)
2125 {
2126 tyinfo->dobj.objType = DO_DUMMY_TYPE;
2127
2128 /*
2129 * Fall through to set the dump flag; we assume that the subsequent
2130 * rules will do the same thing as they would for the array's base
2131 * type or multirange's range type. (We cannot reliably look up the
2132 * base type here, since getTypes may not have processed it yet.)
2133 */
2134 }
2135
2137 return; /* extension membership overrides all else */
2138
2139 /* Dump based on if the contents of the namespace are being dumped */
2140 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2141}
2142
2143/*
2144 * selectDumpableDefaultACL: policy-setting subroutine
2145 * Mark a default ACL as to be dumped or not
2146 *
2147 * For per-schema default ACLs, dump if the schema is to be dumped.
2148 * Otherwise dump if we are dumping "everything". Note that dumpSchema
2149 * and aclsSkip are checked separately.
2150 */
2151static void
2153{
2154 /* Default ACLs can't be extension members */
2155
2156 if (dinfo->dobj.namespace)
2157 /* default ACLs are considered part of the namespace */
2158 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2159 else
2160 dinfo->dobj.dump = dopt->include_everything ?
2162}
2163
2164/*
2165 * selectDumpableCast: policy-setting subroutine
2166 * Mark a cast as to be dumped or not
2167 *
2168 * Casts do not belong to any particular namespace (since they haven't got
2169 * names), nor do they have identifiable owners. To distinguish user-defined
2170 * casts from built-in ones, we must resort to checking whether the cast's
2171 * OID is in the range reserved for initdb.
2172 */
2173static void
2175{
2176 if (checkExtensionMembership(&cast->dobj, fout))
2177 return; /* extension membership overrides all else */
2178
2179 /*
2180 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2181 * support ACLs currently.
2182 */
2183 if (cast->dobj.catId.oid <= g_last_builtin_oid)
2184 cast->dobj.dump = DUMP_COMPONENT_NONE;
2185 else
2186 cast->dobj.dump = fout->dopt->include_everything ?
2188}
2189
2190/*
2191 * selectDumpableProcLang: policy-setting subroutine
2192 * Mark a procedural language as to be dumped or not
2193 *
2194 * Procedural languages do not belong to any particular namespace. To
2195 * identify built-in languages, we must resort to checking whether the
2196 * language's OID is in the range reserved for initdb.
2197 */
2198static void
2200{
2201 if (checkExtensionMembership(&plang->dobj, fout))
2202 return; /* extension membership overrides all else */
2203
2204 /*
2205 * Only include procedural languages when we are dumping everything.
2206 *
2207 * For from-initdb procedural languages, only include ACLs, as we do for
2208 * the pg_catalog namespace. We need this because procedural languages do
2209 * not live in any namespace.
2210 */
2212 plang->dobj.dump = DUMP_COMPONENT_NONE;
2213 else
2214 {
2215 if (plang->dobj.catId.oid <= g_last_builtin_oid)
2216 plang->dobj.dump = fout->remoteVersion < 90600 ?
2218 else
2219 plang->dobj.dump = DUMP_COMPONENT_ALL;
2220 }
2221}
2222
2223/*
2224 * selectDumpableAccessMethod: policy-setting subroutine
2225 * Mark an access method as to be dumped or not
2226 *
2227 * Access methods do not belong to any particular namespace. To identify
2228 * built-in access methods, we must resort to checking whether the
2229 * method's OID is in the range reserved for initdb.
2230 */
2231static void
2233{
2234 /* see getAccessMethods() comment about v9.6. */
2235 if (fout->remoteVersion < 90600)
2236 {
2237 method->dobj.dump = DUMP_COMPONENT_NONE;
2238 return;
2239 }
2240
2241 if (checkExtensionMembership(&method->dobj, fout))
2242 return; /* extension membership overrides all else */
2243
2244 /*
2245 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2246 * they do not support ACLs currently.
2247 */
2248 if (method->dobj.catId.oid <= g_last_builtin_oid)
2249 method->dobj.dump = DUMP_COMPONENT_NONE;
2250 else
2251 method->dobj.dump = fout->dopt->include_everything ?
2253}
2254
2255/*
2256 * selectDumpableExtension: policy-setting subroutine
2257 * Mark an extension as to be dumped or not
2258 *
2259 * Built-in extensions should be skipped except for checking ACLs, since we
2260 * assume those will already be installed in the target database. We identify
2261 * such extensions by their having OIDs in the range reserved for initdb.
2262 * We dump all user-added extensions by default. No extensions are dumped
2263 * if include_everything is false (i.e., a --schema or --table switch was
2264 * given), except if --extension specifies a list of extensions to dump.
2265 */
2266static void
2268{
2269 /*
2270 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2271 * change permissions on their member objects, if they wish to, and have
2272 * those changes preserved.
2273 */
2274 if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2275 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2276 else
2277 {
2278 /* check if there is a list of extensions to dump */
2280 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2282 extinfo->dobj.catId.oid) ?
2284 else
2285 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2286 dopt->include_everything ?
2288
2289 /* check that the extension is not explicitly excluded */
2290 if (extinfo->dobj.dump &&
2292 extinfo->dobj.catId.oid))
2293 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2294 }
2295}
2296
2297/*
2298 * selectDumpablePublicationObject: policy-setting subroutine
2299 * Mark a publication object as to be dumped or not
2300 *
2301 * A publication can have schemas and tables which have schemas, but those are
2302 * ignored in decision making, because publications are only dumped when we are
2303 * dumping everything.
2304 */
2305static void
2307{
2308 if (checkExtensionMembership(dobj, fout))
2309 return; /* extension membership overrides all else */
2310
2311 dobj->dump = fout->dopt->include_everything ?
2313}
2314
2315/*
2316 * selectDumpableStatisticsObject: policy-setting subroutine
2317 * Mark an extended statistics object as to be dumped or not
2318 *
2319 * We dump an extended statistics object if the schema it's in and the table
2320 * it's for are being dumped. (This'll need more thought if statistics
2321 * objects ever support cross-table stats.)
2322 */
2323static void
2325{
2326 if (checkExtensionMembership(&sobj->dobj, fout))
2327 return; /* extension membership overrides all else */
2328
2329 sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2330 if (sobj->stattable == NULL ||
2331 !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2332 sobj->dobj.dump = DUMP_COMPONENT_NONE;
2333}
2334
2335/*
2336 * selectDumpableObject: policy-setting subroutine
2337 * Mark a generic dumpable object as to be dumped or not
2338 *
2339 * Use this only for object types without a special-case routine above.
2340 */
2341static void
2343{
2344 if (checkExtensionMembership(dobj, fout))
2345 return; /* extension membership overrides all else */
2346
2347 /*
2348 * Default policy is to dump if parent namespace is dumpable, or for
2349 * non-namespace-associated items, dump if we're dumping "everything".
2350 */
2351 if (dobj->namespace)
2352 dobj->dump = dobj->namespace->dobj.dump_contains;
2353 else
2354 dobj->dump = fout->dopt->include_everything ?
2356}
2357
2358/*
2359 * Dump a table's contents for loading using the COPY command
2360 * - this routine is called by the Archiver when it wants the table
2361 * to be dumped.
2362 */
2363static int
2365{
2366 const TableDataInfo *tdinfo = dcontext;
2367 const TableInfo *tbinfo = tdinfo->tdtable;
2368 const char *classname = tbinfo->dobj.name;
2370
2371 /*
2372 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2373 * which uses it already.
2374 */
2377 PGresult *res;
2378 int ret;
2379 char *copybuf;
2380 const char *column_list;
2381
2382 pg_log_info("dumping contents of table \"%s.%s\"",
2383 tbinfo->dobj.namespace->dobj.name, classname);
2384
2385 /*
2386 * Specify the column list explicitly so that we have no possibility of
2387 * retrieving data in the wrong column order. (The default column
2388 * ordering of COPY will not be what we want in certain corner cases
2389 * involving ADD COLUMN and inheritance.)
2390 */
2392
2393 /*
2394 * Use COPY (SELECT ...) TO when dumping a foreign table's data, when a
2395 * filter condition was specified, and when in binary upgrade mode and
2396 * dumping an old pg_largeobject_metadata defined WITH OIDS. For other
2397 * cases a simple COPY suffices.
2398 */
2399 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
2400 (fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
2401 tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
2402 {
2403 /* Temporary allows to access to foreign tables to dump data */
2404 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2406
2407 appendPQExpBufferStr(q, "COPY (SELECT ");
2408 /* klugery to get rid of parens in column list */
2409 if (strlen(column_list) > 2)
2410 {
2412 q->data[q->len - 1] = ' ';
2413 }
2414 else
2415 appendPQExpBufferStr(q, "* ");
2416
2417 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2419 tdinfo->filtercond ? tdinfo->filtercond : "");
2420 }
2421 else
2422 {
2423 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2425 column_list);
2426 }
2428 PQclear(res);
2430
2431 for (;;)
2432 {
2433 ret = PQgetCopyData(conn, &copybuf, 0);
2434
2435 if (ret < 0)
2436 break; /* done or error */
2437
2438 if (copybuf)
2439 {
2440 WriteData(fout, copybuf, ret);
2442 }
2443
2444 /* ----------
2445 * THROTTLE:
2446 *
2447 * There was considerable discussion in late July, 2000 regarding
2448 * slowing down pg_dump when backing up large tables. Users with both
2449 * slow & fast (multi-processor) machines experienced performance
2450 * degradation when doing a backup.
2451 *
2452 * Initial attempts based on sleeping for a number of ms for each ms
2453 * of work were deemed too complex, then a simple 'sleep in each loop'
2454 * implementation was suggested. The latter failed because the loop
2455 * was too tight. Finally, the following was implemented:
2456 *
2457 * If throttle is non-zero, then
2458 * See how long since the last sleep.
2459 * Work out how long to sleep (based on ratio).
2460 * If sleep is more than 100ms, then
2461 * sleep
2462 * reset timer
2463 * EndIf
2464 * EndIf
2465 *
2466 * where the throttle value was the number of ms to sleep per ms of
2467 * work. The calculation was done in each loop.
2468 *
2469 * Most of the hard work is done in the backend, and this solution
2470 * still did not work particularly well: on slow machines, the ratio
2471 * was 50:1, and on medium paced machines, 1:1, and on fast
2472 * multi-processor machines, it had little or no effect, for reasons
2473 * that were unclear.
2474 *
2475 * Further discussion ensued, and the proposal was dropped.
2476 *
2477 * For those people who want this feature, it can be implemented using
2478 * gettimeofday in each loop, calculating the time since last sleep,
2479 * multiplying that by the sleep ratio, then if the result is more
2480 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2481 * function to sleep for a subsecond period ie.
2482 *
2483 * select(0, NULL, NULL, NULL, &tvi);
2484 *
2485 * This will return after the interval specified in the structure tvi.
2486 * Finally, call gettimeofday again to save the 'last sleep time'.
2487 * ----------
2488 */
2489 }
2490 archprintf(fout, "\\.\n\n\n");
2491
2492 if (ret == -2)
2493 {
2494 /* copy data transfer failed */
2495 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2496 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2497 pg_log_error_detail("Command was: %s", q->data);
2498 exit_nicely(1);
2499 }
2500
2501 /* Check command status and return to normal libpq state */
2502 res = PQgetResult(conn);
2503 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2504 {
2505 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2506 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2507 pg_log_error_detail("Command was: %s", q->data);
2508 exit_nicely(1);
2509 }
2510 PQclear(res);
2511
2512 /* Do this to ensure we've pumped libpq back to idle state */
2513 if (PQgetResult(conn) != NULL)
2514 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2515 classname);
2516
2518
2519 /* Revert back the setting */
2520 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2521 set_restrict_relation_kind(fout, "view, foreign-table");
2522
2523 return 1;
2524}
2525
2526/*
2527 * Dump table data using INSERT commands.
2528 *
2529 * Caution: when we restore from an archive file direct to database, the
2530 * INSERT commands emitted by this function have to be parsed by
2531 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2532 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2533 */
2534static int
2536{
2537 const TableDataInfo *tdinfo = dcontext;
2538 const TableInfo *tbinfo = tdinfo->tdtable;
2539 DumpOptions *dopt = fout->dopt;
2542 char *attgenerated;
2543 PGresult *res;
2544 int nfields,
2545 i;
2546 int rows_per_statement = dopt->dump_inserts;
2547 int rows_this_statement = 0;
2548
2549 /* Temporary allows to access to foreign tables to dump data */
2550 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2552
2553 /*
2554 * If we're going to emit INSERTs with column names, the most efficient
2555 * way to deal with generated columns is to exclude them entirely. For
2556 * INSERTs without column names, we have to emit DEFAULT rather than the
2557 * actual column value --- but we can save a few cycles by fetching nulls
2558 * rather than the uninteresting-to-us value.
2559 */
2560 attgenerated = pg_malloc_array(char, tbinfo->numatts);
2561 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2562 nfields = 0;
2563 for (i = 0; i < tbinfo->numatts; i++)
2564 {
2565 if (tbinfo->attisdropped[i])
2566 continue;
2567 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2568 continue;
2569 if (nfields > 0)
2570 appendPQExpBufferStr(q, ", ");
2571 if (tbinfo->attgenerated[i])
2572 appendPQExpBufferStr(q, "NULL");
2573 else
2574 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2575 attgenerated[nfields] = tbinfo->attgenerated[i];
2576 nfields++;
2577 }
2578 /* Servers before 9.4 will complain about zero-column SELECT */
2579 if (nfields == 0)
2580 appendPQExpBufferStr(q, "NULL");
2581 appendPQExpBuffer(q, " FROM ONLY %s",
2583 if (tdinfo->filtercond)
2584 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2585
2587
2588 while (1)
2589 {
2590 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2592
2593 /* cross-check field count, allowing for dummy NULL if any */
2594 if (nfields != PQnfields(res) &&
2595 !(nfields == 0 && PQnfields(res) == 1))
2596 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2597 tbinfo->dobj.name);
2598
2599 /*
2600 * First time through, we build as much of the INSERT statement as
2601 * possible in "insertStmt", which we can then just print for each
2602 * statement. If the table happens to have zero dumpable columns then
2603 * this will be a complete statement, otherwise it will end in
2604 * "VALUES" and be ready to have the row's column values printed.
2605 */
2606 if (insertStmt == NULL)
2607 {
2608 const TableInfo *targettab;
2609
2611
2612 /*
2613 * When load-via-partition-root is set or forced, get the root
2614 * table name for the partition table, so that we can reload data
2615 * through the root table.
2616 */
2617 if (tbinfo->ispartition &&
2618 (dopt->load_via_partition_root ||
2621 else
2622 targettab = tbinfo;
2623
2624 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2626
2627 /* corner case for zero-column table */
2628 if (nfields == 0)
2629 {
2630 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2631 }
2632 else
2633 {
2634 /* append the list of column names if required */
2635 if (dopt->column_inserts)
2636 {
2638 for (int field = 0; field < nfields; field++)
2639 {
2640 if (field > 0)
2643 fmtId(PQfname(res, field)));
2644 }
2646 }
2647
2648 if (tbinfo->needs_override)
2649 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2650
2652 }
2653 }
2654
2655 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2656 {
2657 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2658 if (rows_this_statement == 0)
2659 archputs(insertStmt->data, fout);
2660
2661 /*
2662 * If it is zero-column table then we've already written the
2663 * complete statement, which will mean we've disobeyed
2664 * --rows-per-insert when it's set greater than 1. We do support
2665 * a way to make this multi-row with: SELECT UNION ALL SELECT
2666 * UNION ALL ... but that's non-standard so we should avoid it
2667 * given that using INSERTs is mostly only ever needed for
2668 * cross-database exports.
2669 */
2670 if (nfields == 0)
2671 continue;
2672
2673 /* Emit a row heading */
2674 if (rows_per_statement == 1)
2675 archputs(" (", fout);
2676 else if (rows_this_statement > 0)
2677 archputs(",\n\t(", fout);
2678 else
2679 archputs("\n\t(", fout);
2680
2681 for (int field = 0; field < nfields; field++)
2682 {
2683 if (field > 0)
2684 archputs(", ", fout);
2685 if (attgenerated[field])
2686 {
2687 archputs("DEFAULT", fout);
2688 continue;
2689 }
2690 if (PQgetisnull(res, tuple, field))
2691 {
2692 archputs("NULL", fout);
2693 continue;
2694 }
2695
2696 /* XXX This code is partially duplicated in ruleutils.c */
2697 switch (PQftype(res, field))
2698 {
2699 case INT2OID:
2700 case INT4OID:
2701 case INT8OID:
2702 case OIDOID:
2703 case FLOAT4OID:
2704 case FLOAT8OID:
2705 case NUMERICOID:
2706 {
2707 /*
2708 * These types are printed without quotes unless
2709 * they contain values that aren't accepted by the
2710 * scanner unquoted (e.g., 'NaN'). Note that
2711 * strtod() and friends might accept NaN, so we
2712 * can't use that to test.
2713 *
2714 * In reality we only need to defend against
2715 * infinity and NaN, so we need not get too crazy
2716 * about pattern matching here.
2717 */
2718 const char *s = PQgetvalue(res, tuple, field);
2719
2720 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2721 archputs(s, fout);
2722 else
2723 archprintf(fout, "'%s'", s);
2724 }
2725 break;
2726
2727 case BITOID:
2728 case VARBITOID:
2729 archprintf(fout, "B'%s'",
2730 PQgetvalue(res, tuple, field));
2731 break;
2732
2733 case BOOLOID:
2734 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2735 archputs("true", fout);
2736 else
2737 archputs("false", fout);
2738 break;
2739
2740 default:
2741 /* All other types are printed as string literals. */
2744 PQgetvalue(res, tuple, field),
2745 fout);
2746 archputs(q->data, fout);
2747 break;
2748 }
2749 }
2750
2751 /* Terminate the row ... */
2752 archputs(")", fout);
2753
2754 /* ... and the statement, if the target no. of rows is reached */
2756 {
2757 if (dopt->do_nothing)
2758 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2759 else
2760 archputs(";\n", fout);
2761 /* Reset the row counter */
2763 }
2764 }
2765
2766 if (PQntuples(res) <= 0)
2767 {
2768 PQclear(res);
2769 break;
2770 }
2771 PQclear(res);
2772 }
2773
2774 /* Terminate any statements that didn't make the row count. */
2775 if (rows_this_statement > 0)
2776 {
2777 if (dopt->do_nothing)
2778 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2779 else
2780 archputs(";\n", fout);
2781 }
2782
2783 archputs("\n\n", fout);
2784
2785 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2786
2788 if (insertStmt != NULL)
2790 free(attgenerated);
2791
2792 /* Revert back the setting */
2793 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2794 set_restrict_relation_kind(fout, "view, foreign-table");
2795
2796 return 1;
2797}
2798
2799/*
2800 * getRootTableInfo:
2801 * get the root TableInfo for the given partition table.
2802 */
2803static TableInfo *
2805{
2807
2808 Assert(tbinfo->ispartition);
2809 Assert(tbinfo->numParents == 1);
2810
2811 parentTbinfo = tbinfo->parents[0];
2812 while (parentTbinfo->ispartition)
2813 {
2814 Assert(parentTbinfo->numParents == 1);
2815 parentTbinfo = parentTbinfo->parents[0];
2816 }
2817
2818 return parentTbinfo;
2819}
2820
2821/*
2822 * forcePartitionRootLoad
2823 * Check if we must force load_via_partition_root for this partition.
2824 *
2825 * This is required if any level of ancestral partitioned table has an
2826 * unsafe partitioning scheme.
2827 */
2828static bool
2830{
2832
2833 Assert(tbinfo->ispartition);
2834 Assert(tbinfo->numParents == 1);
2835
2836 parentTbinfo = tbinfo->parents[0];
2837 if (parentTbinfo->unsafe_partitions)
2838 return true;
2839 while (parentTbinfo->ispartition)
2840 {
2841 Assert(parentTbinfo->numParents == 1);
2842 parentTbinfo = parentTbinfo->parents[0];
2843 if (parentTbinfo->unsafe_partitions)
2844 return true;
2845 }
2846
2847 return false;
2848}
2849
2850/*
2851 * dumpTableData -
2852 * dump the contents of a single table
2853 *
2854 * Actually, this just makes an ArchiveEntry for the table contents.
2855 */
2856static void
2858{
2859 DumpOptions *dopt = fout->dopt;
2860 const TableInfo *tbinfo = tdinfo->tdtable;
2863 DataDumperPtr dumpFn;
2864 char *tdDefn = NULL;
2865 char *copyStmt;
2866 const char *copyFrom;
2867
2868 /* We had better have loaded per-column details about this table */
2869 Assert(tbinfo->interesting);
2870
2871 /*
2872 * When load-via-partition-root is set or forced, get the root table name
2873 * for the partition table, so that we can reload data through the root
2874 * table. Then construct a comment to be inserted into the TOC entry's
2875 * defn field, so that such cases can be identified reliably.
2876 */
2877 if (tbinfo->ispartition &&
2878 (dopt->load_via_partition_root ||
2880 {
2881 const TableInfo *parentTbinfo;
2882 char *sanitized;
2883
2887 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2888 sanitized);
2889 free(sanitized);
2890 tdDefn = pg_strdup(copyBuf->data);
2891 }
2892 else
2894
2895 if (dopt->dump_inserts == 0)
2896 {
2897 /* Dump/restore using COPY */
2898 dumpFn = dumpTableData_copy;
2899 /* must use 2 steps here 'cause fmtId is nonreentrant */
2900 printfPQExpBuffer(copyBuf, "COPY %s ",
2901 copyFrom);
2902 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2904 copyStmt = copyBuf->data;
2905 }
2906 else
2907 {
2908 /* Restore using INSERT */
2909 dumpFn = dumpTableData_insert;
2910 copyStmt = NULL;
2911 }
2912
2913 /*
2914 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2915 * dependency on its table as "special" and pass it to ArchiveEntry now.
2916 * See comments for BuildArchiveDependencies.
2917 */
2918 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2919 {
2920 TocEntry *te;
2921
2922 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2923 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2924 .namespace = tbinfo->dobj.namespace->dobj.name,
2925 .owner = tbinfo->rolname,
2926 .description = "TABLE DATA",
2927 .section = SECTION_DATA,
2928 .createStmt = tdDefn,
2929 .copyStmt = copyStmt,
2930 .deps = &(tbinfo->dobj.dumpId),
2931 .nDeps = 1,
2932 .dumpFn = dumpFn,
2933 .dumpArg = tdinfo));
2934
2935 /*
2936 * Set the TocEntry's dataLength in case we are doing a parallel dump
2937 * and want to order dump jobs by table size. We choose to measure
2938 * dataLength in table pages (including TOAST pages) during dump, so
2939 * no scaling is needed.
2940 *
2941 * However, relpages is declared as "integer" in pg_class, and hence
2942 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2943 * Cast so that we get the right interpretation of table sizes
2944 * exceeding INT_MAX pages.
2945 */
2946 te->dataLength = (BlockNumber) tbinfo->relpages;
2947 te->dataLength += (BlockNumber) tbinfo->toastpages;
2948
2949 /*
2950 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2951 * and instead we'd better worry about integer overflow. Clamp to
2952 * INT_MAX if the correct result exceeds that.
2953 */
2954 if (sizeof(te->dataLength) == 4 &&
2955 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2956 te->dataLength < 0))
2957 te->dataLength = INT_MAX;
2958 }
2959
2962}
2963
2964/*
2965 * refreshMatViewData -
2966 * load or refresh the contents of a single materialized view
2967 *
2968 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2969 * statement.
2970 */
2971static void
2973{
2974 TableInfo *tbinfo = tdinfo->tdtable;
2975 PQExpBuffer q;
2976
2977 /* If the materialized view is not flagged as populated, skip this. */
2978 if (!tbinfo->relispopulated)
2979 return;
2980
2981 q = createPQExpBuffer();
2982
2983 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2985
2986 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2988 tdinfo->dobj.catId, /* catalog ID */
2989 tdinfo->dobj.dumpId, /* dump ID */
2990 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2991 .namespace = tbinfo->dobj.namespace->dobj.name,
2992 .owner = tbinfo->rolname,
2993 .description = "MATERIALIZED VIEW DATA",
2994 .section = SECTION_POST_DATA,
2995 .createStmt = q->data,
2996 .deps = tdinfo->dobj.dependencies,
2997 .nDeps = tdinfo->dobj.nDeps));
2998
3000}
3001
3002/*
3003 * getTableData -
3004 * set up dumpable objects representing the contents of tables
3005 */
3006static void
3007getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3008{
3009 int i;
3010
3011 for (i = 0; i < numTables; i++)
3012 {
3013 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3014 (!relkind || tblinfo[i].relkind == relkind))
3015 makeTableDataInfo(dopt, &(tblinfo[i]));
3016 }
3017}
3018
3019/*
3020 * Make a dumpable object for the data of this specific table
3021 *
3022 * Note: we make a TableDataInfo if and only if we are going to dump the
3023 * table data; the "dump" field in such objects isn't very interesting.
3024 */
3025static void
3027{
3029
3030 /*
3031 * Nothing to do if we already decided to dump the table. This will
3032 * happen for "config" tables.
3033 */
3034 if (tbinfo->dataObj != NULL)
3035 return;
3036
3037 /* Skip property graphs (no data to dump) */
3038 if (tbinfo->relkind == RELKIND_PROPGRAPH)
3039 return;
3040 /* Skip VIEWs (no data to dump) */
3041 if (tbinfo->relkind == RELKIND_VIEW)
3042 return;
3043 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3044 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3047 tbinfo->foreign_server)))
3048 return;
3049 /* Skip partitioned tables (data in partitions) */
3050 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3051 return;
3052
3053 /* Don't dump data in unlogged tables, if so requested */
3054 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3056 return;
3057
3058 /* Check that the data is not explicitly excluded */
3060 tbinfo->dobj.catId.oid))
3061 return;
3062
3063 /* OK, let's dump it */
3065
3066 if (tbinfo->relkind == RELKIND_MATVIEW)
3067 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3068 else if (tbinfo->relkind == RELKIND_SEQUENCE)
3069 tdinfo->dobj.objType = DO_SEQUENCE_SET;
3070 else
3071 tdinfo->dobj.objType = DO_TABLE_DATA;
3072
3073 /*
3074 * Note: use tableoid 0 so that this object won't be mistaken for
3075 * something that pg_depend entries apply to.
3076 */
3077 tdinfo->dobj.catId.tableoid = 0;
3078 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3079 AssignDumpId(&tdinfo->dobj);
3080 tdinfo->dobj.name = tbinfo->dobj.name;
3081 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3082 tdinfo->tdtable = tbinfo;
3083 tdinfo->filtercond = NULL; /* might get set later */
3084 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3085
3086 /* A TableDataInfo contains data, of course */
3087 tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3088
3089 tbinfo->dataObj = tdinfo;
3090
3091 /*
3092 * Materialized view statistics must be restored after the data, because
3093 * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3094 *
3095 * The dependency is added here because the statistics objects are created
3096 * first.
3097 */
3098 if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3099 {
3100 tbinfo->stats->section = SECTION_POST_DATA;
3101 addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3102 }
3103
3104 /* Make sure that we'll collect per-column info for this table. */
3105 tbinfo->interesting = true;
3106}
3107
3108/*
3109 * The refresh for a materialized view must be dependent on the refresh for
3110 * any materialized view that this one is dependent on.
3111 *
3112 * This must be called after all the objects are created, but before they are
3113 * sorted.
3114 */
3115static void
3117{
3118 PQExpBuffer query;
3119 PGresult *res;
3120 int ntups,
3121 i;
3122 int i_classid,
3123 i_objid,
3124 i_refobjid;
3125
3126 /* No Mat Views before 9.3. */
3127 if (fout->remoteVersion < 90300)
3128 return;
3129
3130 query = createPQExpBuffer();
3131
3132 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3133 "( "
3134 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3135 "FROM pg_depend d1 "
3136 "JOIN pg_class c1 ON c1.oid = d1.objid "
3137 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3138 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3139 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3140 "AND d2.objid = r1.oid "
3141 "AND d2.refobjid <> d1.objid "
3142 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3143 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3145 "WHERE d1.classid = 'pg_class'::regclass "
3146 "UNION "
3147 "SELECT w.objid, d3.refobjid, c3.relkind "
3148 "FROM w "
3149 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3150 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3151 "AND d3.objid = r3.oid "
3152 "AND d3.refobjid <> w.refobjid "
3153 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3154 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3156 ") "
3157 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3158 "FROM w "
3159 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3160
3161 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3162
3163 ntups = PQntuples(res);
3164
3165 i_classid = PQfnumber(res, "classid");
3166 i_objid = PQfnumber(res, "objid");
3167 i_refobjid = PQfnumber(res, "refobjid");
3168
3169 for (i = 0; i < ntups; i++)
3170 {
3171 CatalogId objId;
3173 DumpableObject *dobj;
3177
3178 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3179 objId.oid = atooid(PQgetvalue(res, i, i_objid));
3180 refobjId.tableoid = objId.tableoid;
3181 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3182
3183 dobj = findObjectByCatalogId(objId);
3184 if (dobj == NULL)
3185 continue;
3186
3187 Assert(dobj->objType == DO_TABLE);
3188 tbinfo = (TableInfo *) dobj;
3189 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3190 dobj = (DumpableObject *) tbinfo->dataObj;
3191 if (dobj == NULL)
3192 continue;
3194
3196 if (refdobj == NULL)
3197 continue;
3198
3199 Assert(refdobj->objType == DO_TABLE);
3201 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3202 refdobj = (DumpableObject *) reftbinfo->dataObj;
3203 if (refdobj == NULL)
3204 continue;
3205 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3206
3208
3209 if (!reftbinfo->relispopulated)
3210 tbinfo->relispopulated = false;
3211 }
3212
3213 PQclear(res);
3214
3215 destroyPQExpBuffer(query);
3216}
3217
3218/*
3219 * getTableDataFKConstraints -
3220 * add dump-order dependencies reflecting foreign key constraints
3221 *
3222 * This code is executed only in a data-only dump --- in schema+data dumps
3223 * we handle foreign key issues by not creating the FK constraints until
3224 * after the data is loaded. In a data-only dump, however, we want to
3225 * order the table data objects in such a way that a table's referenced
3226 * tables are restored first. (In the presence of circular references or
3227 * self-references this may be impossible; we'll detect and complain about
3228 * that during the dependency sorting step.)
3229 */
3230static void
3232{
3234 int numObjs;
3235 int i;
3236
3237 /* Search through all the dumpable objects for FK constraints */
3239 for (i = 0; i < numObjs; i++)
3240 {
3241 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3242 {
3245
3246 /* Not interesting unless both tables are to be dumped */
3247 if (cinfo->contable == NULL ||
3248 cinfo->contable->dataObj == NULL)
3249 continue;
3250 ftable = findTableByOid(cinfo->confrelid);
3251 if (ftable == NULL ||
3252 ftable->dataObj == NULL)
3253 continue;
3254
3255 /*
3256 * Okay, make referencing table's TABLE_DATA object depend on the
3257 * referenced table's TABLE_DATA object.
3258 */
3259 addObjectDependency(&cinfo->contable->dataObj->dobj,
3260 ftable->dataObj->dobj.dumpId);
3261 }
3262 }
3263 free(dobjs);
3264}
3265
3266
3267/*
3268 * dumpDatabase:
3269 * dump the database definition
3270 */
3271static void
3273{
3274 DumpOptions *dopt = fout->dopt;
3280 PGresult *res;
3281 int i_tableoid,
3282 i_oid,
3283 i_datname,
3284 i_datdba,
3285 i_encoding,
3287 i_collate,
3288 i_ctype,
3292 i_minmxid,
3293 i_datacl,
3302 const char *datname,
3303 *dba,
3304 *encoding,
3306 *collate,
3307 *ctype,
3308 *locale,
3309 *icurules,
3311 *datconnlimit,
3312 *tablespace;
3313 uint32 frozenxid,
3314 minmxid;
3315 char *qdatname;
3316
3317 pg_log_info("saving database definition");
3318
3319 /*
3320 * Fetch the database-level properties for this database.
3321 */
3322 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3323 "datdba, "
3324 "pg_encoding_to_char(encoding) AS encoding, "
3325 "datcollate, datctype, datfrozenxid, "
3326 "datacl, acldefault('d', datdba) AS acldefault, "
3327 "datistemplate, datconnlimit, ");
3328 if (fout->remoteVersion >= 90300)
3329 appendPQExpBufferStr(dbQry, "datminmxid, ");
3330 else
3331 appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3332 if (fout->remoteVersion >= 170000)
3333 appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3334 else if (fout->remoteVersion >= 150000)
3335 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3336 else
3337 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3338 if (fout->remoteVersion >= 160000)
3339 appendPQExpBufferStr(dbQry, "daticurules, ");
3340 else
3341 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3343 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3344 "shobj_description(oid, 'pg_database') AS description "
3345 "FROM pg_database "
3346 "WHERE datname = current_database()");
3347
3349
3350 i_tableoid = PQfnumber(res, "tableoid");
3351 i_oid = PQfnumber(res, "oid");
3352 i_datname = PQfnumber(res, "datname");
3353 i_datdba = PQfnumber(res, "datdba");
3354 i_encoding = PQfnumber(res, "encoding");
3355 i_datlocprovider = PQfnumber(res, "datlocprovider");
3356 i_collate = PQfnumber(res, "datcollate");
3357 i_ctype = PQfnumber(res, "datctype");
3358 i_datlocale = PQfnumber(res, "datlocale");
3359 i_daticurules = PQfnumber(res, "daticurules");
3360 i_frozenxid = PQfnumber(res, "datfrozenxid");
3361 i_minmxid = PQfnumber(res, "datminmxid");
3362 i_datacl = PQfnumber(res, "datacl");
3363 i_acldefault = PQfnumber(res, "acldefault");
3364 i_datistemplate = PQfnumber(res, "datistemplate");
3365 i_datconnlimit = PQfnumber(res, "datconnlimit");
3366 i_datcollversion = PQfnumber(res, "datcollversion");
3367 i_tablespace = PQfnumber(res, "tablespace");
3368
3369 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3370 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3371 datname = PQgetvalue(res, 0, i_datname);
3372 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3373 encoding = PQgetvalue(res, 0, i_encoding);
3375 collate = PQgetvalue(res, 0, i_collate);
3376 ctype = PQgetvalue(res, 0, i_ctype);
3377 if (!PQgetisnull(res, 0, i_datlocale))
3378 locale = PQgetvalue(res, 0, i_datlocale);
3379 else
3380 locale = NULL;
3381 if (!PQgetisnull(res, 0, i_daticurules))
3383 else
3384 icurules = NULL;
3385 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3386 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3387 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3388 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3392
3394
3395 /*
3396 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3397 * to preserve that), as well as the encoding, locale, and tablespace
3398 * since those can't be altered later. Other DB properties are left to
3399 * the DATABASE PROPERTIES entry, so that they can be applied after
3400 * reconnecting to the target DB.
3401 *
3402 * For binary upgrade, we use the FILE_COPY strategy because testing has
3403 * shown it to be faster. When the server is in binary upgrade mode, it
3404 * will also skip the checkpoints this strategy ordinarily performs.
3405 */
3406 if (dopt->binary_upgrade)
3407 {
3409 "CREATE DATABASE %s WITH TEMPLATE = template0 "
3410 "OID = %u STRATEGY = FILE_COPY",
3411 qdatname, dbCatId.oid);
3412 }
3413 else
3414 {
3415 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3416 qdatname);
3417 }
3418 if (strlen(encoding) > 0)
3419 {
3420 appendPQExpBufferStr(creaQry, " ENCODING = ");
3422 }
3423
3424 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3425 if (datlocprovider[0] == 'b')
3426 appendPQExpBufferStr(creaQry, "builtin");
3427 else if (datlocprovider[0] == 'c')
3429 else if (datlocprovider[0] == 'i')
3431 else
3432 pg_fatal("unrecognized locale provider: %s",
3434
3435 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3436 {
3437 appendPQExpBufferStr(creaQry, " LOCALE = ");
3439 }
3440 else
3441 {
3442 if (strlen(collate) > 0)
3443 {
3444 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3446 }
3447 if (strlen(ctype) > 0)
3448 {
3449 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3451 }
3452 }
3453 if (locale)
3454 {
3455 if (datlocprovider[0] == 'b')
3456 appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3457 else
3458 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3459
3461 }
3462
3463 if (icurules)
3464 {
3465 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3467 }
3468
3469 /*
3470 * For binary upgrade, carry over the collation version. For normal
3471 * dump/restore, omit the version, so that it is computed upon restore.
3472 */
3473 if (dopt->binary_upgrade)
3474 {
3475 if (!PQgetisnull(res, 0, i_datcollversion))
3476 {
3477 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3480 fout);
3481 }
3482 }
3483
3484 /*
3485 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3486 * thing; the decision whether to specify a tablespace should be left till
3487 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3488 * label the DATABASE entry with the tablespace and let the normal
3489 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3490 * attention to default_tablespace, so that won't work.
3491 */
3492 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3493 !dopt->outputNoTablespaces)
3494 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3495 fmtId(tablespace));
3497
3498 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3499 qdatname);
3500
3502
3504 dbCatId, /* catalog ID */
3505 dbDumpId, /* dump ID */
3506 ARCHIVE_OPTS(.tag = datname,
3507 .owner = dba,
3508 .description = "DATABASE",
3509 .section = SECTION_PRE_DATA,
3510 .createStmt = creaQry->data,
3511 .dropStmt = delQry->data));
3512
3513 /* Compute correct tag for archive entry */
3514 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3515
3516 /* Dump DB comment if any */
3517 {
3518 /*
3519 * 8.2 and up keep comments on shared objects in a shared table, so we
3520 * cannot use the dumpComment() code used for other database objects.
3521 * Be careful that the ArchiveEntry parameters match that function.
3522 */
3523 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3524
3525 if (comment && *comment && !dopt->no_comments)
3526 {
3528
3529 /*
3530 * Generates warning when loaded into a differently-named
3531 * database.
3532 */
3533 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3536
3538 ARCHIVE_OPTS(.tag = labelq->data,
3539 .owner = dba,
3540 .description = "COMMENT",
3541 .section = SECTION_NONE,
3542 .createStmt = dbQry->data,
3543 .deps = &dbDumpId,
3544 .nDeps = 1));
3545 }
3546 }
3547
3548 /* Dump DB security label, if enabled */
3549 if (!dopt->no_security_labels)
3550 {
3551 PGresult *shres;
3553
3555
3556 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3560 if (seclabelQry->len > 0)
3562 ARCHIVE_OPTS(.tag = labelq->data,
3563 .owner = dba,
3564 .description = "SECURITY LABEL",
3565 .section = SECTION_NONE,
3566 .createStmt = seclabelQry->data,
3567 .deps = &dbDumpId,
3568 .nDeps = 1));
3570 PQclear(shres);
3571 }
3572
3573 /*
3574 * Dump ACL if any. Note that we do not support initial privileges
3575 * (pg_init_privs) on databases.
3576 */
3577 dbdacl.privtype = 0;
3578 dbdacl.initprivs = NULL;
3579
3580 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3581 qdatname, NULL, NULL,
3582 NULL, dba, &dbdacl);
3583
3584 /*
3585 * Now construct a DATABASE PROPERTIES archive entry to restore any
3586 * non-default database-level properties. (The reason this must be
3587 * separate is that we cannot put any additional commands into the TOC
3588 * entry that has CREATE DATABASE. pg_restore would execute such a group
3589 * in an implicit transaction block, and the backend won't allow CREATE
3590 * DATABASE in that context.)
3591 */
3594
3595 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3596 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3598
3599 if (strcmp(datistemplate, "t") == 0)
3600 {
3601 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3602 qdatname);
3603
3604 /*
3605 * The backend won't accept DROP DATABASE on a template database. We
3606 * can deal with that by removing the template marking before the DROP
3607 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3608 * since no such command is currently supported, fake it with a direct
3609 * UPDATE on pg_database.
3610 */
3611 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3612 "SET datistemplate = false WHERE datname = ");
3615 }
3616
3617 /*
3618 * We do not restore pg_database.dathasloginevt because it is set
3619 * automatically on login event trigger creation.
3620 */
3621
3622 /* Add database-specific SET options */
3624
3625 /*
3626 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3627 * entry, too, for lack of a better place.
3628 */
3629 if (dopt->binary_upgrade)
3630 {
3631 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3632 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3633 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3634 "WHERE datname = ",
3635 frozenxid, minmxid);
3638 }
3639
3640 if (creaQry->len > 0)
3642 ARCHIVE_OPTS(.tag = datname,
3643 .owner = dba,
3644 .description = "DATABASE PROPERTIES",
3645 .section = SECTION_PRE_DATA,
3646 .createStmt = creaQry->data,
3647 .dropStmt = delQry->data,
3648 .deps = &dbDumpId));
3649
3650 /*
3651 * pg_largeobject comes from the old system intact, so set its
3652 * relfrozenxids, relminmxids and relfilenode.
3653 *
3654 * pg_largeobject_metadata also comes from the old system intact for
3655 * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3656 * relfilenode, too. pg_upgrade can't copy/link the files from older
3657 * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3658 * changed its storage format in v16.
3659 */
3660 if (dopt->binary_upgrade)
3661 {
3668 int ii_relfrozenxid,
3670 ii_oid,
3672
3673 if (fout->remoteVersion >= 90300)
3674 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3675 "FROM pg_catalog.pg_class\n"
3676 "WHERE oid IN (%u, %u, %u, %u);\n",
3679 else
3680 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3681 "FROM pg_catalog.pg_class\n"
3682 "WHERE oid IN (%u, %u);\n",
3684
3686
3687 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3688 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3689 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3690 ii_oid = PQfnumber(lo_res, "oid");
3691
3692 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3693 appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3694 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3695 appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3696 for (int i = 0; i < PQntuples(lo_res); ++i)
3697 {
3698 Oid oid;
3699 RelFileNumber relfilenumber;
3702
3703 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3704 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3705
3706 if (oid == LargeObjectRelationId ||
3708 {
3710 outQry = loOutQry;
3711 }
3712 else
3713 {
3715 outQry = lomOutQry;
3716 }
3717
3718 appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3719 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3720 "WHERE oid = %u;\n",
3724
3725 if (oid == LargeObjectRelationId ||
3728 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3729 relfilenumber);
3730 else if (oid == LargeObjectLOidPNIndexId ||
3733 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3734 relfilenumber);
3735 }
3736
3738 "TRUNCATE pg_catalog.pg_largeobject;\n");
3740 "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3741
3744
3746 ARCHIVE_OPTS(.tag = "pg_largeobject",
3747 .description = "pg_largeobject",
3748 .section = SECTION_PRE_DATA,
3749 .createStmt = loOutQry->data));
3750
3751 if (fout->remoteVersion >= 160000)
3753 ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3754 .description = "pg_largeobject_metadata",
3755 .section = SECTION_PRE_DATA,
3756 .createStmt = lomOutQry->data));
3757
3758 PQclear(lo_res);
3759
3765 }
3766
3767 PQclear(res);
3768
3769 free(qdatname);
3774}
3775
3776/*
3777 * Collect any database-specific or role-and-database-specific SET options
3778 * for this database, and append them to outbuf.
3779 */
3780static void
3782 const char *dbname, Oid dboid)
3783{
3784 PGconn *conn = GetConnection(AH);
3786 PGresult *res;
3787
3788 /* First collect database-specific options */
3789 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3790 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3791 dboid);
3792
3793 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3794
3795 for (int i = 0; i < PQntuples(res); i++)
3797 "DATABASE", dbname, NULL, NULL,
3798 outbuf);
3799
3800 PQclear(res);
3801
3802 /* Now look for role-and-database-specific options */
3803 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3804 "FROM pg_db_role_setting s, pg_roles r "
3805 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3806 dboid);
3807
3808 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3809
3810 for (int i = 0; i < PQntuples(res); i++)
3812 "ROLE", PQgetvalue(res, i, 0),
3813 "DATABASE", dbname,
3814 outbuf);
3815
3816 PQclear(res);
3817
3819}
3820
3821/*
3822 * dumpEncoding: put the correct encoding into the archive
3823 */
3824static void
3826{
3827 const char *encname = pg_encoding_to_char(AH->encoding);
3829
3830 pg_log_info("saving encoding = %s", encname);
3831
3832 appendPQExpBufferStr(qry, "SET client_encoding = ");
3834 appendPQExpBufferStr(qry, ";\n");
3835
3837 ARCHIVE_OPTS(.tag = "ENCODING",
3838 .description = "ENCODING",
3839 .section = SECTION_PRE_DATA,
3840 .createStmt = qry->data));
3841
3842 destroyPQExpBuffer(qry);
3843}
3844
3845
3846/*
3847 * dumpStdStrings: put the correct escape string behavior into the archive
3848 */
3849static void
3851{
3852 const char *stdstrings = AH->std_strings ? "on" : "off";
3854
3855 pg_log_info("saving \"standard_conforming_strings = %s\"",
3856 stdstrings);
3857
3858 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3859 stdstrings);
3860
3862 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3863 .description = "STDSTRINGS",
3864 .section = SECTION_PRE_DATA,
3865 .createStmt = qry->data));
3866
3867 destroyPQExpBuffer(qry);
3868}
3869
3870/*
3871 * dumpSearchPath: record the active search_path in the archive
3872 */
3873static void
3875{
3878 PGresult *res;
3879 char **schemanames = NULL;
3880 int nschemanames = 0;
3881 int i;
3882
3883 /*
3884 * We use the result of current_schemas(), not the search_path GUC,
3885 * because that might contain wildcards such as "$user", which won't
3886 * necessarily have the same value during restore. Also, this way avoids
3887 * listing schemas that may appear in search_path but not actually exist,
3888 * which seems like a prudent exclusion.
3889 */
3891 "SELECT pg_catalog.current_schemas(false)");
3892
3893 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3894 pg_fatal("could not parse result of current_schemas()");
3895
3896 /*
3897 * We use set_config(), not a simple "SET search_path" command, because
3898 * the latter has less-clean behavior if the search path is empty. While
3899 * that's likely to get fixed at some point, it seems like a good idea to
3900 * be as backwards-compatible as possible in what we put into archives.
3901 */
3902 for (i = 0; i < nschemanames; i++)
3903 {
3904 if (i > 0)
3905 appendPQExpBufferStr(path, ", ");
3907 }
3908
3909 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3910 appendStringLiteralAH(qry, path->data, AH);
3911 appendPQExpBufferStr(qry, ", false);\n");
3912
3913 pg_log_info("saving \"search_path = %s\"", path->data);
3914
3916 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3917 .description = "SEARCHPATH",
3918 .section = SECTION_PRE_DATA,
3919 .createStmt = qry->data));
3920
3921 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3922 AH->searchpath = pg_strdup(qry->data);
3923
3925 PQclear(res);
3926 destroyPQExpBuffer(qry);
3927 destroyPQExpBuffer(path);
3928}
3929
3930
3931/*
3932 * getLOs:
3933 * Collect schema-level data about large objects
3934 */
3935static void
3937{
3938 DumpOptions *dopt = fout->dopt;
3940 PGresult *res;
3941 int ntups;
3942 int i;
3943 int n;
3944 int i_oid;
3945 int i_lomowner;
3946 int i_lomacl;
3947 int i_acldefault;
3948
3949 pg_log_info("reading large objects");
3950
3951 /*
3952 * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3953 * with the same owner/ACL appear together.
3954 */
3956 "SELECT oid, lomowner, lomacl, "
3957 "acldefault('L', lomowner) AS acldefault "
3958 "FROM pg_largeobject_metadata ");
3959
3960 /*
3961 * For binary upgrades, we transfer pg_largeobject_metadata via COPY or by
3962 * copying/linking its files from the old cluster. On such upgrades, we
3963 * only need to consider large objects that have comments or security
3964 * labels, since we still restore those objects via COMMENT/SECURITY LABEL
3965 * commands.
3966 */
3967 if (dopt->binary_upgrade)
3969 "WHERE oid IN "
3970 "(SELECT objoid FROM pg_description "
3971 "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
3972 "UNION SELECT objoid FROM pg_seclabel "
3973 "WHERE classoid = " CppAsString2(LargeObjectRelationId) ") ");
3974
3976 "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3977
3979
3980 i_oid = PQfnumber(res, "oid");
3981 i_lomowner = PQfnumber(res, "lomowner");
3982 i_lomacl = PQfnumber(res, "lomacl");
3983 i_acldefault = PQfnumber(res, "acldefault");
3984
3985 ntups = PQntuples(res);
3986
3987 /*
3988 * Group the blobs into suitably-sized groups that have the same owner and
3989 * ACL setting, and build a metadata and a data DumpableObject for each
3990 * group. (If we supported initprivs for blobs, we'd have to insist that
3991 * groups also share initprivs settings, since the DumpableObject only has
3992 * room for one.) i is the index of the first tuple in the current group,
3993 * and n is the number of tuples we include in the group.
3994 */
3995 for (i = 0; i < ntups; i += n)
3996 {
3997 Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3998 char *thisowner = PQgetvalue(res, i, i_lomowner);
3999 char *thisacl = PQgetvalue(res, i, i_lomacl);
4000 LoInfo *loinfo;
4002 char namebuf[64];
4003
4004 /* Scan to find first tuple not to be included in group */
4005 n = 1;
4006 while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4007 {
4008 if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4009 strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4010 break;
4011 n++;
4012 }
4013
4014 /* Build the metadata DumpableObject */
4015 loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4016
4018 loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4019 loinfo->dobj.catId.oid = thisoid;
4020 AssignDumpId(&loinfo->dobj);
4021
4022 if (n > 1)
4023 snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4024 atooid(PQgetvalue(res, i + n - 1, i_oid)));
4025 else
4026 snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4027 loinfo->dobj.name = pg_strdup(namebuf);
4028 loinfo->dacl.acl = pg_strdup(thisacl);
4029 loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4030 loinfo->dacl.privtype = 0;
4031 loinfo->dacl.initprivs = NULL;
4032 loinfo->rolname = getRoleName(thisowner);
4033 loinfo->numlos = n;
4034 loinfo->looids[0] = thisoid;
4035 /* Collect OIDs of the remaining blobs in this group */
4036 for (int k = 1; k < n; k++)
4037 {
4039
4040 loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4041
4042 /* Make sure we can look up loinfo by any of the blobs' OIDs */
4043 extraID.tableoid = LargeObjectRelationId;
4044 extraID.oid = loinfo->looids[k];
4046 }
4047
4048 /* LOs have data */
4049 loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4050
4051 /* Mark whether LO group has a non-empty ACL */
4052 if (!PQgetisnull(res, i, i_lomacl))
4053 loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4054
4055 /*
4056 * In binary upgrade mode, pg_largeobject and pg_largeobject_metadata
4057 * are transferred via COPY or by copying/linking the files from the
4058 * old cluster. Thus, we do not need to dump LO data, definitions, or
4059 * ACLs.
4060 */
4061 if (dopt->binary_upgrade)
4063
4064 /*
4065 * Create a "BLOBS" data item for the group, too. This is just a
4066 * placeholder for sorting; it carries no data now.
4067 */
4069 lodata->objType = DO_LARGE_OBJECT_DATA;
4070 lodata->catId = nilCatalogId;
4072 lodata->name = pg_strdup(namebuf);
4073 lodata->components |= DUMP_COMPONENT_DATA;
4074 /* Set up explicit dependency from data to metadata */
4075 lodata->dependencies = pg_malloc_object(DumpId);
4076 lodata->dependencies[0] = loinfo->dobj.dumpId;
4077 lodata->nDeps = lodata->allocDeps = 1;
4078 }
4079
4080 PQclear(res);
4082}
4083
4084/*
4085 * dumpLO
4086 *
4087 * dump the definition (metadata) of the given large object group
4088 */
4089static void
4091{
4093
4094 /*
4095 * The "definition" is just a newline-separated list of OIDs. We need to
4096 * put something into the dropStmt too, but it can just be a comment.
4097 */
4098 for (int i = 0; i < loinfo->numlos; i++)
4099 appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4100
4101 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4102 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4103 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4104 .owner = loinfo->rolname,
4105 .description = "BLOB METADATA",
4106 .section = SECTION_DATA,
4107 .createStmt = cquery->data,
4108 .dropStmt = "-- dummy"));
4109
4110 /*
4111 * Dump per-blob comments and seclabels if any. We assume these are rare
4112 * enough that it's okay to generate retail TOC entries for them.
4113 */
4114 if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4116 {
4117 for (int i = 0; i < loinfo->numlos; i++)
4118 {
4119 CatalogId catId;
4120 char namebuf[32];
4121
4122 /* Build identifying info for this blob */
4123 catId.tableoid = loinfo->dobj.catId.tableoid;
4124 catId.oid = loinfo->looids[i];
4125 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4126
4127 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4128 dumpComment(fout, "LARGE OBJECT", namebuf,
4129 NULL, loinfo->rolname,
4130 catId, 0, loinfo->dobj.dumpId);
4131
4132 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4133 dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4134 NULL, loinfo->rolname,
4135 catId, 0, loinfo->dobj.dumpId);
4136 }
4137 }
4138
4139 /*
4140 * Dump the ACLs if any (remember that all blobs in the group will have
4141 * the same ACL). If there's just one blob, dump a simple ACL entry; if
4142 * there's more, make a "LARGE OBJECTS" entry that really contains only
4143 * the ACL for the first blob. _printTocEntry() will be cued by the tag
4144 * string to emit a mutated version for each blob.
4145 */
4146 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4147 {
4148 char namebuf[32];
4149
4150 /* Build identifying info for the first blob */
4151 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4152
4153 if (loinfo->numlos > 1)
4154 {
4155 char tagbuf[64];
4156
4157 snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4158 loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4159
4160 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4161 "LARGE OBJECT", namebuf, NULL, NULL,
4162 tagbuf, loinfo->rolname, &loinfo->dacl);
4163 }
4164 else
4165 {
4166 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4167 "LARGE OBJECT", namebuf, NULL, NULL,
4168 NULL, loinfo->rolname, &loinfo->dacl);
4169 }
4170 }
4171
4173}
4174
4175/*
4176 * dumpLOs:
4177 * dump the data contents of the large objects in the given group
4178 */
4179static int
4180dumpLOs(Archive *fout, const void *arg)
4181{
4182 const LoInfo *loinfo = (const LoInfo *) arg;
4184 char buf[LOBBUFSIZE];
4185
4186 pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4187
4188 for (int i = 0; i < loinfo->numlos; i++)
4189 {
4190 Oid loOid = loinfo->looids[i];
4191 int loFd;
4192 int cnt;
4193
4194 /* Open the LO */
4195 loFd = lo_open(conn, loOid, INV_READ);
4196 if (loFd == -1)
4197 pg_fatal("could not open large object %u: %s",
4199
4200 StartLO(fout, loOid);
4201
4202 /* Now read it in chunks, sending data to archive */
4203 do
4204 {
4205 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4206 if (cnt < 0)
4207 pg_fatal("error reading large object %u: %s",
4209
4210 WriteData(fout, buf, cnt);
4211 } while (cnt > 0);
4212
4213 lo_close(conn, loFd);
4214
4215 EndLO(fout, loOid);
4216 }
4217
4218 return 1;
4219}
4220
4221/*
4222 * getPolicies
4223 * get information about all RLS policies on dumpable tables.
4224 */
4225void
4227{
4228 DumpOptions *dopt = fout->dopt;
4229 PQExpBuffer query;
4231 PGresult *res;
4233 int i_oid;
4234 int i_tableoid;
4235 int i_polrelid;
4236 int i_polname;
4237 int i_polcmd;
4238 int i_polpermissive;
4239 int i_polroles;
4240 int i_polqual;
4241 int i_polwithcheck;
4242 int i,
4243 j,
4244 ntups;
4245
4246 /* No policies before 9.5 */
4247 if (fout->remoteVersion < 90500)
4248 return;
4249
4250 /* Skip if --no-policies was specified */
4251 if (dopt->no_policies)
4252 return;
4253
4254 query = createPQExpBuffer();
4256
4257 /*
4258 * Identify tables of interest, and check which ones have RLS enabled.
4259 */
4261 for (i = 0; i < numTables; i++)
4262 {
4263 TableInfo *tbinfo = &tblinfo[i];
4264
4265 /* Ignore row security on tables not to be dumped */
4266 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4267 continue;
4268
4269 /* It can't have RLS or policies if it's not a table */
4270 if (tbinfo->relkind != RELKIND_RELATION &&
4272 continue;
4273
4274 /* Add it to the list of table OIDs to be probed below */
4275 if (tbloids->len > 1) /* do we have more than the '{'? */
4277 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4278
4279 /* Is RLS enabled? (That's separate from whether it has policies) */
4280 if (tbinfo->rowsec)
4281 {
4282 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4283
4284 /*
4285 * We represent RLS being enabled on a table by creating a
4286 * PolicyInfo object with null polname.
4287 *
4288 * Note: use tableoid 0 so that this object won't be mistaken for
4289 * something that pg_depend entries apply to.
4290 */
4292 polinfo->dobj.objType = DO_POLICY;
4293 polinfo->dobj.catId.tableoid = 0;
4294 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4295 AssignDumpId(&polinfo->dobj);
4296 polinfo->dobj.namespace = tbinfo->dobj.namespace;
4297 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4298 polinfo->poltable = tbinfo;
4299 polinfo->polname = NULL;
4300 polinfo->polcmd = '\0';
4301 polinfo->polpermissive = 0;
4302 polinfo->polroles = NULL;
4303 polinfo->polqual = NULL;
4304 polinfo->polwithcheck = NULL;
4305 }
4306 }
4308
4309 /*
4310 * Now, read all RLS policies belonging to the tables of interest, and
4311 * create PolicyInfo objects for them. (Note that we must filter the
4312 * results server-side not locally, because we dare not apply pg_get_expr
4313 * to tables we don't have lock on.)
4314 */
4315 pg_log_info("reading row-level security policies");
4316
4317 printfPQExpBuffer(query,
4318 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4319 if (fout->remoteVersion >= 100000)
4320 appendPQExpBufferStr(query, "pol.polpermissive, ");
4321 else
4322 appendPQExpBufferStr(query, "'t' as polpermissive, ");
4323 appendPQExpBuffer(query,
4324 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4325 " 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, "
4326 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4327 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4328 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4329 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4330 tbloids->data);
4331
4332 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4333
4334 ntups = PQntuples(res);
4335 if (ntups > 0)
4336 {
4337 i_oid = PQfnumber(res, "oid");
4338 i_tableoid = PQfnumber(res, "tableoid");
4339 i_polrelid = PQfnumber(res, "polrelid");
4340 i_polname = PQfnumber(res, "polname");
4341 i_polcmd = PQfnumber(res, "polcmd");
4342 i_polpermissive = PQfnumber(res, "polpermissive");
4343 i_polroles = PQfnumber(res, "polroles");
4344 i_polqual = PQfnumber(res, "polqual");
4345 i_polwithcheck = PQfnumber(res, "polwithcheck");
4346
4348
4349 for (j = 0; j < ntups; j++)
4350 {
4353
4354 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4355
4356 polinfo[j].dobj.objType = DO_POLICY;
4357 polinfo[j].dobj.catId.tableoid =
4358 atooid(PQgetvalue(res, j, i_tableoid));
4359 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4360 AssignDumpId(&polinfo[j].dobj);
4361 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4362 polinfo[j].poltable = tbinfo;
4363 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4364 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4365
4366 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4367 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4368
4369 if (PQgetisnull(res, j, i_polroles))
4370 polinfo[j].polroles = NULL;
4371 else
4372 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4373
4374 if (PQgetisnull(res, j, i_polqual))
4375 polinfo[j].polqual = NULL;
4376 else
4377 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4378
4379 if (PQgetisnull(res, j, i_polwithcheck))
4380 polinfo[j].polwithcheck = NULL;
4381 else
4382 polinfo[j].polwithcheck
4384 }
4385 }
4386
4387 PQclear(res);
4388
4389 destroyPQExpBuffer(query);
4391}
4392
4393/*
4394 * dumpPolicy
4395 * dump the definition of the given policy
4396 */
4397static void
4399{
4400 DumpOptions *dopt = fout->dopt;
4401 TableInfo *tbinfo = polinfo->poltable;
4402 PQExpBuffer query;
4405 char *qtabname;
4406 const char *cmd;
4407 char *tag;
4408
4409 /* Do nothing if not dumping schema */
4410 if (!dopt->dumpSchema)
4411 return;
4412
4413 /*
4414 * If polname is NULL, then this record is just indicating that ROW LEVEL
4415 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4416 * ROW LEVEL SECURITY.
4417 */
4418 if (polinfo->polname == NULL)
4419 {
4420 query = createPQExpBuffer();
4421
4422 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4424
4425 /*
4426 * We must emit the ROW SECURITY object's dependency on its table
4427 * explicitly, because it will not match anything in pg_depend (unlike
4428 * the case for other PolicyInfo objects).
4429 */
4430 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4431 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4432 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4433 .namespace = polinfo->dobj.namespace->dobj.name,
4434 .owner = tbinfo->rolname,
4435 .description = "ROW SECURITY",
4436 .section = SECTION_POST_DATA,
4437 .createStmt = query->data,
4438 .deps = &(tbinfo->dobj.dumpId),
4439 .nDeps = 1));
4440
4441 destroyPQExpBuffer(query);
4442 return;
4443 }
4444
4445 if (polinfo->polcmd == '*')
4446 cmd = "";
4447 else if (polinfo->polcmd == 'r')
4448 cmd = " FOR SELECT";
4449 else if (polinfo->polcmd == 'a')
4450 cmd = " FOR INSERT";
4451 else if (polinfo->polcmd == 'w')
4452 cmd = " FOR UPDATE";
4453 else if (polinfo->polcmd == 'd')
4454 cmd = " FOR DELETE";
4455 else
4456 pg_fatal("unexpected policy command type: %c",
4457 polinfo->polcmd);
4458
4459 query = createPQExpBuffer();
4462
4463 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4464
4465 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4466
4467 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4468 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4469
4470 if (polinfo->polroles != NULL)
4471 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4472
4473 if (polinfo->polqual != NULL)
4474 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4475
4476 if (polinfo->polwithcheck != NULL)
4477 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4478
4479 appendPQExpBufferStr(query, ";\n");
4480
4481 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4483
4484 appendPQExpBuffer(polprefix, "POLICY %s ON",
4485 fmtId(polinfo->polname));
4486
4487 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4488
4489 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4490 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4491 ARCHIVE_OPTS(.tag = tag,
4492 .namespace = polinfo->dobj.namespace->dobj.name,
4493 .owner = tbinfo->rolname,
4494 .description = "POLICY",
4495 .section = SECTION_POST_DATA,
4496 .createStmt = query->data,
4497 .dropStmt = delqry->data));
4498
4499 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4501 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4502 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4503
4504 free(tag);
4505 destroyPQExpBuffer(query);
4508 free(qtabname);
4509}
4510
4511/*
4512 * getPublications
4513 * get information about publications
4514 */
4515void
4517{
4518 DumpOptions *dopt = fout->dopt;
4519 PQExpBuffer query;
4520 PGresult *res;
4522 int i_tableoid;
4523 int i_oid;
4524 int i_pubname;
4525 int i_pubowner;
4526 int i_puballtables;
4528 int i_pubinsert;
4529 int i_pubupdate;
4530 int i_pubdelete;
4531 int i_pubtruncate;
4532 int i_pubviaroot;
4533 int i_pubgencols;
4534 int i,
4535 ntups;
4536
4537 if (dopt->no_publications || fout->remoteVersion < 100000)
4538 return;
4539
4540 query = createPQExpBuffer();
4541
4542 /* Get the publications. */
4543 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4544 "p.pubowner, p.puballtables, p.pubinsert, "
4545 "p.pubupdate, p.pubdelete, ");
4546
4547 if (fout->remoteVersion >= 110000)
4548 appendPQExpBufferStr(query, "p.pubtruncate, ");
4549 else
4550 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4551
4552 if (fout->remoteVersion >= 130000)
4553 appendPQExpBufferStr(query, "p.pubviaroot, ");
4554 else
4555 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4556
4557 if (fout->remoteVersion >= 180000)
4558 appendPQExpBufferStr(query, "p.pubgencols, ");
4559 else
4560 appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4561
4562 if (fout->remoteVersion >= 190000)
4563 appendPQExpBufferStr(query, "p.puballsequences ");
4564 else
4565 appendPQExpBufferStr(query, "false AS puballsequences ");
4566
4567 appendPQExpBufferStr(query, "FROM pg_publication p");
4568
4569 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4570
4571 ntups = PQntuples(res);
4572
4573 if (ntups == 0)
4574 goto cleanup;
4575
4576 i_tableoid = PQfnumber(res, "tableoid");
4577 i_oid = PQfnumber(res, "oid");
4578 i_pubname = PQfnumber(res, "pubname");
4579 i_pubowner = PQfnumber(res, "pubowner");
4580 i_puballtables = PQfnumber(res, "puballtables");
4581 i_puballsequences = PQfnumber(res, "puballsequences");
4582 i_pubinsert = PQfnumber(res, "pubinsert");
4583 i_pubupdate = PQfnumber(res, "pubupdate");
4584 i_pubdelete = PQfnumber(res, "pubdelete");
4585 i_pubtruncate = PQfnumber(res, "pubtruncate");
4586 i_pubviaroot = PQfnumber(res, "pubviaroot");
4587 i_pubgencols = PQfnumber(res, "pubgencols");
4588
4590
4591 for (i = 0; i < ntups; i++)
4592 {
4593 pubinfo[i].dobj.objType = DO_PUBLICATION;
4594 pubinfo[i].dobj.catId.tableoid =
4595 atooid(PQgetvalue(res, i, i_tableoid));
4596 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4597 AssignDumpId(&pubinfo[i].dobj);
4598 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4599 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4600 pubinfo[i].puballtables =
4601 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4602 pubinfo[i].puballsequences =
4603 (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4604 pubinfo[i].pubinsert =
4605 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4606 pubinfo[i].pubupdate =
4607 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4608 pubinfo[i].pubdelete =
4609 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4610 pubinfo[i].pubtruncate =
4611 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4612 pubinfo[i].pubviaroot =
4613 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4614 pubinfo[i].pubgencols_type =
4615 *(PQgetvalue(res, i, i_pubgencols));
4616 pubinfo[i].except_tables = (SimplePtrList)
4617 {
4618 NULL, NULL
4619 };
4620
4621 /* Decide whether we want to dump it */
4623
4624 /*
4625 * Get the list of tables for publications specified in the EXCEPT
4626 * TABLE clause.
4627 *
4628 * Although individual table entries in EXCEPT list could be stored in
4629 * PublicationRelInfo, dumpPublicationTable cannot be used to emit
4630 * them, because there is no ALTER PUBLICATION ... ADD command to add
4631 * individual table entries to the EXCEPT list.
4632 *
4633 * Therefore, the approach is to dump the complete EXCEPT list in a
4634 * single CREATE PUBLICATION statement. PublicationInfo is used to
4635 * collect this information, which is then emitted by
4636 * dumpPublication().
4637 */
4638 if (fout->remoteVersion >= 190000)
4639 {
4640 int ntbls;
4642
4643 resetPQExpBuffer(query);
4644 appendPQExpBuffer(query,
4645 "SELECT prrelid\n"
4646 "FROM pg_catalog.pg_publication_rel\n"
4647 "WHERE prpubid = %u AND prexcept",
4648 pubinfo[i].dobj.catId.oid);
4649
4651
4653
4654 for (int j = 0; j < ntbls; j++)
4655 {
4656 Oid prrelid;
4658
4660
4662
4663 if (tbinfo != NULL)
4664 simple_ptr_list_append(&pubinfo[i].except_tables, tbinfo);
4665 }
4666
4668 }
4669 }
4670
4671cleanup:
4672 PQclear(res);
4673
4674 destroyPQExpBuffer(query);
4675}
4676
4677/*
4678 * dumpPublication
4679 * dump the definition of the given publication
4680 */
4681static void
4683{
4684 DumpOptions *dopt = fout->dopt;
4686 PQExpBuffer query;
4687 char *qpubname;
4688 bool first = true;
4689
4690 /* Do nothing if not dumping schema */
4691 if (!dopt->dumpSchema)
4692 return;
4693
4695 query = createPQExpBuffer();
4696
4697 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4698
4699 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4700 qpubname);
4701
4702 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4703 qpubname);
4704
4705 if (pubinfo->puballtables)
4706 {
4707 int n_except = 0;
4708
4709 appendPQExpBufferStr(query, " FOR ALL TABLES");
4710
4711 /* Include EXCEPT (TABLE) clause if there are except_tables. */
4712 for (SimplePtrListCell *cell = pubinfo->except_tables.head; cell; cell = cell->next)
4713 {
4714 TableInfo *tbinfo = (TableInfo *) cell->ptr;
4715
4716 if (++n_except == 1)
4717 appendPQExpBufferStr(query, " EXCEPT (");
4718 else
4719 appendPQExpBufferStr(query, ", ");
4720 appendPQExpBuffer(query, "TABLE ONLY %s", fmtQualifiedDumpable(tbinfo));
4721 }
4722 if (n_except > 0)
4723 appendPQExpBufferChar(query, ')');
4724
4725 if (pubinfo->puballsequences)
4726 appendPQExpBufferStr(query, ", ALL SEQUENCES");
4727 }
4728 else if (pubinfo->puballsequences)
4729 appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4730
4731 appendPQExpBufferStr(query, " WITH (publish = '");
4732 if (pubinfo->pubinsert)
4733 {
4734 appendPQExpBufferStr(query, "insert");
4735 first = false;
4736 }
4737
4738 if (pubinfo->pubupdate)
4739 {
4740 if (!first)
4741 appendPQExpBufferStr(query, ", ");
4742
4743 appendPQExpBufferStr(query, "update");
4744 first = false;
4745 }
4746
4747 if (pubinfo->pubdelete)
4748 {
4749 if (!first)
4750 appendPQExpBufferStr(query, ", ");
4751
4752 appendPQExpBufferStr(query, "delete");
4753 first = false;
4754 }
4755
4756 if (pubinfo->pubtruncate)
4757 {
4758 if (!first)
4759 appendPQExpBufferStr(query, ", ");
4760
4761 appendPQExpBufferStr(query, "truncate");
4762 first = false;
4763 }
4764
4765 appendPQExpBufferChar(query, '\'');
4766
4767 if (pubinfo->pubviaroot)
4768 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4769
4770 if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4771 appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4772
4773 appendPQExpBufferStr(query, ");\n");
4774
4775 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4776 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4777 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4778 .owner = pubinfo->rolname,
4779 .description = "PUBLICATION",
4780 .section = SECTION_POST_DATA,
4781 .createStmt = query->data,
4782 .dropStmt = delq->data));
4783
4784 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4785 dumpComment(fout, "PUBLICATION", qpubname,
4786 NULL, pubinfo->rolname,
4787 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4788
4789 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4790 dumpSecLabel(fout, "PUBLICATION", qpubname,
4791 NULL, pubinfo->rolname,
4792 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4793
4795 destroyPQExpBuffer(query);
4796 free(qpubname);
4797}
4798
4799/*
4800 * getPublicationNamespaces
4801 * get information about publication membership for dumpable schemas.
4802 */
4803void
4805{
4806 PQExpBuffer query;
4807 PGresult *res;
4809 DumpOptions *dopt = fout->dopt;
4810 int i_tableoid;
4811 int i_oid;
4812 int i_pnpubid;
4813 int i_pnnspid;
4814 int i,
4815 j,
4816 ntups;
4817
4818 if (dopt->no_publications || fout->remoteVersion < 150000)
4819 return;
4820
4821 query = createPQExpBuffer();
4822
4823 /* Collect all publication membership info. */
4825 "SELECT tableoid, oid, pnpubid, pnnspid "
4826 "FROM pg_catalog.pg_publication_namespace");
4827 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4828
4829 ntups = PQntuples(res);
4830
4831 i_tableoid = PQfnumber(res, "tableoid");
4832 i_oid = PQfnumber(res, "oid");
4833 i_pnpubid = PQfnumber(res, "pnpubid");
4834 i_pnnspid = PQfnumber(res, "pnnspid");
4835
4836 /* this allocation may be more than we need */
4838 j = 0;
4839
4840 for (i = 0; i < ntups; i++)
4841 {
4846
4847 /*
4848 * Ignore any entries for which we aren't interested in either the
4849 * publication or the rel.
4850 */
4852 if (pubinfo == NULL)
4853 continue;
4855 if (nspinfo == NULL)
4856 continue;
4857
4858 /* OK, make a DumpableObject for this relationship */
4860 pubsinfo[j].dobj.catId.tableoid =
4861 atooid(PQgetvalue(res, i, i_tableoid));
4862 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4863 AssignDumpId(&pubsinfo[j].dobj);
4864 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4865 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4866 pubsinfo[j].publication = pubinfo;
4867 pubsinfo[j].pubschema = nspinfo;
4868
4869 /* Decide whether we want to dump it */
4871
4872 j++;
4873 }
4874
4875 PQclear(res);
4876 destroyPQExpBuffer(query);
4877}
4878
4879/*
4880 * getPublicationTables
4881 * get information about publication membership for dumpable tables.
4882 */
4883void
4885{
4886 PQExpBuffer query;
4887 PGresult *res;
4889 DumpOptions *dopt = fout->dopt;
4890 int i_tableoid;
4891 int i_oid;
4892 int i_prpubid;
4893 int i_prrelid;
4894 int i_prrelqual;
4895 int i_prattrs;
4896 int i,
4897 j,
4898 ntups;
4899
4900 if (dopt->no_publications || fout->remoteVersion < 100000)
4901 return;
4902
4903 query = createPQExpBuffer();
4904
4905 /* Collect all publication membership info. */
4906 if (fout->remoteVersion >= 150000)
4907 {
4909 "SELECT tableoid, oid, prpubid, prrelid, "
4910 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4911 "(CASE\n"
4912 " WHEN pr.prattrs IS NOT NULL THEN\n"
4913 " (SELECT array_agg(attname)\n"
4914 " FROM\n"
4915 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4916 " pg_catalog.pg_attribute\n"
4917 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4918 " ELSE NULL END) prattrs "
4919 "FROM pg_catalog.pg_publication_rel pr");
4920 if (fout->remoteVersion >= 190000)
4921 appendPQExpBufferStr(query, " WHERE NOT pr.prexcept");
4922 }
4923 else
4925 "SELECT tableoid, oid, prpubid, prrelid, "
4926 "NULL AS prrelqual, NULL AS prattrs "
4927 "FROM pg_catalog.pg_publication_rel");
4928 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4929
4930 ntups = PQntuples(res);
4931
4932 i_tableoid = PQfnumber(res, "tableoid");
4933 i_oid = PQfnumber(res, "oid");
4934 i_prpubid = PQfnumber(res, "prpubid");
4935 i_prrelid = PQfnumber(res, "prrelid");
4936 i_prrelqual = PQfnumber(res, "prrelqual");
4937 i_prattrs = PQfnumber(res, "prattrs");
4938
4939 /* this allocation may be more than we need */
4941 j = 0;
4942
4943 for (i = 0; i < ntups; i++)
4944 {
4949
4950 /*
4951 * Ignore any entries for which we aren't interested in either the
4952 * publication or the rel.
4953 */
4955 if (pubinfo == NULL)
4956 continue;
4958 if (tbinfo == NULL)
4959 continue;
4960
4961 /* OK, make a DumpableObject for this relationship */
4962 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4963 pubrinfo[j].dobj.catId.tableoid =
4964 atooid(PQgetvalue(res, i, i_tableoid));
4965 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4966 AssignDumpId(&pubrinfo[j].dobj);
4967 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4968 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4969 pubrinfo[j].publication = pubinfo;
4970 pubrinfo[j].pubtable = tbinfo;
4971 if (PQgetisnull(res, i, i_prrelqual))
4972 pubrinfo[j].pubrelqual = NULL;
4973 else
4974 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4975
4976 if (!PQgetisnull(res, i, i_prattrs))
4977 {
4978 char **attnames;
4979 int nattnames;
4981
4982 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4983 &attnames, &nattnames))
4984 pg_fatal("could not parse %s array", "prattrs");
4986 for (int k = 0; k < nattnames; k++)
4987 {
4988 if (k > 0)
4990
4991 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4992 }
4993 pubrinfo[j].pubrattrs = attribs->data;
4994 free(attribs); /* but not attribs->data */
4995 free(attnames);
4996 }
4997 else
4998 pubrinfo[j].pubrattrs = NULL;
4999
5000 /* Decide whether we want to dump it */
5002
5003 j++;
5004 }
5005
5006 PQclear(res);
5007 destroyPQExpBuffer(query);
5008}
5009
5010/*
5011 * dumpPublicationNamespace
5012 * dump the definition of the given publication schema mapping.
5013 */
5014static void
5016{
5017 DumpOptions *dopt = fout->dopt;
5018 NamespaceInfo *schemainfo = pubsinfo->pubschema;
5019 PublicationInfo *pubinfo = pubsinfo->publication;
5020 PQExpBuffer query;
5021 char *tag;
5022
5023 /* Do nothing if not dumping schema */
5024 if (!dopt->dumpSchema)
5025 return;
5026
5027 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
5028
5029 query = createPQExpBuffer();
5030
5031 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
5032 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
5033
5034 /*
5035 * There is no point in creating drop query as the drop is done by schema
5036 * drop.
5037 */
5038 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5039 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
5040 ARCHIVE_OPTS(.tag = tag,
5041 .namespace = schemainfo->dobj.name,
5042 .owner = pubinfo->rolname,
5043 .description = "PUBLICATION TABLES IN SCHEMA",
5044 .section = SECTION_POST_DATA,
5045 .createStmt = query->data));
5046
5047 /* These objects can't currently have comments or seclabels */
5048
5049 free(tag);
5050 destroyPQExpBuffer(query);
5051}
5052
5053/*
5054 * dumpPublicationTable
5055 * dump the definition of the given publication table mapping
5056 */
5057static void
5059{
5060 DumpOptions *dopt = fout->dopt;
5061 PublicationInfo *pubinfo = pubrinfo->publication;
5062 TableInfo *tbinfo = pubrinfo->pubtable;
5063 PQExpBuffer query;
5064 char *tag;
5065
5066 /* Do nothing if not dumping schema */
5067 if (!dopt->dumpSchema)
5068 return;
5069
5070 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5071
5072 query = createPQExpBuffer();
5073
5074 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5075 fmtId(pubinfo->dobj.name));
5076 appendPQExpBuffer(query, " %s",
5078
5079 if (pubrinfo->pubrattrs)
5080 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5081
5082 if (pubrinfo->pubrelqual)
5083 {
5084 /*
5085 * It's necessary to add parentheses around the expression because
5086 * pg_get_expr won't supply the parentheses for things like WHERE
5087 * TRUE.
5088 */
5089 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5090 }
5091 appendPQExpBufferStr(query, ";\n");
5092
5093 /*
5094 * There is no point in creating a drop query as the drop is done by table
5095 * drop. (If you think to change this, see also _printTocEntry().)
5096 * Although this object doesn't really have ownership as such, set the
5097 * owner field anyway to ensure that the command is run by the correct
5098 * role at restore time.
5099 */
5100 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5101 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5102 ARCHIVE_OPTS(.tag = tag,
5103 .namespace = tbinfo->dobj.namespace->dobj.name,
5104 .owner = pubinfo->rolname,
5105 .description = "PUBLICATION TABLE",
5106 .section = SECTION_POST_DATA,
5107 .createStmt = query->data));
5108
5109 /* These objects can't currently have comments or seclabels */
5110
5111 free(tag);
5112 destroyPQExpBuffer(query);
5113}
5114
5115/*
5116 * Is the currently connected user a superuser?
5117 */
5118static bool
5120{
5122 const char *val;
5123
5124 val = PQparameterStatus(AH->connection, "is_superuser");
5125
5126 if (val && strcmp(val, "on") == 0)
5127 return true;
5128
5129 return false;
5130}
5131
5132/*
5133 * Set the given value to restrict_nonsystem_relation_kind value. Since
5134 * restrict_nonsystem_relation_kind is introduced in minor version releases,
5135 * the setting query is effective only where available.
5136 */
5137static void
5139{
5141 PGresult *res;
5142
5143 appendPQExpBuffer(query,
5144 "SELECT set_config(name, '%s', false) "
5145 "FROM pg_settings "
5146 "WHERE name = 'restrict_nonsystem_relation_kind'",
5147 value);
5148 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5149
5150 PQclear(res);
5151 destroyPQExpBuffer(query);
5152}
5153
5154/*
5155 * getSubscriptions
5156 * get information about subscriptions
5157 */
5158void
5160{
5161 DumpOptions *dopt = fout->dopt;
5162 PQExpBuffer query;
5163 PGresult *res;
5164 SubscriptionInfo *subinfo;
5165 int i_tableoid;
5166 int i_oid;
5167 int i_subname;
5168 int i_subowner;
5169 int i_subbinary;
5170 int i_substream;
5174 int i_subrunasowner;
5175 int i_subservername;
5176 int i_subconninfo;
5177 int i_subslotname;
5178 int i_subsynccommit;
5181 int i_suborigin;
5183 int i_subenabled;
5184 int i_subfailover;
5187 int i,
5188 ntups;
5189
5190 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5191 return;
5192
5193 if (!is_superuser(fout))
5194 {
5195 int n;
5196
5197 res = ExecuteSqlQuery(fout,
5198 "SELECT count(*) FROM pg_subscription "
5199 "WHERE subdbid = (SELECT oid FROM pg_database"
5200 " WHERE datname = current_database())",
5202 n = atoi(PQgetvalue(res, 0, 0));
5203 if (n > 0)
5204 pg_log_warning("subscriptions not dumped because current user is not a superuser");
5205 PQclear(res);
5206 return;
5207 }
5208
5209 query = createPQExpBuffer();
5210
5211 /* Get the subscriptions in current database. */
5213 "SELECT s.tableoid, s.oid, s.subname,\n"
5214 " s.subowner,\n"
5215 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5216 " s.subpublications,\n");
5217
5218 if (fout->remoteVersion >= 140000)
5219 appendPQExpBufferStr(query, " s.subbinary,\n");
5220 else
5221 appendPQExpBufferStr(query, " false AS subbinary,\n");
5222
5223 if (fout->remoteVersion >= 140000)
5224 appendPQExpBufferStr(query, " s.substream,\n");
5225 else
5226 appendPQExpBufferStr(query, " 'f' AS substream,\n");
5227
5228 if (fout->remoteVersion >= 150000)
5230 " s.subtwophasestate,\n"
5231 " s.subdisableonerr,\n");
5232 else
5233 appendPQExpBuffer(query,
5234 " '%c' AS subtwophasestate,\n"
5235 " false AS subdisableonerr,\n",
5237
5238 if (fout->remoteVersion >= 160000)
5240 " s.subpasswordrequired,\n"
5241 " s.subrunasowner,\n"
5242 " s.suborigin,\n");
5243 else
5244 appendPQExpBuffer(query,
5245 " 't' AS subpasswordrequired,\n"
5246 " 't' AS subrunasowner,\n"
5247 " '%s' AS suborigin,\n",
5249
5250 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5251 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5252 " s.subenabled,\n");
5253 else
5254 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5255 " false AS subenabled,\n");
5256
5257 if (fout->remoteVersion >= 170000)
5259 " s.subfailover,\n");
5260 else
5262 " false AS subfailover,\n");
5263
5264 if (fout->remoteVersion >= 190000)
5266 " s.subretaindeadtuples,\n");
5267 else
5269 " false AS subretaindeadtuples,\n");
5270
5271 if (fout->remoteVersion >= 190000)
5273 " s.submaxretention,\n");
5274 else
5275 appendPQExpBufferStr(query, " 0 AS submaxretention,\n");
5276
5277 if (fout->remoteVersion >= 190000)
5279 " s.subwalrcvtimeout,\n");
5280 else
5282 " '-1' AS subwalrcvtimeout,\n");
5283
5284 if (fout->remoteVersion >= 190000)
5285 appendPQExpBufferStr(query, " fs.srvname AS subservername\n");
5286 else
5287 appendPQExpBufferStr(query, " NULL AS subservername\n");
5288
5290 "FROM pg_subscription s\n");
5291
5292 if (fout->remoteVersion >= 190000)
5294 "LEFT JOIN pg_catalog.pg_foreign_server fs \n"
5295 " ON fs.oid = s.subserver \n");
5296
5297 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5299 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5300 " ON o.external_id = 'pg_' || s.oid::text \n");
5301
5303 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5304 " WHERE datname = current_database())");
5305
5306 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5307
5308 ntups = PQntuples(res);
5309
5310 /*
5311 * Get subscription fields. We don't include subskiplsn in the dump as
5312 * after restoring the dump this value may no longer be relevant.
5313 */
5314 i_tableoid = PQfnumber(res, "tableoid");
5315 i_oid = PQfnumber(res, "oid");
5316 i_subname = PQfnumber(res, "subname");
5317 i_subowner = PQfnumber(res, "subowner");
5318 i_subenabled = PQfnumber(res, "subenabled");
5319 i_subbinary = PQfnumber(res, "subbinary");
5320 i_substream = PQfnumber(res, "substream");
5321 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5322 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5323 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5324 i_subrunasowner = PQfnumber(res, "subrunasowner");
5325 i_subfailover = PQfnumber(res, "subfailover");
5326 i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5327 i_submaxretention = PQfnumber(res, "submaxretention");
5328 i_subservername = PQfnumber(res, "subservername");
5329 i_subconninfo = PQfnumber(res, "subconninfo");
5330 i_subslotname = PQfnumber(res, "subslotname");
5331 i_subsynccommit = PQfnumber(res, "subsynccommit");
5332 i_subwalrcvtimeout = PQfnumber(res, "subwalrcvtimeout");
5333 i_subpublications = PQfnumber(res, "subpublications");
5334 i_suborigin = PQfnumber(res, "suborigin");
5335 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5336
5337 subinfo = pg_malloc_array(SubscriptionInfo, ntups);
5338
5339 for (i = 0; i < ntups; i++)
5340 {
5341 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5342 subinfo[i].dobj.catId.tableoid =
5343 atooid(PQgetvalue(res, i, i_tableoid));
5344 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5345 AssignDumpId(&subinfo[i].dobj);
5346 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5347 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5348
5349 subinfo[i].subenabled =
5350 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5351 if (PQgetisnull(res, i, i_subservername))
5352 subinfo[i].subservername = NULL;
5353 else
5355 subinfo[i].subbinary =
5356 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5357 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5358 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5359 subinfo[i].subdisableonerr =
5360 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5361 subinfo[i].subpasswordrequired =
5362 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5363 subinfo[i].subrunasowner =
5364 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5365 subinfo[i].subfailover =
5366 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5367 subinfo[i].subretaindeadtuples =
5368 (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5369 subinfo[i].submaxretention =
5371 if (PQgetisnull(res, i, i_subconninfo))
5372 subinfo[i].subconninfo = NULL;
5373 else
5374 subinfo[i].subconninfo =
5376 if (PQgetisnull(res, i, i_subslotname))
5377 subinfo[i].subslotname = NULL;
5378 else
5379 subinfo[i].subslotname =
5381 subinfo[i].subsynccommit =
5383 subinfo[i].subwalrcvtimeout =
5385 subinfo[i].subpublications =
5387 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5389 subinfo[i].suboriginremotelsn = NULL;
5390 else
5391 subinfo[i].suboriginremotelsn =
5393
5394 /* Decide whether we want to dump it */
5395 selectDumpableObject(&(subinfo[i].dobj), fout);
5396 }
5397 PQclear(res);
5398
5399 destroyPQExpBuffer(query);
5400}
5401
5402/*
5403 * getSubscriptionRelations
5404 * Get information about subscription membership for dumpable relations. This
5405 * will be used only in binary-upgrade mode for PG17 or later versions.
5406 */
5407void
5409{
5410 DumpOptions *dopt = fout->dopt;
5411 SubscriptionInfo *subinfo = NULL;
5413 PGresult *res;
5414 int i_srsubid;
5415 int i_srrelid;
5416 int i_srsubstate;
5417 int i_srsublsn;
5418 int ntups;
5420
5421 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5422 fout->remoteVersion < 170000)
5423 return;
5424
5425 res = ExecuteSqlQuery(fout,
5426 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5427 "FROM pg_catalog.pg_subscription_rel "
5428 "ORDER BY srsubid",
5430 ntups = PQntuples(res);
5431 if (ntups == 0)
5432 goto cleanup;
5433
5434 /* Get pg_subscription_rel attributes */
5435 i_srsubid = PQfnumber(res, "srsubid");
5436 i_srrelid = PQfnumber(res, "srrelid");
5437 i_srsubstate = PQfnumber(res, "srsubstate");
5438 i_srsublsn = PQfnumber(res, "srsublsn");
5439
5441 for (int i = 0; i < ntups; i++)
5442 {
5444 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5445 TableInfo *tblinfo;
5446
5447 /*
5448 * If we switched to a new subscription, check if the subscription
5449 * exists.
5450 */
5452 {
5454 if (subinfo == NULL)
5455 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5456
5458 }
5459
5460 tblinfo = findTableByOid(relid);
5461 if (tblinfo == NULL)
5462 pg_fatal("failed sanity check, relation with OID %u not found",
5463 relid);
5464
5465 /* OK, make a DumpableObject for this relationship */
5466 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5467 subrinfo[i].dobj.catId.tableoid = relid;
5468 subrinfo[i].dobj.catId.oid = cur_srsubid;
5469 AssignDumpId(&subrinfo[i].dobj);
5470 subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5471 subrinfo[i].dobj.name = tblinfo->dobj.name;
5472 subrinfo[i].subinfo = subinfo;
5473 subrinfo[i].tblinfo = tblinfo;
5474 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5475 if (PQgetisnull(res, i, i_srsublsn))
5476 subrinfo[i].srsublsn = NULL;
5477 else
5478 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5479
5480 /* Decide whether we want to dump it */
5482 }
5483
5484cleanup:
5485 PQclear(res);
5486}
5487
5488/*
5489 * dumpSubscriptionTable
5490 * Dump the definition of the given subscription table mapping. This will be
5491 * used only in binary-upgrade mode for PG17 or later versions.
5492 */
5493static void
5495{
5496 DumpOptions *dopt = fout->dopt;
5497 SubscriptionInfo *subinfo = subrinfo->subinfo;
5498 PQExpBuffer query;
5499 char *tag;
5500
5501 /* Do nothing if not dumping schema */
5502 if (!dopt->dumpSchema)
5503 return;
5504
5506
5507 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5508
5509 query = createPQExpBuffer();
5510
5511 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5512 {
5513 /*
5514 * binary_upgrade_add_sub_rel_state will add the subscription relation
5515 * to pg_subscription_rel table. This will be used only in
5516 * binary-upgrade mode.
5517 */
5519 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5521 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5522 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5523 appendPQExpBuffer(query,
5524 ", %u, '%c'",
5525 subrinfo->tblinfo->dobj.catId.oid,
5526 subrinfo->srsubstate);
5527
5528 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5529 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5530 else
5531 appendPQExpBufferStr(query, ", NULL");
5532
5533 appendPQExpBufferStr(query, ");\n");
5534 }
5535
5536 /*
5537 * There is no point in creating a drop query as the drop is done by table
5538 * drop. (If you think to change this, see also _printTocEntry().)
5539 * Although this object doesn't really have ownership as such, set the
5540 * owner field anyway to ensure that the command is run by the correct
5541 * role at restore time.
5542 */
5543 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5544 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5545 ARCHIVE_OPTS(.tag = tag,
5546 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5547 .owner = subinfo->rolname,
5548 .description = "SUBSCRIPTION TABLE",
5549 .section = SECTION_POST_DATA,
5550 .createStmt = query->data));
5551
5552 /* These objects can't currently have comments or seclabels */
5553
5554 free(tag);
5555 destroyPQExpBuffer(query);
5556}
5557
5558/*
5559 * dumpSubscription
5560 * dump the definition of the given subscription
5561 */
5562static void
5564{
5565 DumpOptions *dopt = fout->dopt;
5567 PQExpBuffer query;
5568 PQExpBuffer publications;
5569 char *qsubname;
5570 char **pubnames = NULL;
5571 int npubnames = 0;
5572 int i;
5573
5574 /* Do nothing if not dumping schema */
5575 if (!dopt->dumpSchema)
5576 return;
5577
5579 query = createPQExpBuffer();
5580
5581 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5582
5583 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5584 qsubname);
5585
5586 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s ",
5587 qsubname);
5588 if (subinfo->subservername)
5589 {
5590 appendPQExpBuffer(query, "SERVER %s", fmtId(subinfo->subservername));
5591 }
5592 else
5593 {
5594 appendPQExpBufferStr(query, "CONNECTION ");
5595 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5596 }
5597
5598 /* Build list of quoted publications and append them to query. */
5600 pg_fatal("could not parse %s array", "subpublications");
5601
5602 publications = createPQExpBuffer();
5603 for (i = 0; i < npubnames; i++)
5604 {
5605 if (i > 0)
5606 appendPQExpBufferStr(publications, ", ");
5607
5608 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5609 }
5610
5611 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5612 if (subinfo->subslotname)
5613 appendStringLiteralAH(query, subinfo->subslotname, fout);
5614 else
5615 appendPQExpBufferStr(query, "NONE");
5616
5617 if (subinfo->subbinary)
5618 appendPQExpBufferStr(query, ", binary = true");
5619
5620 if (subinfo->substream == LOGICALREP_STREAM_ON)
5621 appendPQExpBufferStr(query, ", streaming = on");
5622 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5623 appendPQExpBufferStr(query, ", streaming = parallel");
5624 else
5625 appendPQExpBufferStr(query, ", streaming = off");
5626
5628 appendPQExpBufferStr(query, ", two_phase = on");
5629
5630 if (subinfo->subdisableonerr)
5631 appendPQExpBufferStr(query, ", disable_on_error = true");
5632
5633 if (!subinfo->subpasswordrequired)
5634 appendPQExpBufferStr(query, ", password_required = false");
5635
5636 if (subinfo->subrunasowner)
5637 appendPQExpBufferStr(query, ", run_as_owner = true");
5638
5639 if (subinfo->subfailover)
5640 appendPQExpBufferStr(query, ", failover = true");
5641
5642 if (subinfo->subretaindeadtuples)
5643 appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5644
5645 if (subinfo->submaxretention)
5646 appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5647
5648 if (strcmp(subinfo->subsynccommit, "off") != 0)
5649 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5650
5651 if (strcmp(subinfo->subwalrcvtimeout, "-1") != 0)
5652 appendPQExpBuffer(query, ", wal_receiver_timeout = %s", fmtId(subinfo->subwalrcvtimeout));
5653
5654 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5655 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5656
5657 appendPQExpBufferStr(query, ");\n");
5658
5659 /*
5660 * In binary-upgrade mode, we allow the replication to continue after the
5661 * upgrade.
5662 */
5663 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5664 {
5665 if (subinfo->suboriginremotelsn)
5666 {
5667 /*
5668 * Preserve the remote_lsn for the subscriber's replication
5669 * origin. This value is required to start the replication from
5670 * the position before the upgrade. This value will be stale if
5671 * the publisher gets upgraded before the subscriber node.
5672 * However, this shouldn't be a problem as the upgrade of the
5673 * publisher ensures that all the transactions were replicated
5674 * before upgrading it.
5675 */
5677 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5679 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5680 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5681 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5682 }
5683
5684 if (subinfo->subenabled)
5685 {
5686 /*
5687 * Enable the subscription to allow the replication to continue
5688 * after the upgrade.
5689 */
5691 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5692 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5693 }
5694 }
5695
5696 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5697 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5698 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5699 .owner = subinfo->rolname,
5700 .description = "SUBSCRIPTION",
5701 .section = SECTION_POST_DATA,
5702 .createStmt = query->data,
5703 .dropStmt = delq->data));
5704
5705 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5706 dumpComment(fout, "SUBSCRIPTION", qsubname,
5707 NULL, subinfo->rolname,
5708 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5709
5710 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5711 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5712 NULL, subinfo->rolname,
5713 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5714
5715 destroyPQExpBuffer(publications);
5716 free(pubnames);
5717
5719 destroyPQExpBuffer(query);
5720 free(qsubname);
5721}
5722
5723/*
5724 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5725 * the object needs.
5726 */
5727static void
5729 PQExpBuffer create,
5730 const DumpableObject *dobj,
5731 const char *catalog,
5732 const char *keyword,
5733 const char *objname)
5734{
5735 if (dobj->depends_on_ext)
5736 {
5737 char *nm;
5738 PGresult *res;
5739 PQExpBuffer query;
5740 int ntups;
5741 int i_extname;
5742 int i;
5743
5744 /* dodge fmtId() non-reentrancy */
5745 nm = pg_strdup(objname);
5746
5747 query = createPQExpBuffer();
5748 appendPQExpBuffer(query,
5749 "SELECT e.extname "
5750 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5751 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5752 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5753 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5754 catalog,
5755 dobj->catId.oid);
5756 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5757 ntups = PQntuples(res);
5758 i_extname = PQfnumber(res, "extname");
5759 for (i = 0; i < ntups; i++)
5760 {
5761 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5762 keyword, nm,
5763 fmtId(PQgetvalue(res, i, i_extname)));
5764 }
5765
5766 PQclear(res);
5767 destroyPQExpBuffer(query);
5768 pg_free(nm);
5769 }
5770}
5771
5772static Oid
5774{
5775 /*
5776 * If the old version didn't assign an array type, but the new version
5777 * does, we must select an unused type OID to assign. This currently only
5778 * happens for domains, when upgrading pre-v11 to v11 and up.
5779 *
5780 * Note: local state here is kind of ugly, but we must have some, since we
5781 * mustn't choose the same unused OID more than once.
5782 */
5784 PGresult *res;
5785 bool is_dup;
5786
5787 do
5788 {
5791 "SELECT EXISTS(SELECT 1 "
5792 "FROM pg_catalog.pg_type "
5793 "WHERE oid = '%u'::pg_catalog.oid);",
5796 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5797 PQclear(res);
5798 } while (is_dup);
5799
5801}
5802
5803static void
5807 bool force_array_type,
5809{
5811 PGresult *res;
5815 TypeInfo *tinfo;
5816
5817 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5819 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5820 pg_type_oid);
5821
5823 if (tinfo)
5824 pg_type_array_oid = tinfo->typarray;
5825 else
5827
5830
5832 {
5834 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5836 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5838 }
5839
5840 /*
5841 * Pre-set the multirange type oid and its own array type oid.
5842 */
5844 {
5845 if (fout->remoteVersion >= 140000)
5846 {
5848 "SELECT t.oid, t.typarray "
5849 "FROM pg_catalog.pg_type t "
5850 "JOIN pg_catalog.pg_range r "
5851 "ON t.oid = r.rngmultitypid "
5852 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5853 pg_type_oid);
5854
5856
5857 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5858 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5859
5860 PQclear(res);
5861 }
5862 else
5863 {
5866 }
5867
5869 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5871 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5874 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5876 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5878 }
5879
5881}
5882
5883static void
5894
5895/*
5896 * bsearch() comparator for BinaryUpgradeClassOidItem
5897 */
5898static int
5899BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5900{
5903
5904 return pg_cmp_u32(v1.oid, v2.oid);
5905}
5906
5907/*
5908 * collectBinaryUpgradeClassOids
5909 *
5910 * Construct a table of pg_class information required for
5911 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5912 * lookup.
5913 */
5914static void
5916{
5917 PGresult *res;
5918 const char *query;
5919
5920 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5921 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5922 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5923 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5924 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5925 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5926 "ORDER BY c.oid;";
5927
5928 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5929
5933
5934 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5935 {
5943 }
5944
5945 PQclear(res);
5946}
5947
5948static void
5951{
5952 BinaryUpgradeClassOidItem key = {0};
5954
5956
5957 /*
5958 * Preserve the OID and relfilenumber of the table, table's index, table's
5959 * toast table and toast table's index if any.
5960 *
5961 * One complexity is that the current table definition might not require
5962 * the creation of a TOAST table, but the old database might have a TOAST
5963 * table that was created earlier, before some wide columns were dropped.
5964 * By setting the TOAST oid we force creation of the TOAST heap and index
5965 * by the new backend, so we can copy the files during binary upgrade
5966 * without worrying about this case.
5967 */
5968 key.oid = pg_class_oid;
5972
5974 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5975
5976 if (entry->relkind != RELKIND_INDEX &&
5978 {
5980 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5981 pg_class_oid);
5982
5983 /*
5984 * Not every relation has storage. Also, in a pre-v12 database,
5985 * partitioned tables have a relfilenumber, which should not be
5986 * preserved when upgrading.
5987 */
5988 if (RelFileNumberIsValid(entry->relfilenumber) &&
5991 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5992 entry->relfilenumber);
5993
5994 /*
5995 * In a pre-v12 database, partitioned tables might be marked as having
5996 * toast tables, but we should ignore them if so.
5997 */
5998 if (OidIsValid(entry->toast_oid) &&
6000 {
6002 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
6003 entry->toast_oid);
6005 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
6006 entry->toast_relfilenumber);
6007
6008 /* every toast table has an index */
6010 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6011 entry->toast_index_oid);
6013 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6015 }
6016 }
6017 else
6018 {
6019 /* Preserve the OID and relfilenumber of the index */
6021 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
6022 pg_class_oid);
6024 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
6025 entry->relfilenumber);
6026 }
6027
6029}
6030
6031/*
6032 * If the DumpableObject is a member of an extension, add a suitable
6033 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
6034 *
6035 * For somewhat historical reasons, objname should already be quoted,
6036 * but not objnamespace (if any).
6037 */
6038static void
6040 const DumpableObject *dobj,
6041 const char *objtype,
6042 const char *objname,
6043 const char *objnamespace)
6044{
6046 int i;
6047
6048 if (!dobj->ext_member)
6049 return;
6050
6051 /*
6052 * Find the parent extension. We could avoid this search if we wanted to
6053 * add a link field to DumpableObject, but the space costs of that would
6054 * be considerable. We assume that member objects could only have a
6055 * direct dependency on their own extension, not any others.
6056 */
6057 for (i = 0; i < dobj->nDeps; i++)
6058 {
6060 if (extobj && extobj->objType == DO_EXTENSION)
6061 break;
6062 extobj = NULL;
6063 }
6064 if (extobj == NULL)
6065 pg_fatal("could not find parent extension for %s %s",
6066 objtype, objname);
6067
6069 "\n-- For binary upgrade, handle extension membership the hard way\n");
6070 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
6071 fmtId(extobj->name),
6072 objtype);
6073 if (objnamespace && *objnamespace)
6075 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
6076}
6077
6078/*
6079 * getNamespaces:
6080 * get information about all namespaces in the system catalogs
6081 */
6082void
6084{
6085 PGresult *res;
6086 int ntups;
6087 int i;
6088 PQExpBuffer query;
6090 int i_tableoid;
6091 int i_oid;
6092 int i_nspname;
6093 int i_nspowner;
6094 int i_nspacl;
6095 int i_acldefault;
6096
6097 query = createPQExpBuffer();
6098
6099 /*
6100 * we fetch all namespaces including system ones, so that every object we
6101 * read in can be linked to a containing namespace.
6102 */
6103 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6104 "n.nspowner, "
6105 "n.nspacl, "
6106 "acldefault('n', n.nspowner) AS acldefault "
6107 "FROM pg_namespace n");
6108
6109 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6110
6111 ntups = PQntuples(res);
6112
6114
6115 i_tableoid = PQfnumber(res, "tableoid");
6116 i_oid = PQfnumber(res, "oid");
6117 i_nspname = PQfnumber(res, "nspname");
6118 i_nspowner = PQfnumber(res, "nspowner");
6119 i_nspacl = PQfnumber(res, "nspacl");
6120 i_acldefault = PQfnumber(res, "acldefault");
6121
6122 for (i = 0; i < ntups; i++)
6123 {
6124 const char *nspowner;
6125
6126 nsinfo[i].dobj.objType = DO_NAMESPACE;
6127 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6128 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6129 AssignDumpId(&nsinfo[i].dobj);
6130 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6131 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6132 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6133 nsinfo[i].dacl.privtype = 0;
6134 nsinfo[i].dacl.initprivs = NULL;
6135 nspowner = PQgetvalue(res, i, i_nspowner);
6136 nsinfo[i].nspowner = atooid(nspowner);
6137 nsinfo[i].rolname = getRoleName(nspowner);
6138
6139 /* Decide whether to dump this namespace */
6141
6142 /* Mark whether namespace has an ACL */
6143 if (!PQgetisnull(res, i, i_nspacl))
6144 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6145
6146 /*
6147 * We ignore any pg_init_privs.initprivs entry for the public schema
6148 * and assume a predetermined default, for several reasons. First,
6149 * dropping and recreating the schema removes its pg_init_privs entry,
6150 * but an empty destination database starts with this ACL nonetheless.
6151 * Second, we support dump/reload of public schema ownership changes.
6152 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6153 * initprivs continues to reflect the initial owner. Hence,
6154 * synthesize the value that nspacl will have after the restore's
6155 * ALTER SCHEMA OWNER. Third, this makes the destination database
6156 * match the source's ACL, even if the latter was an initdb-default
6157 * ACL, which changed in v15. An upgrade pulls in changes to most
6158 * system object ACLs that the DBA had not customized. We've made the
6159 * public schema depart from that, because changing its ACL so easily
6160 * breaks applications.
6161 */
6162 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6163 {
6166
6167 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6178
6179 nsinfo[i].dacl.privtype = 'i';
6180 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6181 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6182
6185 }
6186 }
6187
6188 PQclear(res);
6189 destroyPQExpBuffer(query);
6190}
6191
6192/*
6193 * findNamespace:
6194 * given a namespace OID, look up the info read by getNamespaces
6195 */
6196static NamespaceInfo *
6198{
6200
6202 if (nsinfo == NULL)
6203 pg_fatal("schema with OID %u does not exist", nsoid);
6204 return nsinfo;
6205}
6206
6207/*
6208 * getExtensions:
6209 * read all extensions in the system catalogs and return them in the
6210 * ExtensionInfo* structure
6211 *
6212 * numExtensions is set to the number of extensions read in
6213 */
6216{
6217 DumpOptions *dopt = fout->dopt;
6218 PGresult *res;
6219 int ntups;
6220 int i;
6221 PQExpBuffer query;
6223 int i_tableoid;
6224 int i_oid;
6225 int i_extname;
6226 int i_nspname;
6227 int i_extrelocatable;
6228 int i_extversion;
6229 int i_extconfig;
6230 int i_extcondition;
6231
6232 query = createPQExpBuffer();
6233
6234 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6235 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6236 "FROM pg_extension x "
6237 "JOIN pg_namespace n ON n.oid = x.extnamespace");
6238
6239 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6240
6241 ntups = PQntuples(res);
6242 if (ntups == 0)
6243 goto cleanup;
6244
6246
6247 i_tableoid = PQfnumber(res, "tableoid");
6248 i_oid = PQfnumber(res, "oid");
6249 i_extname = PQfnumber(res, "extname");
6250 i_nspname = PQfnumber(res, "nspname");
6251 i_extrelocatable = PQfnumber(res, "extrelocatable");
6252 i_extversion = PQfnumber(res, "extversion");
6253 i_extconfig = PQfnumber(res, "extconfig");
6254 i_extcondition = PQfnumber(res, "extcondition");
6255
6256 for (i = 0; i < ntups; i++)
6257 {
6258 extinfo[i].dobj.objType = DO_EXTENSION;
6259 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6260 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6261 AssignDumpId(&extinfo[i].dobj);
6262 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6263 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6264 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6265 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6266 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6267 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6268
6269 /* Decide whether we want to dump it */
6271 }
6272
6273cleanup:
6274 PQclear(res);
6275 destroyPQExpBuffer(query);
6276
6277 *numExtensions = ntups;
6278
6279 return extinfo;
6280}
6281
6282/*
6283 * getTypes:
6284 * get information about all types in the system catalogs
6285 *
6286 * NB: this must run after getFuncs() because we assume we can do
6287 * findFuncByOid().
6288 */
6289void
6291{
6292 PGresult *res;
6293 int ntups;
6294 int i;
6298 int i_tableoid;
6299 int i_oid;
6300 int i_typname;
6301 int i_typnamespace;
6302 int i_typacl;
6303 int i_acldefault;
6304 int i_typowner;
6305 int i_typelem;
6306 int i_typrelid;
6307 int i_typrelkind;
6308 int i_typtype;
6309 int i_typisdefined;
6310 int i_isarray;
6311 int i_typarray;
6312
6313 /*
6314 * we include even the built-in types because those may be used as array
6315 * elements by user-defined types
6316 *
6317 * we filter out the built-in types when we dump out the types
6318 *
6319 * same approach for undefined (shell) types and array types
6320 *
6321 * Note: as of 8.3 we can reliably detect whether a type is an
6322 * auto-generated array type by checking the element type's typarray.
6323 * (Before that the test is capable of generating false positives.) We
6324 * still check for name beginning with '_', though, so as to avoid the
6325 * cost of the subselect probe for all standard types. This would have to
6326 * be revisited if the backend ever allows renaming of array types.
6327 */
6328 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6329 "typnamespace, typacl, "
6330 "acldefault('T', typowner) AS acldefault, "
6331 "typowner, "
6332 "typelem, typrelid, typarray, "
6333 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6334 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6335 "typtype, typisdefined, "
6336 "typname[0] = '_' AND typelem != 0 AND "
6337 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6338 "FROM pg_type");
6339
6340 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6341
6342 ntups = PQntuples(res);
6343
6345
6346 i_tableoid = PQfnumber(res, "tableoid");
6347 i_oid = PQfnumber(res, "oid");
6348 i_typname = PQfnumber(res, "typname");
6349 i_typnamespace = PQfnumber(res, "typnamespace");
6350 i_typacl = PQfnumber(res, "typacl");
6351 i_acldefault = PQfnumber(res, "acldefault");
6352 i_typowner = PQfnumber(res, "typowner");
6353 i_typelem = PQfnumber(res, "typelem");
6354 i_typrelid = PQfnumber(res, "typrelid");
6355 i_typrelkind = PQfnumber(res, "typrelkind");
6356 i_typtype = PQfnumber(res, "typtype");
6357 i_typisdefined = PQfnumber(res, "typisdefined");
6358 i_isarray = PQfnumber(res, "isarray");
6359 i_typarray = PQfnumber(res, "typarray");
6360
6361 for (i = 0; i < ntups; i++)
6362 {
6363 tyinfo[i].dobj.objType = DO_TYPE;
6364 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6365 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6366 AssignDumpId(&tyinfo[i].dobj);
6367 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6368 tyinfo[i].dobj.namespace =
6370 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6371 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6372 tyinfo[i].dacl.privtype = 0;
6373 tyinfo[i].dacl.initprivs = NULL;
6374 tyinfo[i].ftypname = NULL; /* may get filled later */
6375 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6376 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6377 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6378 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6379 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6380 tyinfo[i].shellType = NULL;
6381
6382 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6383 tyinfo[i].isDefined = true;
6384 else
6385 tyinfo[i].isDefined = false;
6386
6387 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6388 tyinfo[i].isArray = true;
6389 else
6390 tyinfo[i].isArray = false;
6391
6392 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6393
6394 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6395 tyinfo[i].isMultirange = true;
6396 else
6397 tyinfo[i].isMultirange = false;
6398
6399 /* Decide whether we want to dump it */
6401
6402 /* Mark whether type has an ACL */
6403 if (!PQgetisnull(res, i, i_typacl))
6404 tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6405
6406 /*
6407 * If it's a domain, fetch info about its constraints, if any
6408 */
6409 tyinfo[i].nDomChecks = 0;
6410 tyinfo[i].domChecks = NULL;
6411 tyinfo[i].notnull = NULL;
6412 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6413 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6415
6416 /*
6417 * If it's a base type, make a DumpableObject representing a shell
6418 * definition of the type. We will need to dump that ahead of the I/O
6419 * functions for the type. Similarly, range types need a shell
6420 * definition in case they have a canonicalize function.
6421 *
6422 * Note: the shell type doesn't have a catId. You might think it
6423 * should copy the base type's catId, but then it might capture the
6424 * pg_depend entries for the type, which we don't want.
6425 */
6426 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6427 (tyinfo[i].typtype == TYPTYPE_BASE ||
6428 tyinfo[i].typtype == TYPTYPE_RANGE))
6429 {
6431 stinfo->dobj.objType = DO_SHELL_TYPE;
6432 stinfo->dobj.catId = nilCatalogId;
6433 AssignDumpId(&stinfo->dobj);
6434 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6435 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6436 stinfo->baseType = &(tyinfo[i]);
6437 tyinfo[i].shellType = stinfo;
6438
6439 /*
6440 * Initially mark the shell type as not to be dumped. We'll only
6441 * dump it if the I/O or canonicalize functions need to be dumped;
6442 * this is taken care of while sorting dependencies.
6443 */
6444 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6445 }
6446 }
6447
6448 PQclear(res);
6449
6450 destroyPQExpBuffer(query);
6451}
6452
6453/*
6454 * getOperators:
6455 * get information about all operators in the system catalogs
6456 */
6457void
6459{
6460 PGresult *res;
6461 int ntups;
6462 int i;
6465 int i_tableoid;
6466 int i_oid;
6467 int i_oprname;
6468 int i_oprnamespace;
6469 int i_oprowner;
6470 int i_oprkind;
6471 int i_oprleft;
6472 int i_oprright;
6473 int i_oprcode;
6474
6475 /*
6476 * find all operators, including builtin operators; we filter out
6477 * system-defined operators at dump-out time.
6478 */
6479
6480 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6481 "oprnamespace, "
6482 "oprowner, "
6483 "oprkind, "
6484 "oprleft, "
6485 "oprright, "
6486 "oprcode::oid AS oprcode "
6487 "FROM pg_operator");
6488
6489 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6490
6491 ntups = PQntuples(res);
6492
6494
6495 i_tableoid = PQfnumber(res, "tableoid");
6496 i_oid = PQfnumber(res, "oid");
6497 i_oprname = PQfnumber(res, "oprname");
6498 i_oprnamespace = PQfnumber(res, "oprnamespace");
6499 i_oprowner = PQfnumber(res, "oprowner");
6500 i_oprkind = PQfnumber(res, "oprkind");
6501 i_oprleft = PQfnumber(res, "oprleft");
6502 i_oprright = PQfnumber(res, "oprright");
6503 i_oprcode = PQfnumber(res, "oprcode");
6504
6505 for (i = 0; i < ntups; i++)
6506 {
6507 oprinfo[i].dobj.objType = DO_OPERATOR;
6508 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6509 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6510 AssignDumpId(&oprinfo[i].dobj);
6511 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6512 oprinfo[i].dobj.namespace =
6514 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6515 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6516 oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6517 oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6518 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6519
6520 /* Decide whether we want to dump it */
6522 }
6523
6524 PQclear(res);
6525
6526 destroyPQExpBuffer(query);
6527}
6528
6529/*
6530 * getCollations:
6531 * get information about all collations in the system catalogs
6532 */
6533void
6535{
6536 PGresult *res;
6537 int ntups;
6538 int i;
6539 PQExpBuffer query;
6541 int i_tableoid;
6542 int i_oid;
6543 int i_collname;
6544 int i_collnamespace;
6545 int i_collowner;
6546 int i_collencoding;
6547
6548 query = createPQExpBuffer();
6549
6550 /*
6551 * find all collations, including builtin collations; we filter out
6552 * system-defined collations at dump-out time.
6553 */
6554
6555 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6556 "collnamespace, "
6557 "collowner, "
6558 "collencoding "
6559 "FROM pg_collation");
6560
6561 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6562
6563 ntups = PQntuples(res);
6564
6566
6567 i_tableoid = PQfnumber(res, "tableoid");
6568 i_oid = PQfnumber(res, "oid");
6569 i_collname = PQfnumber(res, "collname");
6570 i_collnamespace = PQfnumber(res, "collnamespace");
6571 i_collowner = PQfnumber(res, "collowner");
6572 i_collencoding = PQfnumber(res, "collencoding");
6573
6574 for (i = 0; i < ntups; i++)
6575 {
6576 collinfo[i].dobj.objType = DO_COLLATION;
6577 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6578 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6579 AssignDumpId(&collinfo[i].dobj);
6580 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6581 collinfo[i].dobj.namespace =
6583 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6584 collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6585
6586 /* Decide whether we want to dump it */
6588 }
6589
6590 PQclear(res);
6591
6592 destroyPQExpBuffer(query);
6593}
6594
6595/*
6596 * getConversions:
6597 * get information about all conversions in the system catalogs
6598 */
6599void
6601{
6602 PGresult *res;
6603 int ntups;
6604 int i;
6605 PQExpBuffer query;
6607 int i_tableoid;
6608 int i_oid;
6609 int i_conname;
6610 int i_connamespace;
6611 int i_conowner;
6612
6613 query = createPQExpBuffer();
6614
6615 /*
6616 * find all conversions, including builtin conversions; we filter out
6617 * system-defined conversions at dump-out time.
6618 */
6619
6620 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6621 "connamespace, "
6622 "conowner "
6623 "FROM pg_conversion");
6624
6625 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6626
6627 ntups = PQntuples(res);
6628
6630
6631 i_tableoid = PQfnumber(res, "tableoid");
6632 i_oid = PQfnumber(res, "oid");
6633 i_conname = PQfnumber(res, "conname");
6634 i_connamespace = PQfnumber(res, "connamespace");
6635 i_conowner = PQfnumber(res, "conowner");
6636
6637 for (i = 0; i < ntups; i++)
6638 {
6639 convinfo[i].dobj.objType = DO_CONVERSION;
6640 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6641 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6642 AssignDumpId(&convinfo[i].dobj);
6643 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6644 convinfo[i].dobj.namespace =
6646 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6647
6648 /* Decide whether we want to dump it */
6650 }
6651
6652 PQclear(res);
6653
6654 destroyPQExpBuffer(query);
6655}
6656
6657/*
6658 * getAccessMethods:
6659 * get information about all user-defined access methods
6660 */
6661void
6663{
6664 PGresult *res;
6665 int ntups;
6666 int i;
6667 PQExpBuffer query;
6669 int i_tableoid;
6670 int i_oid;
6671 int i_amname;
6672 int i_amhandler;
6673 int i_amtype;
6674
6675 query = createPQExpBuffer();
6676
6677 /*
6678 * Select all access methods from pg_am table. v9.6 introduced CREATE
6679 * ACCESS METHOD, so earlier versions usually have only built-in access
6680 * methods. v9.6 also changed the access method API, replacing dozens of
6681 * pg_am columns with amhandler. Even if a user created an access method
6682 * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6683 * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6684 * pg_am just to facilitate findAccessMethodByOid() providing the
6685 * OID-to-name mapping.
6686 */
6687 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6688 if (fout->remoteVersion >= 90600)
6690 "amtype, "
6691 "amhandler::pg_catalog.regproc AS amhandler ");
6692 else
6694 "'i'::pg_catalog.\"char\" AS amtype, "
6695 "'-'::pg_catalog.regproc AS amhandler ");
6696 appendPQExpBufferStr(query, "FROM pg_am");
6697
6698 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6699
6700 ntups = PQntuples(res);
6701
6703
6704 i_tableoid = PQfnumber(res, "tableoid");
6705 i_oid = PQfnumber(res, "oid");
6706 i_amname = PQfnumber(res, "amname");
6707 i_amhandler = PQfnumber(res, "amhandler");
6708 i_amtype = PQfnumber(res, "amtype");
6709
6710 for (i = 0; i < ntups; i++)
6711 {
6712 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6713 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6714 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6715 AssignDumpId(&aminfo[i].dobj);
6716 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6717 aminfo[i].dobj.namespace = NULL;
6718 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6719 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6720
6721 /* Decide whether we want to dump it */
6723 }
6724
6725 PQclear(res);
6726
6727 destroyPQExpBuffer(query);
6728}
6729
6730
6731/*
6732 * getOpclasses:
6733 * get information about all opclasses in the system catalogs
6734 */
6735void
6737{
6738 PGresult *res;
6739 int ntups;
6740 int i;
6743 int i_tableoid;
6744 int i_oid;
6745 int i_opcmethod;
6746 int i_opcname;
6747 int i_opcnamespace;
6748 int i_opcowner;
6749
6750 /*
6751 * find all opclasses, including builtin opclasses; we filter out
6752 * system-defined opclasses at dump-out time.
6753 */
6754
6755 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6756 "opcnamespace, "
6757 "opcowner "
6758 "FROM pg_opclass");
6759
6760 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6761
6762 ntups = PQntuples(res);
6763
6765
6766 i_tableoid = PQfnumber(res, "tableoid");
6767 i_oid = PQfnumber(res, "oid");
6768 i_opcmethod = PQfnumber(res, "opcmethod");
6769 i_opcname = PQfnumber(res, "opcname");
6770 i_opcnamespace = PQfnumber(res, "opcnamespace");
6771 i_opcowner = PQfnumber(res, "opcowner");
6772
6773 for (i = 0; i < ntups; i++)
6774 {
6775 opcinfo[i].dobj.objType = DO_OPCLASS;
6776 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6777 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6778 AssignDumpId(&opcinfo[i].dobj);
6779 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6780 opcinfo[i].dobj.namespace =
6782 opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6783 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6784
6785 /* Decide whether we want to dump it */
6787 }
6788
6789 PQclear(res);
6790
6791 destroyPQExpBuffer(query);
6792}
6793
6794/*
6795 * getOpfamilies:
6796 * get information about all opfamilies in the system catalogs
6797 */
6798void
6800{
6801 PGresult *res;
6802 int ntups;
6803 int i;
6804 PQExpBuffer query;
6806 int i_tableoid;
6807 int i_oid;
6808 int i_opfmethod;
6809 int i_opfname;
6810 int i_opfnamespace;
6811 int i_opfowner;
6812
6813 query = createPQExpBuffer();
6814
6815 /*
6816 * find all opfamilies, including builtin opfamilies; we filter out
6817 * system-defined opfamilies at dump-out time.
6818 */
6819
6820 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6821 "opfnamespace, "
6822 "opfowner "
6823 "FROM pg_opfamily");
6824
6825 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6826
6827 ntups = PQntuples(res);
6828
6830
6831 i_tableoid = PQfnumber(res, "tableoid");
6832 i_oid = PQfnumber(res, "oid");
6833 i_opfname = PQfnumber(res, "opfname");
6834 i_opfmethod = PQfnumber(res, "opfmethod");
6835 i_opfnamespace = PQfnumber(res, "opfnamespace");
6836 i_opfowner = PQfnumber(res, "opfowner");
6837
6838 for (i = 0; i < ntups; i++)
6839 {
6840 opfinfo[i].dobj.objType = DO_OPFAMILY;
6841 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6842 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6843 AssignDumpId(&opfinfo[i].dobj);
6844 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6845 opfinfo[i].dobj.namespace =
6847 opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6848 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6849
6850 /* Decide whether we want to dump it */
6852 }
6853
6854 PQclear(res);
6855
6856 destroyPQExpBuffer(query);
6857}
6858
6859/*
6860 * getAggregates:
6861 * get information about all user-defined aggregates in the system catalogs
6862 */
6863void
6865{
6866 DumpOptions *dopt = fout->dopt;
6867 PGresult *res;
6868 int ntups;
6869 int i;
6872 int i_tableoid;
6873 int i_oid;
6874 int i_aggname;
6875 int i_aggnamespace;
6876 int i_pronargs;
6877 int i_proargtypes;
6878 int i_proowner;
6879 int i_aggacl;
6880 int i_acldefault;
6881
6882 /*
6883 * Find all interesting aggregates. See comment in getFuncs() for the
6884 * rationale behind the filtering logic.
6885 */
6886 if (fout->remoteVersion >= 90600)
6887 {
6888 const char *agg_check;
6889
6890 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6891 : "p.proisagg");
6892
6893 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6894 "p.proname AS aggname, "
6895 "p.pronamespace AS aggnamespace, "
6896 "p.pronargs, p.proargtypes, "
6897 "p.proowner, "
6898 "p.proacl AS aggacl, "
6899 "acldefault('f', p.proowner) AS acldefault "
6900 "FROM pg_proc p "
6901 "LEFT JOIN pg_init_privs pip ON "
6902 "(p.oid = pip.objoid "
6903 "AND pip.classoid = 'pg_proc'::regclass "
6904 "AND pip.objsubid = 0) "
6905 "WHERE %s AND ("
6906 "p.pronamespace != "
6907 "(SELECT oid FROM pg_namespace "
6908 "WHERE nspname = 'pg_catalog') OR "
6909 "p.proacl IS DISTINCT FROM pip.initprivs",
6910 agg_check);
6911 if (dopt->binary_upgrade)
6913 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6914 "classid = 'pg_proc'::regclass AND "
6915 "objid = p.oid AND "
6916 "refclassid = 'pg_extension'::regclass AND "
6917 "deptype = 'e')");
6918 appendPQExpBufferChar(query, ')');
6919 }
6920 else
6921 {
6922 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6923 "pronamespace AS aggnamespace, "
6924 "pronargs, proargtypes, "
6925 "proowner, "
6926 "proacl AS aggacl, "
6927 "acldefault('f', proowner) AS acldefault "
6928 "FROM pg_proc p "
6929 "WHERE proisagg AND ("
6930 "pronamespace != "
6931 "(SELECT oid FROM pg_namespace "
6932 "WHERE nspname = 'pg_catalog')");
6933 if (dopt->binary_upgrade)
6935 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6936 "classid = 'pg_proc'::regclass AND "
6937 "objid = p.oid AND "
6938 "refclassid = 'pg_extension'::regclass AND "
6939 "deptype = 'e')");
6940 appendPQExpBufferChar(query, ')');
6941 }
6942
6943 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6944
6945 ntups = PQntuples(res);
6946
6948
6949 i_tableoid = PQfnumber(res, "tableoid");
6950 i_oid = PQfnumber(res, "oid");
6951 i_aggname = PQfnumber(res, "aggname");
6952 i_aggnamespace = PQfnumber(res, "aggnamespace");
6953 i_pronargs = PQfnumber(res, "pronargs");
6954 i_proargtypes = PQfnumber(res, "proargtypes");
6955 i_proowner = PQfnumber(res, "proowner");
6956 i_aggacl = PQfnumber(res, "aggacl");
6957 i_acldefault = PQfnumber(res, "acldefault");
6958
6959 for (i = 0; i < ntups; i++)
6960 {
6961 agginfo[i].aggfn.dobj.objType = DO_AGG;
6962 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6963 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6964 AssignDumpId(&agginfo[i].aggfn.dobj);
6965 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6966 agginfo[i].aggfn.dobj.namespace =
6968 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6969 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6970 agginfo[i].aggfn.dacl.privtype = 0;
6971 agginfo[i].aggfn.dacl.initprivs = NULL;
6972 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6973 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6974 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6975 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6976 if (agginfo[i].aggfn.nargs == 0)
6977 agginfo[i].aggfn.argtypes = NULL;
6978 else
6979 {
6980 agginfo[i].aggfn.argtypes = pg_malloc_array(Oid, agginfo[i].aggfn.nargs);
6982 agginfo[i].aggfn.argtypes,
6983 agginfo[i].aggfn.nargs);
6984 }
6985 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6986
6987 /* Decide whether we want to dump it */
6988 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6989
6990 /* Mark whether aggregate has an ACL */
6991 if (!PQgetisnull(res, i, i_aggacl))
6992 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6993 }
6994
6995 PQclear(res);
6996
6997 destroyPQExpBuffer(query);
6998}
6999
7000/*
7001 * getFuncs:
7002 * get information about all user-defined functions in the system catalogs
7003 */
7004void
7006{
7007 DumpOptions *dopt = fout->dopt;
7008 PGresult *res;
7009 int ntups;
7010 int i;
7012 FuncInfo *finfo;
7013 int i_tableoid;
7014 int i_oid;
7015 int i_proname;
7016 int i_pronamespace;
7017 int i_proowner;
7018 int i_prolang;
7019 int i_pronargs;
7020 int i_proargtypes;
7021 int i_prorettype;
7022 int i_proacl;
7023 int i_acldefault;
7024
7025 /*
7026 * Find all interesting functions. This is a bit complicated:
7027 *
7028 * 1. Always exclude aggregates; those are handled elsewhere.
7029 *
7030 * 2. Always exclude functions that are internally dependent on something
7031 * else, since presumably those will be created as a result of creating
7032 * the something else. This currently acts only to suppress constructor
7033 * functions for range types. Note this is OK only because the
7034 * constructors don't have any dependencies the range type doesn't have;
7035 * otherwise we might not get creation ordering correct.
7036 *
7037 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
7038 * they're members of extensions and we are in binary-upgrade mode then
7039 * include them, since we want to dump extension members individually in
7040 * that mode. Also, if they are used by casts or transforms then we need
7041 * to gather the information about them, though they won't be dumped if
7042 * they are built-in. Also, in 9.6 and up, include functions in
7043 * pg_catalog if they have an ACL different from what's shown in
7044 * pg_init_privs (so we have to join to pg_init_privs; annoying).
7045 */
7046 if (fout->remoteVersion >= 90600)
7047 {
7048 const char *not_agg_check;
7049
7050 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
7051 : "NOT p.proisagg");
7052
7053 appendPQExpBuffer(query,
7054 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
7055 "p.pronargs, p.proargtypes, p.prorettype, "
7056 "p.proacl, "
7057 "acldefault('f', p.proowner) AS acldefault, "
7058 "p.pronamespace, "
7059 "p.proowner "
7060 "FROM pg_proc p "
7061 "LEFT JOIN pg_init_privs pip ON "
7062 "(p.oid = pip.objoid "
7063 "AND pip.classoid = 'pg_proc'::regclass "
7064 "AND pip.objsubid = 0) "
7065 "WHERE %s"
7066 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7067 "WHERE classid = 'pg_proc'::regclass AND "
7068 "objid = p.oid AND deptype = 'i')"
7069 "\n AND ("
7070 "\n pronamespace != "
7071 "(SELECT oid FROM pg_namespace "
7072 "WHERE nspname = 'pg_catalog')"
7073 "\n OR EXISTS (SELECT 1 FROM pg_cast"
7074 "\n WHERE pg_cast.oid > %u "
7075 "\n AND p.oid = pg_cast.castfunc)"
7076 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7077 "\n WHERE pg_transform.oid > %u AND "
7078 "\n (p.oid = pg_transform.trffromsql"
7079 "\n OR p.oid = pg_transform.trftosql))",
7083 if (dopt->binary_upgrade)
7085 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7086 "classid = 'pg_proc'::regclass AND "
7087 "objid = p.oid AND "
7088 "refclassid = 'pg_extension'::regclass AND "
7089 "deptype = 'e')");
7091 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7092 appendPQExpBufferChar(query, ')');
7093 }
7094 else
7095 {
7096 appendPQExpBuffer(query,
7097 "SELECT tableoid, oid, proname, prolang, "
7098 "pronargs, proargtypes, prorettype, proacl, "
7099 "acldefault('f', proowner) AS acldefault, "
7100 "pronamespace, "
7101 "proowner "
7102 "FROM pg_proc p "
7103 "WHERE NOT proisagg"
7104 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7105 "WHERE classid = 'pg_proc'::regclass AND "
7106 "objid = p.oid AND deptype = 'i')"
7107 "\n AND ("
7108 "\n pronamespace != "
7109 "(SELECT oid FROM pg_namespace "
7110 "WHERE nspname = 'pg_catalog')"
7111 "\n OR EXISTS (SELECT 1 FROM pg_cast"
7112 "\n WHERE pg_cast.oid > '%u'::oid"
7113 "\n AND p.oid = pg_cast.castfunc)",
7115
7116 if (fout->remoteVersion >= 90500)
7117 appendPQExpBuffer(query,
7118 "\n OR EXISTS (SELECT 1 FROM pg_transform"
7119 "\n WHERE pg_transform.oid > '%u'::oid"
7120 "\n AND (p.oid = pg_transform.trffromsql"
7121 "\n OR p.oid = pg_transform.trftosql))",
7123
7124 if (dopt->binary_upgrade)
7126 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7127 "classid = 'pg_proc'::regclass AND "
7128 "objid = p.oid AND "
7129 "refclassid = 'pg_extension'::regclass AND "
7130 "deptype = 'e')");
7131 appendPQExpBufferChar(query, ')');
7132 }
7133
7134 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7135
7136 ntups = PQntuples(res);
7137
7138 finfo = pg_malloc0_array(FuncInfo, ntups);
7139
7140 i_tableoid = PQfnumber(res, "tableoid");
7141 i_oid = PQfnumber(res, "oid");
7142 i_proname = PQfnumber(res, "proname");
7143 i_pronamespace = PQfnumber(res, "pronamespace");
7144 i_proowner = PQfnumber(res, "proowner");
7145 i_prolang = PQfnumber(res, "prolang");
7146 i_pronargs = PQfnumber(res, "pronargs");
7147 i_proargtypes = PQfnumber(res, "proargtypes");
7148 i_prorettype = PQfnumber(res, "prorettype");
7149 i_proacl = PQfnumber(res, "proacl");
7150 i_acldefault = PQfnumber(res, "acldefault");
7151
7152 for (i = 0; i < ntups; i++)
7153 {
7154 finfo[i].dobj.objType = DO_FUNC;
7155 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7156 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7157 AssignDumpId(&finfo[i].dobj);
7158 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7159 finfo[i].dobj.namespace =
7161 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7163 finfo[i].dacl.privtype = 0;
7164 finfo[i].dacl.initprivs = NULL;
7165 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7166 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7167 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7168 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7169 if (finfo[i].nargs == 0)
7170 finfo[i].argtypes = NULL;
7171 else
7172 {
7173 finfo[i].argtypes = pg_malloc_array(Oid, finfo[i].nargs);
7175 finfo[i].argtypes, finfo[i].nargs);
7176 }
7177 finfo[i].postponed_def = false; /* might get set during sort */
7178
7179 /* Decide whether we want to dump it */
7180 selectDumpableObject(&(finfo[i].dobj), fout);
7181
7182 /* Mark whether function has an ACL */
7183 if (!PQgetisnull(res, i, i_proacl))
7185 }
7186
7187 PQclear(res);
7188
7189 destroyPQExpBuffer(query);
7190}
7191
7192/*
7193 * getRelationStatistics
7194 * register the statistics object as a dependent of the relation.
7195 *
7196 * reltuples is passed as a string to avoid complexities in converting from/to
7197 * floating point.
7198 */
7199static RelStatsInfo *
7201 char *reltuples, int32 relallvisible,
7202 int32 relallfrozen, char relkind,
7203 char **indAttNames, int nindAttNames)
7204{
7205 if (!fout->dopt->dumpStatistics)
7206 return NULL;
7207
7208 if ((relkind == RELKIND_RELATION) ||
7209 (relkind == RELKIND_PARTITIONED_TABLE) ||
7210 (relkind == RELKIND_INDEX) ||
7211 (relkind == RELKIND_PARTITIONED_INDEX) ||
7212 (relkind == RELKIND_MATVIEW ||
7213 relkind == RELKIND_FOREIGN_TABLE))
7214 {
7216 DumpableObject *dobj = &info->dobj;
7217
7218 dobj->objType = DO_REL_STATS;
7219 dobj->catId.tableoid = 0;
7220 dobj->catId.oid = 0;
7221 AssignDumpId(dobj);
7223 dobj->dependencies[0] = rel->dumpId;
7224 dobj->nDeps = 1;
7225 dobj->allocDeps = 1;
7227 dobj->name = pg_strdup(rel->name);
7228 dobj->namespace = rel->namespace;
7229 info->relid = rel->catId.oid;
7230 info->relpages = relpages;
7231 info->reltuples = pstrdup(reltuples);
7232 info->relallvisible = relallvisible;
7233 info->relallfrozen = relallfrozen;
7234 info->relkind = relkind;
7235 info->indAttNames = indAttNames;
7236 info->nindAttNames = nindAttNames;
7237
7238 /*
7239 * Ordinarily, stats go in SECTION_DATA for tables and
7240 * SECTION_POST_DATA for indexes.
7241 *
7242 * However, the section may be updated later for materialized view
7243 * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7244 * the stats, so the stats must be restored after the data. Also, the
7245 * materialized view definition may be postponed to SECTION_POST_DATA
7246 * (see repairMatViewBoundaryMultiLoop()).
7247 */
7248 switch (info->relkind)
7249 {
7250 case RELKIND_RELATION:
7252 case RELKIND_MATVIEW:
7254 info->section = SECTION_DATA;
7255 break;
7256 case RELKIND_INDEX:
7258 info->section = SECTION_POST_DATA;
7259 break;
7260 default:
7261 pg_fatal("cannot dump statistics for relation kind \"%c\"",
7262 info->relkind);
7263 }
7264
7265 return info;
7266 }
7267 return NULL;
7268}
7269
7270/*
7271 * getTables
7272 * read all the tables (no indexes) in the system catalogs,
7273 * and return them as an array of TableInfo structures
7274 *
7275 * *numTables is set to the number of tables read in
7276 */
7277TableInfo *
7279{
7280 DumpOptions *dopt = fout->dopt;
7281 PGresult *res;
7282 int ntups;
7283 int i;
7285 TableInfo *tblinfo;
7286 int i_reltableoid;
7287 int i_reloid;
7288 int i_relname;
7289 int i_relnamespace;
7290 int i_relkind;
7291 int i_reltype;
7292 int i_relowner;
7293 int i_relchecks;
7294 int i_relhasindex;
7295 int i_relhasrules;
7296 int i_relpages;
7297 int i_reltuples;
7298 int i_relallvisible;
7299 int i_relallfrozen;
7300 int i_toastpages;
7301 int i_owning_tab;
7302 int i_owning_col;
7303 int i_reltablespace;
7304 int i_relhasoids;
7305 int i_relhastriggers;
7306 int i_relpersistence;
7307 int i_relispopulated;
7308 int i_relreplident;
7309 int i_relrowsec;
7310 int i_relforcerowsec;
7311 int i_relfrozenxid;
7312 int i_toastfrozenxid;
7313 int i_toastoid;
7314 int i_relminmxid;
7315 int i_toastminmxid;
7316 int i_reloptions;
7317 int i_checkoption;
7319 int i_reloftype;
7320 int i_foreignserver;
7321 int i_amname;
7323 int i_relacl;
7324 int i_acldefault;
7325 int i_ispartition;
7326
7327 /*
7328 * Find all the tables and table-like objects.
7329 *
7330 * We must fetch all tables in this phase because otherwise we cannot
7331 * correctly identify inherited columns, owned sequences, etc.
7332 *
7333 * We include system catalogs, so that we can work if a user table is
7334 * defined to inherit from a system catalog (pretty weird, but...)
7335 *
7336 * Note: in this phase we should collect only a minimal amount of
7337 * information about each table, basically just enough to decide if it is
7338 * interesting. In particular, since we do not yet have lock on any user
7339 * table, we MUST NOT invoke any server-side data collection functions
7340 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7341 * wrong answers if any concurrent DDL is happening.
7342 */
7343
7345 "SELECT c.tableoid, c.oid, c.relname, "
7346 "c.relnamespace, c.relkind, c.reltype, "
7347 "c.relowner, "
7348 "c.relchecks, "
7349 "c.relhasindex, c.relhasrules, c.relpages, "
7350 "c.reltuples, c.relallvisible, ");
7351
7352 if (fout->remoteVersion >= 180000)
7353 appendPQExpBufferStr(query, "c.relallfrozen, ");
7354 else
7355 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7356
7358 "c.relhastriggers, c.relpersistence, "
7359 "c.reloftype, "
7360 "c.relacl, "
7361 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7362 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7363 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7364 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7365 "ELSE 0 END AS foreignserver, "
7366 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7367 "tc.oid AS toid, "
7368 "tc.relpages AS toastpages, "
7369 "tc.reloptions AS toast_reloptions, "
7370 "d.refobjid AS owning_tab, "
7371 "d.refobjsubid AS owning_col, "
7372 "tsp.spcname AS reltablespace, ");
7373
7374 if (fout->remoteVersion >= 120000)
7376 "false AS relhasoids, ");
7377 else
7379 "c.relhasoids, ");
7380
7381 if (fout->remoteVersion >= 90300)
7383 "c.relispopulated, ");
7384 else
7386 "'t' as relispopulated, ");
7387
7388 if (fout->remoteVersion >= 90400)
7390 "c.relreplident, ");
7391 else
7393 "'d' AS relreplident, ");
7394
7395 if (fout->remoteVersion >= 90500)
7397 "c.relrowsecurity, c.relforcerowsecurity, ");
7398 else
7400 "false AS relrowsecurity, "
7401 "false AS relforcerowsecurity, ");
7402
7403 if (fout->remoteVersion >= 90300)
7405 "c.relminmxid, tc.relminmxid AS tminmxid, ");
7406 else
7408 "0 AS relminmxid, 0 AS tminmxid, ");
7409
7410 if (fout->remoteVersion >= 90300)
7412 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7413 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7414 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7415 else
7417 "c.reloptions, NULL AS checkoption, ");
7418
7419 if (fout->remoteVersion >= 90600)
7421 "am.amname, ");
7422 else
7424 "NULL AS amname, ");
7425
7426 if (fout->remoteVersion >= 90600)
7428 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7429 else
7431 "false AS is_identity_sequence, ");
7432
7433 if (fout->remoteVersion >= 100000)
7435 "c.relispartition AS ispartition ");
7436 else
7438 "false AS ispartition ");
7439
7440 /*
7441 * Left join to pg_depend to pick up dependency info linking sequences to
7442 * their owning column, if any (note this dependency is AUTO except for
7443 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7444 * collect the spcname.
7445 */
7447 "\nFROM pg_class c\n"
7448 "LEFT JOIN pg_depend d ON "
7449 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7450 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7451 "d.objsubid = 0 AND "
7452 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7453 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7454
7455 /*
7456 * In 9.6 and up, left join to pg_am to pick up the amname.
7457 */
7458 if (fout->remoteVersion >= 90600)
7460 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7461
7462 /*
7463 * We purposefully ignore toast OIDs for partitioned tables; the reason is
7464 * that versions 10 and 11 have them, but later versions do not, so
7465 * emitting them causes the upgrade to fail.
7466 */
7468 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7469 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7470 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7471
7472 /*
7473 * Restrict to interesting relkinds (in particular, not indexes). Not all
7474 * relkinds are possible in older servers, but it's not worth the trouble
7475 * to emit a version-dependent list.
7476 *
7477 * Composite-type table entries won't be dumped as such, but we have to
7478 * make a DumpableObject for them so that we can track dependencies of the
7479 * composite type (pg_depend entries for columns of the composite type
7480 * link to the pg_class entry not the pg_type entry).
7481 */
7483 "WHERE c.relkind IN ("
7492 "ORDER BY c.oid");
7493
7494 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7495
7496 ntups = PQntuples(res);
7497
7498 *numTables = ntups;
7499
7500 /*
7501 * Extract data from result and lock dumpable tables. We do the locking
7502 * before anything else, to minimize the window wherein a table could
7503 * disappear under us.
7504 *
7505 * Note that we have to save info about all tables here, even when dumping
7506 * only one, because we don't yet know which tables might be inheritance
7507 * ancestors of the target table.
7508 */
7509 tblinfo = pg_malloc0_array(TableInfo, ntups);
7510
7511 i_reltableoid = PQfnumber(res, "tableoid");
7512 i_reloid = PQfnumber(res, "oid");
7513 i_relname = PQfnumber(res, "relname");
7514 i_relnamespace = PQfnumber(res, "relnamespace");
7515 i_relkind = PQfnumber(res, "relkind");
7516 i_reltype = PQfnumber(res, "reltype");
7517 i_relowner = PQfnumber(res, "relowner");
7518 i_relchecks = PQfnumber(res, "relchecks");
7519 i_relhasindex = PQfnumber(res, "relhasindex");
7520 i_relhasrules = PQfnumber(res, "relhasrules");
7521 i_relpages = PQfnumber(res, "relpages");
7522 i_reltuples = PQfnumber(res, "reltuples");
7523 i_relallvisible = PQfnumber(res, "relallvisible");
7524 i_relallfrozen = PQfnumber(res, "relallfrozen");
7525 i_toastpages = PQfnumber(res, "toastpages");
7526 i_owning_tab = PQfnumber(res, "owning_tab");
7527 i_owning_col = PQfnumber(res, "owning_col");
7528 i_reltablespace = PQfnumber(res, "reltablespace");
7529 i_relhasoids = PQfnumber(res, "relhasoids");
7530 i_relhastriggers = PQfnumber(res, "relhastriggers");
7531 i_relpersistence = PQfnumber(res, "relpersistence");
7532 i_relispopulated = PQfnumber(res, "relispopulated");
7533 i_relreplident = PQfnumber(res, "relreplident");
7534 i_relrowsec = PQfnumber(res, "relrowsecurity");
7535 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7536 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7537 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7538 i_toastoid = PQfnumber(res, "toid");
7539 i_relminmxid = PQfnumber(res, "relminmxid");
7540 i_toastminmxid = PQfnumber(res, "tminmxid");
7541 i_reloptions = PQfnumber(res, "reloptions");
7542 i_checkoption = PQfnumber(res, "checkoption");
7543 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7544 i_reloftype = PQfnumber(res, "reloftype");
7545 i_foreignserver = PQfnumber(res, "foreignserver");
7546 i_amname = PQfnumber(res, "amname");
7547 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7548 i_relacl = PQfnumber(res, "relacl");
7549 i_acldefault = PQfnumber(res, "acldefault");
7550 i_ispartition = PQfnumber(res, "ispartition");
7551
7552 if (dopt->lockWaitTimeout)
7553 {
7554 /*
7555 * Arrange to fail instead of waiting forever for a table lock.
7556 *
7557 * NB: this coding assumes that the only queries issued within the
7558 * following loop are LOCK TABLEs; else the timeout may be undesirably
7559 * applied to other things too.
7560 */
7561 resetPQExpBuffer(query);
7562 appendPQExpBufferStr(query, "SET statement_timeout = ");
7564 ExecuteSqlStatement(fout, query->data);
7565 }
7566
7567 resetPQExpBuffer(query);
7568
7569 for (i = 0; i < ntups; i++)
7570 {
7571 int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7572 int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7573
7574 tblinfo[i].dobj.objType = DO_TABLE;
7575 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7576 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7577 AssignDumpId(&tblinfo[i].dobj);
7578 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7579 tblinfo[i].dobj.namespace =
7581 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7582 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7583 tblinfo[i].dacl.privtype = 0;
7584 tblinfo[i].dacl.initprivs = NULL;
7585 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7586 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7587 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7588 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7589 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7590 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7591 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7592 if (PQgetisnull(res, i, i_toastpages))
7593 tblinfo[i].toastpages = 0;
7594 else
7595 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7596 if (PQgetisnull(res, i, i_owning_tab))
7597 {
7598 tblinfo[i].owning_tab = InvalidOid;
7599 tblinfo[i].owning_col = 0;
7600 }
7601 else
7602 {
7603 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7604 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7605 }
7607 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7608 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7609 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7610 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7611 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7612 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7613 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7614 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7616 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7617 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7618 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7619 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7620 if (PQgetisnull(res, i, i_checkoption))
7621 tblinfo[i].checkoption = NULL;
7622 else
7623 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7625 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7627 if (PQgetisnull(res, i, i_amname))
7628 tblinfo[i].amname = NULL;
7629 else
7630 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7631 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7632 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7633
7634 /* other fields were zeroed above */
7635
7636 /*
7637 * Decide whether we want to dump this table.
7638 */
7639 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7640 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7641 else
7642 selectDumpableTable(&tblinfo[i], fout);
7643
7644 /*
7645 * Now, consider the table "interesting" if we need to dump its
7646 * definition, data or its statistics. Later on, we'll skip a lot of
7647 * data collection for uninteresting tables.
7648 *
7649 * Note: the "interesting" flag will also be set by flagInhTables for
7650 * parents of interesting tables, so that we collect necessary
7651 * inheritance info even when the parents are not themselves being
7652 * dumped. This is the main reason why we need an "interesting" flag
7653 * that's separate from the components-to-dump bitmask.
7654 */
7655 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7659
7660 tblinfo[i].dummy_view = false; /* might get set during sort */
7661 tblinfo[i].postponed_def = false; /* might get set during sort */
7662
7663 /* Tables have data */
7665
7666 /* Mark whether table has an ACL */
7667 if (!PQgetisnull(res, i, i_relacl))
7668 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7669 tblinfo[i].hascolumnACLs = false; /* may get set later */
7670
7671 /* Add statistics */
7672 if (tblinfo[i].interesting)
7673 {
7674 RelStatsInfo *stats;
7675
7676 stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7677 tblinfo[i].relpages,
7678 PQgetvalue(res, i, i_reltuples),
7679 relallvisible, relallfrozen,
7680 tblinfo[i].relkind, NULL, 0);
7681 if (tblinfo[i].relkind == RELKIND_MATVIEW)
7682 tblinfo[i].stats = stats;
7683 }
7684
7685 /*
7686 * Read-lock target tables to make sure they aren't DROPPED or altered
7687 * in schema before we get around to dumping them.
7688 *
7689 * Note that we don't explicitly lock parents of the target tables; we
7690 * assume our lock on the child is enough to prevent schema
7691 * alterations to parent tables.
7692 *
7693 * NOTE: it'd be kinda nice to lock other relations too, not only
7694 * plain or partitioned tables, but the backend doesn't presently
7695 * allow that.
7696 *
7697 * We only need to lock the table for certain components; see
7698 * pg_dump.h
7699 */
7700 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7701 (tblinfo[i].relkind == RELKIND_RELATION ||
7702 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7703 {
7704 /*
7705 * Tables are locked in batches. When dumping from a remote
7706 * server this can save a significant amount of time by reducing
7707 * the number of round trips.
7708 */
7709 if (query->len == 0)
7710 appendPQExpBuffer(query, "LOCK TABLE %s",
7711 fmtQualifiedDumpable(&tblinfo[i]));
7712 else
7713 {
7714 appendPQExpBuffer(query, ", %s",
7715 fmtQualifiedDumpable(&tblinfo[i]));
7716
7717 /* Arbitrarily end a batch when query length reaches 100K. */
7718 if (query->len >= 100000)
7719 {
7720 /* Lock another batch of tables. */
7721 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7722 ExecuteSqlStatement(fout, query->data);
7723 resetPQExpBuffer(query);
7724 }
7725 }
7726 }
7727 }
7728
7729 if (query->len != 0)
7730 {
7731 /* Lock the tables in the last batch. */
7732 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7733 ExecuteSqlStatement(fout, query->data);
7734 }
7735
7736 if (dopt->lockWaitTimeout)
7737 {
7738 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7739 }
7740
7741 PQclear(res);
7742
7743 destroyPQExpBuffer(query);
7744
7745 return tblinfo;
7746}
7747
7748/*
7749 * getOwnedSeqs
7750 * identify owned sequences and mark them as dumpable if owning table is
7751 *
7752 * We used to do this in getTables(), but it's better to do it after the
7753 * index used by findTableByOid() has been set up.
7754 */
7755void
7757{
7758 int i;
7759
7760 /*
7761 * Force sequences that are "owned" by table columns to be dumped whenever
7762 * their owning table is being dumped.
7763 */
7764 for (i = 0; i < numTables; i++)
7765 {
7766 TableInfo *seqinfo = &tblinfo[i];
7767 TableInfo *owning_tab;
7768
7769 if (!OidIsValid(seqinfo->owning_tab))
7770 continue; /* not an owned sequence */
7771
7772 owning_tab = findTableByOid(seqinfo->owning_tab);
7773 if (owning_tab == NULL)
7774 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7775 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7776
7777 /*
7778 * For an identity sequence, dump exactly the same components for the
7779 * sequence as for the owning table. This is important because we
7780 * treat the identity sequence as an integral part of the table. For
7781 * example, there is not any DDL command that allows creation of such
7782 * a sequence independently of the table.
7783 *
7784 * For other owned sequences such as serial sequences, we need to dump
7785 * the components that are being dumped for the table and any
7786 * components that the sequence is explicitly marked with.
7787 *
7788 * We can't simply use the set of components which are being dumped
7789 * for the table as the table might be in an extension (and only the
7790 * non-extension components, eg: ACLs if changed, security labels, and
7791 * policies, are being dumped) while the sequence is not (and
7792 * therefore the definition and other components should also be
7793 * dumped).
7794 *
7795 * If the sequence is part of the extension then it should be properly
7796 * marked by checkExtensionMembership() and this will be a no-op as
7797 * the table will be equivalently marked.
7798 */
7799 if (seqinfo->is_identity_sequence)
7800 seqinfo->dobj.dump = owning_tab->dobj.dump;
7801 else
7802 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7803
7804 /* Make sure that necessary data is available if we're dumping it */
7805 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7806 {
7807 seqinfo->interesting = true;
7808 owning_tab->interesting = true;
7809 }
7810 }
7811}
7812
7813/*
7814 * getInherits
7815 * read all the inheritance information
7816 * from the system catalogs return them in the InhInfo* structure
7817 *
7818 * numInherits is set to the number of pairs read in
7819 */
7820InhInfo *
7822{
7823 PGresult *res;
7824 int ntups;
7825 int i;
7828
7829 int i_inhrelid;
7830 int i_inhparent;
7831
7832 /* find all the inheritance information */
7833 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7834
7835 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7836
7837 ntups = PQntuples(res);
7838
7839 *numInherits = ntups;
7840
7842
7843 i_inhrelid = PQfnumber(res, "inhrelid");
7844 i_inhparent = PQfnumber(res, "inhparent");
7845
7846 for (i = 0; i < ntups; i++)
7847 {
7848 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7849 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7850 }
7851
7852 PQclear(res);
7853
7854 destroyPQExpBuffer(query);
7855
7856 return inhinfo;
7857}
7858
7859/*
7860 * getPartitioningInfo
7861 * get information about partitioning
7862 *
7863 * For the most part, we only collect partitioning info about tables we
7864 * intend to dump. However, this function has to consider all partitioned
7865 * tables in the database, because we need to know about parents of partitions
7866 * we are going to dump even if the parents themselves won't be dumped.
7867 *
7868 * Specifically, what we need to know is whether each partitioned table
7869 * has an "unsafe" partitioning scheme that requires us to force
7870 * load-via-partition-root mode for its children. Currently the only case
7871 * for which we force that is hash partitioning on enum columns, since the
7872 * hash codes depend on enum value OIDs which won't be replicated across
7873 * dump-and-reload. There are other cases in which load-via-partition-root
7874 * might be necessary, but we expect users to cope with them.
7875 */
7876void
7878{
7879 PQExpBuffer query;
7880 PGresult *res;
7881 int ntups;
7882
7883 /* hash partitioning didn't exist before v11 */
7884 if (fout->remoteVersion < 110000)
7885 return;
7886 /* needn't bother if not dumping data */
7887 if (!fout->dopt->dumpData)
7888 return;
7889
7890 query = createPQExpBuffer();
7891
7892 /*
7893 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7894 * appears among the partition opclasses. We needn't check partstrat.
7895 *
7896 * Note that this query may well retrieve info about tables we aren't
7897 * going to dump and hence have no lock on. That's okay since we need not
7898 * invoke any unsafe server-side functions.
7899 */
7901 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7902 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7903 "ON c.opcmethod = a.oid\n"
7904 "WHERE opcname = 'enum_ops' "
7905 "AND opcnamespace = 'pg_catalog'::regnamespace "
7906 "AND amname = 'hash') = ANY(partclass)");
7907
7908 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7909
7910 ntups = PQntuples(res);
7911
7912 for (int i = 0; i < ntups; i++)
7913 {
7914 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7916
7918 if (tbinfo == NULL)
7919 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7920 tabrelid);
7921 tbinfo->unsafe_partitions = true;
7922 }
7923
7924 PQclear(res);
7925
7926 destroyPQExpBuffer(query);
7927}
7928
7929/*
7930 * getIndexes
7931 * get information about every index on a dumpable table
7932 *
7933 * Note: index data is not returned directly to the caller, but it
7934 * does get entered into the DumpableObject tables.
7935 */
7936void
7938{
7941 PGresult *res;
7942 int ntups;
7943 int curtblindx;
7945 int i_tableoid,
7946 i_oid,
7947 i_indrelid,
7949 i_relpages,
7954 i_indexdef,
7956 i_indnatts,
7957 i_indkey,
7961 i_contype,
7962 i_conname,
7967 i_conoid,
7968 i_condef,
7974
7975 /*
7976 * We want to perform just one query against pg_index. However, we
7977 * mustn't try to select every row of the catalog and then sort it out on
7978 * the client side, because some of the server-side functions we need
7979 * would be unsafe to apply to tables we don't have lock on. Hence, we
7980 * build an array of the OIDs of tables we care about (and now have lock
7981 * on!), and use a WHERE clause to constrain which rows are selected.
7982 */
7984 for (int i = 0; i < numTables; i++)
7985 {
7986 TableInfo *tbinfo = &tblinfo[i];
7987
7988 if (!tbinfo->hasindex)
7989 continue;
7990
7991 /*
7992 * We can ignore indexes of uninteresting tables.
7993 */
7994 if (!tbinfo->interesting)
7995 continue;
7996
7997 /* OK, we need info for this table */
7998 if (tbloids->len > 1) /* do we have more than the '{'? */
8000 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8001 }
8003
8005 "SELECT t.tableoid, t.oid, i.indrelid, "
8006 "t.relname AS indexname, "
8007 "t.relpages, t.reltuples, t.relallvisible, ");
8008
8009 if (fout->remoteVersion >= 180000)
8010 appendPQExpBufferStr(query, "t.relallfrozen, ");
8011 else
8012 appendPQExpBufferStr(query, "0 AS relallfrozen, ");
8013
8015 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
8016 "i.indkey, i.indisclustered, "
8017 "c.contype, c.conname, "
8018 "c.condeferrable, c.condeferred, "
8019 "c.tableoid AS contableoid, "
8020 "c.oid AS conoid, "
8021 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
8022 "CASE WHEN i.indexprs IS NOT NULL THEN "
8023 "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
8024 " FROM pg_catalog.pg_attribute "
8025 " WHERE attrelid = i.indexrelid) "
8026 "ELSE NULL END AS indattnames, "
8027 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
8028 "t.reloptions AS indreloptions, ");
8029
8030
8031 if (fout->remoteVersion >= 90400)
8033 "i.indisreplident, ");
8034 else
8036 "false AS indisreplident, ");
8037
8038 if (fout->remoteVersion >= 110000)
8040 "inh.inhparent AS parentidx, "
8041 "i.indnkeyatts AS indnkeyatts, "
8042 "i.indnatts AS indnatts, "
8043 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
8044 " FROM pg_catalog.pg_attribute "
8045 " WHERE attrelid = i.indexrelid AND "
8046 " attstattarget >= 0) AS indstatcols, "
8047 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
8048 " FROM pg_catalog.pg_attribute "
8049 " WHERE attrelid = i.indexrelid AND "
8050 " attstattarget >= 0) AS indstatvals, ");
8051 else
8053 "0 AS parentidx, "
8054 "i.indnatts AS indnkeyatts, "
8055 "i.indnatts AS indnatts, "
8056 "'' AS indstatcols, "
8057 "'' AS indstatvals, ");
8058
8059 if (fout->remoteVersion >= 150000)
8061 "i.indnullsnotdistinct, ");
8062 else
8064 "false AS indnullsnotdistinct, ");
8065
8066 if (fout->remoteVersion >= 180000)
8068 "c.conperiod ");
8069 else
8071 "NULL AS conperiod ");
8072
8073 /*
8074 * The point of the messy-looking outer join is to find a constraint that
8075 * is related by an internal dependency link to the index. If we find one,
8076 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
8077 * index won't have more than one internal dependency.
8078 *
8079 * Note: the check on conrelid is redundant, but useful because that
8080 * column is indexed while conindid is not.
8081 */
8082 if (fout->remoteVersion >= 110000)
8083 {
8084 appendPQExpBuffer(query,
8085 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8086 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8087 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8088 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
8089 "LEFT JOIN pg_catalog.pg_constraint c "
8090 "ON (i.indrelid = c.conrelid AND "
8091 "i.indexrelid = c.conindid AND "
8092 "c.contype IN ('p','u','x')) "
8093 "LEFT JOIN pg_catalog.pg_inherits inh "
8094 "ON (inh.inhrelid = indexrelid) "
8095 "WHERE (i.indisvalid OR t2.relkind = 'p') "
8096 "AND i.indisready "
8097 "ORDER BY i.indrelid, indexname",
8098 tbloids->data);
8099 }
8100 else
8101 {
8102 /*
8103 * the test on indisready is necessary in 9.2, and harmless in
8104 * earlier/later versions
8105 */
8106 appendPQExpBuffer(query,
8107 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8108 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8109 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8110 "LEFT JOIN pg_catalog.pg_constraint c "
8111 "ON (i.indrelid = c.conrelid AND "
8112 "i.indexrelid = c.conindid AND "
8113 "c.contype IN ('p','u','x')) "
8114 "WHERE i.indisvalid AND i.indisready "
8115 "ORDER BY i.indrelid, indexname",
8116 tbloids->data);
8117 }
8118
8119 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8120
8121 ntups = PQntuples(res);
8122
8123 i_tableoid = PQfnumber(res, "tableoid");
8124 i_oid = PQfnumber(res, "oid");
8125 i_indrelid = PQfnumber(res, "indrelid");
8126 i_indexname = PQfnumber(res, "indexname");
8127 i_relpages = PQfnumber(res, "relpages");
8128 i_reltuples = PQfnumber(res, "reltuples");
8129 i_relallvisible = PQfnumber(res, "relallvisible");
8130 i_relallfrozen = PQfnumber(res, "relallfrozen");
8131 i_parentidx = PQfnumber(res, "parentidx");
8132 i_indexdef = PQfnumber(res, "indexdef");
8133 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8134 i_indnatts = PQfnumber(res, "indnatts");
8135 i_indkey = PQfnumber(res, "indkey");
8136 i_indisclustered = PQfnumber(res, "indisclustered");
8137 i_indisreplident = PQfnumber(res, "indisreplident");
8138 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8139 i_contype = PQfnumber(res, "contype");
8140 i_conname = PQfnumber(res, "conname");
8141 i_condeferrable = PQfnumber(res, "condeferrable");
8142 i_condeferred = PQfnumber(res, "condeferred");
8143 i_conperiod = PQfnumber(res, "conperiod");
8144 i_contableoid = PQfnumber(res, "contableoid");
8145 i_conoid = PQfnumber(res, "conoid");
8146 i_condef = PQfnumber(res, "condef");
8147 i_indattnames = PQfnumber(res, "indattnames");
8148 i_tablespace = PQfnumber(res, "tablespace");
8149 i_indreloptions = PQfnumber(res, "indreloptions");
8150 i_indstatcols = PQfnumber(res, "indstatcols");
8151 i_indstatvals = PQfnumber(res, "indstatvals");
8152
8154
8155 /*
8156 * Outer loop iterates once per table, not once per row. Incrementing of
8157 * j is handled by the inner loop.
8158 */
8159 curtblindx = -1;
8160 for (int j = 0; j < ntups;)
8161 {
8164 int numinds;
8165
8166 /* Count rows for this table */
8167 for (numinds = 1; numinds < ntups - j; numinds++)
8168 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8169 break;
8170
8171 /*
8172 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8173 * order.
8174 */
8175 while (++curtblindx < numTables)
8176 {
8177 tbinfo = &tblinfo[curtblindx];
8178 if (tbinfo->dobj.catId.oid == indrelid)
8179 break;
8180 }
8181 if (curtblindx >= numTables)
8182 pg_fatal("unrecognized table OID %u", indrelid);
8183 /* cross-check that we only got requested tables */
8184 if (!tbinfo->hasindex ||
8185 !tbinfo->interesting)
8186 pg_fatal("unexpected index data for table \"%s\"",
8187 tbinfo->dobj.name);
8188
8189 /* Save data for this table */
8190 tbinfo->indexes = indxinfo + j;
8191 tbinfo->numIndexes = numinds;
8192
8193 for (int c = 0; c < numinds; c++, j++)
8194 {
8195 char contype;
8196 char indexkind;
8197 char **indAttNames = NULL;
8198 int nindAttNames = 0;
8200 int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8201 int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8202 int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8203
8204 indxinfo[j].dobj.objType = DO_INDEX;
8205 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8206 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8207 AssignDumpId(&indxinfo[j].dobj);
8208 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8209 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8210 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8211 indxinfo[j].indextable = tbinfo;
8212 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8213 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8214 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8215 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8216 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8217 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8218 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8219 indxinfo[j].indkeys = pg_malloc_array(Oid, indxinfo[j].indnattrs);
8221 indxinfo[j].indkeys, indxinfo[j].indnattrs);
8222 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8223 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8224 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8225 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8226 indxinfo[j].partattaches = (SimplePtrList)
8227 {
8228 NULL, NULL
8229 };
8230
8231 if (indxinfo[j].parentidx == 0)
8233 else
8235
8236 if (!PQgetisnull(res, j, i_indattnames))
8237 {
8239 &indAttNames, &nindAttNames))
8240 pg_fatal("could not parse %s array", "indattnames");
8241 }
8242
8243 relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8244 PQgetvalue(res, j, i_reltuples),
8245 relallvisible, relallfrozen, indexkind,
8246 indAttNames, nindAttNames);
8247
8248 contype = *(PQgetvalue(res, j, i_contype));
8249 if (contype == 'p' || contype == 'u' || contype == 'x')
8250 {
8251 /*
8252 * If we found a constraint matching the index, create an
8253 * entry for it.
8254 */
8256
8258 constrinfo->dobj.objType = DO_CONSTRAINT;
8259 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8260 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8261 AssignDumpId(&constrinfo->dobj);
8262 constrinfo->dobj.dump = tbinfo->dobj.dump;
8263 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8264 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8265 constrinfo->contable = tbinfo;
8266 constrinfo->condomain = NULL;
8267 constrinfo->contype = contype;
8268 if (contype == 'x')
8269 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8270 else
8271 constrinfo->condef = NULL;
8272 constrinfo->confrelid = InvalidOid;
8273 constrinfo->conindex = indxinfo[j].dobj.dumpId;
8274 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8275 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8276 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8277 constrinfo->conislocal = true;
8278 constrinfo->separate = true;
8279
8280 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8281 if (relstats != NULL)
8282 addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8283 }
8284 else
8285 {
8286 /* Plain secondary index */
8287 indxinfo[j].indexconstraint = 0;
8288 }
8289 }
8290 }
8291
8292 PQclear(res);
8293
8294 destroyPQExpBuffer(query);
8296}
8297
8298/*
8299 * getExtendedStatistics
8300 * get information about extended-statistics objects.
8301 *
8302 * Note: extended statistics data is not returned directly to the caller, but
8303 * it does get entered into the DumpableObject tables.
8304 */
8305void
8307{
8308 PQExpBuffer query;
8309 PGresult *res;
8311 int ntups;
8312 int i_tableoid;
8313 int i_oid;
8314 int i_stxname;
8315 int i_stxnamespace;
8316 int i_stxowner;
8317 int i_stxrelid;
8318 int i_stattarget;
8319 int i;
8320
8321 /* Extended statistics were new in v10 */
8322 if (fout->remoteVersion < 100000)
8323 return;
8324
8325 query = createPQExpBuffer();
8326
8327 if (fout->remoteVersion < 130000)
8328 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8329 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8330 "FROM pg_catalog.pg_statistic_ext");
8331 else
8332 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8333 "stxnamespace, stxowner, stxrelid, stxstattarget "
8334 "FROM pg_catalog.pg_statistic_ext");
8335
8336 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8337
8338 ntups = PQntuples(res);
8339
8340 i_tableoid = PQfnumber(res, "tableoid");
8341 i_oid = PQfnumber(res, "oid");
8342 i_stxname = PQfnumber(res, "stxname");
8343 i_stxnamespace = PQfnumber(res, "stxnamespace");
8344 i_stxowner = PQfnumber(res, "stxowner");
8345 i_stxrelid = PQfnumber(res, "stxrelid");
8346 i_stattarget = PQfnumber(res, "stxstattarget");
8347
8349
8350 for (i = 0; i < ntups; i++)
8351 {
8352 statsextinfo[i].dobj.objType = DO_STATSEXT;
8353 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8354 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8355 AssignDumpId(&statsextinfo[i].dobj);
8356 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8357 statsextinfo[i].dobj.namespace =
8359 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8360 statsextinfo[i].stattable =
8362 if (PQgetisnull(res, i, i_stattarget))
8363 statsextinfo[i].stattarget = -1;
8364 else
8365 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8366
8367 /* Decide whether we want to dump it */
8369
8370 if (fout->dopt->dumpStatistics)
8371 statsextinfo[i].dobj.components |= DUMP_COMPONENT_STATISTICS;
8372 }
8373
8374 PQclear(res);
8375 destroyPQExpBuffer(query);
8376}
8377
8378/*
8379 * getConstraints
8380 *
8381 * Get info about constraints on dumpable tables.
8382 *
8383 * Currently handles foreign keys only.
8384 * Unique and primary key constraints are handled with indexes,
8385 * while check constraints are processed in getTableAttrs().
8386 */
8387void
8389{
8392 PGresult *res;
8393 int ntups;
8394 int curtblindx;
8397 int i_contableoid,
8398 i_conoid,
8399 i_conrelid,
8400 i_conname,
8402 i_conindid,
8403 i_condef;
8404
8405 /*
8406 * We want to perform just one query against pg_constraint. However, we
8407 * mustn't try to select every row of the catalog and then sort it out on
8408 * the client side, because some of the server-side functions we need
8409 * would be unsafe to apply to tables we don't have lock on. Hence, we
8410 * build an array of the OIDs of tables we care about (and now have lock
8411 * on!), and use a WHERE clause to constrain which rows are selected.
8412 */
8414 for (int i = 0; i < numTables; i++)
8415 {
8416 TableInfo *tinfo = &tblinfo[i];
8417
8418 if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8419 continue;
8420
8421 /* OK, we need info for this table */
8422 if (tbloids->len > 1) /* do we have more than the '{'? */
8424 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8425 }
8427
8429 "SELECT c.tableoid, c.oid, "
8430 "conrelid, conname, confrelid, ");
8431 if (fout->remoteVersion >= 110000)
8432 appendPQExpBufferStr(query, "conindid, ");
8433 else
8434 appendPQExpBufferStr(query, "0 AS conindid, ");
8435 appendPQExpBuffer(query,
8436 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8437 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8438 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8439 "WHERE contype = 'f' ",
8440 tbloids->data);
8441 if (fout->remoteVersion >= 110000)
8443 "AND conparentid = 0 ");
8445 "ORDER BY conrelid, conname");
8446
8447 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8448
8449 ntups = PQntuples(res);
8450
8451 i_contableoid = PQfnumber(res, "tableoid");
8452 i_conoid = PQfnumber(res, "oid");
8453 i_conrelid = PQfnumber(res, "conrelid");
8454 i_conname = PQfnumber(res, "conname");
8455 i_confrelid = PQfnumber(res, "confrelid");
8456 i_conindid = PQfnumber(res, "conindid");
8457 i_condef = PQfnumber(res, "condef");
8458
8460
8461 curtblindx = -1;
8462 for (int j = 0; j < ntups; j++)
8463 {
8464 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8466
8467 /*
8468 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8469 * order.
8470 */
8471 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8472 {
8473 while (++curtblindx < numTables)
8474 {
8475 tbinfo = &tblinfo[curtblindx];
8476 if (tbinfo->dobj.catId.oid == conrelid)
8477 break;
8478 }
8479 if (curtblindx >= numTables)
8480 pg_fatal("unrecognized table OID %u", conrelid);
8481 }
8482
8483 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8484 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8485 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8486 AssignDumpId(&constrinfo[j].dobj);
8487 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8488 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8489 constrinfo[j].contable = tbinfo;
8490 constrinfo[j].condomain = NULL;
8491 constrinfo[j].contype = 'f';
8492 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8493 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8494 constrinfo[j].conindex = 0;
8495 constrinfo[j].condeferrable = false;
8496 constrinfo[j].condeferred = false;
8497 constrinfo[j].conislocal = true;
8498 constrinfo[j].separate = true;
8499
8500 /*
8501 * Restoring an FK that points to a partitioned table requires that
8502 * all partition indexes have been attached beforehand. Ensure that
8503 * happens by making the constraint depend on each index partition
8504 * attach object.
8505 */
8506 reftable = findTableByOid(constrinfo[j].confrelid);
8507 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8508 {
8509 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8510
8511 if (indexOid != InvalidOid)
8512 {
8513 for (int k = 0; k < reftable->numIndexes; k++)
8514 {
8516
8517 /* not our index? */
8518 if (reftable->indexes[k].dobj.catId.oid != indexOid)
8519 continue;
8520
8521 refidx = &reftable->indexes[k];
8523 break;
8524 }
8525 }
8526 }
8527 }
8528
8529 PQclear(res);
8530
8531 destroyPQExpBuffer(query);
8533}
8534
8535/*
8536 * addConstrChildIdxDeps
8537 *
8538 * Recursive subroutine for getConstraints
8539 *
8540 * Given an object representing a foreign key constraint and an index on the
8541 * partitioned table it references, mark the constraint object as dependent
8542 * on the DO_INDEX_ATTACH object of each index partition, recursively
8543 * drilling down to their partitions if any. This ensures that the FK is not
8544 * restored until the index is fully marked valid.
8545 */
8546static void
8548{
8549 SimplePtrListCell *cell;
8550
8552
8553 for (cell = refidx->partattaches.head; cell; cell = cell->next)
8554 {
8556
8557 addObjectDependency(dobj, attach->dobj.dumpId);
8558
8559 if (attach->partitionIdx->partattaches.head != NULL)
8560 addConstrChildIdxDeps(dobj, attach->partitionIdx);
8561 }
8562}
8563
8564/*
8565 * getDomainConstraints
8566 *
8567 * Get info about constraints on a domain.
8568 */
8569static void
8571{
8574 PGresult *res;
8575 int i_tableoid,
8576 i_oid,
8577 i_conname,
8578 i_consrc,
8580 i_contype;
8581 int ntups;
8582
8584 {
8585 /*
8586 * Set up query for constraint-specific details. For servers 17 and
8587 * up, domains have constraints of type 'n' as well as 'c', otherwise
8588 * just the latter.
8589 */
8590 appendPQExpBuffer(query,
8591 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8592 "SELECT tableoid, oid, conname, "
8593 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8594 "convalidated, contype "
8595 "FROM pg_catalog.pg_constraint "
8596 "WHERE contypid = $1 AND contype IN (%s) "
8597 "ORDER BY conname",
8598 fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8599
8600 ExecuteSqlStatement(fout, query->data);
8601
8603 }
8604
8605 printfPQExpBuffer(query,
8606 "EXECUTE getDomainConstraints('%u')",
8607 tyinfo->dobj.catId.oid);
8608
8609 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8610
8611 ntups = PQntuples(res);
8612
8613 i_tableoid = PQfnumber(res, "tableoid");
8614 i_oid = PQfnumber(res, "oid");
8615 i_conname = PQfnumber(res, "conname");
8616 i_consrc = PQfnumber(res, "consrc");
8617 i_convalidated = PQfnumber(res, "convalidated");
8618 i_contype = PQfnumber(res, "contype");
8619
8621 tyinfo->domChecks = constrinfo;
8622
8623 /* 'i' tracks result rows; 'j' counts CHECK constraints */
8624 for (int i = 0, j = 0; i < ntups; i++)
8625 {
8626 bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8627 char contype = (PQgetvalue(res, i, i_contype))[0];
8628 ConstraintInfo *constraint;
8629
8630 if (contype == CONSTRAINT_CHECK)
8631 {
8632 constraint = &constrinfo[j++];
8633 tyinfo->nDomChecks++;
8634 }
8635 else
8636 {
8637 Assert(contype == CONSTRAINT_NOTNULL);
8638 Assert(tyinfo->notnull == NULL);
8639 /* use last item in array for the not-null constraint */
8640 tyinfo->notnull = &(constrinfo[ntups - 1]);
8641 constraint = tyinfo->notnull;
8642 }
8643
8644 constraint->dobj.objType = DO_CONSTRAINT;
8645 constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8646 constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8647 AssignDumpId(&(constraint->dobj));
8648 constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8649 constraint->dobj.namespace = tyinfo->dobj.namespace;
8650 constraint->contable = NULL;
8651 constraint->condomain = tyinfo;
8652 constraint->contype = contype;
8653 constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8654 constraint->confrelid = InvalidOid;
8655 constraint->conindex = 0;
8656 constraint->condeferrable = false;
8657 constraint->condeferred = false;
8658 constraint->conislocal = true;
8659
8660 constraint->separate = !validated;
8661
8662 /*
8663 * Make the domain depend on the constraint, ensuring it won't be
8664 * output till any constraint dependencies are OK. If the constraint
8665 * has not been validated, it's going to be dumped after the domain
8666 * anyway, so this doesn't matter.
8667 */
8668 if (validated)
8669 addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8670 }
8671
8672 PQclear(res);
8673
8674 destroyPQExpBuffer(query);
8675}
8676
8677/*
8678 * getRules
8679 * get basic information about every rule in the system
8680 */
8681void
8683{
8684 PGresult *res;
8685 int ntups;
8686 int i;
8689 int i_tableoid;
8690 int i_oid;
8691 int i_rulename;
8692 int i_ruletable;
8693 int i_ev_type;
8694 int i_is_instead;
8695 int i_ev_enabled;
8696
8697 appendPQExpBufferStr(query, "SELECT "
8698 "tableoid, oid, rulename, "
8699 "ev_class AS ruletable, ev_type, is_instead, "
8700 "ev_enabled "
8701 "FROM pg_rewrite "
8702 "ORDER BY oid");
8703
8704 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8705
8706 ntups = PQntuples(res);
8707
8709
8710 i_tableoid = PQfnumber(res, "tableoid");
8711 i_oid = PQfnumber(res, "oid");
8712 i_rulename = PQfnumber(res, "rulename");
8713 i_ruletable = PQfnumber(res, "ruletable");
8714 i_ev_type = PQfnumber(res, "ev_type");
8715 i_is_instead = PQfnumber(res, "is_instead");
8716 i_ev_enabled = PQfnumber(res, "ev_enabled");
8717
8718 for (i = 0; i < ntups; i++)
8719 {
8721
8722 ruleinfo[i].dobj.objType = DO_RULE;
8723 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8724 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8725 AssignDumpId(&ruleinfo[i].dobj);
8726 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8728 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8729 if (ruleinfo[i].ruletable == NULL)
8730 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8731 ruletableoid, ruleinfo[i].dobj.catId.oid);
8732 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8733 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8734 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8735 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8736 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8737 if (ruleinfo[i].ruletable)
8738 {
8739 /*
8740 * If the table is a view or materialized view, force its ON
8741 * SELECT rule to be sorted before the view itself --- this
8742 * ensures that any dependencies for the rule affect the table's
8743 * positioning. Other rules are forced to appear after their
8744 * table.
8745 */
8746 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8747 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8748 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8749 {
8750 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8751 ruleinfo[i].dobj.dumpId);
8752 /* We'll merge the rule into CREATE VIEW, if possible */
8753 ruleinfo[i].separate = false;
8754 }
8755 else
8756 {
8758 ruleinfo[i].ruletable->dobj.dumpId);
8759 ruleinfo[i].separate = true;
8760 }
8761 }
8762 else
8763 ruleinfo[i].separate = true;
8764 }
8765
8766 PQclear(res);
8767
8768 destroyPQExpBuffer(query);
8769}
8770
8771/*
8772 * getTriggers
8773 * get information about every trigger on a dumpable table
8774 *
8775 * Note: trigger data is not returned directly to the caller, but it
8776 * does get entered into the DumpableObject tables.
8777 */
8778void
8780{
8783 PGresult *res;
8784 int ntups;
8785 int curtblindx;
8787 int i_tableoid,
8788 i_oid,
8789 i_tgrelid,
8790 i_tgname,
8793 i_tgdef;
8794
8795 /*
8796 * We want to perform just one query against pg_trigger. However, we
8797 * mustn't try to select every row of the catalog and then sort it out on
8798 * the client side, because some of the server-side functions we need
8799 * would be unsafe to apply to tables we don't have lock on. Hence, we
8800 * build an array of the OIDs of tables we care about (and now have lock
8801 * on!), and use a WHERE clause to constrain which rows are selected.
8802 */
8804 for (int i = 0; i < numTables; i++)
8805 {
8806 TableInfo *tbinfo = &tblinfo[i];
8807
8808 if (!tbinfo->hastriggers ||
8809 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8810 continue;
8811
8812 /* OK, we need info for this table */
8813 if (tbloids->len > 1) /* do we have more than the '{'? */
8815 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8816 }
8818
8819 if (fout->remoteVersion >= 150000)
8820 {
8821 /*
8822 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8823 * result in non-forward-compatible dumps of WHEN clauses due to
8824 * under-parenthesization.
8825 *
8826 * NB: We need to see partition triggers in case the tgenabled flag
8827 * has been changed from the parent.
8828 */
8829 appendPQExpBuffer(query,
8830 "SELECT t.tgrelid, t.tgname, "
8831 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8832 "t.tgenabled, t.tableoid, t.oid, "
8833 "t.tgparentid <> 0 AS tgispartition\n"
8834 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8835 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8836 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8837 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8838 "OR t.tgenabled != u.tgenabled) "
8839 "ORDER BY t.tgrelid, t.tgname",
8840 tbloids->data);
8841 }
8842 else if (fout->remoteVersion >= 130000)
8843 {
8844 /*
8845 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8846 * result in non-forward-compatible dumps of WHEN clauses due to
8847 * under-parenthesization.
8848 *
8849 * NB: We need to see tgisinternal triggers in partitions, in case the
8850 * tgenabled flag has been changed from the parent.
8851 */
8852 appendPQExpBuffer(query,
8853 "SELECT t.tgrelid, t.tgname, "
8854 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8855 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8856 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8857 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8858 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8859 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8860 "ORDER BY t.tgrelid, t.tgname",
8861 tbloids->data);
8862 }
8863 else if (fout->remoteVersion >= 110000)
8864 {
8865 /*
8866 * NB: We need to see tgisinternal triggers in partitions, in case the
8867 * tgenabled flag has been changed from the parent. No tgparentid in
8868 * version 11-12, so we have to match them via pg_depend.
8869 *
8870 * See above about pretty=true in pg_get_triggerdef.
8871 */
8872 appendPQExpBuffer(query,
8873 "SELECT t.tgrelid, t.tgname, "
8874 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8875 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8876 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8877 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8878 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8879 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8880 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8881 " d.objid = t.oid "
8882 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8883 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8884 "ORDER BY t.tgrelid, t.tgname",
8885 tbloids->data);
8886 }
8887 else
8888 {
8889 /* See above about pretty=true in pg_get_triggerdef */
8890 appendPQExpBuffer(query,
8891 "SELECT t.tgrelid, t.tgname, "
8892 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8893 "t.tgenabled, false as tgispartition, "
8894 "t.tableoid, t.oid "
8895 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8896 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8897 "WHERE NOT tgisinternal "
8898 "ORDER BY t.tgrelid, t.tgname",
8899 tbloids->data);
8900 }
8901
8902 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8903
8904 ntups = PQntuples(res);
8905
8906 i_tableoid = PQfnumber(res, "tableoid");
8907 i_oid = PQfnumber(res, "oid");
8908 i_tgrelid = PQfnumber(res, "tgrelid");
8909 i_tgname = PQfnumber(res, "tgname");
8910 i_tgenabled = PQfnumber(res, "tgenabled");
8911 i_tgispartition = PQfnumber(res, "tgispartition");
8912 i_tgdef = PQfnumber(res, "tgdef");
8913
8915
8916 /*
8917 * Outer loop iterates once per table, not once per row. Incrementing of
8918 * j is handled by the inner loop.
8919 */
8920 curtblindx = -1;
8921 for (int j = 0; j < ntups;)
8922 {
8925 int numtrigs;
8926
8927 /* Count rows for this table */
8928 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8929 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8930 break;
8931
8932 /*
8933 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8934 * order.
8935 */
8936 while (++curtblindx < numTables)
8937 {
8938 tbinfo = &tblinfo[curtblindx];
8939 if (tbinfo->dobj.catId.oid == tgrelid)
8940 break;
8941 }
8942 if (curtblindx >= numTables)
8943 pg_fatal("unrecognized table OID %u", tgrelid);
8944
8945 /* Save data for this table */
8946 tbinfo->triggers = tginfo + j;
8947 tbinfo->numTriggers = numtrigs;
8948
8949 for (int c = 0; c < numtrigs; c++, j++)
8950 {
8951 tginfo[j].dobj.objType = DO_TRIGGER;
8952 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8953 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8954 AssignDumpId(&tginfo[j].dobj);
8955 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8956 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8957 tginfo[j].tgtable = tbinfo;
8958 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8959 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8960 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8961 }
8962 }
8963
8964 PQclear(res);
8965
8966 destroyPQExpBuffer(query);
8968}
8969
8970/*
8971 * getEventTriggers
8972 * get information about event triggers
8973 */
8974void
8976{
8977 int i;
8978 PQExpBuffer query;
8979 PGresult *res;
8981 int i_tableoid,
8982 i_oid,
8983 i_evtname,
8984 i_evtevent,
8985 i_evtowner,
8986 i_evttags,
8987 i_evtfname,
8989 int ntups;
8990
8991 /* Before 9.3, there are no event triggers */
8992 if (fout->remoteVersion < 90300)
8993 return;
8994
8995 query = createPQExpBuffer();
8996
8998 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8999 "evtevent, evtowner, "
9000 "array_to_string(array("
9001 "select quote_literal(x) "
9002 " from unnest(evttags) as t(x)), ', ') as evttags, "
9003 "e.evtfoid::regproc as evtfname "
9004 "FROM pg_event_trigger e "
9005 "ORDER BY e.oid");
9006
9007 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9008
9009 ntups = PQntuples(res);
9010
9012
9013 i_tableoid = PQfnumber(res, "tableoid");
9014 i_oid = PQfnumber(res, "oid");
9015 i_evtname = PQfnumber(res, "evtname");
9016 i_evtevent = PQfnumber(res, "evtevent");
9017 i_evtowner = PQfnumber(res, "evtowner");
9018 i_evttags = PQfnumber(res, "evttags");
9019 i_evtfname = PQfnumber(res, "evtfname");
9020 i_evtenabled = PQfnumber(res, "evtenabled");
9021
9022 for (i = 0; i < ntups; i++)
9023 {
9024 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
9025 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9026 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9027 AssignDumpId(&evtinfo[i].dobj);
9028 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
9029 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
9030 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
9031 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
9032 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
9033 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
9034 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
9035
9036 /* Decide whether we want to dump it */
9038 }
9039
9040 PQclear(res);
9041
9042 destroyPQExpBuffer(query);
9043}
9044
9045/*
9046 * getProcLangs
9047 * get basic information about every procedural language in the system
9048 *
9049 * NB: this must run after getFuncs() because we assume we can do
9050 * findFuncByOid().
9051 */
9052void
9054{
9055 PGresult *res;
9056 int ntups;
9057 int i;
9060 int i_tableoid;
9061 int i_oid;
9062 int i_lanname;
9063 int i_lanpltrusted;
9064 int i_lanplcallfoid;
9065 int i_laninline;
9066 int i_lanvalidator;
9067 int i_lanacl;
9068 int i_acldefault;
9069 int i_lanowner;
9070
9071 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9072 "lanname, lanpltrusted, lanplcallfoid, "
9073 "laninline, lanvalidator, "
9074 "lanacl, "
9075 "acldefault('l', lanowner) AS acldefault, "
9076 "lanowner "
9077 "FROM pg_language "
9078 "WHERE lanispl "
9079 "ORDER BY oid");
9080
9081 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9082
9083 ntups = PQntuples(res);
9084
9086
9087 i_tableoid = PQfnumber(res, "tableoid");
9088 i_oid = PQfnumber(res, "oid");
9089 i_lanname = PQfnumber(res, "lanname");
9090 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
9091 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
9092 i_laninline = PQfnumber(res, "laninline");
9093 i_lanvalidator = PQfnumber(res, "lanvalidator");
9094 i_lanacl = PQfnumber(res, "lanacl");
9095 i_acldefault = PQfnumber(res, "acldefault");
9096 i_lanowner = PQfnumber(res, "lanowner");
9097
9098 for (i = 0; i < ntups; i++)
9099 {
9100 planginfo[i].dobj.objType = DO_PROCLANG;
9101 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9102 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9103 AssignDumpId(&planginfo[i].dobj);
9104
9105 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9106 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9107 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9108 planginfo[i].dacl.privtype = 0;
9109 planginfo[i].dacl.initprivs = NULL;
9110 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9111 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9112 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9113 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9114 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9115
9116 /* Decide whether we want to dump it */
9118
9119 /* Mark whether language has an ACL */
9120 if (!PQgetisnull(res, i, i_lanacl))
9121 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9122 }
9123
9124 PQclear(res);
9125
9126 destroyPQExpBuffer(query);
9127}
9128
9129/*
9130 * getCasts
9131 * get basic information about most casts in the system
9132 *
9133 * Skip casts from a range to its multirange, since we'll create those
9134 * automatically.
9135 */
9136void
9138{
9139 PGresult *res;
9140 int ntups;
9141 int i;
9144 int i_tableoid;
9145 int i_oid;
9146 int i_castsource;
9147 int i_casttarget;
9148 int i_castfunc;
9149 int i_castcontext;
9150 int i_castmethod;
9151
9152 if (fout->remoteVersion >= 140000)
9153 {
9154 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9155 "castsource, casttarget, castfunc, castcontext, "
9156 "castmethod "
9157 "FROM pg_cast c "
9158 "WHERE NOT EXISTS ( "
9159 "SELECT 1 FROM pg_range r "
9160 "WHERE c.castsource = r.rngtypid "
9161 "AND c.casttarget = r.rngmultitypid "
9162 ") "
9163 "ORDER BY 3,4");
9164 }
9165 else
9166 {
9167 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9168 "castsource, casttarget, castfunc, castcontext, "
9169 "castmethod "
9170 "FROM pg_cast ORDER BY 3,4");
9171 }
9172
9173 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9174
9175 ntups = PQntuples(res);
9176
9178
9179 i_tableoid = PQfnumber(res, "tableoid");
9180 i_oid = PQfnumber(res, "oid");
9181 i_castsource = PQfnumber(res, "castsource");
9182 i_casttarget = PQfnumber(res, "casttarget");
9183 i_castfunc = PQfnumber(res, "castfunc");
9184 i_castcontext = PQfnumber(res, "castcontext");
9185 i_castmethod = PQfnumber(res, "castmethod");
9186
9187 for (i = 0; i < ntups; i++)
9188 {
9192
9194 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9195 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9196 AssignDumpId(&castinfo[i].dobj);
9197 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9198 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9199 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9200 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9201 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9202
9203 /*
9204 * Try to name cast as concatenation of typnames. This is only used
9205 * for purposes of sorting. If we fail to find either type, the name
9206 * will be an empty string.
9207 */
9209 sTypeInfo = findTypeByOid(castinfo[i].castsource);
9210 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9211 if (sTypeInfo && tTypeInfo)
9212 appendPQExpBuffer(&namebuf, "%s %s",
9213 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9214 castinfo[i].dobj.name = namebuf.data;
9215
9216 /* Decide whether we want to dump it */
9218 }
9219
9220 PQclear(res);
9221
9222 destroyPQExpBuffer(query);
9223}
9224
9225static char *
9227{
9228 PQExpBuffer query;
9229 PGresult *res;
9230 char *lanname;
9231
9232 query = createPQExpBuffer();
9233 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9234 res = ExecuteSqlQueryForSingleRow(fout, query->data);
9235 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9236 destroyPQExpBuffer(query);
9237 PQclear(res);
9238
9239 return lanname;
9240}
9241
9242/*
9243 * getTransforms
9244 * get basic information about every transform in the system
9245 */
9246void
9248{
9249 PGresult *res;
9250 int ntups;
9251 int i;
9252 PQExpBuffer query;
9254 int i_tableoid;
9255 int i_oid;
9256 int i_trftype;
9257 int i_trflang;
9258 int i_trffromsql;
9259 int i_trftosql;
9260
9261 /* Transforms didn't exist pre-9.5 */
9262 if (fout->remoteVersion < 90500)
9263 return;
9264
9265 query = createPQExpBuffer();
9266
9267 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9268 "trftype, trflang, trffromsql::oid, trftosql::oid "
9269 "FROM pg_transform "
9270 "ORDER BY 3,4");
9271
9272 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9273
9274 ntups = PQntuples(res);
9275
9277
9278 i_tableoid = PQfnumber(res, "tableoid");
9279 i_oid = PQfnumber(res, "oid");
9280 i_trftype = PQfnumber(res, "trftype");
9281 i_trflang = PQfnumber(res, "trflang");
9282 i_trffromsql = PQfnumber(res, "trffromsql");
9283 i_trftosql = PQfnumber(res, "trftosql");
9284
9285 for (i = 0; i < ntups; i++)
9286 {
9289 char *lanname;
9290
9292 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9293 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9295 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9296 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9297 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9298 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9299
9300 /*
9301 * Try to name transform as concatenation of type and language name.
9302 * This is only used for purposes of sorting. If we fail to find
9303 * either, the name will be an empty string.
9304 */
9308 if (typeInfo && lanname)
9309 appendPQExpBuffer(&namebuf, "%s %s",
9310 typeInfo->dobj.name, lanname);
9311 transforminfo[i].dobj.name = namebuf.data;
9312 free(lanname);
9313
9314 /* Decide whether we want to dump it */
9316 }
9317
9318 PQclear(res);
9319
9320 destroyPQExpBuffer(query);
9321}
9322
9323/*
9324 * getTableAttrs -
9325 * for each interesting table, read info about its attributes
9326 * (names, types, default values, CHECK constraints, etc)
9327 *
9328 * modifies tblinfo
9329 */
9330void
9332{
9333 DumpOptions *dopt = fout->dopt;
9338 PGresult *res;
9339 int ntups;
9340 int curtblindx;
9341 int i_attrelid;
9342 int i_attnum;
9343 int i_attname;
9344 int i_atttypname;
9345 int i_attstattarget;
9346 int i_attstorage;
9347 int i_typstorage;
9348 int i_attidentity;
9349 int i_attgenerated;
9350 int i_attisdropped;
9351 int i_attlen;
9352 int i_attalign;
9353 int i_attislocal;
9354 int i_notnull_name;
9359 int i_attoptions;
9360 int i_attcollation;
9361 int i_attcompression;
9362 int i_attfdwoptions;
9363 int i_attmissingval;
9364 int i_atthasdef;
9365
9366 /*
9367 * We want to perform just one query against pg_attribute, and then just
9368 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9369 * (for CHECK constraints and for NOT NULL constraints). However, we
9370 * mustn't try to select every row of those catalogs and then sort it out
9371 * on the client side, because some of the server-side functions we need
9372 * would be unsafe to apply to tables we don't have lock on. Hence, we
9373 * build an array of the OIDs of tables we care about (and now have lock
9374 * on!), and use a WHERE clause to constrain which rows are selected.
9375 */
9378 for (int i = 0; i < numTables; i++)
9379 {
9380 TableInfo *tbinfo = &tblinfo[i];
9381
9382 /* Don't bother to collect info for sequences */
9383 if (tbinfo->relkind == RELKIND_SEQUENCE)
9384 continue;
9385
9386 /*
9387 * Don't bother with uninteresting tables, either. For binary
9388 * upgrades, this is bypassed for pg_largeobject_metadata and
9389 * pg_shdepend so that the columns names are collected for the
9390 * corresponding COPY commands. Restoring the data for those catalogs
9391 * is faster than restoring the equivalent set of large object
9392 * commands.
9393 */
9394 if (!tbinfo->interesting &&
9395 !(fout->dopt->binary_upgrade &&
9396 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9397 tbinfo->dobj.catId.oid == SharedDependRelationId)))
9398 continue;
9399
9400 /* OK, we need info for this table */
9401 if (tbloids->len > 1) /* do we have more than the '{'? */
9403 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9404
9405 if (tbinfo->ncheck > 0)
9406 {
9407 /* Also make a list of the ones with check constraints */
9408 if (checkoids->len > 1) /* do we have more than the '{'? */
9410 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9411 }
9412 }
9415
9416 /*
9417 * Find all the user attributes and their types.
9418 *
9419 * Since we only want to dump COLLATE clauses for attributes whose
9420 * collation is different from their type's default, we use a CASE here to
9421 * suppress uninteresting attcollations cheaply.
9422 */
9424 "SELECT\n"
9425 "a.attrelid,\n"
9426 "a.attnum,\n"
9427 "a.attname,\n"
9428 "a.attstattarget,\n"
9429 "a.attstorage,\n"
9430 "t.typstorage,\n"
9431 "a.atthasdef,\n"
9432 "a.attisdropped,\n"
9433 "a.attlen,\n"
9434 "a.attalign,\n"
9435 "a.attislocal,\n"
9436 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9437 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9438 "CASE WHEN a.attcollation <> t.typcollation "
9439 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9440 "pg_catalog.array_to_string(ARRAY("
9441 "SELECT pg_catalog.quote_ident(option_name) || "
9442 "' ' || pg_catalog.quote_literal(option_value) "
9443 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9444 "ORDER BY option_name"
9445 "), E',\n ') AS attfdwoptions,\n");
9446
9447 /*
9448 * Find out any NOT NULL markings for each column. In 18 and up we read
9449 * pg_constraint to obtain the constraint name, and for valid constraints
9450 * also pg_description to obtain its comment. notnull_noinherit is set
9451 * according to the NO INHERIT property. For versions prior to 18, we
9452 * store an empty string as the name when a constraint is marked as
9453 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9454 * without a name); also, such cases are never NO INHERIT.
9455 *
9456 * For invalid constraints, we need to store their OIDs for processing
9457 * elsewhere, so we bring the pg_constraint.oid value when the constraint
9458 * is invalid, and NULL otherwise. Their comments are handled not here
9459 * but by collectComments, because they're their own dumpable object.
9460 *
9461 * We track in notnull_islocal whether the constraint was defined directly
9462 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9463 * might modify this later.
9464 */
9465 if (fout->remoteVersion >= 180000)
9467 "co.conname AS notnull_name,\n"
9468 "CASE WHEN co.convalidated THEN pt.description"
9469 " ELSE NULL END AS notnull_comment,\n"
9470 "CASE WHEN NOT co.convalidated THEN co.oid "
9471 "ELSE NULL END AS notnull_invalidoid,\n"
9472 "co.connoinherit AS notnull_noinherit,\n"
9473 "co.conislocal AS notnull_islocal,\n");
9474 else
9476 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9477 "NULL AS notnull_comment,\n"
9478 "NULL AS notnull_invalidoid,\n"
9479 "false AS notnull_noinherit,\n"
9480 "CASE WHEN a.attislocal THEN true\n"
9481 " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9482 " ELSE false\n"
9483 "END AS notnull_islocal,\n");
9484
9485 if (fout->remoteVersion >= 140000)
9487 "a.attcompression AS attcompression,\n");
9488 else
9490 "'' AS attcompression,\n");
9491
9492 if (fout->remoteVersion >= 100000)
9494 "a.attidentity,\n");
9495 else
9497 "'' AS attidentity,\n");
9498
9499 if (fout->remoteVersion >= 110000)
9501 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9502 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9503 else
9505 "NULL AS attmissingval,\n");
9506
9507 if (fout->remoteVersion >= 120000)
9509 "a.attgenerated\n");
9510 else
9512 "'' AS attgenerated\n");
9513
9514 /* need left join to pg_type to not fail on dropped columns ... */
9516 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9517 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9518 "LEFT JOIN pg_catalog.pg_type t "
9519 "ON (a.atttypid = t.oid)\n",
9520 tbloids->data);
9521
9522 /*
9523 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9524 * entries and pg_description to get their comments.
9525 */
9526 if (fout->remoteVersion >= 180000)
9528 " LEFT JOIN pg_catalog.pg_constraint co ON "
9529 "(a.attrelid = co.conrelid\n"
9530 " AND co.contype = 'n' AND "
9531 "co.conkey = array[a.attnum])\n"
9532 " LEFT JOIN pg_catalog.pg_description pt ON "
9533 "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9534
9536 "WHERE a.attnum > 0::pg_catalog.int2\n");
9537
9538 /*
9539 * For binary upgrades from <v12, be sure to pick up
9540 * pg_largeobject_metadata's oid column.
9541 */
9542 if (fout->dopt->binary_upgrade && fout->remoteVersion < 120000)
9544 "OR (a.attnum = -2::pg_catalog.int2 AND src.tbloid = "
9546
9548 "ORDER BY a.attrelid, a.attnum");
9549
9551
9552 ntups = PQntuples(res);
9553
9554 i_attrelid = PQfnumber(res, "attrelid");
9555 i_attnum = PQfnumber(res, "attnum");
9556 i_attname = PQfnumber(res, "attname");
9557 i_atttypname = PQfnumber(res, "atttypname");
9558 i_attstattarget = PQfnumber(res, "attstattarget");
9559 i_attstorage = PQfnumber(res, "attstorage");
9560 i_typstorage = PQfnumber(res, "typstorage");
9561 i_attidentity = PQfnumber(res, "attidentity");
9562 i_attgenerated = PQfnumber(res, "attgenerated");
9563 i_attisdropped = PQfnumber(res, "attisdropped");
9564 i_attlen = PQfnumber(res, "attlen");
9565 i_attalign = PQfnumber(res, "attalign");
9566 i_attislocal = PQfnumber(res, "attislocal");
9567 i_notnull_name = PQfnumber(res, "notnull_name");
9568 i_notnull_comment = PQfnumber(res, "notnull_comment");
9569 i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9570 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9571 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9572 i_attoptions = PQfnumber(res, "attoptions");
9573 i_attcollation = PQfnumber(res, "attcollation");
9574 i_attcompression = PQfnumber(res, "attcompression");
9575 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9576 i_attmissingval = PQfnumber(res, "attmissingval");
9577 i_atthasdef = PQfnumber(res, "atthasdef");
9578
9579 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9582
9583 /*
9584 * Outer loop iterates once per table, not once per row. Incrementing of
9585 * r is handled by the inner loop.
9586 */
9587 curtblindx = -1;
9588 for (int r = 0; r < ntups;)
9589 {
9590 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9592 int numatts;
9593 bool hasdefaults;
9594
9595 /* Count rows for this table */
9596 for (numatts = 1; numatts < ntups - r; numatts++)
9597 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9598 break;
9599
9600 /*
9601 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9602 * order.
9603 */
9604 while (++curtblindx < numTables)
9605 {
9606 tbinfo = &tblinfo[curtblindx];
9607 if (tbinfo->dobj.catId.oid == attrelid)
9608 break;
9609 }
9610 if (curtblindx >= numTables)
9611 pg_fatal("unrecognized table OID %u", attrelid);
9612 /* cross-check that we only got requested tables */
9613 if (tbinfo->relkind == RELKIND_SEQUENCE ||
9614 (!tbinfo->interesting &&
9615 !(fout->dopt->binary_upgrade &&
9616 (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9617 tbinfo->dobj.catId.oid == SharedDependRelationId))))
9618 pg_fatal("unexpected column data for table \"%s\"",
9619 tbinfo->dobj.name);
9620
9621 /* Save data for this table */
9622 tbinfo->numatts = numatts;
9623 tbinfo->attnames = pg_malloc_array(char *, numatts);
9624 tbinfo->atttypnames = pg_malloc_array(char *, numatts);
9625 tbinfo->attstattarget = pg_malloc_array(int, numatts);
9626 tbinfo->attstorage = pg_malloc_array(char, numatts);
9627 tbinfo->typstorage = pg_malloc_array(char, numatts);
9628 tbinfo->attidentity = pg_malloc_array(char, numatts);
9629 tbinfo->attgenerated = pg_malloc_array(char, numatts);
9630 tbinfo->attisdropped = pg_malloc_array(bool, numatts);
9631 tbinfo->attlen = pg_malloc_array(int, numatts);
9632 tbinfo->attalign = pg_malloc_array(char, numatts);
9633 tbinfo->attislocal = pg_malloc_array(bool, numatts);
9634 tbinfo->attoptions = pg_malloc_array(char *, numatts);
9635 tbinfo->attcollation = pg_malloc_array(Oid, numatts);
9636 tbinfo->attcompression = pg_malloc_array(char, numatts);
9637 tbinfo->attfdwoptions = pg_malloc_array(char *, numatts);
9638 tbinfo->attmissingval = pg_malloc_array(char *, numatts);
9639 tbinfo->notnull_constrs = pg_malloc_array(char *, numatts);
9640 tbinfo->notnull_comment = pg_malloc_array(char *, numatts);
9641 tbinfo->notnull_invalid = pg_malloc_array(bool, numatts);
9642 tbinfo->notnull_noinh = pg_malloc_array(bool, numatts);
9643 tbinfo->notnull_islocal = pg_malloc_array(bool, numatts);
9644 tbinfo->attrdefs = pg_malloc_array(AttrDefInfo *, numatts);
9645 hasdefaults = false;
9646
9647 for (int j = 0; j < numatts; j++, r++)
9648 {
9649 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)) &&
9650 !(fout->dopt->binary_upgrade && fout->remoteVersion < 120000 &&
9651 tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId))
9652 pg_fatal("invalid column numbering in table \"%s\"",
9653 tbinfo->dobj.name);
9654 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9655 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9656 if (PQgetisnull(res, r, i_attstattarget))
9657 tbinfo->attstattarget[j] = -1;
9658 else
9659 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9660 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9661 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9662 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9663 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9664 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9665 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9666 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9667 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9668 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9669
9670 /* Handle not-null constraint name and flags */
9672 tbinfo, j,
9679
9680 tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9682 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9683 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9684 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9685 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9686 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9687 tbinfo->attrdefs[j] = NULL; /* fix below */
9688 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9689 hasdefaults = true;
9690 }
9691
9692 if (hasdefaults)
9693 {
9694 /* Collect OIDs of interesting tables that have defaults */
9695 if (tbloids->len > 1) /* do we have more than the '{'? */
9697 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9698 }
9699 }
9700
9701 /* If invalidnotnulloids has any data, finalize it */
9702 if (invalidnotnulloids != NULL)
9704
9705 PQclear(res);
9706
9707 /*
9708 * Now get info about column defaults. This is skipped for a data-only
9709 * dump, as it is only needed for table schemas.
9710 */
9711 if (dopt->dumpSchema && tbloids->len > 1)
9712 {
9713 AttrDefInfo *attrdefs;
9714 int numDefaults;
9716
9717 pg_log_info("finding table default expressions");
9718
9720
9721 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9722 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9723 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9724 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9725 "ORDER BY a.adrelid, a.adnum",
9726 tbloids->data);
9727
9729
9730 numDefaults = PQntuples(res);
9732
9733 curtblindx = -1;
9734 for (int j = 0; j < numDefaults; j++)
9735 {
9736 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9737 Oid adoid = atooid(PQgetvalue(res, j, 1));
9738 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9739 int adnum = atoi(PQgetvalue(res, j, 3));
9740 char *adsrc = PQgetvalue(res, j, 4);
9741
9742 /*
9743 * Locate the associated TableInfo; we rely on tblinfo[] being in
9744 * OID order.
9745 */
9746 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9747 {
9748 while (++curtblindx < numTables)
9749 {
9750 tbinfo = &tblinfo[curtblindx];
9751 if (tbinfo->dobj.catId.oid == adrelid)
9752 break;
9753 }
9754 if (curtblindx >= numTables)
9755 pg_fatal("unrecognized table OID %u", adrelid);
9756 }
9757
9758 if (adnum <= 0 || adnum > tbinfo->numatts)
9759 pg_fatal("invalid adnum value %d for table \"%s\"",
9760 adnum, tbinfo->dobj.name);
9761
9762 /*
9763 * dropped columns shouldn't have defaults, but just in case,
9764 * ignore 'em
9765 */
9766 if (tbinfo->attisdropped[adnum - 1])
9767 continue;
9768
9769 attrdefs[j].dobj.objType = DO_ATTRDEF;
9770 attrdefs[j].dobj.catId.tableoid = adtableoid;
9771 attrdefs[j].dobj.catId.oid = adoid;
9772 AssignDumpId(&attrdefs[j].dobj);
9773 attrdefs[j].adtable = tbinfo;
9774 attrdefs[j].adnum = adnum;
9775 attrdefs[j].adef_expr = pg_strdup(adsrc);
9776
9777 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9778 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9779
9780 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9781
9782 /*
9783 * Figure out whether the default/generation expression should be
9784 * dumped as part of the main CREATE TABLE (or similar) command or
9785 * as a separate ALTER TABLE (or similar) command. The preference
9786 * is to put it into the CREATE command, but in some cases that's
9787 * not possible.
9788 */
9789 if (tbinfo->attgenerated[adnum - 1])
9790 {
9791 /*
9792 * Column generation expressions cannot be dumped separately,
9793 * because there is no syntax for it. By setting separate to
9794 * false here we prevent the "default" from being processed as
9795 * its own dumpable object. Later, flagInhAttrs() will mark
9796 * it as not to be dumped at all, if possible (that is, if it
9797 * can be inherited from a parent).
9798 */
9799 attrdefs[j].separate = false;
9800 }
9801 else if (tbinfo->relkind == RELKIND_VIEW)
9802 {
9803 /*
9804 * Defaults on a VIEW must always be dumped as separate ALTER
9805 * TABLE commands.
9806 */
9807 attrdefs[j].separate = true;
9808 }
9809 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9810 {
9811 /* column will be suppressed, print default separately */
9812 attrdefs[j].separate = true;
9813 }
9814 else
9815 {
9816 attrdefs[j].separate = false;
9817 }
9818
9819 if (!attrdefs[j].separate)
9820 {
9821 /*
9822 * Mark the default as needing to appear before the table, so
9823 * that any dependencies it has must be emitted before the
9824 * CREATE TABLE. If this is not possible, we'll change to
9825 * "separate" mode while sorting dependencies.
9826 */
9828 attrdefs[j].dobj.dumpId);
9829 }
9830
9831 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9832 }
9833
9834 PQclear(res);
9835 }
9836
9837 /*
9838 * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9839 * data-only dump, as it is only needed for table schemas.
9840 */
9841 if (dopt->dumpSchema && invalidnotnulloids)
9842 {
9844 int numConstrs;
9845 int i_tableoid;
9846 int i_oid;
9847 int i_conrelid;
9848 int i_conname;
9849 int i_consrc;
9850 int i_conislocal;
9851
9852 pg_log_info("finding invalid not-null constraints");
9853
9856 "SELECT c.tableoid, c.oid, conrelid, conname, "
9857 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9858 "conislocal, convalidated "
9859 "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9860 "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9861 "ORDER BY c.conrelid, c.conname",
9863
9865
9866 numConstrs = PQntuples(res);
9868
9869 i_tableoid = PQfnumber(res, "tableoid");
9870 i_oid = PQfnumber(res, "oid");
9871 i_conrelid = PQfnumber(res, "conrelid");
9872 i_conname = PQfnumber(res, "conname");
9873 i_consrc = PQfnumber(res, "consrc");
9874 i_conislocal = PQfnumber(res, "conislocal");
9875
9876 /* As above, this loop iterates once per table, not once per row */
9877 curtblindx = -1;
9878 for (int j = 0; j < numConstrs;)
9879 {
9880 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9882 int numcons;
9883
9884 /* Count rows for this table */
9885 for (numcons = 1; numcons < numConstrs - j; numcons++)
9886 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9887 break;
9888
9889 /*
9890 * Locate the associated TableInfo; we rely on tblinfo[] being in
9891 * OID order.
9892 */
9893 while (++curtblindx < numTables)
9894 {
9895 tbinfo = &tblinfo[curtblindx];
9896 if (tbinfo->dobj.catId.oid == conrelid)
9897 break;
9898 }
9899 if (curtblindx >= numTables)
9900 pg_fatal("unrecognized table OID %u", conrelid);
9901
9902 for (int c = 0; c < numcons; c++, j++)
9903 {
9904 constrs[j].dobj.objType = DO_CONSTRAINT;
9905 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9906 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9907 AssignDumpId(&constrs[j].dobj);
9908 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9909 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9910 constrs[j].contable = tbinfo;
9911 constrs[j].condomain = NULL;
9912 constrs[j].contype = 'n';
9913 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9914 constrs[j].confrelid = InvalidOid;
9915 constrs[j].conindex = 0;
9916 constrs[j].condeferrable = false;
9917 constrs[j].condeferred = false;
9918 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9919
9920 /*
9921 * All invalid not-null constraints must be dumped separately,
9922 * because CREATE TABLE would not create them as invalid, and
9923 * also because they must be created after potentially
9924 * violating data has been loaded.
9925 */
9926 constrs[j].separate = true;
9927
9928 constrs[j].dobj.dump = tbinfo->dobj.dump;
9929 }
9930 }
9931 PQclear(res);
9932 }
9933
9934 /*
9935 * Get info about table CHECK constraints. This is skipped for a
9936 * data-only dump, as it is only needed for table schemas.
9937 */
9938 if (dopt->dumpSchema && checkoids->len > 2)
9939 {
9941 int numConstrs;
9942 int i_tableoid;
9943 int i_oid;
9944 int i_conrelid;
9945 int i_conname;
9946 int i_consrc;
9947 int i_conislocal;
9948 int i_convalidated;
9949
9950 pg_log_info("finding table check constraints");
9951
9954 "SELECT c.tableoid, c.oid, conrelid, conname, "
9955 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9956 "conislocal, convalidated "
9957 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9958 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9959 "WHERE contype = 'c' "
9960 "ORDER BY c.conrelid, c.conname",
9961 checkoids->data);
9962
9964
9965 numConstrs = PQntuples(res);
9967
9968 i_tableoid = PQfnumber(res, "tableoid");
9969 i_oid = PQfnumber(res, "oid");
9970 i_conrelid = PQfnumber(res, "conrelid");
9971 i_conname = PQfnumber(res, "conname");
9972 i_consrc = PQfnumber(res, "consrc");
9973 i_conislocal = PQfnumber(res, "conislocal");
9974 i_convalidated = PQfnumber(res, "convalidated");
9975
9976 /* As above, this loop iterates once per table, not once per row */
9977 curtblindx = -1;
9978 for (int j = 0; j < numConstrs;)
9979 {
9980 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9982 int numcons;
9983
9984 /* Count rows for this table */
9985 for (numcons = 1; numcons < numConstrs - j; numcons++)
9986 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9987 break;
9988
9989 /*
9990 * Locate the associated TableInfo; we rely on tblinfo[] being in
9991 * OID order.
9992 */
9993 while (++curtblindx < numTables)
9994 {
9995 tbinfo = &tblinfo[curtblindx];
9996 if (tbinfo->dobj.catId.oid == conrelid)
9997 break;
9998 }
9999 if (curtblindx >= numTables)
10000 pg_fatal("unrecognized table OID %u", conrelid);
10001
10002 if (numcons != tbinfo->ncheck)
10003 {
10004 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
10005 "expected %d check constraints on table \"%s\" but found %d",
10006 tbinfo->ncheck),
10007 tbinfo->ncheck, tbinfo->dobj.name, numcons);
10008 pg_log_error_hint("The system catalogs might be corrupted.");
10009 exit_nicely(1);
10010 }
10011
10012 tbinfo->checkexprs = constrs + j;
10013
10014 for (int c = 0; c < numcons; c++, j++)
10015 {
10016 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
10017
10018 constrs[j].dobj.objType = DO_CONSTRAINT;
10019 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
10020 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
10021 AssignDumpId(&constrs[j].dobj);
10022 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
10023 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
10024 constrs[j].contable = tbinfo;
10025 constrs[j].condomain = NULL;
10026 constrs[j].contype = 'c';
10027 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
10028 constrs[j].confrelid = InvalidOid;
10029 constrs[j].conindex = 0;
10030 constrs[j].condeferrable = false;
10031 constrs[j].condeferred = false;
10032 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
10033
10034 /*
10035 * An unvalidated constraint needs to be dumped separately, so
10036 * that potentially-violating existing data is loaded before
10037 * the constraint.
10038 */
10039 constrs[j].separate = !validated;
10040
10041 constrs[j].dobj.dump = tbinfo->dobj.dump;
10042
10043 /*
10044 * Mark the constraint as needing to appear before the table
10045 * --- this is so that any other dependencies of the
10046 * constraint will be emitted before we try to create the
10047 * table. If the constraint is to be dumped separately, it
10048 * will be dumped after data is loaded anyway, so don't do it.
10049 * (There's an automatic dependency in the opposite direction
10050 * anyway, so don't need to add one manually here.)
10051 */
10052 if (!constrs[j].separate)
10054 constrs[j].dobj.dumpId);
10055
10056 /*
10057 * We will detect later whether the constraint must be split
10058 * out from the table definition.
10059 */
10060 }
10061 }
10062
10063 PQclear(res);
10064 }
10065
10069}
10070
10071/*
10072 * Based on the getTableAttrs query's row corresponding to one column, set
10073 * the name and flags to handle a not-null constraint for that column in
10074 * the tbinfo struct.
10075 *
10076 * Result row 'r' is for tbinfo's attribute 'j'.
10077 *
10078 * There are four possibilities:
10079 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
10080 * (the constraint name) remains NULL.
10081 * 2) The column has a constraint with no name (this is the case when
10082 * constraints come from pre-18 servers). In this case, ->notnull_constrs
10083 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
10084 * 3) The column has an invalid not-null constraint. This must be treated
10085 * as a separate object (because it must be created after the table data
10086 * is loaded). So we add its OID to invalidnotnulloids for processing
10087 * elsewhere and do nothing further with it here. We distinguish this
10088 * case because the "notnull_invalidoid" column has been set to a non-NULL
10089 * value, which is the constraint OID. Valid constraints have a null OID.
10090 * 4) The column has a constraint with a known name; in that case
10091 * notnull_constrs carries that name and dumpTableSchema will print
10092 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
10093 * (table_column_not_null) and there's no comment on the constraint,
10094 * there's no need to print that name in the dump, so notnull_constrs
10095 * is set to the empty string and it behaves as case 2.
10096 *
10097 * In a child table that inherits from a parent already containing NOT NULL
10098 * constraints and the columns in the child don't have their own NOT NULL
10099 * declarations, we suppress printing constraints in the child: the
10100 * constraints are acquired at the point where the child is attached to the
10101 * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
10102 * set not here but in flagInhAttrs. That flag is also used when the
10103 * constraint was validated in a child but all its parent have it as NOT
10104 * VALID.
10105 *
10106 * Any of these constraints might have the NO INHERIT bit. If so we set
10107 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10108 *
10109 * In case 4 above, the name comparison is a bit of a hack; it actually fails
10110 * to do the right thing in all but the trivial case. However, the downside
10111 * of getting it wrong is simply that the name is printed rather than
10112 * suppressed, so it's not a big deal.
10113 *
10114 * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10115 * constraints are found, it is initialized and filled with the array of
10116 * OIDs of such constraints, for later processing.
10117 */
10118static void
10120 TableInfo *tbinfo, int j,
10121 int i_notnull_name,
10127{
10128 DumpOptions *dopt = fout->dopt;
10129
10130 /*
10131 * If this not-null constraint is not valid, list its OID in
10132 * invalidnotnulloids and do nothing further. It'll be processed
10133 * elsewhere later.
10134 *
10135 * Because invalid not-null constraints are rare, we don't want to malloc
10136 * invalidnotnulloids until we're sure we're going it need it, which
10137 * happens here.
10138 */
10139 if (!PQgetisnull(res, r, i_notnull_invalidoid))
10140 {
10141 char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10142
10143 if (*invalidnotnulloids == NULL)
10144 {
10148 }
10149 else
10151
10152 /*
10153 * Track when a parent constraint is invalid for the cases where a
10154 * child constraint has been validated independenly.
10155 */
10156 tbinfo->notnull_invalid[j] = true;
10157
10158 /* nothing else to do */
10159 tbinfo->notnull_constrs[j] = NULL;
10160 return;
10161 }
10162
10163 /*
10164 * notnull_noinh is straight from the query result. notnull_islocal also,
10165 * though flagInhAttrs may change that one later.
10166 */
10167 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10168 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10169 tbinfo->notnull_invalid[j] = false;
10170
10171 /*
10172 * Determine a constraint name to use. If the column is not marked not-
10173 * null, we set NULL which cues ... to do nothing. An empty string says
10174 * to print an unnamed NOT NULL, and anything else is a constraint name to
10175 * use.
10176 */
10177 if (fout->remoteVersion < 180000)
10178 {
10179 /*
10180 * < 18 doesn't have not-null names, so an unnamed constraint is
10181 * sufficient.
10182 */
10183 if (PQgetisnull(res, r, i_notnull_name))
10184 tbinfo->notnull_constrs[j] = NULL;
10185 else
10186 tbinfo->notnull_constrs[j] = "";
10187 }
10188 else
10189 {
10190 if (PQgetisnull(res, r, i_notnull_name))
10191 tbinfo->notnull_constrs[j] = NULL;
10192 else
10193 {
10194 /*
10195 * In binary upgrade of inheritance child tables, must have a
10196 * constraint name that we can UPDATE later; same if there's a
10197 * comment on the constraint.
10198 */
10199 if ((dopt->binary_upgrade &&
10200 !tbinfo->ispartition &&
10201 !tbinfo->notnull_islocal[j]) ||
10203 {
10204 tbinfo->notnull_constrs[j] =
10206 }
10207 else
10208 {
10209 char *default_name;
10210
10211 /* XXX should match ChooseConstraintName better */
10212 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10213 tbinfo->attnames[j]);
10214 if (strcmp(default_name,
10215 PQgetvalue(res, r, i_notnull_name)) == 0)
10216 tbinfo->notnull_constrs[j] = "";
10217 else
10218 {
10219 tbinfo->notnull_constrs[j] =
10221 }
10223 }
10224 }
10225 }
10226}
10227
10228/*
10229 * Test whether a column should be printed as part of table's CREATE TABLE.
10230 * Column number is zero-based.
10231 *
10232 * Normally this is always true, but it's false for dropped columns, as well
10233 * as those that were inherited without any local definition. (If we print
10234 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10235 * For partitions, it's always true, because we want the partitions to be
10236 * created independently and ATTACH PARTITION used afterwards.
10237 *
10238 * In binary_upgrade mode, we must print all columns and fix the attislocal/
10239 * attisdropped state later, so as to keep control of the physical column
10240 * order.
10241 *
10242 * This function exists because there are scattered nonobvious places that
10243 * must be kept in sync with this decision.
10244 */
10245bool
10246shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10247{
10248 if (dopt->binary_upgrade)
10249 return true;
10250 if (tbinfo->attisdropped[colno])
10251 return false;
10252 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10253}
10254
10255
10256/*
10257 * getTSParsers:
10258 * get information about all text search parsers in the system catalogs
10259 */
10260void
10262{
10263 PGresult *res;
10264 int ntups;
10265 int i;
10266 PQExpBuffer query;
10268 int i_tableoid;
10269 int i_oid;
10270 int i_prsname;
10271 int i_prsnamespace;
10272 int i_prsstart;
10273 int i_prstoken;
10274 int i_prsend;
10275 int i_prsheadline;
10276 int i_prslextype;
10277
10278 query = createPQExpBuffer();
10279
10280 /*
10281 * find all text search objects, including builtin ones; we filter out
10282 * system-defined objects at dump-out time.
10283 */
10284
10285 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10286 "prsstart::oid, prstoken::oid, "
10287 "prsend::oid, prsheadline::oid, prslextype::oid "
10288 "FROM pg_ts_parser");
10289
10290 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10291
10292 ntups = PQntuples(res);
10293
10295
10296 i_tableoid = PQfnumber(res, "tableoid");
10297 i_oid = PQfnumber(res, "oid");
10298 i_prsname = PQfnumber(res, "prsname");
10299 i_prsnamespace = PQfnumber(res, "prsnamespace");
10300 i_prsstart = PQfnumber(res, "prsstart");
10301 i_prstoken = PQfnumber(res, "prstoken");
10302 i_prsend = PQfnumber(res, "prsend");
10303 i_prsheadline = PQfnumber(res, "prsheadline");
10304 i_prslextype = PQfnumber(res, "prslextype");
10305
10306 for (i = 0; i < ntups; i++)
10307 {
10308 prsinfo[i].dobj.objType = DO_TSPARSER;
10309 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10310 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10311 AssignDumpId(&prsinfo[i].dobj);
10312 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10313 prsinfo[i].dobj.namespace =
10315 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10316 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10317 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10318 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10319 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10320
10321 /* Decide whether we want to dump it */
10323 }
10324
10325 PQclear(res);
10326
10327 destroyPQExpBuffer(query);
10328}
10329
10330/*
10331 * getTSDictionaries:
10332 * get information about all text search dictionaries in the system catalogs
10333 */
10334void
10336{
10337 PGresult *res;
10338 int ntups;
10339 int i;
10340 PQExpBuffer query;
10342 int i_tableoid;
10343 int i_oid;
10344 int i_dictname;
10345 int i_dictnamespace;
10346 int i_dictowner;
10347 int i_dicttemplate;
10348 int i_dictinitoption;
10349
10350 query = createPQExpBuffer();
10351
10352 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10353 "dictnamespace, dictowner, "
10354 "dicttemplate, dictinitoption "
10355 "FROM pg_ts_dict");
10356
10357 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10358
10359 ntups = PQntuples(res);
10360
10362
10363 i_tableoid = PQfnumber(res, "tableoid");
10364 i_oid = PQfnumber(res, "oid");
10365 i_dictname = PQfnumber(res, "dictname");
10366 i_dictnamespace = PQfnumber(res, "dictnamespace");
10367 i_dictowner = PQfnumber(res, "dictowner");
10368 i_dictinitoption = PQfnumber(res, "dictinitoption");
10369 i_dicttemplate = PQfnumber(res, "dicttemplate");
10370
10371 for (i = 0; i < ntups; i++)
10372 {
10373 dictinfo[i].dobj.objType = DO_TSDICT;
10374 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10375 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10376 AssignDumpId(&dictinfo[i].dobj);
10377 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10378 dictinfo[i].dobj.namespace =
10380 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10381 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10382 if (PQgetisnull(res, i, i_dictinitoption))
10383 dictinfo[i].dictinitoption = NULL;
10384 else
10385 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10386
10387 /* Decide whether we want to dump it */
10389 }
10390
10391 PQclear(res);
10392
10393 destroyPQExpBuffer(query);
10394}
10395
10396/*
10397 * getTSTemplates:
10398 * get information about all text search templates in the system catalogs
10399 */
10400void
10402{
10403 PGresult *res;
10404 int ntups;
10405 int i;
10406 PQExpBuffer query;
10408 int i_tableoid;
10409 int i_oid;
10410 int i_tmplname;
10411 int i_tmplnamespace;
10412 int i_tmplinit;
10413 int i_tmpllexize;
10414
10415 query = createPQExpBuffer();
10416
10417 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10418 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10419 "FROM pg_ts_template");
10420
10421 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10422
10423 ntups = PQntuples(res);
10424
10426
10427 i_tableoid = PQfnumber(res, "tableoid");
10428 i_oid = PQfnumber(res, "oid");
10429 i_tmplname = PQfnumber(res, "tmplname");
10430 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10431 i_tmplinit = PQfnumber(res, "tmplinit");
10432 i_tmpllexize = PQfnumber(res, "tmpllexize");
10433
10434 for (i = 0; i < ntups; i++)
10435 {
10436 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10437 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10438 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10439 AssignDumpId(&tmplinfo[i].dobj);
10440 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10441 tmplinfo[i].dobj.namespace =
10443 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10444 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10445
10446 /* Decide whether we want to dump it */
10448 }
10449
10450 PQclear(res);
10451
10452 destroyPQExpBuffer(query);
10453}
10454
10455/*
10456 * getTSConfigurations:
10457 * get information about all text search configurations
10458 */
10459void
10461{
10462 PGresult *res;
10463 int ntups;
10464 int i;
10465 PQExpBuffer query;
10467 int i_tableoid;
10468 int i_oid;
10469 int i_cfgname;
10470 int i_cfgnamespace;
10471 int i_cfgowner;
10472 int i_cfgparser;
10473
10474 query = createPQExpBuffer();
10475
10476 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10477 "cfgnamespace, cfgowner, cfgparser "
10478 "FROM pg_ts_config");
10479
10480 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10481
10482 ntups = PQntuples(res);
10483
10485
10486 i_tableoid = PQfnumber(res, "tableoid");
10487 i_oid = PQfnumber(res, "oid");
10488 i_cfgname = PQfnumber(res, "cfgname");
10489 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10490 i_cfgowner = PQfnumber(res, "cfgowner");
10491 i_cfgparser = PQfnumber(res, "cfgparser");
10492
10493 for (i = 0; i < ntups; i++)
10494 {
10495 cfginfo[i].dobj.objType = DO_TSCONFIG;
10496 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10497 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10498 AssignDumpId(&cfginfo[i].dobj);
10499 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10500 cfginfo[i].dobj.namespace =
10502 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10503 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10504
10505 /* Decide whether we want to dump it */
10507 }
10508
10509 PQclear(res);
10510
10511 destroyPQExpBuffer(query);
10512}
10513
10514/*
10515 * getForeignDataWrappers:
10516 * get information about all foreign-data wrappers in the system catalogs
10517 */
10518void
10520{
10521 PGresult *res;
10522 int ntups;
10523 int i;
10524 PQExpBuffer query;
10526 int i_tableoid;
10527 int i_oid;
10528 int i_fdwname;
10529 int i_fdwowner;
10530 int i_fdwhandler;
10531 int i_fdwvalidator;
10532 int i_fdwconnection;
10533 int i_fdwacl;
10534 int i_acldefault;
10535 int i_fdwoptions;
10536
10537 query = createPQExpBuffer();
10538
10539 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10540 "fdwowner, "
10541 "fdwhandler::pg_catalog.regproc, "
10542 "fdwvalidator::pg_catalog.regproc, ");
10543
10544 if (fout->remoteVersion >= 190000)
10545 appendPQExpBufferStr(query, "fdwconnection::pg_catalog.regproc, ");
10546 else
10547 appendPQExpBufferStr(query, "'-' AS fdwconnection, ");
10548
10550 "fdwacl, "
10551 "acldefault('F', fdwowner) AS acldefault, "
10552 "array_to_string(ARRAY("
10553 "SELECT quote_ident(option_name) || ' ' || "
10554 "quote_literal(option_value) "
10555 "FROM pg_options_to_table(fdwoptions) "
10556 "ORDER BY option_name"
10557 "), E',\n ') AS fdwoptions "
10558 "FROM pg_foreign_data_wrapper");
10559
10560 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10561
10562 ntups = PQntuples(res);
10563
10565
10566 i_tableoid = PQfnumber(res, "tableoid");
10567 i_oid = PQfnumber(res, "oid");
10568 i_fdwname = PQfnumber(res, "fdwname");
10569 i_fdwowner = PQfnumber(res, "fdwowner");
10570 i_fdwhandler = PQfnumber(res, "fdwhandler");
10571 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10572 i_fdwconnection = PQfnumber(res, "fdwconnection");
10573 i_fdwacl = PQfnumber(res, "fdwacl");
10574 i_acldefault = PQfnumber(res, "acldefault");
10575 i_fdwoptions = PQfnumber(res, "fdwoptions");
10576
10577 for (i = 0; i < ntups; i++)
10578 {
10579 fdwinfo[i].dobj.objType = DO_FDW;
10580 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10581 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10582 AssignDumpId(&fdwinfo[i].dobj);
10583 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10584 fdwinfo[i].dobj.namespace = NULL;
10585 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10586 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10587 fdwinfo[i].dacl.privtype = 0;
10588 fdwinfo[i].dacl.initprivs = NULL;
10589 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10590 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10591 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10592 fdwinfo[i].fdwconnection = pg_strdup(PQgetvalue(res, i, i_fdwconnection));
10593 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10594
10595 /* Decide whether we want to dump it */
10597
10598 /* Mark whether FDW has an ACL */
10599 if (!PQgetisnull(res, i, i_fdwacl))
10600 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10601 }
10602
10603 PQclear(res);
10604
10605 destroyPQExpBuffer(query);
10606}
10607
10608/*
10609 * getForeignServers:
10610 * get information about all foreign servers in the system catalogs
10611 */
10612void
10614{
10615 PGresult *res;
10616 int ntups;
10617 int i;
10618 PQExpBuffer query;
10620 int i_tableoid;
10621 int i_oid;
10622 int i_srvname;
10623 int i_srvowner;
10624 int i_srvfdw;
10625 int i_srvtype;
10626 int i_srvversion;
10627 int i_srvacl;
10628 int i_acldefault;
10629 int i_srvoptions;
10630
10631 query = createPQExpBuffer();
10632
10633 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10634 "srvowner, "
10635 "srvfdw, srvtype, srvversion, srvacl, "
10636 "acldefault('S', srvowner) AS acldefault, "
10637 "array_to_string(ARRAY("
10638 "SELECT quote_ident(option_name) || ' ' || "
10639 "quote_literal(option_value) "
10640 "FROM pg_options_to_table(srvoptions) "
10641 "ORDER BY option_name"
10642 "), E',\n ') AS srvoptions "
10643 "FROM pg_foreign_server");
10644
10645 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10646
10647 ntups = PQntuples(res);
10648
10650
10651 i_tableoid = PQfnumber(res, "tableoid");
10652 i_oid = PQfnumber(res, "oid");
10653 i_srvname = PQfnumber(res, "srvname");
10654 i_srvowner = PQfnumber(res, "srvowner");
10655 i_srvfdw = PQfnumber(res, "srvfdw");
10656 i_srvtype = PQfnumber(res, "srvtype");
10657 i_srvversion = PQfnumber(res, "srvversion");
10658 i_srvacl = PQfnumber(res, "srvacl");
10659 i_acldefault = PQfnumber(res, "acldefault");
10660 i_srvoptions = PQfnumber(res, "srvoptions");
10661
10662 for (i = 0; i < ntups; i++)
10663 {
10664 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10665 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10666 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10667 AssignDumpId(&srvinfo[i].dobj);
10668 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10669 srvinfo[i].dobj.namespace = NULL;
10670 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10671 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10672 srvinfo[i].dacl.privtype = 0;
10673 srvinfo[i].dacl.initprivs = NULL;
10674 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10675 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10676 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10677 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10678 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10679
10680 /* Decide whether we want to dump it */
10682
10683 /* Servers have user mappings */
10684 srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10685
10686 /* Mark whether server has an ACL */
10687 if (!PQgetisnull(res, i, i_srvacl))
10688 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10689 }
10690
10691 PQclear(res);
10692
10693 destroyPQExpBuffer(query);
10694}
10695
10696/*
10697 * getDefaultACLs:
10698 * get information about all default ACL information in the system catalogs
10699 */
10700void
10702{
10703 DumpOptions *dopt = fout->dopt;
10705 PQExpBuffer query;
10706 PGresult *res;
10707 int i_oid;
10708 int i_tableoid;
10709 int i_defaclrole;
10711 int i_defaclobjtype;
10712 int i_defaclacl;
10713 int i_acldefault;
10714 int i,
10715 ntups;
10716
10717 query = createPQExpBuffer();
10718
10719 /*
10720 * Global entries (with defaclnamespace=0) replace the hard-wired default
10721 * ACL for their object type. We should dump them as deltas from the
10722 * default ACL, since that will be used as a starting point for
10723 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10724 * non-global entries can only add privileges not revoke them. We must
10725 * dump those as-is (i.e., as deltas from an empty ACL).
10726 *
10727 * We can use defaclobjtype as the object type for acldefault(), except
10728 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10729 * 's'.
10730 */
10732 "SELECT oid, tableoid, "
10733 "defaclrole, "
10734 "defaclnamespace, "
10735 "defaclobjtype, "
10736 "defaclacl, "
10737 "CASE WHEN defaclnamespace = 0 THEN "
10738 "acldefault(CASE WHEN defaclobjtype = 'S' "
10739 "THEN 's'::\"char\" ELSE defaclobjtype END, "
10740 "defaclrole) ELSE '{}' END AS acldefault "
10741 "FROM pg_default_acl");
10742
10743 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10744
10745 ntups = PQntuples(res);
10746
10748
10749 i_oid = PQfnumber(res, "oid");
10750 i_tableoid = PQfnumber(res, "tableoid");
10751 i_defaclrole = PQfnumber(res, "defaclrole");
10752 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10753 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10754 i_defaclacl = PQfnumber(res, "defaclacl");
10755 i_acldefault = PQfnumber(res, "acldefault");
10756
10757 for (i = 0; i < ntups; i++)
10758 {
10760
10761 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10762 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10763 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10764 AssignDumpId(&daclinfo[i].dobj);
10765 /* cheesy ... is it worth coming up with a better object name? */
10766 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10767
10768 if (nspid != InvalidOid)
10769 daclinfo[i].dobj.namespace = findNamespace(nspid);
10770 else
10771 daclinfo[i].dobj.namespace = NULL;
10772
10773 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10774 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10775 daclinfo[i].dacl.privtype = 0;
10776 daclinfo[i].dacl.initprivs = NULL;
10777 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10778 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10779
10780 /* Default ACLs are ACLs, of course */
10781 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10782
10783 /* Decide whether we want to dump it */
10785 }
10786
10787 PQclear(res);
10788
10789 destroyPQExpBuffer(query);
10790}
10791
10792/*
10793 * getRoleName -- look up the name of a role, given its OID
10794 *
10795 * In current usage, we don't expect failures, so error out for a bad OID.
10796 */
10797static const char *
10799{
10800 Oid roleoid = atooid(roleoid_str);
10801
10802 /*
10803 * Do binary search to find the appropriate item.
10804 */
10805 if (nrolenames > 0)
10806 {
10807 RoleNameItem *low = &rolenames[0];
10808 RoleNameItem *high = &rolenames[nrolenames - 1];
10809
10810 while (low <= high)
10811 {
10812 RoleNameItem *middle = low + (high - low) / 2;
10813
10814 if (roleoid < middle->roleoid)
10815 high = middle - 1;
10816 else if (roleoid > middle->roleoid)
10817 low = middle + 1;
10818 else
10819 return middle->rolename; /* found a match */
10820 }
10821 }
10822
10823 pg_fatal("role with OID %u does not exist", roleoid);
10824 return NULL; /* keep compiler quiet */
10825}
10826
10827/*
10828 * collectRoleNames --
10829 *
10830 * Construct a table of all known roles.
10831 * The table is sorted by OID for speed in lookup.
10832 */
10833static void
10835{
10836 PGresult *res;
10837 const char *query;
10838 int i;
10839
10840 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10841
10842 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10843
10844 nrolenames = PQntuples(res);
10845
10847
10848 for (i = 0; i < nrolenames; i++)
10849 {
10850 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10852 }
10853
10854 PQclear(res);
10855}
10856
10857/*
10858 * getAdditionalACLs
10859 *
10860 * We have now created all the DumpableObjects, and collected the ACL data
10861 * that appears in the directly-associated catalog entries. However, there's
10862 * more ACL-related info to collect. If any of a table's columns have ACLs,
10863 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10864 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10865 * Also, in versions having the pg_init_privs catalog, read that and load the
10866 * information into the relevant DumpableObjects.
10867 */
10868static void
10870{
10872 PGresult *res;
10873 int ntups,
10874 i;
10875
10876 /* Check for per-column ACLs */
10878 "SELECT DISTINCT attrelid FROM pg_attribute "
10879 "WHERE attacl IS NOT NULL");
10880
10881 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10882
10883 ntups = PQntuples(res);
10884 for (i = 0; i < ntups; i++)
10885 {
10886 Oid relid = atooid(PQgetvalue(res, i, 0));
10887 TableInfo *tblinfo;
10888
10889 tblinfo = findTableByOid(relid);
10890 /* OK to ignore tables we haven't got a DumpableObject for */
10891 if (tblinfo)
10892 {
10894 tblinfo->hascolumnACLs = true;
10895 }
10896 }
10897 PQclear(res);
10898
10899 /* Fetch initial-privileges data */
10900 if (fout->remoteVersion >= 90600)
10901 {
10902 printfPQExpBuffer(query,
10903 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10904 "FROM pg_init_privs");
10905
10906 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10907
10908 ntups = PQntuples(res);
10909 for (i = 0; i < ntups; i++)
10910 {
10911 Oid objoid = atooid(PQgetvalue(res, i, 0));
10912 Oid classoid = atooid(PQgetvalue(res, i, 1));
10913 int objsubid = atoi(PQgetvalue(res, i, 2));
10914 char privtype = *(PQgetvalue(res, i, 3));
10915 char *initprivs = PQgetvalue(res, i, 4);
10916 CatalogId objId;
10917 DumpableObject *dobj;
10918
10919 objId.tableoid = classoid;
10920 objId.oid = objoid;
10921 dobj = findObjectByCatalogId(objId);
10922 /* OK to ignore entries we haven't got a DumpableObject for */
10923 if (dobj)
10924 {
10925 /* Cope with sub-object initprivs */
10926 if (objsubid != 0)
10927 {
10928 if (dobj->objType == DO_TABLE)
10929 {
10930 /* For a column initprivs, set the table's ACL flags */
10932 ((TableInfo *) dobj)->hascolumnACLs = true;
10933 }
10934 else
10935 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10936 classoid, objoid, objsubid);
10937 continue;
10938 }
10939
10940 /*
10941 * We ignore any pg_init_privs.initprivs entry for the public
10942 * schema, as explained in getNamespaces().
10943 */
10944 if (dobj->objType == DO_NAMESPACE &&
10945 strcmp(dobj->name, "public") == 0)
10946 continue;
10947
10948 /* Else it had better be of a type we think has ACLs */
10949 if (dobj->objType == DO_NAMESPACE ||
10950 dobj->objType == DO_TYPE ||
10951 dobj->objType == DO_FUNC ||
10952 dobj->objType == DO_AGG ||
10953 dobj->objType == DO_TABLE ||
10954 dobj->objType == DO_PROCLANG ||
10955 dobj->objType == DO_FDW ||
10956 dobj->objType == DO_FOREIGN_SERVER)
10957 {
10959
10960 daobj->dacl.privtype = privtype;
10961 daobj->dacl.initprivs = pstrdup(initprivs);
10962 }
10963 else
10964 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10965 classoid, objoid, objsubid);
10966 }
10967 }
10968 PQclear(res);
10969 }
10970
10971 destroyPQExpBuffer(query);
10972}
10973
10974/*
10975 * dumpCommentExtended --
10976 *
10977 * This routine is used to dump any comments associated with the
10978 * object handed to this routine. The routine takes the object type
10979 * and object name (ready to print, except for schema decoration), plus
10980 * the namespace and owner of the object (for labeling the ArchiveEntry),
10981 * plus catalog ID and subid which are the lookup key for pg_description,
10982 * plus the dump ID for the object (for setting a dependency).
10983 * If a matching pg_description entry is found, it is dumped.
10984 *
10985 * Note: in some cases, such as comments for triggers and rules, the "type"
10986 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10987 * but it doesn't seem worth complicating the API for all callers to make
10988 * it cleaner.
10989 *
10990 * Note: although this routine takes a dumpId for dependency purposes,
10991 * that purpose is just to mark the dependency in the emitted dump file
10992 * for possible future use by pg_restore. We do NOT use it for determining
10993 * ordering of the comment in the dump file, because this routine is called
10994 * after dependency sorting occurs. This routine should be called just after
10995 * calling ArchiveEntry() for the specified object.
10996 */
10997static void
10999 const char *name, const char *namespace,
11000 const char *owner, CatalogId catalogId,
11001 int subid, DumpId dumpId,
11002 const char *initdb_comment)
11003{
11004 DumpOptions *dopt = fout->dopt;
11006 int ncomments;
11007
11008 /* do nothing, if --no-comments is supplied */
11009 if (dopt->no_comments)
11010 return;
11011
11012 /* Comments are schema not data ... except LO comments are data */
11013 if (strcmp(type, "LARGE OBJECT") != 0)
11014 {
11015 if (!dopt->dumpSchema)
11016 return;
11017 }
11018 else
11019 {
11020 /* We do dump LO comments in binary-upgrade mode */
11021 if (!dopt->dumpData && !dopt->binary_upgrade)
11022 return;
11023 }
11024
11025 /* Search for comments associated with catalogId, using table */
11026 ncomments = findComments(catalogId.tableoid, catalogId.oid,
11027 &comments);
11028
11029 /* Is there one matching the subid? */
11030 while (ncomments > 0)
11031 {
11032 if (comments->objsubid == subid)
11033 break;
11034 comments++;
11035 ncomments--;
11036 }
11037
11038 if (initdb_comment != NULL)
11039 {
11040 static CommentItem empty_comment = {.descr = ""};
11041
11042 /*
11043 * initdb creates this object with a comment. Skip dumping the
11044 * initdb-provided comment, which would complicate matters for
11045 * non-superuser use of pg_dump. When the DBA has removed initdb's
11046 * comment, replicate that.
11047 */
11048 if (ncomments == 0)
11049 {
11051 ncomments = 1;
11052 }
11053 else if (strcmp(comments->descr, initdb_comment) == 0)
11054 ncomments = 0;
11055 }
11056
11057 /* If a comment exists, build COMMENT ON statement */
11058 if (ncomments > 0)
11059 {
11062
11063 appendPQExpBuffer(query, "COMMENT ON %s ", type);
11064 if (namespace && *namespace)
11065 appendPQExpBuffer(query, "%s.", fmtId(namespace));
11066 appendPQExpBuffer(query, "%s IS ", name);
11068 appendPQExpBufferStr(query, ";\n");
11069
11070 appendPQExpBuffer(tag, "%s %s", type, name);
11071
11072 /*
11073 * We mark comments as SECTION_NONE because they really belong in the
11074 * same section as their parent, whether that is pre-data or
11075 * post-data.
11076 */
11078 ARCHIVE_OPTS(.tag = tag->data,
11079 .namespace = namespace,
11080 .owner = owner,
11081 .description = "COMMENT",
11082 .section = SECTION_NONE,
11083 .createStmt = query->data,
11084 .deps = &dumpId,
11085 .nDeps = 1));
11086
11087 destroyPQExpBuffer(query);
11088 destroyPQExpBuffer(tag);
11089 }
11090}
11091
11092/*
11093 * dumpComment --
11094 *
11095 * Typical simplification of the above function.
11096 */
11097static inline void
11099 const char *name, const char *namespace,
11100 const char *owner, CatalogId catalogId,
11101 int subid, DumpId dumpId)
11102{
11103 dumpCommentExtended(fout, type, name, namespace, owner,
11104 catalogId, subid, dumpId, NULL);
11105}
11106
11107/*
11108 * appendNamedArgument --
11109 *
11110 * Convenience routine for constructing parameters of the form:
11111 * 'paraname', 'value'::type
11112 */
11113static void
11114appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
11115 const char *argtype, const char *argval)
11116{
11117 appendPQExpBufferStr(out, ",\n\t");
11118
11119 appendStringLiteralAH(out, argname, fout);
11120 appendPQExpBufferStr(out, ", ");
11121
11123 appendPQExpBuffer(out, "::%s", argtype);
11124}
11125
11126/*
11127 * fetchAttributeStats --
11128 *
11129 * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11130 */
11131static PGresult *
11133{
11135 PQExpBuffer relids = createPQExpBuffer();
11138 int count = 0;
11139 PGresult *res = NULL;
11140 static TocEntry *te;
11141 static bool restarted;
11143
11144 /*
11145 * Our query for retrieving statistics for multiple relations uses WITH
11146 * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11147 * in v9.4. For older versions, we resort to gathering statistics for a
11148 * single relation at a time.
11149 */
11150 if (fout->remoteVersion < 90400)
11151 max_rels = 1;
11152
11153 /* If we're just starting, set our TOC pointer. */
11154 if (!te)
11155 te = AH->toc->next;
11156
11157 /*
11158 * We can't easily avoid a second TOC scan for the tar format because it
11159 * writes restore.sql separately, which means we must execute the queries
11160 * twice. This feels risky, but there is no known reason it should
11161 * generate different output than the first pass. Even if it does, the
11162 * worst-case scenario is that restore.sql might have different statistics
11163 * data than the archive.
11164 */
11165 if (!restarted && te == AH->toc && AH->format == archTar)
11166 {
11167 te = AH->toc->next;
11168 restarted = true;
11169 }
11170
11171 appendPQExpBufferChar(relids, '{');
11174
11175 /*
11176 * Scan the TOC for the next set of relevant stats entries. We assume
11177 * that statistics are dumped in the order they are listed in the TOC.
11178 * This is perhaps not the sturdiest assumption, so we verify it matches
11179 * reality in dumpRelationStats_dumper().
11180 */
11181 for (; te != AH->toc && count < max_rels; te = te->next)
11182 {
11183 if ((te->reqs & REQ_STATS) == 0 ||
11184 strcmp(te->desc, "STATISTICS DATA") != 0)
11185 continue;
11186
11187 if (fout->remoteVersion >= 190000)
11188 {
11189 const RelStatsInfo *rsinfo = (const RelStatsInfo *) te->defnDumperArg;
11190 char relid[32];
11191
11192 sprintf(relid, "%u", rsinfo->relid);
11193 appendPGArray(relids, relid);
11194 }
11195 else
11196 {
11197 appendPGArray(nspnames, te->namespace);
11199 }
11200
11201 count++;
11202 }
11203
11204 appendPQExpBufferChar(relids, '}');
11207
11208 /* Execute the query for the next batch of relations. */
11209 if (count > 0)
11210 {
11212
11213 appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11214
11215 if (fout->remoteVersion >= 190000)
11216 {
11217 appendStringLiteralAH(query, relids->data, fout);
11218 appendPQExpBufferStr(query, "::pg_catalog.oid[])");
11219 }
11220 else
11221 {
11223 appendPQExpBufferStr(query, "::pg_catalog.name[],");
11225 appendPQExpBufferStr(query, "::pg_catalog.name[])");
11226 }
11227
11228 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11229 destroyPQExpBuffer(query);
11230 }
11231
11232 destroyPQExpBuffer(relids);
11235 return res;
11236}
11237
11238/*
11239 * dumpRelationStats_dumper --
11240 *
11241 * Generate command to import stats into the relation on the new database.
11242 * This routine is called by the Archiver when it wants the statistics to be
11243 * dumped.
11244 */
11245static char *
11247{
11248 const RelStatsInfo *rsinfo = userArg;
11249 static PGresult *res;
11250 static int rownum;
11251 PQExpBuffer query;
11253 PQExpBuffer out = &out_data;
11254 int i_schemaname;
11255 int i_tablename;
11256 int i_attname;
11257 int i_inherited;
11258 int i_null_frac;
11259 int i_avg_width;
11260 int i_n_distinct;
11264 int i_correlation;
11271 static TocEntry *expected_te;
11272
11273 /*
11274 * fetchAttributeStats() assumes that the statistics are dumped in the
11275 * order they are listed in the TOC. We verify that here for safety.
11276 */
11277 if (!expected_te)
11278 expected_te = ((ArchiveHandle *) fout)->toc;
11279
11281 while ((expected_te->reqs & REQ_STATS) == 0 ||
11282 strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11284
11285 if (te != expected_te)
11286 pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11287 te->dumpId, te->desc, te->tag,
11288 expected_te->dumpId, expected_te->desc, expected_te->tag);
11289
11290 query = createPQExpBuffer();
11292 {
11293 if (fout->remoteVersion >= 190000)
11295 "PREPARE getAttributeStats(pg_catalog.oid[]) AS\n");
11296 else
11298 "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n");
11299
11301 "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11302 "s.null_frac, s.avg_width, s.n_distinct, "
11303 "s.most_common_vals, s.most_common_freqs, "
11304 "s.histogram_bounds, s.correlation, "
11305 "s.most_common_elems, s.most_common_elem_freqs, "
11306 "s.elem_count_histogram, ");
11307
11308 if (fout->remoteVersion >= 170000)
11310 "s.range_length_histogram, "
11311 "s.range_empty_frac, "
11312 "s.range_bounds_histogram ");
11313 else
11315 "NULL AS range_length_histogram,"
11316 "NULL AS range_empty_frac,"
11317 "NULL AS range_bounds_histogram ");
11318
11319 /*
11320 * The results must be in the order of the relations supplied in the
11321 * parameters to ensure we remain in sync as we walk through the TOC.
11322 *
11323 * For v9.4 through v18, the redundant filter clause on s.tablename =
11324 * ANY(...) seems sufficient to convince the planner to use
11325 * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11326 * In newer versions, pg_stats returns the table OIDs, eliminating the
11327 * need for that hack.
11328 *
11329 * Our query for retrieving statistics for multiple relations uses
11330 * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11331 * introduced in v9.4. For older versions, we resort to gathering
11332 * statistics for a single relation at a time.
11333 */
11334 if (fout->remoteVersion >= 190000)
11336 "FROM pg_catalog.pg_stats s "
11337 "JOIN unnest($1) WITH ORDINALITY AS u (tableid, ord) "
11338 "ON s.tableid = u.tableid "
11339 "ORDER BY u.ord, s.attname, s.inherited");
11340 else if (fout->remoteVersion >= 90400)
11342 "FROM pg_catalog.pg_stats s "
11343 "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11344 "ON s.schemaname = u.schemaname "
11345 "AND s.tablename = u.tablename "
11346 "WHERE s.tablename = ANY($2) "
11347 "ORDER BY u.ord, s.attname, s.inherited");
11348 else
11350 "FROM pg_catalog.pg_stats s "
11351 "WHERE s.schemaname = $1[1] "
11352 "AND s.tablename = $2[1] "
11353 "ORDER BY s.attname, s.inherited");
11354
11355 ExecuteSqlStatement(fout, query->data);
11356
11358 resetPQExpBuffer(query);
11359 }
11360
11361 initPQExpBuffer(out);
11362
11363 /* restore relation stats */
11364 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11365 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11367 appendPQExpBufferStr(out, "\t'schemaname', ");
11368 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11369 appendPQExpBufferStr(out, ",\n");
11370 appendPQExpBufferStr(out, "\t'relname', ");
11371 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11372 appendPQExpBufferStr(out, ",\n");
11373 appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11374
11375 /*
11376 * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11377 * the relation is empty, or it could mean that it hadn't yet been
11378 * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11379 * This ambiguity allegedly can cause the planner to choose inefficient
11380 * plans after restoring to v18 or newer. To deal with this, let's just
11381 * set reltuples to -1 in that case.
11382 */
11383 if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11384 appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11385 else
11386 appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11387
11388 appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11389 rsinfo->relallvisible);
11390
11391 if (fout->remoteVersion >= 180000)
11392 appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11393
11394 appendPQExpBufferStr(out, "\n);\n");
11395
11396 /* Fetch the next batch of attribute statistics if needed. */
11397 if (rownum >= PQntuples(res))
11398 {
11399 PQclear(res);
11401 rownum = 0;
11402 }
11403
11404 i_schemaname = PQfnumber(res, "schemaname");
11405 i_tablename = PQfnumber(res, "tablename");
11406 i_attname = PQfnumber(res, "attname");
11407 i_inherited = PQfnumber(res, "inherited");
11408 i_null_frac = PQfnumber(res, "null_frac");
11409 i_avg_width = PQfnumber(res, "avg_width");
11410 i_n_distinct = PQfnumber(res, "n_distinct");
11411 i_most_common_vals = PQfnumber(res, "most_common_vals");
11412 i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11413 i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11414 i_correlation = PQfnumber(res, "correlation");
11415 i_most_common_elems = PQfnumber(res, "most_common_elems");
11416 i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11417 i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11418 i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11419 i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11420 i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11421
11422 /* restore attribute stats */
11423 for (; rownum < PQntuples(res); rownum++)
11424 {
11425 const char *attname;
11426
11427 /* Stop if the next stat row in our cache isn't for this relation. */
11428 if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11429 strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11430 break;
11431
11432 appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11433 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11435 appendPQExpBufferStr(out, "\t'schemaname', ");
11436 appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11437 appendPQExpBufferStr(out, ",\n\t'relname', ");
11438 appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11439
11440 if (PQgetisnull(res, rownum, i_attname))
11441 pg_fatal("unexpected null attname");
11442 attname = PQgetvalue(res, rownum, i_attname);
11443
11444 /*
11445 * Indexes look up attname in indAttNames to derive attnum, all others
11446 * use attname directly. We must specify attnum for indexes, since
11447 * their attnames are not necessarily stable across dump/reload.
11448 */
11449 if (rsinfo->nindAttNames == 0)
11450 {
11451 appendPQExpBufferStr(out, ",\n\t'attname', ");
11453 }
11454 else
11455 {
11456 bool found = false;
11457
11458 for (int i = 0; i < rsinfo->nindAttNames; i++)
11459 {
11460 if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11461 {
11462 appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11463 i + 1);
11464 found = true;
11465 break;
11466 }
11467 }
11468
11469 if (!found)
11470 pg_fatal("could not find index attname \"%s\"", attname);
11471 }
11472
11473 if (!PQgetisnull(res, rownum, i_inherited))
11474 appendNamedArgument(out, fout, "inherited", "boolean",
11475 PQgetvalue(res, rownum, i_inherited));
11476 if (!PQgetisnull(res, rownum, i_null_frac))
11477 appendNamedArgument(out, fout, "null_frac", "real",
11478 PQgetvalue(res, rownum, i_null_frac));
11479 if (!PQgetisnull(res, rownum, i_avg_width))
11480 appendNamedArgument(out, fout, "avg_width", "integer",
11481 PQgetvalue(res, rownum, i_avg_width));
11482 if (!PQgetisnull(res, rownum, i_n_distinct))
11483 appendNamedArgument(out, fout, "n_distinct", "real",
11484 PQgetvalue(res, rownum, i_n_distinct));
11485 if (!PQgetisnull(res, rownum, i_most_common_vals))
11486 appendNamedArgument(out, fout, "most_common_vals", "text",
11487 PQgetvalue(res, rownum, i_most_common_vals));
11488 if (!PQgetisnull(res, rownum, i_most_common_freqs))
11489 appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11490 PQgetvalue(res, rownum, i_most_common_freqs));
11491 if (!PQgetisnull(res, rownum, i_histogram_bounds))
11492 appendNamedArgument(out, fout, "histogram_bounds", "text",
11493 PQgetvalue(res, rownum, i_histogram_bounds));
11494 if (!PQgetisnull(res, rownum, i_correlation))
11495 appendNamedArgument(out, fout, "correlation", "real",
11496 PQgetvalue(res, rownum, i_correlation));
11497 if (!PQgetisnull(res, rownum, i_most_common_elems))
11498 appendNamedArgument(out, fout, "most_common_elems", "text",
11499 PQgetvalue(res, rownum, i_most_common_elems));
11500 if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11501 appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11502 PQgetvalue(res, rownum, i_most_common_elem_freqs));
11503 if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11504 appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11505 PQgetvalue(res, rownum, i_elem_count_histogram));
11506 if (fout->remoteVersion >= 170000)
11507 {
11508 if (!PQgetisnull(res, rownum, i_range_length_histogram))
11509 appendNamedArgument(out, fout, "range_length_histogram", "text",
11510 PQgetvalue(res, rownum, i_range_length_histogram));
11511 if (!PQgetisnull(res, rownum, i_range_empty_frac))
11512 appendNamedArgument(out, fout, "range_empty_frac", "real",
11513 PQgetvalue(res, rownum, i_range_empty_frac));
11514 if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11515 appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11516 PQgetvalue(res, rownum, i_range_bounds_histogram));
11517 }
11518 appendPQExpBufferStr(out, "\n);\n");
11519 }
11520
11521 destroyPQExpBuffer(query);
11522 return out->data;
11523}
11524
11525/*
11526 * dumpRelationStats --
11527 *
11528 * Make an ArchiveEntry for the relation statistics. The Archiver will take
11529 * care of gathering the statistics and generating the restore commands when
11530 * they are needed.
11531 */
11532static void
11534{
11535 const DumpableObject *dobj = &rsinfo->dobj;
11536
11537 /* nothing to do if we are not dumping statistics */
11538 if (!fout->dopt->dumpStatistics)
11539 return;
11540
11542 ARCHIVE_OPTS(.tag = dobj->name,
11543 .namespace = dobj->namespace->dobj.name,
11544 .description = "STATISTICS DATA",
11545 .section = rsinfo->section,
11546 .defnFn = dumpRelationStats_dumper,
11547 .defnArg = rsinfo,
11548 .deps = dobj->dependencies,
11549 .nDeps = dobj->nDeps));
11550}
11551
11552/*
11553 * dumpTableComment --
11554 *
11555 * As above, but dump comments for both the specified table (or view)
11556 * and its columns.
11557 */
11558static void
11560 const char *reltypename)
11561{
11562 DumpOptions *dopt = fout->dopt;
11564 int ncomments;
11565 PQExpBuffer query;
11566 PQExpBuffer tag;
11567
11568 /* do nothing, if --no-comments is supplied */
11569 if (dopt->no_comments)
11570 return;
11571
11572 /* Comments are SCHEMA not data */
11573 if (!dopt->dumpSchema)
11574 return;
11575
11576 /* Search for comments associated with relation, using table */
11577 ncomments = findComments(tbinfo->dobj.catId.tableoid,
11578 tbinfo->dobj.catId.oid,
11579 &comments);
11580
11581 /* If comments exist, build COMMENT ON statements */
11582 if (ncomments <= 0)
11583 return;
11584
11585 query = createPQExpBuffer();
11586 tag = createPQExpBuffer();
11587
11588 while (ncomments > 0)
11589 {
11590 const char *descr = comments->descr;
11591 int objsubid = comments->objsubid;
11592
11593 if (objsubid == 0)
11594 {
11595 resetPQExpBuffer(tag);
11596 appendPQExpBuffer(tag, "%s %s", reltypename,
11597 fmtId(tbinfo->dobj.name));
11598
11599 resetPQExpBuffer(query);
11600 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11602 appendStringLiteralAH(query, descr, fout);
11603 appendPQExpBufferStr(query, ";\n");
11604
11606 ARCHIVE_OPTS(.tag = tag->data,
11607 .namespace = tbinfo->dobj.namespace->dobj.name,
11608 .owner = tbinfo->rolname,
11609 .description = "COMMENT",
11610 .section = SECTION_NONE,
11611 .createStmt = query->data,
11612 .deps = &(tbinfo->dobj.dumpId),
11613 .nDeps = 1));
11614 }
11615 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11616 {
11617 resetPQExpBuffer(tag);
11618 appendPQExpBuffer(tag, "COLUMN %s.",
11619 fmtId(tbinfo->dobj.name));
11620 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11621
11622 resetPQExpBuffer(query);
11623 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11625 appendPQExpBuffer(query, "%s IS ",
11626 fmtId(tbinfo->attnames[objsubid - 1]));
11627 appendStringLiteralAH(query, descr, fout);
11628 appendPQExpBufferStr(query, ";\n");
11629
11631 ARCHIVE_OPTS(.tag = tag->data,
11632 .namespace = tbinfo->dobj.namespace->dobj.name,
11633 .owner = tbinfo->rolname,
11634 .description = "COMMENT",
11635 .section = SECTION_NONE,
11636 .createStmt = query->data,
11637 .deps = &(tbinfo->dobj.dumpId),
11638 .nDeps = 1));
11639 }
11640
11641 comments++;
11642 ncomments--;
11643 }
11644
11645 destroyPQExpBuffer(query);
11646 destroyPQExpBuffer(tag);
11647}
11648
11649/*
11650 * findComments --
11651 *
11652 * Find the comment(s), if any, associated with the given object. All the
11653 * objsubid values associated with the given classoid/objoid are found with
11654 * one search.
11655 */
11656static int
11658{
11660 CommentItem *low;
11661 CommentItem *high;
11662 int nmatch;
11663
11664 /*
11665 * Do binary search to find some item matching the object.
11666 */
11667 low = &comments[0];
11668 high = &comments[ncomments - 1];
11669 while (low <= high)
11670 {
11671 middle = low + (high - low) / 2;
11672
11673 if (classoid < middle->classoid)
11674 high = middle - 1;
11675 else if (classoid > middle->classoid)
11676 low = middle + 1;
11677 else if (objoid < middle->objoid)
11678 high = middle - 1;
11679 else if (objoid > middle->objoid)
11680 low = middle + 1;
11681 else
11682 break; /* found a match */
11683 }
11684
11685 if (low > high) /* no matches */
11686 {
11687 *items = NULL;
11688 return 0;
11689 }
11690
11691 /*
11692 * Now determine how many items match the object. The search loop
11693 * invariant still holds: only items between low and high inclusive could
11694 * match.
11695 */
11696 nmatch = 1;
11697 while (middle > low)
11698 {
11699 if (classoid != middle[-1].classoid ||
11700 objoid != middle[-1].objoid)
11701 break;
11702 middle--;
11703 nmatch++;
11704 }
11705
11706 *items = middle;
11707
11708 middle += nmatch;
11709 while (middle <= high)
11710 {
11711 if (classoid != middle->classoid ||
11712 objoid != middle->objoid)
11713 break;
11714 middle++;
11715 nmatch++;
11716 }
11717
11718 return nmatch;
11719}
11720
11721/*
11722 * collectComments --
11723 *
11724 * Construct a table of all comments available for database objects;
11725 * also set the has-comment component flag for each relevant object.
11726 *
11727 * We used to do per-object queries for the comments, but it's much faster
11728 * to pull them all over at once, and on most databases the memory cost
11729 * isn't high.
11730 *
11731 * The table is sorted by classoid/objid/objsubid for speed in lookup.
11732 */
11733static void
11735{
11736 PGresult *res;
11737 PQExpBuffer query;
11738 int i_description;
11739 int i_classoid;
11740 int i_objoid;
11741 int i_objsubid;
11742 int ntups;
11743 int i;
11744 DumpableObject *dobj;
11745
11746 query = createPQExpBuffer();
11747
11748 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11749 "FROM pg_catalog.pg_description "
11750 "ORDER BY classoid, objoid, objsubid");
11751
11752 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11753
11754 /* Construct lookup table containing OIDs in numeric form */
11755
11756 i_description = PQfnumber(res, "description");
11757 i_classoid = PQfnumber(res, "classoid");
11758 i_objoid = PQfnumber(res, "objoid");
11759 i_objsubid = PQfnumber(res, "objsubid");
11760
11761 ntups = PQntuples(res);
11762
11764 ncomments = 0;
11765 dobj = NULL;
11766
11767 for (i = 0; i < ntups; i++)
11768 {
11769 CatalogId objId;
11770 int subid;
11771
11772 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11773 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11774 subid = atoi(PQgetvalue(res, i, i_objsubid));
11775
11776 /* We needn't remember comments that don't match any dumpable object */
11777 if (dobj == NULL ||
11778 dobj->catId.tableoid != objId.tableoid ||
11779 dobj->catId.oid != objId.oid)
11780 dobj = findObjectByCatalogId(objId);
11781 if (dobj == NULL)
11782 continue;
11783
11784 /*
11785 * Comments on columns of composite types are linked to the type's
11786 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11787 * in the type's own DumpableObject.
11788 */
11789 if (subid != 0 && dobj->objType == DO_TABLE &&
11790 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11791 {
11793
11794 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11795 if (cTypeInfo)
11796 cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11797 }
11798 else
11799 dobj->components |= DUMP_COMPONENT_COMMENT;
11800
11803 comments[ncomments].objoid = objId.oid;
11804 comments[ncomments].objsubid = subid;
11805 ncomments++;
11806 }
11807
11808 PQclear(res);
11809 destroyPQExpBuffer(query);
11810}
11811
11812/*
11813 * dumpDumpableObject
11814 *
11815 * This routine and its subsidiaries are responsible for creating
11816 * ArchiveEntries (TOC objects) for each object to be dumped.
11817 */
11818static void
11820{
11821 /*
11822 * Clear any dump-request bits for components that don't exist for this
11823 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11824 * request for every kind of object.)
11825 */
11826 dobj->dump &= dobj->components;
11827
11828 /* Now, short-circuit if there's nothing to be done here. */
11829 if (dobj->dump == 0)
11830 return;
11831
11832 switch (dobj->objType)
11833 {
11834 case DO_NAMESPACE:
11835 dumpNamespace(fout, (const NamespaceInfo *) dobj);
11836 break;
11837 case DO_EXTENSION:
11838 dumpExtension(fout, (const ExtensionInfo *) dobj);
11839 break;
11840 case DO_TYPE:
11841 dumpType(fout, (const TypeInfo *) dobj);
11842 break;
11843 case DO_SHELL_TYPE:
11844 dumpShellType(fout, (const ShellTypeInfo *) dobj);
11845 break;
11846 case DO_FUNC:
11847 dumpFunc(fout, (const FuncInfo *) dobj);
11848 break;
11849 case DO_AGG:
11850 dumpAgg(fout, (const AggInfo *) dobj);
11851 break;
11852 case DO_OPERATOR:
11853 dumpOpr(fout, (const OprInfo *) dobj);
11854 break;
11855 case DO_ACCESS_METHOD:
11856 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11857 break;
11858 case DO_OPCLASS:
11859 dumpOpclass(fout, (const OpclassInfo *) dobj);
11860 break;
11861 case DO_OPFAMILY:
11862 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11863 break;
11864 case DO_COLLATION:
11865 dumpCollation(fout, (const CollInfo *) dobj);
11866 break;
11867 case DO_CONVERSION:
11868 dumpConversion(fout, (const ConvInfo *) dobj);
11869 break;
11870 case DO_TABLE:
11871 dumpTable(fout, (const TableInfo *) dobj);
11872 break;
11873 case DO_TABLE_ATTACH:
11874 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11875 break;
11876 case DO_ATTRDEF:
11877 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11878 break;
11879 case DO_INDEX:
11880 dumpIndex(fout, (const IndxInfo *) dobj);
11881 break;
11882 case DO_INDEX_ATTACH:
11883 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11884 break;
11885 case DO_STATSEXT:
11886 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11887 dumpStatisticsExtStats(fout, (const StatsExtInfo *) dobj);
11888 break;
11889 case DO_REFRESH_MATVIEW:
11890 refreshMatViewData(fout, (const TableDataInfo *) dobj);
11891 break;
11892 case DO_RULE:
11893 dumpRule(fout, (const RuleInfo *) dobj);
11894 break;
11895 case DO_TRIGGER:
11896 dumpTrigger(fout, (const TriggerInfo *) dobj);
11897 break;
11898 case DO_EVENT_TRIGGER:
11899 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11900 break;
11901 case DO_CONSTRAINT:
11902 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11903 break;
11904 case DO_FK_CONSTRAINT:
11905 dumpConstraint(fout, (const ConstraintInfo *) dobj);
11906 break;
11907 case DO_PROCLANG:
11908 dumpProcLang(fout, (const ProcLangInfo *) dobj);
11909 break;
11910 case DO_CAST:
11911 dumpCast(fout, (const CastInfo *) dobj);
11912 break;
11913 case DO_TRANSFORM:
11914 dumpTransform(fout, (const TransformInfo *) dobj);
11915 break;
11916 case DO_SEQUENCE_SET:
11917 dumpSequenceData(fout, (const TableDataInfo *) dobj);
11918 break;
11919 case DO_TABLE_DATA:
11920 dumpTableData(fout, (const TableDataInfo *) dobj);
11921 break;
11922 case DO_DUMMY_TYPE:
11923 /* table rowtypes and array types are never dumped separately */
11924 break;
11925 case DO_TSPARSER:
11926 dumpTSParser(fout, (const TSParserInfo *) dobj);
11927 break;
11928 case DO_TSDICT:
11929 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11930 break;
11931 case DO_TSTEMPLATE:
11932 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11933 break;
11934 case DO_TSCONFIG:
11935 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11936 break;
11937 case DO_FDW:
11938 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11939 break;
11940 case DO_FOREIGN_SERVER:
11941 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11942 break;
11943 case DO_DEFAULT_ACL:
11944 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11945 break;
11946 case DO_LARGE_OBJECT:
11947 dumpLO(fout, (const LoInfo *) dobj);
11948 break;
11950 if (dobj->dump & DUMP_COMPONENT_DATA)
11951 {
11952 LoInfo *loinfo;
11953 TocEntry *te;
11954
11955 loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11956 if (loinfo == NULL)
11957 pg_fatal("missing metadata for large objects \"%s\"",
11958 dobj->name);
11959
11960 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11961 ARCHIVE_OPTS(.tag = dobj->name,
11962 .owner = loinfo->rolname,
11963 .description = "BLOBS",
11964 .section = SECTION_DATA,
11965 .deps = dobj->dependencies,
11966 .nDeps = dobj->nDeps,
11967 .dumpFn = dumpLOs,
11968 .dumpArg = loinfo));
11969
11970 /*
11971 * Set the TocEntry's dataLength in case we are doing a
11972 * parallel dump and want to order dump jobs by table size.
11973 * (We need some size estimate for every TocEntry with a
11974 * DataDumper function.) We don't currently have any cheap
11975 * way to estimate the size of LOs, but fortunately it doesn't
11976 * matter too much as long as we get large batches of LOs
11977 * processed reasonably early. Assume 8K per blob.
11978 */
11979 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11980 }
11981 break;
11982 case DO_POLICY:
11983 dumpPolicy(fout, (const PolicyInfo *) dobj);
11984 break;
11985 case DO_PUBLICATION:
11986 dumpPublication(fout, (const PublicationInfo *) dobj);
11987 break;
11988 case DO_PUBLICATION_REL:
11990 break;
11993 (const PublicationSchemaInfo *) dobj);
11994 break;
11995 case DO_SUBSCRIPTION:
11996 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11997 break;
11999 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
12000 break;
12001 case DO_REL_STATS:
12002 dumpRelationStats(fout, (const RelStatsInfo *) dobj);
12003 break;
12006 /* never dumped, nothing to do */
12007 break;
12008 }
12009}
12010
12011/*
12012 * dumpNamespace
12013 * writes out to fout the queries to recreate a user-defined namespace
12014 */
12015static void
12017{
12018 DumpOptions *dopt = fout->dopt;
12019 PQExpBuffer q;
12021 char *qnspname;
12022
12023 /* Do nothing if not dumping schema */
12024 if (!dopt->dumpSchema)
12025 return;
12026
12027 q = createPQExpBuffer();
12029
12030 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
12031
12032 if (nspinfo->create)
12033 {
12034 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
12035 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
12036 }
12037 else
12038 {
12039 /* see selectDumpableNamespace() */
12041 "-- *not* dropping schema, since initdb creates it\n");
12043 "-- *not* creating schema, since initdb creates it\n");
12044 }
12045
12046 if (dopt->binary_upgrade)
12048 "SCHEMA", qnspname, NULL);
12049
12050 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12051 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
12052 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
12053 .owner = nspinfo->rolname,
12054 .description = "SCHEMA",
12055 .section = SECTION_PRE_DATA,
12056 .createStmt = q->data,
12057 .dropStmt = delq->data));
12058
12059 /* Dump Schema Comments and Security Labels */
12060 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12061 {
12062 const char *initdb_comment = NULL;
12063
12064 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
12065 initdb_comment = "standard public schema";
12067 NULL, nspinfo->rolname,
12068 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
12070 }
12071
12072 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12073 dumpSecLabel(fout, "SCHEMA", qnspname,
12074 NULL, nspinfo->rolname,
12075 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
12076
12077 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
12078 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
12079 qnspname, NULL, NULL,
12080 NULL, nspinfo->rolname, &nspinfo->dacl);
12081
12082 free(qnspname);
12083
12086}
12087
12088/*
12089 * dumpExtension
12090 * writes out to fout the queries to recreate an extension
12091 */
12092static void
12094{
12095 DumpOptions *dopt = fout->dopt;
12096 PQExpBuffer q;
12098 char *qextname;
12099
12100 /* Do nothing if not dumping schema */
12101 if (!dopt->dumpSchema)
12102 return;
12103
12104 q = createPQExpBuffer();
12106
12107 qextname = pg_strdup(fmtId(extinfo->dobj.name));
12108
12109 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
12110
12111 if (!dopt->binary_upgrade)
12112 {
12113 /*
12114 * In a regular dump, we simply create the extension, intentionally
12115 * not specifying a version, so that the destination installation's
12116 * default version is used.
12117 *
12118 * Use of IF NOT EXISTS here is unlike our behavior for other object
12119 * types; but there are various scenarios in which it's convenient to
12120 * manually create the desired extension before restoring, so we
12121 * prefer to allow it to exist already.
12122 */
12123 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
12124 qextname, fmtId(extinfo->namespace));
12125 }
12126 else
12127 {
12128 /*
12129 * In binary-upgrade mode, it's critical to reproduce the state of the
12130 * database exactly, so our procedure is to create an empty extension,
12131 * restore all the contained objects normally, and add them to the
12132 * extension one by one. This function performs just the first of
12133 * those steps. binary_upgrade_extension_member() takes care of
12134 * adding member objects as they're created.
12135 */
12136 int i;
12137 int n;
12138
12139 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
12140
12141 /*
12142 * We unconditionally create the extension, so we must drop it if it
12143 * exists. This could happen if the user deleted 'plpgsql' and then
12144 * readded it, causing its oid to be greater than g_last_builtin_oid.
12145 */
12146 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
12147
12149 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
12150 appendStringLiteralAH(q, extinfo->dobj.name, fout);
12151 appendPQExpBufferStr(q, ", ");
12152 appendStringLiteralAH(q, extinfo->namespace, fout);
12153 appendPQExpBufferStr(q, ", ");
12154 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
12155 appendStringLiteralAH(q, extinfo->extversion, fout);
12156 appendPQExpBufferStr(q, ", ");
12157
12158 /*
12159 * Note that we're pushing extconfig (an OID array) back into
12160 * pg_extension exactly as-is. This is OK because pg_class OIDs are
12161 * preserved in binary upgrade.
12162 */
12163 if (strlen(extinfo->extconfig) > 2)
12164 appendStringLiteralAH(q, extinfo->extconfig, fout);
12165 else
12166 appendPQExpBufferStr(q, "NULL");
12167 appendPQExpBufferStr(q, ", ");
12168 if (strlen(extinfo->extcondition) > 2)
12169 appendStringLiteralAH(q, extinfo->extcondition, fout);
12170 else
12171 appendPQExpBufferStr(q, "NULL");
12172 appendPQExpBufferStr(q, ", ");
12173 appendPQExpBufferStr(q, "ARRAY[");
12174 n = 0;
12175 for (i = 0; i < extinfo->dobj.nDeps; i++)
12176 {
12178
12179 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12180 if (extobj && extobj->objType == DO_EXTENSION)
12181 {
12182 if (n++ > 0)
12183 appendPQExpBufferChar(q, ',');
12185 }
12186 }
12187 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12188 appendPQExpBufferStr(q, ");\n");
12189 }
12190
12191 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12192 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12193 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12194 .description = "EXTENSION",
12195 .section = SECTION_PRE_DATA,
12196 .createStmt = q->data,
12197 .dropStmt = delq->data));
12198
12199 /* Dump Extension Comments */
12200 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12201 dumpComment(fout, "EXTENSION", qextname,
12202 NULL, "",
12203 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12204
12205 free(qextname);
12206
12209}
12210
12211/*
12212 * dumpType
12213 * writes out to fout the queries to recreate a user-defined type
12214 */
12215static void
12217{
12218 DumpOptions *dopt = fout->dopt;
12219
12220 /* Do nothing if not dumping schema */
12221 if (!dopt->dumpSchema)
12222 return;
12223
12224 /* Dump out in proper style */
12225 if (tyinfo->typtype == TYPTYPE_BASE)
12227 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12229 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12231 else if (tyinfo->typtype == TYPTYPE_ENUM)
12233 else if (tyinfo->typtype == TYPTYPE_RANGE)
12235 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12237 else
12238 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12239 tyinfo->dobj.name);
12240}
12241
12242/*
12243 * dumpEnumType
12244 * writes out to fout the queries to recreate a user-defined enum type
12245 */
12246static void
12248{
12249 DumpOptions *dopt = fout->dopt;
12253 PGresult *res;
12254 int num,
12255 i;
12256 Oid enum_oid;
12257 char *qtypname;
12258 char *qualtypname;
12259 char *label;
12260 int i_enumlabel;
12261 int i_oid;
12262
12264 {
12265 /* Set up query for enum-specific details */
12267 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12268 "SELECT oid, enumlabel "
12269 "FROM pg_catalog.pg_enum "
12270 "WHERE enumtypid = $1 "
12271 "ORDER BY enumsortorder");
12272
12273 ExecuteSqlStatement(fout, query->data);
12274
12276 }
12277
12278 printfPQExpBuffer(query,
12279 "EXECUTE dumpEnumType('%u')",
12280 tyinfo->dobj.catId.oid);
12281
12282 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12283
12284 num = PQntuples(res);
12285
12286 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12288
12289 /*
12290 * CASCADE shouldn't be required here as for normal types since the I/O
12291 * functions are generic and do not get dropped.
12292 */
12293 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12294
12295 if (dopt->binary_upgrade)
12297 tyinfo->dobj.catId.oid,
12298 false, false);
12299
12300 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12301 qualtypname);
12302
12303 if (!dopt->binary_upgrade)
12304 {
12305 i_enumlabel = PQfnumber(res, "enumlabel");
12306
12307 /* Labels with server-assigned oids */
12308 for (i = 0; i < num; i++)
12309 {
12310 label = PQgetvalue(res, i, i_enumlabel);
12311 if (i > 0)
12312 appendPQExpBufferChar(q, ',');
12313 appendPQExpBufferStr(q, "\n ");
12315 }
12316 }
12317
12318 appendPQExpBufferStr(q, "\n);\n");
12319
12320 if (dopt->binary_upgrade)
12321 {
12322 i_oid = PQfnumber(res, "oid");
12323 i_enumlabel = PQfnumber(res, "enumlabel");
12324
12325 /* Labels with dump-assigned (preserved) oids */
12326 for (i = 0; i < num; i++)
12327 {
12328 enum_oid = atooid(PQgetvalue(res, i, i_oid));
12329 label = PQgetvalue(res, i, i_enumlabel);
12330
12331 if (i == 0)
12332 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12334 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12335 enum_oid);
12336 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12338 appendPQExpBufferStr(q, ";\n\n");
12339 }
12340 }
12341
12342 if (dopt->binary_upgrade)
12344 "TYPE", qtypname,
12345 tyinfo->dobj.namespace->dobj.name);
12346
12347 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12348 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12349 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12350 .namespace = tyinfo->dobj.namespace->dobj.name,
12351 .owner = tyinfo->rolname,
12352 .description = "TYPE",
12353 .section = SECTION_PRE_DATA,
12354 .createStmt = q->data,
12355 .dropStmt = delq->data));
12356
12357 /* Dump Type Comments and Security Labels */
12358 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12359 dumpComment(fout, "TYPE", qtypname,
12360 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12361 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12362
12363 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12364 dumpSecLabel(fout, "TYPE", qtypname,
12365 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12366 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12367
12368 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12369 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12370 qtypname, NULL,
12371 tyinfo->dobj.namespace->dobj.name,
12372 NULL, tyinfo->rolname, &tyinfo->dacl);
12373
12374 PQclear(res);
12377 destroyPQExpBuffer(query);
12378 free(qtypname);
12380}
12381
12382/*
12383 * dumpRangeType
12384 * writes out to fout the queries to recreate a user-defined range type
12385 */
12386static void
12388{
12389 DumpOptions *dopt = fout->dopt;
12393 PGresult *res;
12395 char *qtypname;
12396 char *qualtypname;
12397 char *procname;
12398
12400 {
12401 /* Set up query for range-specific details */
12403 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12404
12406 "SELECT ");
12407
12408 if (fout->remoteVersion >= 140000)
12410 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12411 else
12413 "NULL AS rngmultitype, ");
12414
12416 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12417 "opc.opcname AS opcname, "
12418 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12419 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12420 "opc.opcdefault, "
12421 "CASE WHEN rngcollation = st.typcollation THEN 0 "
12422 " ELSE rngcollation END AS collation, "
12423 "rngcanonical, rngsubdiff "
12424 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12425 " pg_catalog.pg_opclass opc "
12426 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12427 "rngtypid = $1");
12428
12429 ExecuteSqlStatement(fout, query->data);
12430
12432 }
12433
12434 printfPQExpBuffer(query,
12435 "EXECUTE dumpRangeType('%u')",
12436 tyinfo->dobj.catId.oid);
12437
12438 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12439
12440 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12442
12443 /*
12444 * CASCADE shouldn't be required here as for normal types since the I/O
12445 * functions are generic and do not get dropped.
12446 */
12447 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12448
12449 if (dopt->binary_upgrade)
12451 tyinfo->dobj.catId.oid,
12452 false, true);
12453
12454 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12455 qualtypname);
12456
12457 appendPQExpBuffer(q, "\n subtype = %s",
12458 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12459
12460 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12461 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12462 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12463
12464 /* print subtype_opclass only if not default for subtype */
12465 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12466 {
12467 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12468 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12469
12470 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12471 fmtId(nspname));
12473 }
12474
12475 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12477 {
12479
12480 if (coll)
12481 appendPQExpBuffer(q, ",\n collation = %s",
12482 fmtQualifiedDumpable(coll));
12483 }
12484
12485 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12486 if (strcmp(procname, "-") != 0)
12487 appendPQExpBuffer(q, ",\n canonical = %s", procname);
12488
12489 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12490 if (strcmp(procname, "-") != 0)
12491 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12492
12493 appendPQExpBufferStr(q, "\n);\n");
12494
12495 if (dopt->binary_upgrade)
12497 "TYPE", qtypname,
12498 tyinfo->dobj.namespace->dobj.name);
12499
12500 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12501 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12502 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12503 .namespace = tyinfo->dobj.namespace->dobj.name,
12504 .owner = tyinfo->rolname,
12505 .description = "TYPE",
12506 .section = SECTION_PRE_DATA,
12507 .createStmt = q->data,
12508 .dropStmt = delq->data));
12509
12510 /* Dump Type Comments and Security Labels */
12511 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12512 dumpComment(fout, "TYPE", qtypname,
12513 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12514 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12515
12516 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12517 dumpSecLabel(fout, "TYPE", qtypname,
12518 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12519 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12520
12521 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12522 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12523 qtypname, NULL,
12524 tyinfo->dobj.namespace->dobj.name,
12525 NULL, tyinfo->rolname, &tyinfo->dacl);
12526
12527 PQclear(res);
12530 destroyPQExpBuffer(query);
12531 free(qtypname);
12533}
12534
12535/*
12536 * dumpUndefinedType
12537 * writes out to fout the queries to recreate a !typisdefined type
12538 *
12539 * This is a shell type, but we use different terminology to distinguish
12540 * this case from where we have to emit a shell type definition to break
12541 * circular dependencies. An undefined type shouldn't ever have anything
12542 * depending on it.
12543 */
12544static void
12546{
12547 DumpOptions *dopt = fout->dopt;
12550 char *qtypname;
12551 char *qualtypname;
12552
12553 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12555
12556 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12557
12558 if (dopt->binary_upgrade)
12560 tyinfo->dobj.catId.oid,
12561 false, false);
12562
12563 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12564 qualtypname);
12565
12566 if (dopt->binary_upgrade)
12568 "TYPE", qtypname,
12569 tyinfo->dobj.namespace->dobj.name);
12570
12571 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12572 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12573 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12574 .namespace = tyinfo->dobj.namespace->dobj.name,
12575 .owner = tyinfo->rolname,
12576 .description = "TYPE",
12577 .section = SECTION_PRE_DATA,
12578 .createStmt = q->data,
12579 .dropStmt = delq->data));
12580
12581 /* Dump Type Comments and Security Labels */
12582 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12583 dumpComment(fout, "TYPE", qtypname,
12584 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12585 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12586
12587 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12588 dumpSecLabel(fout, "TYPE", qtypname,
12589 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12590 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12591
12592 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12593 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12594 qtypname, NULL,
12595 tyinfo->dobj.namespace->dobj.name,
12596 NULL, tyinfo->rolname, &tyinfo->dacl);
12597
12600 free(qtypname);
12602}
12603
12604/*
12605 * dumpBaseType
12606 * writes out to fout the queries to recreate a user-defined base type
12607 */
12608static void
12610{
12611 DumpOptions *dopt = fout->dopt;
12615 PGresult *res;
12616 char *qtypname;
12617 char *qualtypname;
12618 char *typlen;
12619 char *typinput;
12620 char *typoutput;
12621 char *typreceive;
12622 char *typsend;
12623 char *typmodin;
12624 char *typmodout;
12625 char *typanalyze;
12626 char *typsubscript;
12633 char *typcategory;
12634 char *typispreferred;
12635 char *typdelim;
12636 char *typbyval;
12637 char *typalign;
12638 char *typstorage;
12639 char *typcollatable;
12640 char *typdefault;
12641 bool typdefault_is_literal = false;
12642
12644 {
12645 /* Set up query for type-specific details */
12647 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12648 "SELECT typlen, "
12649 "typinput, typoutput, typreceive, typsend, "
12650 "typreceive::pg_catalog.oid AS typreceiveoid, "
12651 "typsend::pg_catalog.oid AS typsendoid, "
12652 "typanalyze, "
12653 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12654 "typdelim, typbyval, typalign, typstorage, "
12655 "typmodin, typmodout, "
12656 "typmodin::pg_catalog.oid AS typmodinoid, "
12657 "typmodout::pg_catalog.oid AS typmodoutoid, "
12658 "typcategory, typispreferred, "
12659 "(typcollation <> 0) AS typcollatable, "
12660 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12661
12662 if (fout->remoteVersion >= 140000)
12664 "typsubscript, "
12665 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12666 else
12668 "'-' AS typsubscript, 0 AS typsubscriptoid ");
12669
12670 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12671 "WHERE oid = $1");
12672
12673 ExecuteSqlStatement(fout, query->data);
12674
12676 }
12677
12678 printfPQExpBuffer(query,
12679 "EXECUTE dumpBaseType('%u')",
12680 tyinfo->dobj.catId.oid);
12681
12682 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12683
12684 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12685 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12686 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12687 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12688 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12689 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12690 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12691 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12692 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12693 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12694 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12695 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12696 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12697 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12698 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12699 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12700 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12701 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12702 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12703 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12704 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12705 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12706 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12707 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12708 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12709 {
12710 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12711 typdefault_is_literal = true; /* it needs quotes */
12712 }
12713 else
12714 typdefault = NULL;
12715
12716 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12718
12719 /*
12720 * The reason we include CASCADE is that the circular dependency between
12721 * the type and its I/O functions makes it impossible to drop the type any
12722 * other way.
12723 */
12724 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12725
12726 /*
12727 * We might already have a shell type, but setting pg_type_oid is
12728 * harmless, and in any case we'd better set the array type OID.
12729 */
12730 if (dopt->binary_upgrade)
12732 tyinfo->dobj.catId.oid,
12733 false, false);
12734
12736 "CREATE TYPE %s (\n"
12737 " INTERNALLENGTH = %s",
12739 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12740
12741 /* regproc result is sufficiently quoted already */
12742 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12743 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12745 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12747 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12749 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12751 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12753 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12754
12755 if (strcmp(typcollatable, "t") == 0)
12756 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12757
12758 if (typdefault != NULL)
12759 {
12760 appendPQExpBufferStr(q, ",\n DEFAULT = ");
12763 else
12765 }
12766
12768 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12769
12770 if (OidIsValid(tyinfo->typelem))
12771 appendPQExpBuffer(q, ",\n ELEMENT = %s",
12773 zeroIsError));
12774
12775 if (strcmp(typcategory, "U") != 0)
12776 {
12777 appendPQExpBufferStr(q, ",\n CATEGORY = ");
12779 }
12780
12781 if (strcmp(typispreferred, "t") == 0)
12782 appendPQExpBufferStr(q, ",\n PREFERRED = true");
12783
12784 if (typdelim && strcmp(typdelim, ",") != 0)
12785 {
12786 appendPQExpBufferStr(q, ",\n DELIMITER = ");
12787 appendStringLiteralAH(q, typdelim, fout);
12788 }
12789
12790 if (*typalign == TYPALIGN_CHAR)
12791 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12792 else if (*typalign == TYPALIGN_SHORT)
12793 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12794 else if (*typalign == TYPALIGN_INT)
12795 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12796 else if (*typalign == TYPALIGN_DOUBLE)
12797 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12798
12799 if (*typstorage == TYPSTORAGE_PLAIN)
12800 appendPQExpBufferStr(q, ",\n STORAGE = plain");
12801 else if (*typstorage == TYPSTORAGE_EXTERNAL)
12802 appendPQExpBufferStr(q, ",\n STORAGE = external");
12803 else if (*typstorage == TYPSTORAGE_EXTENDED)
12804 appendPQExpBufferStr(q, ",\n STORAGE = extended");
12805 else if (*typstorage == TYPSTORAGE_MAIN)
12806 appendPQExpBufferStr(q, ",\n STORAGE = main");
12807
12808 if (strcmp(typbyval, "t") == 0)
12809 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12810
12811 appendPQExpBufferStr(q, "\n);\n");
12812
12813 if (dopt->binary_upgrade)
12815 "TYPE", qtypname,
12816 tyinfo->dobj.namespace->dobj.name);
12817
12818 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12819 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12820 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12821 .namespace = tyinfo->dobj.namespace->dobj.name,
12822 .owner = tyinfo->rolname,
12823 .description = "TYPE",
12824 .section = SECTION_PRE_DATA,
12825 .createStmt = q->data,
12826 .dropStmt = delq->data));
12827
12828 /* Dump Type Comments and Security Labels */
12829 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12830 dumpComment(fout, "TYPE", qtypname,
12831 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12832 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12833
12834 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12835 dumpSecLabel(fout, "TYPE", qtypname,
12836 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12837 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12838
12839 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12840 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12841 qtypname, NULL,
12842 tyinfo->dobj.namespace->dobj.name,
12843 NULL, tyinfo->rolname, &tyinfo->dacl);
12844
12845 PQclear(res);
12848 destroyPQExpBuffer(query);
12849 free(qtypname);
12851}
12852
12853/*
12854 * dumpDomain
12855 * writes out to fout the queries to recreate a user-defined domain
12856 */
12857static void
12859{
12860 DumpOptions *dopt = fout->dopt;
12864 PGresult *res;
12865 int i;
12866 char *qtypname;
12867 char *qualtypname;
12868 char *typnotnull;
12869 char *typdefn;
12870 char *typdefault;
12871 Oid typcollation;
12872 bool typdefault_is_literal = false;
12873
12875 {
12876 /* Set up query for domain-specific details */
12878 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12879
12880 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12881 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12882 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12883 "t.typdefault, "
12884 "CASE WHEN t.typcollation <> u.typcollation "
12885 "THEN t.typcollation ELSE 0 END AS typcollation "
12886 "FROM pg_catalog.pg_type t "
12887 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12888 "WHERE t.oid = $1");
12889
12890 ExecuteSqlStatement(fout, query->data);
12891
12893 }
12894
12895 printfPQExpBuffer(query,
12896 "EXECUTE dumpDomain('%u')",
12897 tyinfo->dobj.catId.oid);
12898
12899 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12900
12901 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12902 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12903 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12904 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12905 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12906 {
12907 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12908 typdefault_is_literal = true; /* it needs quotes */
12909 }
12910 else
12911 typdefault = NULL;
12912 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12913
12914 if (dopt->binary_upgrade)
12916 tyinfo->dobj.catId.oid,
12917 true, /* force array type */
12918 false); /* force multirange type */
12919
12920 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12922
12924 "CREATE DOMAIN %s AS %s",
12926 typdefn);
12927
12928 /* Print collation only if different from base type's collation */
12929 if (OidIsValid(typcollation))
12930 {
12931 CollInfo *coll;
12932
12933 coll = findCollationByOid(typcollation);
12934 if (coll)
12935 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12936 }
12937
12938 /*
12939 * Print a not-null constraint if there's one. In servers older than 17
12940 * these don't have names, so just print it unadorned; in newer ones they
12941 * do, but most of the time it's going to be the standard generated one,
12942 * so omit the name in that case also.
12943 */
12944 if (typnotnull[0] == 't')
12945 {
12946 if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12947 appendPQExpBufferStr(q, " NOT NULL");
12948 else
12949 {
12950 ConstraintInfo *notnull = tyinfo->notnull;
12951
12952 if (!notnull->separate)
12953 {
12954 char *default_name;
12955
12956 /* XXX should match ChooseConstraintName better */
12957 default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12958
12959 if (strcmp(default_name, notnull->dobj.name) == 0)
12960 appendPQExpBufferStr(q, " NOT NULL");
12961 else
12962 appendPQExpBuffer(q, " CONSTRAINT %s %s",
12963 fmtId(notnull->dobj.name), notnull->condef);
12965 }
12966 }
12967 }
12968
12969 if (typdefault != NULL)
12970 {
12971 appendPQExpBufferStr(q, " DEFAULT ");
12974 else
12976 }
12977
12978 PQclear(res);
12979
12980 /*
12981 * Add any CHECK constraints for the domain
12982 */
12983 for (i = 0; i < tyinfo->nDomChecks; i++)
12984 {
12985 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12986
12987 if (!domcheck->separate && domcheck->contype == 'c')
12988 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12989 fmtId(domcheck->dobj.name), domcheck->condef);
12990 }
12991
12992 appendPQExpBufferStr(q, ";\n");
12993
12994 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12995
12996 if (dopt->binary_upgrade)
12998 "DOMAIN", qtypname,
12999 tyinfo->dobj.namespace->dobj.name);
13000
13001 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13002 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13003 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13004 .namespace = tyinfo->dobj.namespace->dobj.name,
13005 .owner = tyinfo->rolname,
13006 .description = "DOMAIN",
13007 .section = SECTION_PRE_DATA,
13008 .createStmt = q->data,
13009 .dropStmt = delq->data));
13010
13011 /* Dump Domain Comments and Security Labels */
13012 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13013 dumpComment(fout, "DOMAIN", qtypname,
13014 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13015 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13016
13017 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13018 dumpSecLabel(fout, "DOMAIN", qtypname,
13019 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13020 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13021
13022 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13023 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13024 qtypname, NULL,
13025 tyinfo->dobj.namespace->dobj.name,
13026 NULL, tyinfo->rolname, &tyinfo->dacl);
13027
13028 /* Dump any per-constraint comments */
13029 for (i = 0; i < tyinfo->nDomChecks; i++)
13030 {
13031 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
13033
13034 /* but only if the constraint itself was dumped here */
13035 if (domcheck->separate)
13036 continue;
13037
13039 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13040 fmtId(domcheck->dobj.name));
13041
13042 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
13044 tyinfo->dobj.namespace->dobj.name,
13045 tyinfo->rolname,
13046 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
13047
13049 }
13050
13051 /*
13052 * And a comment on the not-null constraint, if there's one -- but only if
13053 * the constraint itself was dumped here
13054 */
13055 if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
13056 {
13058
13059 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
13060 fmtId(tyinfo->notnull->dobj.name));
13061
13062 if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
13064 tyinfo->dobj.namespace->dobj.name,
13065 tyinfo->rolname,
13066 tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
13068 }
13069
13072 destroyPQExpBuffer(query);
13073 free(qtypname);
13075}
13076
13077/*
13078 * dumpCompositeType
13079 * writes out to fout the queries to recreate a user-defined stand-alone
13080 * composite type
13081 */
13082static void
13084{
13085 DumpOptions *dopt = fout->dopt;
13087 PQExpBuffer dropped = createPQExpBuffer();
13090 PGresult *res;
13091 char *qtypname;
13092 char *qualtypname;
13093 int ntups;
13094 int i_attname;
13095 int i_atttypdefn;
13096 int i_attlen;
13097 int i_attalign;
13098 int i_attisdropped;
13099 int i_attcollation;
13100 int i;
13101 int actual_atts;
13102
13104 {
13105 /*
13106 * Set up query for type-specific details.
13107 *
13108 * Since we only want to dump COLLATE clauses for attributes whose
13109 * collation is different from their type's default, we use a CASE
13110 * here to suppress uninteresting attcollations cheaply. atttypid
13111 * will be 0 for dropped columns; collation does not matter for those.
13112 */
13114 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
13115 "SELECT a.attname, a.attnum, "
13116 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
13117 "a.attlen, a.attalign, a.attisdropped, "
13118 "CASE WHEN a.attcollation <> at.typcollation "
13119 "THEN a.attcollation ELSE 0 END AS attcollation "
13120 "FROM pg_catalog.pg_type ct "
13121 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
13122 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
13123 "WHERE ct.oid = $1 "
13124 "ORDER BY a.attnum");
13125
13126 ExecuteSqlStatement(fout, query->data);
13127
13129 }
13130
13131 printfPQExpBuffer(query,
13132 "EXECUTE dumpCompositeType('%u')",
13133 tyinfo->dobj.catId.oid);
13134
13135 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13136
13137 ntups = PQntuples(res);
13138
13139 i_attname = PQfnumber(res, "attname");
13140 i_atttypdefn = PQfnumber(res, "atttypdefn");
13141 i_attlen = PQfnumber(res, "attlen");
13142 i_attalign = PQfnumber(res, "attalign");
13143 i_attisdropped = PQfnumber(res, "attisdropped");
13144 i_attcollation = PQfnumber(res, "attcollation");
13145
13146 if (dopt->binary_upgrade)
13147 {
13149 tyinfo->dobj.catId.oid,
13150 false, false);
13152 }
13153
13154 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
13156
13157 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
13158 qualtypname);
13159
13160 actual_atts = 0;
13161 for (i = 0; i < ntups; i++)
13162 {
13163 char *attname;
13164 char *atttypdefn;
13165 char *attlen;
13166 char *attalign;
13167 bool attisdropped;
13168 Oid attcollation;
13169
13170 attname = PQgetvalue(res, i, i_attname);
13172 attlen = PQgetvalue(res, i, i_attlen);
13174 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13175 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13176
13177 if (attisdropped && !dopt->binary_upgrade)
13178 continue;
13179
13180 /* Format properly if not first attr */
13181 if (actual_atts++ > 0)
13182 appendPQExpBufferChar(q, ',');
13183 appendPQExpBufferStr(q, "\n\t");
13184
13185 if (!attisdropped)
13186 {
13188
13189 /* Add collation if not default for the column type */
13190 if (OidIsValid(attcollation))
13191 {
13192 CollInfo *coll;
13193
13194 coll = findCollationByOid(attcollation);
13195 if (coll)
13196 appendPQExpBuffer(q, " COLLATE %s",
13197 fmtQualifiedDumpable(coll));
13198 }
13199 }
13200 else
13201 {
13202 /*
13203 * This is a dropped attribute and we're in binary_upgrade mode.
13204 * Insert a placeholder for it in the CREATE TYPE command, and set
13205 * length and alignment with direct UPDATE to the catalogs
13206 * afterwards. See similar code in dumpTableSchema().
13207 */
13208 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13209
13210 /* stash separately for insertion after the CREATE TYPE */
13211 appendPQExpBufferStr(dropped,
13212 "\n-- For binary upgrade, recreate dropped column.\n");
13213 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13214 "SET attlen = %s, "
13215 "attalign = '%s', attbyval = false\n"
13216 "WHERE attname = ", attlen, attalign);
13218 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13220 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13221
13222 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13223 qualtypname);
13224 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13225 fmtId(attname));
13226 }
13227 }
13228 appendPQExpBufferStr(q, "\n);\n");
13229 appendPQExpBufferStr(q, dropped->data);
13230
13231 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13232
13233 if (dopt->binary_upgrade)
13235 "TYPE", qtypname,
13236 tyinfo->dobj.namespace->dobj.name);
13237
13238 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13239 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13240 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13241 .namespace = tyinfo->dobj.namespace->dobj.name,
13242 .owner = tyinfo->rolname,
13243 .description = "TYPE",
13244 .section = SECTION_PRE_DATA,
13245 .createStmt = q->data,
13246 .dropStmt = delq->data));
13247
13248
13249 /* Dump Type Comments and Security Labels */
13250 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13251 dumpComment(fout, "TYPE", qtypname,
13252 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13253 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13254
13255 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13256 dumpSecLabel(fout, "TYPE", qtypname,
13257 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13258 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13259
13260 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13261 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13262 qtypname, NULL,
13263 tyinfo->dobj.namespace->dobj.name,
13264 NULL, tyinfo->rolname, &tyinfo->dacl);
13265
13266 /* Dump any per-column comments */
13267 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13269
13270 PQclear(res);
13272 destroyPQExpBuffer(dropped);
13274 destroyPQExpBuffer(query);
13275 free(qtypname);
13277}
13278
13279/*
13280 * dumpCompositeTypeColComments
13281 * writes out to fout the queries to recreate comments on the columns of
13282 * a user-defined stand-alone composite type.
13283 *
13284 * The caller has already made a query to collect the names and attnums
13285 * of the type's columns, so we just pass that result into here rather
13286 * than reading them again.
13287 */
13288static void
13290 PGresult *res)
13291{
13293 int ncomments;
13294 PQExpBuffer query;
13295 PQExpBuffer target;
13296 int i;
13297 int ntups;
13298 int i_attname;
13299 int i_attnum;
13300 int i_attisdropped;
13301
13302 /* do nothing, if --no-comments is supplied */
13303 if (fout->dopt->no_comments)
13304 return;
13305
13306 /* Search for comments associated with type's pg_class OID */
13308 &comments);
13309
13310 /* If no comments exist, we're done */
13311 if (ncomments <= 0)
13312 return;
13313
13314 /* Build COMMENT ON statements */
13315 query = createPQExpBuffer();
13316 target = createPQExpBuffer();
13317
13318 ntups = PQntuples(res);
13319 i_attnum = PQfnumber(res, "attnum");
13320 i_attname = PQfnumber(res, "attname");
13321 i_attisdropped = PQfnumber(res, "attisdropped");
13322 while (ncomments > 0)
13323 {
13324 const char *attname;
13325
13326 attname = NULL;
13327 for (i = 0; i < ntups; i++)
13328 {
13329 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13330 PQgetvalue(res, i, i_attisdropped)[0] != 't')
13331 {
13332 attname = PQgetvalue(res, i, i_attname);
13333 break;
13334 }
13335 }
13336 if (attname) /* just in case we don't find it */
13337 {
13338 const char *descr = comments->descr;
13339
13340 resetPQExpBuffer(target);
13341 appendPQExpBuffer(target, "COLUMN %s.",
13342 fmtId(tyinfo->dobj.name));
13344
13345 resetPQExpBuffer(query);
13346 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13348 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13349 appendStringLiteralAH(query, descr, fout);
13350 appendPQExpBufferStr(query, ";\n");
13351
13353 ARCHIVE_OPTS(.tag = target->data,
13354 .namespace = tyinfo->dobj.namespace->dobj.name,
13355 .owner = tyinfo->rolname,
13356 .description = "COMMENT",
13357 .section = SECTION_NONE,
13358 .createStmt = query->data,
13359 .deps = &(tyinfo->dobj.dumpId),
13360 .nDeps = 1));
13361 }
13362
13363 comments++;
13364 ncomments--;
13365 }
13366
13367 destroyPQExpBuffer(query);
13368 destroyPQExpBuffer(target);
13369}
13370
13371/*
13372 * dumpShellType
13373 * writes out to fout the queries to create a shell type
13374 *
13375 * We dump a shell definition in advance of the I/O functions for the type.
13376 */
13377static void
13379{
13380 DumpOptions *dopt = fout->dopt;
13381 PQExpBuffer q;
13382
13383 /* Do nothing if not dumping schema */
13384 if (!dopt->dumpSchema)
13385 return;
13386
13387 q = createPQExpBuffer();
13388
13389 /*
13390 * Note the lack of a DROP command for the shell type; any required DROP
13391 * is driven off the base type entry, instead. This interacts with
13392 * _printTocEntry()'s use of the presence of a DROP command to decide
13393 * whether an entry needs an ALTER OWNER command. We don't want to alter
13394 * the shell type's owner immediately on creation; that should happen only
13395 * after it's filled in, otherwise the backend complains.
13396 */
13397
13398 if (dopt->binary_upgrade)
13400 stinfo->baseType->dobj.catId.oid,
13401 false, false);
13402
13403 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13405
13406 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13407 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13408 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13409 .namespace = stinfo->dobj.namespace->dobj.name,
13410 .owner = stinfo->baseType->rolname,
13411 .description = "SHELL TYPE",
13412 .section = SECTION_PRE_DATA,
13413 .createStmt = q->data));
13414
13416}
13417
13418/*
13419 * dumpProcLang
13420 * writes out to fout the queries to recreate a user-defined
13421 * procedural language
13422 */
13423static void
13425{
13426 DumpOptions *dopt = fout->dopt;
13429 bool useParams;
13430 char *qlanname;
13434
13435 /* Do nothing if not dumping schema */
13436 if (!dopt->dumpSchema)
13437 return;
13438
13439 /*
13440 * Try to find the support function(s). It is not an error if we don't
13441 * find them --- if the functions are in the pg_catalog schema, as is
13442 * standard in 8.1 and up, then we won't have loaded them. (In this case
13443 * we will emit a parameterless CREATE LANGUAGE command, which will
13444 * require PL template knowledge in the backend to reload.)
13445 */
13446
13447 funcInfo = findFuncByOid(plang->lanplcallfoid);
13448 if (funcInfo != NULL && !funcInfo->dobj.dump)
13449 funcInfo = NULL; /* treat not-dumped same as not-found */
13450
13451 if (OidIsValid(plang->laninline))
13452 {
13453 inlineInfo = findFuncByOid(plang->laninline);
13454 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13455 inlineInfo = NULL;
13456 }
13457
13458 if (OidIsValid(plang->lanvalidator))
13459 {
13460 validatorInfo = findFuncByOid(plang->lanvalidator);
13461 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13463 }
13464
13465 /*
13466 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13467 * parameters. Otherwise, we'll write a parameterless command, which will
13468 * be interpreted as CREATE EXTENSION.
13469 */
13470 useParams = (funcInfo != NULL &&
13471 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13472 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13473
13476
13477 qlanname = pg_strdup(fmtId(plang->dobj.name));
13478
13479 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13480 qlanname);
13481
13482 if (useParams)
13483 {
13484 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13485 plang->lanpltrusted ? "TRUSTED " : "",
13486 qlanname);
13487 appendPQExpBuffer(defqry, " HANDLER %s",
13489 if (OidIsValid(plang->laninline))
13490 appendPQExpBuffer(defqry, " INLINE %s",
13492 if (OidIsValid(plang->lanvalidator))
13493 appendPQExpBuffer(defqry, " VALIDATOR %s",
13495 }
13496 else
13497 {
13498 /*
13499 * If not dumping parameters, then use CREATE OR REPLACE so that the
13500 * command will not fail if the language is preinstalled in the target
13501 * database.
13502 *
13503 * Modern servers will interpret this as CREATE EXTENSION IF NOT
13504 * EXISTS; perhaps we should emit that instead? But it might just add
13505 * confusion.
13506 */
13507 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13508 qlanname);
13509 }
13511
13512 if (dopt->binary_upgrade)
13514 "LANGUAGE", qlanname, NULL);
13515
13516 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13517 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13518 ARCHIVE_OPTS(.tag = plang->dobj.name,
13519 .owner = plang->lanowner,
13520 .description = "PROCEDURAL LANGUAGE",
13521 .section = SECTION_PRE_DATA,
13522 .createStmt = defqry->data,
13523 .dropStmt = delqry->data,
13524 ));
13525
13526 /* Dump Proc Lang Comments and Security Labels */
13527 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13528 dumpComment(fout, "LANGUAGE", qlanname,
13529 NULL, plang->lanowner,
13530 plang->dobj.catId, 0, plang->dobj.dumpId);
13531
13532 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13533 dumpSecLabel(fout, "LANGUAGE", qlanname,
13534 NULL, plang->lanowner,
13535 plang->dobj.catId, 0, plang->dobj.dumpId);
13536
13537 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13538 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13539 qlanname, NULL, NULL,
13540 NULL, plang->lanowner, &plang->dacl);
13541
13542 free(qlanname);
13543
13546}
13547
13548/*
13549 * format_function_arguments: generate function name and argument list
13550 *
13551 * This is used when we can rely on pg_get_function_arguments to format
13552 * the argument list. Note, however, that pg_get_function_arguments
13553 * does not special-case zero-argument aggregates.
13554 */
13555static char *
13556format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13557{
13559
13562 if (is_agg && finfo->nargs == 0)
13563 appendPQExpBufferStr(&fn, "(*)");
13564 else
13565 appendPQExpBuffer(&fn, "(%s)", funcargs);
13566 return fn.data;
13567}
13568
13569/*
13570 * format_function_signature: generate function name and argument list
13571 *
13572 * Only a minimal list of input argument types is generated; this is
13573 * sufficient to reference the function, but not to define it.
13574 *
13575 * If honor_quotes is false then the function name is never quoted.
13576 * This is appropriate for use in TOC tags, but not in SQL commands.
13577 */
13578static char *
13580{
13582 int j;
13583
13585 if (honor_quotes)
13586 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13587 else
13588 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13589 for (j = 0; j < finfo->nargs; j++)
13590 {
13591 if (j > 0)
13592 appendPQExpBufferStr(&fn, ", ");
13593
13596 zeroIsError));
13597 }
13599 return fn.data;
13600}
13601
13602
13603/*
13604 * dumpFunc:
13605 * dump out one function
13606 */
13607static void
13609{
13610 DumpOptions *dopt = fout->dopt;
13611 PQExpBuffer query;
13612 PQExpBuffer q;
13615 PGresult *res;
13616 char *funcsig; /* identity signature */
13617 char *funcfullsig = NULL; /* full signature */
13618 char *funcsig_tag;
13619 char *qual_funcsig;
13620 char *proretset;
13621 char *prosrc;
13622 char *probin;
13623 char *prosqlbody;
13624 char *funcargs;
13625 char *funciargs;
13626 char *funcresult;
13627 char *protrftypes;
13628 char *prokind;
13629 char *provolatile;
13630 char *proisstrict;
13631 char *prosecdef;
13632 char *proleakproof;
13633 char *proconfig;
13634 char *procost;
13635 char *prorows;
13636 char *prosupport;
13637 char *proparallel;
13638 char *lanname;
13639 char **configitems = NULL;
13640 int nconfigitems = 0;
13641 const char *keyword;
13642
13643 /* Do nothing if not dumping schema */
13644 if (!dopt->dumpSchema)
13645 return;
13646
13647 query = createPQExpBuffer();
13648 q = createPQExpBuffer();
13651
13653 {
13654 /* Set up query for function-specific details */
13656 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13657
13659 "SELECT\n"
13660 "proretset,\n"
13661 "prosrc,\n"
13662 "probin,\n"
13663 "provolatile,\n"
13664 "proisstrict,\n"
13665 "prosecdef,\n"
13666 "lanname,\n"
13667 "proconfig,\n"
13668 "procost,\n"
13669 "prorows,\n"
13670 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13671 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13672 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13673 "proleakproof,\n");
13674
13675 if (fout->remoteVersion >= 90500)
13677 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13678 else
13680 "NULL AS protrftypes,\n");
13681
13682 if (fout->remoteVersion >= 90600)
13684 "proparallel,\n");
13685 else
13687 "'u' AS proparallel,\n");
13688
13689 if (fout->remoteVersion >= 110000)
13691 "prokind,\n");
13692 else
13694 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13695
13696 if (fout->remoteVersion >= 120000)
13698 "prosupport,\n");
13699 else
13701 "'-' AS prosupport,\n");
13702
13703 if (fout->remoteVersion >= 140000)
13705 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13706 else
13708 "NULL AS prosqlbody\n");
13709
13711 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13712 "WHERE p.oid = $1 "
13713 "AND l.oid = p.prolang");
13714
13715 ExecuteSqlStatement(fout, query->data);
13716
13718 }
13719
13720 printfPQExpBuffer(query,
13721 "EXECUTE dumpFunc('%u')",
13722 finfo->dobj.catId.oid);
13723
13724 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13725
13726 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13727 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13728 {
13729 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13730 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13731 prosqlbody = NULL;
13732 }
13733 else
13734 {
13735 prosrc = NULL;
13736 probin = NULL;
13737 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13738 }
13739 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13740 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13741 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13742 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13743 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13744 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13745 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13746 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13747 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13748 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13749 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13750 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13751 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13752 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13753 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13754
13755 /*
13756 * See backend/commands/functioncmds.c for details of how the 'AS' clause
13757 * is used.
13758 */
13759 if (prosqlbody)
13760 {
13762 }
13763 else if (probin[0] != '\0')
13764 {
13767 if (prosrc[0] != '\0')
13768 {
13770
13771 /*
13772 * where we have bin, use dollar quoting if allowed and src
13773 * contains quote or backslash; else use regular quoting.
13774 */
13775 if (dopt->disable_dollar_quoting ||
13776 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13778 else
13780 }
13781 }
13782 else
13783 {
13785 /* with no bin, dollar quote src unconditionally if allowed */
13786 if (dopt->disable_dollar_quoting)
13788 else
13790 }
13791
13792 if (*proconfig)
13793 {
13795 pg_fatal("could not parse %s array", "proconfig");
13796 }
13797 else
13798 {
13799 configitems = NULL;
13800 nconfigitems = 0;
13801 }
13802
13805
13807
13808 qual_funcsig = psprintf("%s.%s",
13809 fmtId(finfo->dobj.namespace->dobj.name),
13810 funcsig);
13811
13812 if (prokind[0] == PROKIND_PROCEDURE)
13813 keyword = "PROCEDURE";
13814 else
13815 keyword = "FUNCTION"; /* works for window functions too */
13816
13817 appendPQExpBuffer(delqry, "DROP %s %s;\n",
13818 keyword, qual_funcsig);
13819
13820 appendPQExpBuffer(q, "CREATE %s %s.%s",
13821 keyword,
13822 fmtId(finfo->dobj.namespace->dobj.name),
13824 funcsig);
13825
13826 if (prokind[0] == PROKIND_PROCEDURE)
13827 /* no result type to output */ ;
13828 else if (funcresult)
13829 appendPQExpBuffer(q, " RETURNS %s", funcresult);
13830 else
13831 appendPQExpBuffer(q, " RETURNS %s%s",
13832 (proretset[0] == 't') ? "SETOF " : "",
13834 zeroIsError));
13835
13836 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13837
13838 if (*protrftypes)
13839 {
13841 int i;
13842
13843 appendPQExpBufferStr(q, " TRANSFORM ");
13845 for (i = 0; typeids[i]; i++)
13846 {
13847 if (i != 0)
13848 appendPQExpBufferStr(q, ", ");
13849 appendPQExpBuffer(q, "FOR TYPE %s",
13851 }
13852
13853 free(typeids);
13854 }
13855
13856 if (prokind[0] == PROKIND_WINDOW)
13857 appendPQExpBufferStr(q, " WINDOW");
13858
13860 {
13862 appendPQExpBufferStr(q, " IMMUTABLE");
13863 else if (provolatile[0] == PROVOLATILE_STABLE)
13864 appendPQExpBufferStr(q, " STABLE");
13865 else if (provolatile[0] != PROVOLATILE_VOLATILE)
13866 pg_fatal("unrecognized provolatile value for function \"%s\"",
13867 finfo->dobj.name);
13868 }
13869
13870 if (proisstrict[0] == 't')
13871 appendPQExpBufferStr(q, " STRICT");
13872
13873 if (prosecdef[0] == 't')
13874 appendPQExpBufferStr(q, " SECURITY DEFINER");
13875
13876 if (proleakproof[0] == 't')
13877 appendPQExpBufferStr(q, " LEAKPROOF");
13878
13879 /*
13880 * COST and ROWS are emitted only if present and not default, so as not to
13881 * break backwards-compatibility of the dump without need. Keep this code
13882 * in sync with the defaults in functioncmds.c.
13883 */
13884 if (strcmp(procost, "0") != 0)
13885 {
13886 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13887 {
13888 /* default cost is 1 */
13889 if (strcmp(procost, "1") != 0)
13890 appendPQExpBuffer(q, " COST %s", procost);
13891 }
13892 else
13893 {
13894 /* default cost is 100 */
13895 if (strcmp(procost, "100") != 0)
13896 appendPQExpBuffer(q, " COST %s", procost);
13897 }
13898 }
13899 if (proretset[0] == 't' &&
13900 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13901 appendPQExpBuffer(q, " ROWS %s", prorows);
13902
13903 if (strcmp(prosupport, "-") != 0)
13904 {
13905 /* We rely on regprocout to provide quoting and qualification */
13906 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13907 }
13908
13910 {
13911 if (proparallel[0] == PROPARALLEL_SAFE)
13912 appendPQExpBufferStr(q, " PARALLEL SAFE");
13913 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13914 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13915 else if (proparallel[0] != PROPARALLEL_UNSAFE)
13916 pg_fatal("unrecognized proparallel value for function \"%s\"",
13917 finfo->dobj.name);
13918 }
13919
13920 for (int i = 0; i < nconfigitems; i++)
13921 {
13922 /* we feel free to scribble on configitems[] here */
13923 char *configitem = configitems[i];
13924 char *pos;
13925
13926 pos = strchr(configitem, '=');
13927 if (pos == NULL)
13928 continue;
13929 *pos++ = '\0';
13930 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13931
13932 /*
13933 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13934 * by flatten_set_variable_args() before they were put into the
13935 * proconfig array. However, because the quoting rules used there
13936 * aren't exactly like SQL's, we have to break the list value apart
13937 * and then quote the elements as string literals. (The elements may
13938 * be double-quoted as-is, but we can't just feed them to the SQL
13939 * parser; it would do the wrong thing with elements that are
13940 * zero-length or longer than NAMEDATALEN.) Also, we need a special
13941 * case for empty lists.
13942 *
13943 * Variables that are not so marked should just be emitted as simple
13944 * string literals. If the variable is not known to
13945 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13946 * to use GUC_LIST_QUOTE for extension variables.
13947 */
13949 {
13950 char **namelist;
13951 char **nameptr;
13952
13953 /* Parse string into list of identifiers */
13954 /* this shouldn't fail really */
13955 if (SplitGUCList(pos, ',', &namelist))
13956 {
13957 /* Special case: represent an empty list as NULL */
13958 if (*namelist == NULL)
13959 appendPQExpBufferStr(q, "NULL");
13960 for (nameptr = namelist; *nameptr; nameptr++)
13961 {
13962 if (nameptr != namelist)
13963 appendPQExpBufferStr(q, ", ");
13965 }
13966 }
13968 }
13969 else
13970 appendStringLiteralAH(q, pos, fout);
13971 }
13972
13973 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13974
13976 "pg_catalog.pg_proc", keyword,
13977 qual_funcsig);
13978
13979 if (dopt->binary_upgrade)
13981 keyword, funcsig,
13982 finfo->dobj.namespace->dobj.name);
13983
13984 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13985 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13987 .namespace = finfo->dobj.namespace->dobj.name,
13988 .owner = finfo->rolname,
13989 .description = keyword,
13990 .section = finfo->postponed_def ?
13992 .createStmt = q->data,
13993 .dropStmt = delqry->data));
13994
13995 /* Dump Function Comments and Security Labels */
13996 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13997 dumpComment(fout, keyword, funcsig,
13998 finfo->dobj.namespace->dobj.name, finfo->rolname,
13999 finfo->dobj.catId, 0, finfo->dobj.dumpId);
14000
14001 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
14002 dumpSecLabel(fout, keyword, funcsig,
14003 finfo->dobj.namespace->dobj.name, finfo->rolname,
14004 finfo->dobj.catId, 0, finfo->dobj.dumpId);
14005
14006 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
14007 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
14008 funcsig, NULL,
14009 finfo->dobj.namespace->dobj.name,
14010 NULL, finfo->rolname, &finfo->dacl);
14011
14012 PQclear(res);
14013
14014 destroyPQExpBuffer(query);
14018 free(funcsig);
14023}
14024
14025
14026/*
14027 * Dump a user-defined cast
14028 */
14029static void
14031{
14032 DumpOptions *dopt = fout->dopt;
14038 const char *sourceType;
14039 const char *targetType;
14040
14041 /* Do nothing if not dumping schema */
14042 if (!dopt->dumpSchema)
14043 return;
14044
14045 /* Cannot dump if we don't have the cast function's info */
14046 if (OidIsValid(cast->castfunc))
14047 {
14048 funcInfo = findFuncByOid(cast->castfunc);
14049 if (funcInfo == NULL)
14050 pg_fatal("could not find function definition for function with OID %u",
14051 cast->castfunc);
14052 }
14053
14058
14061 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
14063
14064 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
14066
14067 switch (cast->castmethod)
14068 {
14070 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
14071 break;
14073 appendPQExpBufferStr(defqry, "WITH INOUT");
14074 break;
14076 if (funcInfo)
14077 {
14079
14080 /*
14081 * Always qualify the function name (format_function_signature
14082 * won't qualify it).
14083 */
14084 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
14085 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
14086 free(fsig);
14087 }
14088 else
14089 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
14090 break;
14091 default:
14092 pg_log_warning("bogus value in pg_cast.castmethod field");
14093 }
14094
14095 if (cast->castcontext == 'a')
14096 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
14097 else if (cast->castcontext == 'i')
14098 appendPQExpBufferStr(defqry, " AS IMPLICIT");
14100
14101 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
14103
14104 appendPQExpBuffer(castargs, "(%s AS %s)",
14106
14107 if (dopt->binary_upgrade)
14109 "CAST", castargs->data, NULL);
14110
14111 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
14112 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
14113 ARCHIVE_OPTS(.tag = labelq->data,
14114 .description = "CAST",
14115 .section = SECTION_PRE_DATA,
14116 .createStmt = defqry->data,
14117 .dropStmt = delqry->data));
14118
14119 /* Dump Cast Comments */
14120 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
14121 dumpComment(fout, "CAST", castargs->data,
14122 NULL, "",
14123 cast->dobj.catId, 0, cast->dobj.dumpId);
14124
14129}
14130
14131/*
14132 * Dump a transform
14133 */
14134static void
14136{
14137 DumpOptions *dopt = fout->dopt;
14144 char *lanname;
14145 const char *transformType;
14146
14147 /* Do nothing if not dumping schema */
14148 if (!dopt->dumpSchema)
14149 return;
14150
14151 /* Cannot dump if we don't have the transform functions' info */
14152 if (OidIsValid(transform->trffromsql))
14153 {
14155 if (fromsqlFuncInfo == NULL)
14156 pg_fatal("could not find function definition for function with OID %u",
14157 transform->trffromsql);
14158 }
14159 if (OidIsValid(transform->trftosql))
14160 {
14161 tosqlFuncInfo = findFuncByOid(transform->trftosql);
14162 if (tosqlFuncInfo == NULL)
14163 pg_fatal("could not find function definition for function with OID %u",
14164 transform->trftosql);
14165 }
14166
14171
14172 lanname = get_language_name(fout, transform->trflang);
14174
14175 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14177
14178 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14180
14181 if (!transform->trffromsql && !transform->trftosql)
14182 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14183
14184 if (transform->trffromsql)
14185 {
14186 if (fromsqlFuncInfo)
14187 {
14189
14190 /*
14191 * Always qualify the function name (format_function_signature
14192 * won't qualify it).
14193 */
14194 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14195 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14196 free(fsig);
14197 }
14198 else
14199 pg_log_warning("bogus value in pg_transform.trffromsql field");
14200 }
14201
14202 if (transform->trftosql)
14203 {
14204 if (transform->trffromsql)
14206
14207 if (tosqlFuncInfo)
14208 {
14210
14211 /*
14212 * Always qualify the function name (format_function_signature
14213 * won't qualify it).
14214 */
14215 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14216 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14217 free(fsig);
14218 }
14219 else
14220 pg_log_warning("bogus value in pg_transform.trftosql field");
14221 }
14222
14224
14225 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14227
14228 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14230
14231 if (dopt->binary_upgrade)
14233 "TRANSFORM", transformargs->data, NULL);
14234
14235 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14236 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14237 ARCHIVE_OPTS(.tag = labelq->data,
14238 .description = "TRANSFORM",
14239 .section = SECTION_PRE_DATA,
14240 .createStmt = defqry->data,
14241 .dropStmt = delqry->data,
14242 .deps = transform->dobj.dependencies,
14243 .nDeps = transform->dobj.nDeps));
14244
14245 /* Dump Transform Comments */
14246 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14247 dumpComment(fout, "TRANSFORM", transformargs->data,
14248 NULL, "",
14249 transform->dobj.catId, 0, transform->dobj.dumpId);
14250
14251 free(lanname);
14256}
14257
14258
14259/*
14260 * dumpOpr
14261 * write out a single operator definition
14262 */
14263static void
14265{
14266 DumpOptions *dopt = fout->dopt;
14267 PQExpBuffer query;
14268 PQExpBuffer q;
14271 PQExpBuffer details;
14272 PGresult *res;
14273 int i_oprkind;
14274 int i_oprcode;
14275 int i_oprleft;
14276 int i_oprright;
14277 int i_oprcom;
14278 int i_oprnegate;
14279 int i_oprrest;
14280 int i_oprjoin;
14281 int i_oprcanmerge;
14282 int i_oprcanhash;
14283 char *oprkind;
14284 char *oprcode;
14285 char *oprleft;
14286 char *oprright;
14287 char *oprcom;
14288 char *oprnegate;
14289 char *oprrest;
14290 char *oprjoin;
14291 char *oprcanmerge;
14292 char *oprcanhash;
14293 char *oprregproc;
14294 char *oprref;
14295
14296 /* Do nothing if not dumping schema */
14297 if (!dopt->dumpSchema)
14298 return;
14299
14300 /*
14301 * some operators are invalid because they were the result of user
14302 * defining operators before commutators exist
14303 */
14304 if (!OidIsValid(oprinfo->oprcode))
14305 return;
14306
14307 query = createPQExpBuffer();
14308 q = createPQExpBuffer();
14311 details = createPQExpBuffer();
14312
14314 {
14315 /* Set up query for operator-specific details */
14317 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14318 "SELECT oprkind, "
14319 "oprcode::pg_catalog.regprocedure, "
14320 "oprleft::pg_catalog.regtype, "
14321 "oprright::pg_catalog.regtype, "
14322 "oprcom, "
14323 "oprnegate, "
14324 "oprrest::pg_catalog.regprocedure, "
14325 "oprjoin::pg_catalog.regprocedure, "
14326 "oprcanmerge, oprcanhash "
14327 "FROM pg_catalog.pg_operator "
14328 "WHERE oid = $1");
14329
14330 ExecuteSqlStatement(fout, query->data);
14331
14333 }
14334
14335 printfPQExpBuffer(query,
14336 "EXECUTE dumpOpr('%u')",
14337 oprinfo->dobj.catId.oid);
14338
14339 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14340
14341 i_oprkind = PQfnumber(res, "oprkind");
14342 i_oprcode = PQfnumber(res, "oprcode");
14343 i_oprleft = PQfnumber(res, "oprleft");
14344 i_oprright = PQfnumber(res, "oprright");
14345 i_oprcom = PQfnumber(res, "oprcom");
14346 i_oprnegate = PQfnumber(res, "oprnegate");
14347 i_oprrest = PQfnumber(res, "oprrest");
14348 i_oprjoin = PQfnumber(res, "oprjoin");
14349 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14350 i_oprcanhash = PQfnumber(res, "oprcanhash");
14351
14352 oprkind = PQgetvalue(res, 0, i_oprkind);
14353 oprcode = PQgetvalue(res, 0, i_oprcode);
14354 oprleft = PQgetvalue(res, 0, i_oprleft);
14355 oprright = PQgetvalue(res, 0, i_oprright);
14356 oprcom = PQgetvalue(res, 0, i_oprcom);
14357 oprnegate = PQgetvalue(res, 0, i_oprnegate);
14358 oprrest = PQgetvalue(res, 0, i_oprrest);
14359 oprjoin = PQgetvalue(res, 0, i_oprjoin);
14362
14363 /* In PG14 upwards postfix operator support does not exist anymore. */
14364 if (strcmp(oprkind, "r") == 0)
14365 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14366 oprcode);
14367
14369 if (oprregproc)
14370 {
14371 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14373 }
14374
14375 appendPQExpBuffer(oprid, "%s (",
14376 oprinfo->dobj.name);
14377
14378 /*
14379 * right unary means there's a left arg and left unary means there's a
14380 * right arg. (Although the "r" case is dead code for PG14 and later,
14381 * continue to support it in case we're dumping from an old server.)
14382 */
14383 if (strcmp(oprkind, "r") == 0 ||
14384 strcmp(oprkind, "b") == 0)
14385 {
14386 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14387 appendPQExpBufferStr(oprid, oprleft);
14388 }
14389 else
14390 appendPQExpBufferStr(oprid, "NONE");
14391
14392 if (strcmp(oprkind, "l") == 0 ||
14393 strcmp(oprkind, "b") == 0)
14394 {
14395 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14396 appendPQExpBuffer(oprid, ", %s)", oprright);
14397 }
14398 else
14399 appendPQExpBufferStr(oprid, ", NONE)");
14400
14402 if (oprref)
14403 {
14404 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14405 free(oprref);
14406 }
14407
14409 if (oprref)
14410 {
14411 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14412 free(oprref);
14413 }
14414
14415 if (strcmp(oprcanmerge, "t") == 0)
14416 appendPQExpBufferStr(details, ",\n MERGES");
14417
14418 if (strcmp(oprcanhash, "t") == 0)
14419 appendPQExpBufferStr(details, ",\n HASHES");
14420
14422 if (oprregproc)
14423 {
14424 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14426 }
14427
14429 if (oprregproc)
14430 {
14431 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14433 }
14434
14435 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14436 fmtId(oprinfo->dobj.namespace->dobj.name),
14437 oprid->data);
14438
14439 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14440 fmtId(oprinfo->dobj.namespace->dobj.name),
14441 oprinfo->dobj.name, details->data);
14442
14443 if (dopt->binary_upgrade)
14445 "OPERATOR", oprid->data,
14446 oprinfo->dobj.namespace->dobj.name);
14447
14448 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14449 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14450 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14451 .namespace = oprinfo->dobj.namespace->dobj.name,
14452 .owner = oprinfo->rolname,
14453 .description = "OPERATOR",
14454 .section = SECTION_PRE_DATA,
14455 .createStmt = q->data,
14456 .dropStmt = delq->data));
14457
14458 /* Dump Operator Comments */
14459 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14460 dumpComment(fout, "OPERATOR", oprid->data,
14461 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14462 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14463
14464 PQclear(res);
14465
14466 destroyPQExpBuffer(query);
14470 destroyPQExpBuffer(details);
14471}
14472
14473/*
14474 * Convert a function reference obtained from pg_operator
14475 *
14476 * Returns allocated string of what to print, or NULL if function references
14477 * is InvalidOid. Returned string is expected to be free'd by the caller.
14478 *
14479 * The input is a REGPROCEDURE display; we have to strip the argument-types
14480 * part.
14481 */
14482static char *
14484{
14485 char *name;
14486 char *paren;
14487 bool inquote;
14488
14489 /* In all cases "-" means a null reference */
14490 if (strcmp(proc, "-") == 0)
14491 return NULL;
14492
14493 name = pg_strdup(proc);
14494 /* find non-double-quoted left paren */
14495 inquote = false;
14496 for (paren = name; *paren; paren++)
14497 {
14498 if (*paren == '(' && !inquote)
14499 {
14500 *paren = '\0';
14501 break;
14502 }
14503 if (*paren == '"')
14504 inquote = !inquote;
14505 }
14506 return name;
14507}
14508
14509/*
14510 * getFormattedOperatorName - retrieve the operator name for the
14511 * given operator OID (presented in string form).
14512 *
14513 * Returns an allocated string, or NULL if the given OID is invalid.
14514 * Caller is responsible for free'ing result string.
14515 *
14516 * What we produce has the format "OPERATOR(schema.oprname)". This is only
14517 * useful in commands where the operator's argument types can be inferred from
14518 * context. We always schema-qualify the name, though. The predecessor to
14519 * this code tried to skip the schema qualification if possible, but that led
14520 * to wrong results in corner cases, such as if an operator and its negator
14521 * are in different schemas.
14522 */
14523static char *
14525{
14527
14528 /* In all cases "0" means a null reference */
14529 if (strcmp(oproid, "0") == 0)
14530 return NULL;
14531
14533 if (oprInfo == NULL)
14534 {
14535 pg_log_warning("could not find operator with OID %s",
14536 oproid);
14537 return NULL;
14538 }
14539
14540 return psprintf("OPERATOR(%s.%s)",
14541 fmtId(oprInfo->dobj.namespace->dobj.name),
14542 oprInfo->dobj.name);
14543}
14544
14545/*
14546 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14547 *
14548 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14549 * argument lists of these functions are predetermined. Note that the
14550 * caller should ensure we are in the proper schema, because the results
14551 * are search path dependent!
14552 */
14553static char *
14555{
14556 char *result;
14557 char query[128];
14558 PGresult *res;
14559
14560 snprintf(query, sizeof(query),
14561 "SELECT '%u'::pg_catalog.regproc", funcOid);
14562 res = ExecuteSqlQueryForSingleRow(fout, query);
14563
14564 result = pg_strdup(PQgetvalue(res, 0, 0));
14565
14566 PQclear(res);
14567
14568 return result;
14569}
14570
14571/*
14572 * dumpAccessMethod
14573 * write out a single access method definition
14574 */
14575static void
14577{
14578 DumpOptions *dopt = fout->dopt;
14579 PQExpBuffer q;
14581 char *qamname;
14582
14583 /* Do nothing if not dumping schema */
14584 if (!dopt->dumpSchema)
14585 return;
14586
14587 q = createPQExpBuffer();
14589
14590 qamname = pg_strdup(fmtId(aminfo->dobj.name));
14591
14592 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14593
14594 switch (aminfo->amtype)
14595 {
14596 case AMTYPE_INDEX:
14597 appendPQExpBufferStr(q, "TYPE INDEX ");
14598 break;
14599 case AMTYPE_TABLE:
14600 appendPQExpBufferStr(q, "TYPE TABLE ");
14601 break;
14602 default:
14603 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14604 aminfo->amtype, qamname);
14607 free(qamname);
14608 return;
14609 }
14610
14611 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14612
14613 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14614 qamname);
14615
14616 if (dopt->binary_upgrade)
14618 "ACCESS METHOD", qamname, NULL);
14619
14620 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14622 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14623 .description = "ACCESS METHOD",
14624 .section = SECTION_PRE_DATA,
14625 .createStmt = q->data,
14626 .dropStmt = delq->data));
14627
14628 /* Dump Access Method Comments */
14629 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14630 dumpComment(fout, "ACCESS METHOD", qamname,
14631 NULL, "",
14632 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14633
14636 free(qamname);
14637}
14638
14639/*
14640 * dumpOpclass
14641 * write out a single operator class definition
14642 */
14643static void
14645{
14646 DumpOptions *dopt = fout->dopt;
14647 PQExpBuffer query;
14648 PQExpBuffer q;
14651 PGresult *res;
14652 int ntups;
14653 int i_opcintype;
14654 int i_opckeytype;
14655 int i_opcdefault;
14656 int i_opcfamily;
14657 int i_opcfamilyname;
14658 int i_opcfamilynsp;
14659 int i_amname;
14660 int i_amopstrategy;
14661 int i_amopopr;
14662 int i_sortfamily;
14663 int i_sortfamilynsp;
14664 int i_amprocnum;
14665 int i_amproc;
14666 int i_amproclefttype;
14668 char *opcintype;
14669 char *opckeytype;
14670 char *opcdefault;
14671 char *opcfamily;
14672 char *opcfamilyname;
14673 char *opcfamilynsp;
14674 char *amname;
14675 char *amopstrategy;
14676 char *amopopr;
14677 char *sortfamily;
14678 char *sortfamilynsp;
14679 char *amprocnum;
14680 char *amproc;
14681 char *amproclefttype;
14682 char *amprocrighttype;
14683 bool needComma;
14684 int i;
14685
14686 /* Do nothing if not dumping schema */
14687 if (!dopt->dumpSchema)
14688 return;
14689
14690 query = createPQExpBuffer();
14691 q = createPQExpBuffer();
14694
14695 /* Get additional fields from the pg_opclass row */
14696 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14697 "opckeytype::pg_catalog.regtype, "
14698 "opcdefault, opcfamily, "
14699 "opfname AS opcfamilyname, "
14700 "nspname AS opcfamilynsp, "
14701 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14702 "FROM pg_catalog.pg_opclass c "
14703 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14704 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14705 "WHERE c.oid = '%u'::pg_catalog.oid",
14706 opcinfo->dobj.catId.oid);
14707
14708 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14709
14710 i_opcintype = PQfnumber(res, "opcintype");
14711 i_opckeytype = PQfnumber(res, "opckeytype");
14712 i_opcdefault = PQfnumber(res, "opcdefault");
14713 i_opcfamily = PQfnumber(res, "opcfamily");
14714 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14715 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14716 i_amname = PQfnumber(res, "amname");
14717
14718 /* opcintype may still be needed after we PQclear res */
14719 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14722 /* opcfamily will still be needed after we PQclear res */
14723 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14726 /* amname will still be needed after we PQclear res */
14727 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14728
14729 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14731 appendPQExpBuffer(delq, " USING %s;\n",
14732 fmtId(amname));
14733
14734 /* Build the fixed portion of the CREATE command */
14735 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14737 if (strcmp(opcdefault, "t") == 0)
14738 appendPQExpBufferStr(q, "DEFAULT ");
14739 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14740 opcintype,
14741 fmtId(amname));
14742 if (strlen(opcfamilyname) > 0)
14743 {
14744 appendPQExpBufferStr(q, " FAMILY ");
14747 }
14748 appendPQExpBufferStr(q, " AS\n ");
14749
14750 needComma = false;
14751
14752 if (strcmp(opckeytype, "-") != 0)
14753 {
14754 appendPQExpBuffer(q, "STORAGE %s",
14755 opckeytype);
14756 needComma = true;
14757 }
14758
14759 PQclear(res);
14760
14761 /*
14762 * Now fetch and print the OPERATOR entries (pg_amop rows).
14763 *
14764 * Print only those opfamily members that are tied to the opclass by
14765 * pg_depend entries.
14766 */
14767 resetPQExpBuffer(query);
14768 appendPQExpBuffer(query, "SELECT amopstrategy, "
14769 "amopopr::pg_catalog.regoperator, "
14770 "opfname AS sortfamily, "
14771 "nspname AS sortfamilynsp "
14772 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14773 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14774 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14775 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14776 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14777 "AND refobjid = '%u'::pg_catalog.oid "
14778 "AND amopfamily = '%s'::pg_catalog.oid "
14779 "ORDER BY amopstrategy",
14780 opcinfo->dobj.catId.oid,
14781 opcfamily);
14782
14783 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14784
14785 ntups = PQntuples(res);
14786
14787 i_amopstrategy = PQfnumber(res, "amopstrategy");
14788 i_amopopr = PQfnumber(res, "amopopr");
14789 i_sortfamily = PQfnumber(res, "sortfamily");
14790 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14791
14792 for (i = 0; i < ntups; i++)
14793 {
14795 amopopr = PQgetvalue(res, i, i_amopopr);
14796 sortfamily = PQgetvalue(res, i, i_sortfamily);
14798
14799 if (needComma)
14800 appendPQExpBufferStr(q, " ,\n ");
14801
14802 appendPQExpBuffer(q, "OPERATOR %s %s",
14804
14805 if (strlen(sortfamily) > 0)
14806 {
14807 appendPQExpBufferStr(q, " FOR ORDER BY ");
14809 appendPQExpBufferStr(q, fmtId(sortfamily));
14810 }
14811
14812 needComma = true;
14813 }
14814
14815 PQclear(res);
14816
14817 /*
14818 * Now fetch and print the FUNCTION entries (pg_amproc rows).
14819 *
14820 * Print only those opfamily members that are tied to the opclass by
14821 * pg_depend entries.
14822 *
14823 * We print the amproclefttype/amprocrighttype even though in most cases
14824 * the backend could deduce the right values, because of the corner case
14825 * of a btree sort support function for a cross-type comparison.
14826 */
14827 resetPQExpBuffer(query);
14828
14829 appendPQExpBuffer(query, "SELECT amprocnum, "
14830 "amproc::pg_catalog.regprocedure, "
14831 "amproclefttype::pg_catalog.regtype, "
14832 "amprocrighttype::pg_catalog.regtype "
14833 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14834 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14835 "AND refobjid = '%u'::pg_catalog.oid "
14836 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14837 "AND objid = ap.oid "
14838 "ORDER BY amprocnum",
14839 opcinfo->dobj.catId.oid);
14840
14841 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14842
14843 ntups = PQntuples(res);
14844
14845 i_amprocnum = PQfnumber(res, "amprocnum");
14846 i_amproc = PQfnumber(res, "amproc");
14847 i_amproclefttype = PQfnumber(res, "amproclefttype");
14848 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14849
14850 for (i = 0; i < ntups; i++)
14851 {
14853 amproc = PQgetvalue(res, i, i_amproc);
14856
14857 if (needComma)
14858 appendPQExpBufferStr(q, " ,\n ");
14859
14860 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14861
14864
14865 appendPQExpBuffer(q, " %s", amproc);
14866
14867 needComma = true;
14868 }
14869
14870 PQclear(res);
14871
14872 /*
14873 * If needComma is still false it means we haven't added anything after
14874 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14875 * clause with the same datatype. This isn't sanctioned by the
14876 * documentation, but actually DefineOpClass will treat it as a no-op.
14877 */
14878 if (!needComma)
14879 appendPQExpBuffer(q, "STORAGE %s", opcintype);
14880
14881 appendPQExpBufferStr(q, ";\n");
14882
14884 appendPQExpBuffer(nameusing, " USING %s",
14885 fmtId(amname));
14886
14887 if (dopt->binary_upgrade)
14889 "OPERATOR CLASS", nameusing->data,
14890 opcinfo->dobj.namespace->dobj.name);
14891
14892 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14893 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14894 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14895 .namespace = opcinfo->dobj.namespace->dobj.name,
14896 .owner = opcinfo->rolname,
14897 .description = "OPERATOR CLASS",
14898 .section = SECTION_PRE_DATA,
14899 .createStmt = q->data,
14900 .dropStmt = delq->data));
14901
14902 /* Dump Operator Class Comments */
14903 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14904 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14905 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14906 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14907
14908 free(opcintype);
14909 free(opcfamily);
14910 free(amname);
14911 destroyPQExpBuffer(query);
14915}
14916
14917/*
14918 * dumpOpfamily
14919 * write out a single operator family definition
14920 *
14921 * Note: this also dumps any "loose" operator members that aren't bound to a
14922 * specific opclass within the opfamily.
14923 */
14924static void
14926{
14927 DumpOptions *dopt = fout->dopt;
14928 PQExpBuffer query;
14929 PQExpBuffer q;
14932 PGresult *res;
14935 int ntups;
14936 int i_amname;
14937 int i_amopstrategy;
14938 int i_amopopr;
14939 int i_sortfamily;
14940 int i_sortfamilynsp;
14941 int i_amprocnum;
14942 int i_amproc;
14943 int i_amproclefttype;
14945 char *amname;
14946 char *amopstrategy;
14947 char *amopopr;
14948 char *sortfamily;
14949 char *sortfamilynsp;
14950 char *amprocnum;
14951 char *amproc;
14952 char *amproclefttype;
14953 char *amprocrighttype;
14954 bool needComma;
14955 int i;
14956
14957 /* Do nothing if not dumping schema */
14958 if (!dopt->dumpSchema)
14959 return;
14960
14961 query = createPQExpBuffer();
14962 q = createPQExpBuffer();
14965
14966 /*
14967 * Fetch only those opfamily members that are tied directly to the
14968 * opfamily by pg_depend entries.
14969 */
14970 appendPQExpBuffer(query, "SELECT amopstrategy, "
14971 "amopopr::pg_catalog.regoperator, "
14972 "opfname AS sortfamily, "
14973 "nspname AS sortfamilynsp "
14974 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14975 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14976 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14977 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14978 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14979 "AND refobjid = '%u'::pg_catalog.oid "
14980 "AND amopfamily = '%u'::pg_catalog.oid "
14981 "ORDER BY amopstrategy",
14982 opfinfo->dobj.catId.oid,
14983 opfinfo->dobj.catId.oid);
14984
14986
14987 resetPQExpBuffer(query);
14988
14989 appendPQExpBuffer(query, "SELECT amprocnum, "
14990 "amproc::pg_catalog.regprocedure, "
14991 "amproclefttype::pg_catalog.regtype, "
14992 "amprocrighttype::pg_catalog.regtype "
14993 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14994 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14995 "AND refobjid = '%u'::pg_catalog.oid "
14996 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14997 "AND objid = ap.oid "
14998 "ORDER BY amprocnum",
14999 opfinfo->dobj.catId.oid);
15000
15002
15003 /* Get additional fields from the pg_opfamily row */
15004 resetPQExpBuffer(query);
15005
15006 appendPQExpBuffer(query, "SELECT "
15007 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
15008 "FROM pg_catalog.pg_opfamily "
15009 "WHERE oid = '%u'::pg_catalog.oid",
15010 opfinfo->dobj.catId.oid);
15011
15012 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15013
15014 i_amname = PQfnumber(res, "amname");
15015
15016 /* amname will still be needed after we PQclear res */
15017 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
15018
15019 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
15021 appendPQExpBuffer(delq, " USING %s;\n",
15022 fmtId(amname));
15023
15024 /* Build the fixed portion of the CREATE command */
15025 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
15027 appendPQExpBuffer(q, " USING %s;\n",
15028 fmtId(amname));
15029
15030 PQclear(res);
15031
15032 /* Do we need an ALTER to add loose members? */
15033 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
15034 {
15035 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
15037 appendPQExpBuffer(q, " USING %s ADD\n ",
15038 fmtId(amname));
15039
15040 needComma = false;
15041
15042 /*
15043 * Now fetch and print the OPERATOR entries (pg_amop rows).
15044 */
15045 ntups = PQntuples(res_ops);
15046
15047 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
15048 i_amopopr = PQfnumber(res_ops, "amopopr");
15049 i_sortfamily = PQfnumber(res_ops, "sortfamily");
15050 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
15051
15052 for (i = 0; i < ntups; i++)
15053 {
15056 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
15058
15059 if (needComma)
15060 appendPQExpBufferStr(q, " ,\n ");
15061
15062 appendPQExpBuffer(q, "OPERATOR %s %s",
15064
15065 if (strlen(sortfamily) > 0)
15066 {
15067 appendPQExpBufferStr(q, " FOR ORDER BY ");
15069 appendPQExpBufferStr(q, fmtId(sortfamily));
15070 }
15071
15072 needComma = true;
15073 }
15074
15075 /*
15076 * Now fetch and print the FUNCTION entries (pg_amproc rows).
15077 */
15078 ntups = PQntuples(res_procs);
15079
15080 i_amprocnum = PQfnumber(res_procs, "amprocnum");
15081 i_amproc = PQfnumber(res_procs, "amproc");
15082 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
15083 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
15084
15085 for (i = 0; i < ntups; i++)
15086 {
15091
15092 if (needComma)
15093 appendPQExpBufferStr(q, " ,\n ");
15094
15095 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
15097 amproc);
15098
15099 needComma = true;
15100 }
15101
15102 appendPQExpBufferStr(q, ";\n");
15103 }
15104
15106 appendPQExpBuffer(nameusing, " USING %s",
15107 fmtId(amname));
15108
15109 if (dopt->binary_upgrade)
15111 "OPERATOR FAMILY", nameusing->data,
15112 opfinfo->dobj.namespace->dobj.name);
15113
15114 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15115 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
15116 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
15117 .namespace = opfinfo->dobj.namespace->dobj.name,
15118 .owner = opfinfo->rolname,
15119 .description = "OPERATOR FAMILY",
15120 .section = SECTION_PRE_DATA,
15121 .createStmt = q->data,
15122 .dropStmt = delq->data));
15123
15124 /* Dump Operator Family Comments */
15125 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15126 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
15127 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
15128 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
15129
15130 free(amname);
15133 destroyPQExpBuffer(query);
15137}
15138
15139/*
15140 * dumpCollation
15141 * write out a single collation definition
15142 */
15143static void
15145{
15146 DumpOptions *dopt = fout->dopt;
15147 PQExpBuffer query;
15148 PQExpBuffer q;
15150 char *qcollname;
15151 PGresult *res;
15152 int i_collprovider;
15154 int i_collcollate;
15155 int i_collctype;
15156 int i_colllocale;
15157 int i_collicurules;
15158 const char *collprovider;
15159 const char *collcollate;
15160 const char *collctype;
15161 const char *colllocale;
15162 const char *collicurules;
15163
15164 /* Do nothing if not dumping schema */
15165 if (!dopt->dumpSchema)
15166 return;
15167
15168 query = createPQExpBuffer();
15169 q = createPQExpBuffer();
15171
15172 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15173
15174 /* Get collation-specific details */
15175 appendPQExpBufferStr(query, "SELECT ");
15176
15177 if (fout->remoteVersion >= 100000)
15179 "collprovider, "
15180 "collversion, ");
15181 else
15183 "'c' AS collprovider, "
15184 "NULL AS collversion, ");
15185
15186 if (fout->remoteVersion >= 120000)
15188 "collisdeterministic, ");
15189 else
15191 "true AS collisdeterministic, ");
15192
15193 if (fout->remoteVersion >= 170000)
15195 "colllocale, ");
15196 else if (fout->remoteVersion >= 150000)
15198 "colliculocale AS colllocale, ");
15199 else
15201 "NULL AS colllocale, ");
15202
15203 if (fout->remoteVersion >= 160000)
15205 "collicurules, ");
15206 else
15208 "NULL AS collicurules, ");
15209
15210 appendPQExpBuffer(query,
15211 "collcollate, "
15212 "collctype "
15213 "FROM pg_catalog.pg_collation c "
15214 "WHERE c.oid = '%u'::pg_catalog.oid",
15215 collinfo->dobj.catId.oid);
15216
15217 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15218
15219 i_collprovider = PQfnumber(res, "collprovider");
15220 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15221 i_collcollate = PQfnumber(res, "collcollate");
15222 i_collctype = PQfnumber(res, "collctype");
15223 i_colllocale = PQfnumber(res, "colllocale");
15224 i_collicurules = PQfnumber(res, "collicurules");
15225
15227
15228 if (!PQgetisnull(res, 0, i_collcollate))
15230 else
15231 collcollate = NULL;
15232
15233 if (!PQgetisnull(res, 0, i_collctype))
15234 collctype = PQgetvalue(res, 0, i_collctype);
15235 else
15236 collctype = NULL;
15237
15238 /*
15239 * Before version 15, collcollate and collctype were of type NAME and
15240 * non-nullable. Treat empty strings as NULL for consistency.
15241 */
15242 if (fout->remoteVersion < 150000)
15243 {
15244 if (collcollate[0] == '\0')
15245 collcollate = NULL;
15246 if (collctype[0] == '\0')
15247 collctype = NULL;
15248 }
15249
15250 if (!PQgetisnull(res, 0, i_colllocale))
15252 else
15253 colllocale = NULL;
15254
15255 if (!PQgetisnull(res, 0, i_collicurules))
15257 else
15259
15260 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15262
15263 appendPQExpBuffer(q, "CREATE COLLATION %s (",
15265
15266 appendPQExpBufferStr(q, "provider = ");
15267 if (collprovider[0] == 'b')
15268 appendPQExpBufferStr(q, "builtin");
15269 else if (collprovider[0] == 'c')
15270 appendPQExpBufferStr(q, "libc");
15271 else if (collprovider[0] == 'i')
15272 appendPQExpBufferStr(q, "icu");
15273 else if (collprovider[0] == 'd')
15274 /* to allow dumping pg_catalog; not accepted on input */
15275 appendPQExpBufferStr(q, "default");
15276 else
15277 pg_fatal("unrecognized collation provider: %s",
15278 collprovider);
15279
15280 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15281 appendPQExpBufferStr(q, ", deterministic = false");
15282
15283 if (collprovider[0] == 'd')
15284 {
15286 pg_log_warning("invalid collation \"%s\"", qcollname);
15287
15288 /* no locale -- the default collation cannot be reloaded anyway */
15289 }
15290 else if (collprovider[0] == 'b')
15291 {
15293 pg_log_warning("invalid collation \"%s\"", qcollname);
15294
15295 appendPQExpBufferStr(q, ", locale = ");
15297 fout);
15298 }
15299 else if (collprovider[0] == 'i')
15300 {
15301 if (fout->remoteVersion >= 150000)
15302 {
15303 if (collcollate || collctype || !colllocale)
15304 pg_log_warning("invalid collation \"%s\"", qcollname);
15305
15306 appendPQExpBufferStr(q, ", locale = ");
15308 fout);
15309 }
15310 else
15311 {
15312 if (!collcollate || !collctype || colllocale ||
15314 pg_log_warning("invalid collation \"%s\"", qcollname);
15315
15316 appendPQExpBufferStr(q, ", locale = ");
15318 }
15319
15320 if (collicurules)
15321 {
15322 appendPQExpBufferStr(q, ", rules = ");
15324 }
15325 }
15326 else if (collprovider[0] == 'c')
15327 {
15329 pg_log_warning("invalid collation \"%s\"", qcollname);
15330
15332 {
15333 appendPQExpBufferStr(q, ", locale = ");
15335 }
15336 else
15337 {
15338 appendPQExpBufferStr(q, ", lc_collate = ");
15340 appendPQExpBufferStr(q, ", lc_ctype = ");
15342 }
15343 }
15344 else
15345 pg_fatal("unrecognized collation provider: %s", collprovider);
15346
15347 /*
15348 * For binary upgrade, carry over the collation version. For normal
15349 * dump/restore, omit the version, so that it is computed upon restore.
15350 */
15351 if (dopt->binary_upgrade)
15352 {
15353 int i_collversion;
15354
15355 i_collversion = PQfnumber(res, "collversion");
15356 if (!PQgetisnull(res, 0, i_collversion))
15357 {
15358 appendPQExpBufferStr(q, ", version = ");
15360 PQgetvalue(res, 0, i_collversion),
15361 fout);
15362 }
15363 }
15364
15365 appendPQExpBufferStr(q, ");\n");
15366
15367 if (dopt->binary_upgrade)
15369 "COLLATION", qcollname,
15370 collinfo->dobj.namespace->dobj.name);
15371
15372 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15373 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15374 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15375 .namespace = collinfo->dobj.namespace->dobj.name,
15376 .owner = collinfo->rolname,
15377 .description = "COLLATION",
15378 .section = SECTION_PRE_DATA,
15379 .createStmt = q->data,
15380 .dropStmt = delq->data));
15381
15382 /* Dump Collation Comments */
15383 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15384 dumpComment(fout, "COLLATION", qcollname,
15385 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15386 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15387
15388 PQclear(res);
15389
15390 destroyPQExpBuffer(query);
15393 free(qcollname);
15394}
15395
15396/*
15397 * dumpConversion
15398 * write out a single conversion definition
15399 */
15400static void
15402{
15403 DumpOptions *dopt = fout->dopt;
15404 PQExpBuffer query;
15405 PQExpBuffer q;
15407 char *qconvname;
15408 PGresult *res;
15409 int i_conforencoding;
15410 int i_contoencoding;
15411 int i_conproc;
15412 int i_condefault;
15413 const char *conforencoding;
15414 const char *contoencoding;
15415 const char *conproc;
15416 bool condefault;
15417
15418 /* Do nothing if not dumping schema */
15419 if (!dopt->dumpSchema)
15420 return;
15421
15422 query = createPQExpBuffer();
15423 q = createPQExpBuffer();
15425
15426 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15427
15428 /* Get conversion-specific details */
15429 appendPQExpBuffer(query, "SELECT "
15430 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15431 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15432 "conproc, condefault "
15433 "FROM pg_catalog.pg_conversion c "
15434 "WHERE c.oid = '%u'::pg_catalog.oid",
15435 convinfo->dobj.catId.oid);
15436
15437 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15438
15439 i_conforencoding = PQfnumber(res, "conforencoding");
15440 i_contoencoding = PQfnumber(res, "contoencoding");
15441 i_conproc = PQfnumber(res, "conproc");
15442 i_condefault = PQfnumber(res, "condefault");
15443
15446 conproc = PQgetvalue(res, 0, i_conproc);
15447 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15448
15449 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15451
15452 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15453 (condefault) ? "DEFAULT " : "",
15456 appendPQExpBufferStr(q, " TO ");
15458 /* regproc output is already sufficiently quoted */
15459 appendPQExpBuffer(q, " FROM %s;\n", conproc);
15460
15461 if (dopt->binary_upgrade)
15463 "CONVERSION", qconvname,
15464 convinfo->dobj.namespace->dobj.name);
15465
15466 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15467 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15468 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15469 .namespace = convinfo->dobj.namespace->dobj.name,
15470 .owner = convinfo->rolname,
15471 .description = "CONVERSION",
15472 .section = SECTION_PRE_DATA,
15473 .createStmt = q->data,
15474 .dropStmt = delq->data));
15475
15476 /* Dump Conversion Comments */
15477 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15478 dumpComment(fout, "CONVERSION", qconvname,
15479 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15480 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15481
15482 PQclear(res);
15483
15484 destroyPQExpBuffer(query);
15487 free(qconvname);
15488}
15489
15490/*
15491 * format_aggregate_signature: generate aggregate name and argument list
15492 *
15493 * The argument type names are qualified if needed. The aggregate name
15494 * is never qualified.
15495 */
15496static char *
15498{
15500 int j;
15501
15503 if (honor_quotes)
15504 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15505 else
15506 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15507
15508 if (agginfo->aggfn.nargs == 0)
15509 appendPQExpBufferStr(&buf, "(*)");
15510 else
15511 {
15513 for (j = 0; j < agginfo->aggfn.nargs; j++)
15514 appendPQExpBuffer(&buf, "%s%s",
15515 (j > 0) ? ", " : "",
15517 agginfo->aggfn.argtypes[j],
15518 zeroIsError));
15520 }
15521 return buf.data;
15522}
15523
15524/*
15525 * dumpAgg
15526 * write out a single aggregate definition
15527 */
15528static void
15530{
15531 DumpOptions *dopt = fout->dopt;
15532 PQExpBuffer query;
15533 PQExpBuffer q;
15535 PQExpBuffer details;
15536 char *aggsig; /* identity signature */
15537 char *aggfullsig = NULL; /* full signature */
15538 char *aggsig_tag;
15539 PGresult *res;
15540 int i_agginitval;
15541 int i_aggminitval;
15542 const char *aggtransfn;
15543 const char *aggfinalfn;
15544 const char *aggcombinefn;
15545 const char *aggserialfn;
15546 const char *aggdeserialfn;
15547 const char *aggmtransfn;
15548 const char *aggminvtransfn;
15549 const char *aggmfinalfn;
15550 bool aggfinalextra;
15551 bool aggmfinalextra;
15552 char aggfinalmodify;
15553 char aggmfinalmodify;
15554 const char *aggsortop;
15555 char *aggsortconvop;
15556 char aggkind;
15557 const char *aggtranstype;
15558 const char *aggtransspace;
15559 const char *aggmtranstype;
15560 const char *aggmtransspace;
15561 const char *agginitval;
15562 const char *aggminitval;
15563 const char *proparallel;
15564 char defaultfinalmodify;
15565
15566 /* Do nothing if not dumping schema */
15567 if (!dopt->dumpSchema)
15568 return;
15569
15570 query = createPQExpBuffer();
15571 q = createPQExpBuffer();
15573 details = createPQExpBuffer();
15574
15576 {
15577 /* Set up query for aggregate-specific details */
15579 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15580
15582 "SELECT "
15583 "aggtransfn,\n"
15584 "aggfinalfn,\n"
15585 "aggtranstype::pg_catalog.regtype,\n"
15586 "agginitval,\n"
15587 "aggsortop,\n"
15588 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15589 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15590
15591 if (fout->remoteVersion >= 90400)
15593 "aggkind,\n"
15594 "aggmtransfn,\n"
15595 "aggminvtransfn,\n"
15596 "aggmfinalfn,\n"
15597 "aggmtranstype::pg_catalog.regtype,\n"
15598 "aggfinalextra,\n"
15599 "aggmfinalextra,\n"
15600 "aggtransspace,\n"
15601 "aggmtransspace,\n"
15602 "aggminitval,\n");
15603 else
15605 "'n' AS aggkind,\n"
15606 "'-' AS aggmtransfn,\n"
15607 "'-' AS aggminvtransfn,\n"
15608 "'-' AS aggmfinalfn,\n"
15609 "0 AS aggmtranstype,\n"
15610 "false AS aggfinalextra,\n"
15611 "false AS aggmfinalextra,\n"
15612 "0 AS aggtransspace,\n"
15613 "0 AS aggmtransspace,\n"
15614 "NULL AS aggminitval,\n");
15615
15616 if (fout->remoteVersion >= 90600)
15618 "aggcombinefn,\n"
15619 "aggserialfn,\n"
15620 "aggdeserialfn,\n"
15621 "proparallel,\n");
15622 else
15624 "'-' AS aggcombinefn,\n"
15625 "'-' AS aggserialfn,\n"
15626 "'-' AS aggdeserialfn,\n"
15627 "'u' AS proparallel,\n");
15628
15629 if (fout->remoteVersion >= 110000)
15631 "aggfinalmodify,\n"
15632 "aggmfinalmodify\n");
15633 else
15635 "'0' AS aggfinalmodify,\n"
15636 "'0' AS aggmfinalmodify\n");
15637
15639 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15640 "WHERE a.aggfnoid = p.oid "
15641 "AND p.oid = $1");
15642
15643 ExecuteSqlStatement(fout, query->data);
15644
15646 }
15647
15648 printfPQExpBuffer(query,
15649 "EXECUTE dumpAgg('%u')",
15650 agginfo->aggfn.dobj.catId.oid);
15651
15652 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15653
15654 i_agginitval = PQfnumber(res, "agginitval");
15655 i_aggminitval = PQfnumber(res, "aggminitval");
15656
15657 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15658 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15659 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15660 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15661 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15662 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15663 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15664 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15665 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15666 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15667 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15668 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15669 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15670 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15671 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15672 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15673 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15674 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15677 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15678
15679 {
15680 char *funcargs;
15681 char *funciargs;
15682
15683 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15684 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15687 }
15688
15690
15691 /* identify default modify flag for aggkind (must match DefineAggregate) */
15693 /* replace omitted flags for old versions */
15694 if (aggfinalmodify == '0')
15696 if (aggmfinalmodify == '0')
15698
15699 /* regproc and regtype output is already sufficiently quoted */
15700 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15701 aggtransfn, aggtranstype);
15702
15703 if (strcmp(aggtransspace, "0") != 0)
15704 {
15705 appendPQExpBuffer(details, ",\n SSPACE = %s",
15706 aggtransspace);
15707 }
15708
15709 if (!PQgetisnull(res, 0, i_agginitval))
15710 {
15711 appendPQExpBufferStr(details, ",\n INITCOND = ");
15713 }
15714
15715 if (strcmp(aggfinalfn, "-") != 0)
15716 {
15717 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15718 aggfinalfn);
15719 if (aggfinalextra)
15720 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15722 {
15723 switch (aggfinalmodify)
15724 {
15726 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15727 break;
15729 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15730 break;
15732 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15733 break;
15734 default:
15735 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15736 agginfo->aggfn.dobj.name);
15737 break;
15738 }
15739 }
15740 }
15741
15742 if (strcmp(aggcombinefn, "-") != 0)
15743 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15744
15745 if (strcmp(aggserialfn, "-") != 0)
15746 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15747
15748 if (strcmp(aggdeserialfn, "-") != 0)
15749 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15750
15751 if (strcmp(aggmtransfn, "-") != 0)
15752 {
15753 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15757 }
15758
15759 if (strcmp(aggmtransspace, "0") != 0)
15760 {
15761 appendPQExpBuffer(details, ",\n MSSPACE = %s",
15763 }
15764
15765 if (!PQgetisnull(res, 0, i_aggminitval))
15766 {
15767 appendPQExpBufferStr(details, ",\n MINITCOND = ");
15769 }
15770
15771 if (strcmp(aggmfinalfn, "-") != 0)
15772 {
15773 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15774 aggmfinalfn);
15775 if (aggmfinalextra)
15776 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15778 {
15779 switch (aggmfinalmodify)
15780 {
15782 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15783 break;
15785 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15786 break;
15788 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15789 break;
15790 default:
15791 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15792 agginfo->aggfn.dobj.name);
15793 break;
15794 }
15795 }
15796 }
15797
15799 if (aggsortconvop)
15800 {
15801 appendPQExpBuffer(details, ",\n SORTOP = %s",
15804 }
15805
15807 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15808
15810 {
15811 if (proparallel[0] == PROPARALLEL_SAFE)
15812 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15813 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15814 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15815 else if (proparallel[0] != PROPARALLEL_UNSAFE)
15816 pg_fatal("unrecognized proparallel value for function \"%s\"",
15817 agginfo->aggfn.dobj.name);
15818 }
15819
15820 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15821 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15822 aggsig);
15823
15824 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15825 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15826 aggfullsig ? aggfullsig : aggsig, details->data);
15827
15828 if (dopt->binary_upgrade)
15830 "AGGREGATE", aggsig,
15831 agginfo->aggfn.dobj.namespace->dobj.name);
15832
15833 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15834 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15835 agginfo->aggfn.dobj.dumpId,
15836 ARCHIVE_OPTS(.tag = aggsig_tag,
15837 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15838 .owner = agginfo->aggfn.rolname,
15839 .description = "AGGREGATE",
15840 .section = SECTION_PRE_DATA,
15841 .createStmt = q->data,
15842 .dropStmt = delq->data));
15843
15844 /* Dump Aggregate Comments */
15845 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15846 dumpComment(fout, "AGGREGATE", aggsig,
15847 agginfo->aggfn.dobj.namespace->dobj.name,
15848 agginfo->aggfn.rolname,
15849 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15850
15851 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15852 dumpSecLabel(fout, "AGGREGATE", aggsig,
15853 agginfo->aggfn.dobj.namespace->dobj.name,
15854 agginfo->aggfn.rolname,
15855 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15856
15857 /*
15858 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15859 * command look like a function's GRANT; in particular this affects the
15860 * syntax for zero-argument aggregates and ordered-set aggregates.
15861 */
15862 free(aggsig);
15863
15864 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15865
15866 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15867 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15868 "FUNCTION", aggsig, NULL,
15869 agginfo->aggfn.dobj.namespace->dobj.name,
15870 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15871
15872 free(aggsig);
15875
15876 PQclear(res);
15877
15878 destroyPQExpBuffer(query);
15881 destroyPQExpBuffer(details);
15882}
15883
15884/*
15885 * dumpTSParser
15886 * write out a single text search parser
15887 */
15888static void
15890{
15891 DumpOptions *dopt = fout->dopt;
15892 PQExpBuffer q;
15894 char *qprsname;
15895
15896 /* Do nothing if not dumping schema */
15897 if (!dopt->dumpSchema)
15898 return;
15899
15900 q = createPQExpBuffer();
15902
15903 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15904
15905 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15907
15908 appendPQExpBuffer(q, " START = %s,\n",
15909 convertTSFunction(fout, prsinfo->prsstart));
15910 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15911 convertTSFunction(fout, prsinfo->prstoken));
15912 appendPQExpBuffer(q, " END = %s,\n",
15913 convertTSFunction(fout, prsinfo->prsend));
15914 if (prsinfo->prsheadline != InvalidOid)
15915 appendPQExpBuffer(q, " HEADLINE = %s,\n",
15916 convertTSFunction(fout, prsinfo->prsheadline));
15917 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15918 convertTSFunction(fout, prsinfo->prslextype));
15919
15920 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15922
15923 if (dopt->binary_upgrade)
15925 "TEXT SEARCH PARSER", qprsname,
15926 prsinfo->dobj.namespace->dobj.name);
15927
15928 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15929 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15930 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15931 .namespace = prsinfo->dobj.namespace->dobj.name,
15932 .description = "TEXT SEARCH PARSER",
15933 .section = SECTION_PRE_DATA,
15934 .createStmt = q->data,
15935 .dropStmt = delq->data));
15936
15937 /* Dump Parser Comments */
15938 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15939 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15940 prsinfo->dobj.namespace->dobj.name, "",
15941 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15942
15945 free(qprsname);
15946}
15947
15948/*
15949 * dumpTSDictionary
15950 * write out a single text search dictionary
15951 */
15952static void
15954{
15955 DumpOptions *dopt = fout->dopt;
15956 PQExpBuffer q;
15958 PQExpBuffer query;
15959 char *qdictname;
15960 PGresult *res;
15961 char *nspname;
15962 char *tmplname;
15963
15964 /* Do nothing if not dumping schema */
15965 if (!dopt->dumpSchema)
15966 return;
15967
15968 q = createPQExpBuffer();
15970 query = createPQExpBuffer();
15971
15972 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15973
15974 /* Fetch name and namespace of the dictionary's template */
15975 appendPQExpBuffer(query, "SELECT nspname, tmplname "
15976 "FROM pg_ts_template p, pg_namespace n "
15977 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15978 dictinfo->dicttemplate);
15979 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15980 nspname = PQgetvalue(res, 0, 0);
15981 tmplname = PQgetvalue(res, 0, 1);
15982
15983 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15985
15986 appendPQExpBufferStr(q, " TEMPLATE = ");
15987 appendPQExpBuffer(q, "%s.", fmtId(nspname));
15989
15990 PQclear(res);
15991
15992 /* the dictinitoption can be dumped straight into the command */
15993 if (dictinfo->dictinitoption)
15994 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15995
15996 appendPQExpBufferStr(q, " );\n");
15997
15998 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
16000
16001 if (dopt->binary_upgrade)
16003 "TEXT SEARCH DICTIONARY", qdictname,
16004 dictinfo->dobj.namespace->dobj.name);
16005
16006 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16007 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
16008 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
16009 .namespace = dictinfo->dobj.namespace->dobj.name,
16010 .owner = dictinfo->rolname,
16011 .description = "TEXT SEARCH DICTIONARY",
16012 .section = SECTION_PRE_DATA,
16013 .createStmt = q->data,
16014 .dropStmt = delq->data));
16015
16016 /* Dump Dictionary Comments */
16017 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16018 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
16019 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
16020 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
16021
16024 destroyPQExpBuffer(query);
16025 free(qdictname);
16026}
16027
16028/*
16029 * dumpTSTemplate
16030 * write out a single text search template
16031 */
16032static void
16034{
16035 DumpOptions *dopt = fout->dopt;
16036 PQExpBuffer q;
16038 char *qtmplname;
16039
16040 /* Do nothing if not dumping schema */
16041 if (!dopt->dumpSchema)
16042 return;
16043
16044 q = createPQExpBuffer();
16046
16047 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
16048
16049 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
16051
16052 if (tmplinfo->tmplinit != InvalidOid)
16053 appendPQExpBuffer(q, " INIT = %s,\n",
16054 convertTSFunction(fout, tmplinfo->tmplinit));
16055 appendPQExpBuffer(q, " LEXIZE = %s );\n",
16056 convertTSFunction(fout, tmplinfo->tmpllexize));
16057
16058 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
16060
16061 if (dopt->binary_upgrade)
16063 "TEXT SEARCH TEMPLATE", qtmplname,
16064 tmplinfo->dobj.namespace->dobj.name);
16065
16066 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16067 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
16068 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
16069 .namespace = tmplinfo->dobj.namespace->dobj.name,
16070 .description = "TEXT SEARCH TEMPLATE",
16071 .section = SECTION_PRE_DATA,
16072 .createStmt = q->data,
16073 .dropStmt = delq->data));
16074
16075 /* Dump Template Comments */
16076 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16077 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
16078 tmplinfo->dobj.namespace->dobj.name, "",
16079 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
16080
16083 free(qtmplname);
16084}
16085
16086/*
16087 * dumpTSConfig
16088 * write out a single text search configuration
16089 */
16090static void
16092{
16093 DumpOptions *dopt = fout->dopt;
16094 PQExpBuffer q;
16096 PQExpBuffer query;
16097 char *qcfgname;
16098 PGresult *res;
16099 char *nspname;
16100 char *prsname;
16101 int ntups,
16102 i;
16103 int i_tokenname;
16104 int i_dictname;
16105
16106 /* Do nothing if not dumping schema */
16107 if (!dopt->dumpSchema)
16108 return;
16109
16110 q = createPQExpBuffer();
16112 query = createPQExpBuffer();
16113
16114 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
16115
16116 /* Fetch name and namespace of the config's parser */
16117 appendPQExpBuffer(query, "SELECT nspname, prsname "
16118 "FROM pg_ts_parser p, pg_namespace n "
16119 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
16120 cfginfo->cfgparser);
16121 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16122 nspname = PQgetvalue(res, 0, 0);
16123 prsname = PQgetvalue(res, 0, 1);
16124
16125 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
16127
16128 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
16129 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
16130
16131 PQclear(res);
16132
16133 resetPQExpBuffer(query);
16134 appendPQExpBuffer(query,
16135 "SELECT\n"
16136 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
16137 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
16138 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
16139 "FROM pg_catalog.pg_ts_config_map AS m\n"
16140 "WHERE m.mapcfg = '%u'\n"
16141 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
16142 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
16143
16144 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16145 ntups = PQntuples(res);
16146
16147 i_tokenname = PQfnumber(res, "tokenname");
16148 i_dictname = PQfnumber(res, "dictname");
16149
16150 for (i = 0; i < ntups; i++)
16151 {
16152 char *tokenname = PQgetvalue(res, i, i_tokenname);
16153 char *dictname = PQgetvalue(res, i, i_dictname);
16154
16155 if (i == 0 ||
16156 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
16157 {
16158 /* starting a new token type, so start a new command */
16159 if (i > 0)
16160 appendPQExpBufferStr(q, ";\n");
16161 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16163 /* tokenname needs quoting, dictname does NOT */
16164 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16165 fmtId(tokenname), dictname);
16166 }
16167 else
16168 appendPQExpBuffer(q, ", %s", dictname);
16169 }
16170
16171 if (ntups > 0)
16172 appendPQExpBufferStr(q, ";\n");
16173
16174 PQclear(res);
16175
16176 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16178
16179 if (dopt->binary_upgrade)
16181 "TEXT SEARCH CONFIGURATION", qcfgname,
16182 cfginfo->dobj.namespace->dobj.name);
16183
16184 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16185 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16186 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16187 .namespace = cfginfo->dobj.namespace->dobj.name,
16188 .owner = cfginfo->rolname,
16189 .description = "TEXT SEARCH CONFIGURATION",
16190 .section = SECTION_PRE_DATA,
16191 .createStmt = q->data,
16192 .dropStmt = delq->data));
16193
16194 /* Dump Configuration Comments */
16195 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16196 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16197 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16198 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16199
16202 destroyPQExpBuffer(query);
16203 free(qcfgname);
16204}
16205
16206/*
16207 * dumpForeignDataWrapper
16208 * write out a single foreign-data wrapper definition
16209 */
16210static void
16212{
16213 DumpOptions *dopt = fout->dopt;
16214 PQExpBuffer q;
16216 char *qfdwname;
16217
16218 /* Do nothing if not dumping schema */
16219 if (!dopt->dumpSchema)
16220 return;
16221
16222 q = createPQExpBuffer();
16224
16225 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16226
16227 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16228 qfdwname);
16229
16230 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16231 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16232
16233 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16234 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16235
16236 if (strcmp(fdwinfo->fdwconnection, "-") != 0)
16237 appendPQExpBuffer(q, " CONNECTION %s", fdwinfo->fdwconnection);
16238
16239 if (strlen(fdwinfo->fdwoptions) > 0)
16240 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16241
16242 appendPQExpBufferStr(q, ";\n");
16243
16244 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16245 qfdwname);
16246
16247 if (dopt->binary_upgrade)
16249 "FOREIGN DATA WRAPPER", qfdwname,
16250 NULL);
16251
16252 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16253 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16254 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16255 .owner = fdwinfo->rolname,
16256 .description = "FOREIGN DATA WRAPPER",
16257 .section = SECTION_PRE_DATA,
16258 .createStmt = q->data,
16259 .dropStmt = delq->data));
16260
16261 /* Dump Foreign Data Wrapper Comments */
16262 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16263 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16264 NULL, fdwinfo->rolname,
16265 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16266
16267 /* Handle the ACL */
16268 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16269 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16270 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16271 NULL, fdwinfo->rolname, &fdwinfo->dacl);
16272
16273 free(qfdwname);
16274
16277}
16278
16279/*
16280 * dumpForeignServer
16281 * write out a foreign server definition
16282 */
16283static void
16285{
16286 DumpOptions *dopt = fout->dopt;
16287 PQExpBuffer q;
16289 PQExpBuffer query;
16290 PGresult *res;
16291 char *qsrvname;
16292 char *fdwname;
16293
16294 /* Do nothing if not dumping schema */
16295 if (!dopt->dumpSchema)
16296 return;
16297
16298 q = createPQExpBuffer();
16300 query = createPQExpBuffer();
16301
16302 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16303
16304 /* look up the foreign-data wrapper */
16305 appendPQExpBuffer(query, "SELECT fdwname "
16306 "FROM pg_foreign_data_wrapper w "
16307 "WHERE w.oid = '%u'",
16308 srvinfo->srvfdw);
16309 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16310 fdwname = PQgetvalue(res, 0, 0);
16311
16312 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16313 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16314 {
16315 appendPQExpBufferStr(q, " TYPE ");
16316 appendStringLiteralAH(q, srvinfo->srvtype, fout);
16317 }
16318 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16319 {
16320 appendPQExpBufferStr(q, " VERSION ");
16321 appendStringLiteralAH(q, srvinfo->srvversion, fout);
16322 }
16323
16324 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16325 appendPQExpBufferStr(q, fmtId(fdwname));
16326
16327 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16328 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16329
16330 appendPQExpBufferStr(q, ";\n");
16331
16332 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16333 qsrvname);
16334
16335 if (dopt->binary_upgrade)
16337 "SERVER", qsrvname, NULL);
16338
16339 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16340 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16341 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16342 .owner = srvinfo->rolname,
16343 .description = "SERVER",
16344 .section = SECTION_PRE_DATA,
16345 .createStmt = q->data,
16346 .dropStmt = delq->data));
16347
16348 /* Dump Foreign Server Comments */
16349 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16350 dumpComment(fout, "SERVER", qsrvname,
16351 NULL, srvinfo->rolname,
16352 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16353
16354 /* Handle the ACL */
16355 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16356 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16357 "FOREIGN SERVER", qsrvname, NULL, NULL,
16358 NULL, srvinfo->rolname, &srvinfo->dacl);
16359
16360 /* Dump user mappings */
16361 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16363 srvinfo->dobj.name, NULL,
16364 srvinfo->rolname,
16365 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16366
16367 PQclear(res);
16368
16369 free(qsrvname);
16370
16373 destroyPQExpBuffer(query);
16374}
16375
16376/*
16377 * dumpUserMappings
16378 *
16379 * This routine is used to dump any user mappings associated with the
16380 * server handed to this routine. Should be called after ArchiveEntry()
16381 * for the server.
16382 */
16383static void
16385 const char *servername, const char *namespace,
16386 const char *owner,
16387 CatalogId catalogId, DumpId dumpId)
16388{
16389 PQExpBuffer q;
16391 PQExpBuffer query;
16392 PQExpBuffer tag;
16393 PGresult *res;
16394 int ntups;
16395 int i_usename;
16396 int i_umoptions;
16397 int i;
16398
16399 q = createPQExpBuffer();
16400 tag = createPQExpBuffer();
16402 query = createPQExpBuffer();
16403
16404 /*
16405 * We read from the publicly accessible view pg_user_mappings, so as not
16406 * to fail if run by a non-superuser. Note that the view will show
16407 * umoptions as null if the user hasn't got privileges for the associated
16408 * server; this means that pg_dump will dump such a mapping, but with no
16409 * OPTIONS clause. A possible alternative is to skip such mappings
16410 * altogether, but it's not clear that that's an improvement.
16411 */
16412 appendPQExpBuffer(query,
16413 "SELECT usename, "
16414 "array_to_string(ARRAY("
16415 "SELECT quote_ident(option_name) || ' ' || "
16416 "quote_literal(option_value) "
16417 "FROM pg_options_to_table(umoptions) "
16418 "ORDER BY option_name"
16419 "), E',\n ') AS umoptions "
16420 "FROM pg_user_mappings "
16421 "WHERE srvid = '%u' "
16422 "ORDER BY usename",
16423 catalogId.oid);
16424
16425 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16426
16427 ntups = PQntuples(res);
16428 i_usename = PQfnumber(res, "usename");
16429 i_umoptions = PQfnumber(res, "umoptions");
16430
16431 for (i = 0; i < ntups; i++)
16432 {
16433 char *usename;
16434 char *umoptions;
16435
16436 usename = PQgetvalue(res, i, i_usename);
16438
16440 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16441 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16442
16443 if (umoptions && strlen(umoptions) > 0)
16444 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16445
16446 appendPQExpBufferStr(q, ";\n");
16447
16449 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16450 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16451
16452 resetPQExpBuffer(tag);
16453 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16454 usename, servername);
16455
16457 ARCHIVE_OPTS(.tag = tag->data,
16458 .namespace = namespace,
16459 .owner = owner,
16460 .description = "USER MAPPING",
16461 .section = SECTION_PRE_DATA,
16462 .createStmt = q->data,
16463 .dropStmt = delq->data));
16464 }
16465
16466 PQclear(res);
16467
16468 destroyPQExpBuffer(query);
16470 destroyPQExpBuffer(tag);
16472}
16473
16474/*
16475 * Write out default privileges information
16476 */
16477static void
16479{
16480 DumpOptions *dopt = fout->dopt;
16481 PQExpBuffer q;
16482 PQExpBuffer tag;
16483 const char *type;
16484
16485 /* Do nothing if not dumping schema, or if we're skipping ACLs */
16486 if (!dopt->dumpSchema || dopt->aclsSkip)
16487 return;
16488
16489 q = createPQExpBuffer();
16490 tag = createPQExpBuffer();
16491
16492 switch (daclinfo->defaclobjtype)
16493 {
16494 case DEFACLOBJ_RELATION:
16495 type = "TABLES";
16496 break;
16497 case DEFACLOBJ_SEQUENCE:
16498 type = "SEQUENCES";
16499 break;
16500 case DEFACLOBJ_FUNCTION:
16501 type = "FUNCTIONS";
16502 break;
16503 case DEFACLOBJ_TYPE:
16504 type = "TYPES";
16505 break;
16507 type = "SCHEMAS";
16508 break;
16510 type = "LARGE OBJECTS";
16511 break;
16512 default:
16513 /* shouldn't get here */
16514 pg_fatal("unrecognized object type in default privileges: %d",
16515 (int) daclinfo->defaclobjtype);
16516 type = ""; /* keep compiler quiet */
16517 }
16518
16519 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16520
16521 /* build the actual command(s) for this tuple */
16523 daclinfo->dobj.namespace != NULL ?
16524 daclinfo->dobj.namespace->dobj.name : NULL,
16525 daclinfo->dacl.acl,
16526 daclinfo->dacl.acldefault,
16527 daclinfo->defaclrole,
16529 q))
16530 pg_fatal("could not parse default ACL list (%s)",
16531 daclinfo->dacl.acl);
16532
16533 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16534 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16535 ARCHIVE_OPTS(.tag = tag->data,
16536 .namespace = daclinfo->dobj.namespace ?
16537 daclinfo->dobj.namespace->dobj.name : NULL,
16538 .owner = daclinfo->defaclrole,
16539 .description = "DEFAULT ACL",
16540 .section = SECTION_POST_DATA,
16541 .createStmt = q->data));
16542
16543 destroyPQExpBuffer(tag);
16545}
16546
16547/*----------
16548 * Write out grant/revoke information
16549 *
16550 * 'objDumpId' is the dump ID of the underlying object.
16551 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16552 * or InvalidDumpId if there is no need for a second dependency.
16553 * 'type' must be one of
16554 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16555 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16556 * 'name' is the formatted name of the object. Must be quoted etc. already.
16557 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16558 * (Currently we assume that subname is only provided for table columns.)
16559 * 'nspname' is the namespace the object is in (NULL if none).
16560 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16561 * to use the default for the object type.
16562 * 'owner' is the owner, NULL if there is no owner (for languages).
16563 * 'dacl' is the DumpableAcl struct for the object.
16564 *
16565 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16566 * no ACL entry was created.
16567 *----------
16568 */
16569static DumpId
16571 const char *type, const char *name, const char *subname,
16572 const char *nspname, const char *tag, const char *owner,
16573 const DumpableAcl *dacl)
16574{
16576 DumpOptions *dopt = fout->dopt;
16577 const char *acls = dacl->acl;
16578 const char *acldefault = dacl->acldefault;
16579 char privtype = dacl->privtype;
16580 const char *initprivs = dacl->initprivs;
16581 const char *baseacls;
16582 PQExpBuffer sql;
16583
16584 /* Do nothing if ACL dump is not enabled */
16585 if (dopt->aclsSkip)
16586 return InvalidDumpId;
16587
16588 /* --data-only skips ACLs *except* large object ACLs */
16589 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16590 return InvalidDumpId;
16591
16592 sql = createPQExpBuffer();
16593
16594 /*
16595 * In binary upgrade mode, we don't run an extension's script but instead
16596 * dump out the objects independently and then recreate them. To preserve
16597 * any initial privileges which were set on extension objects, we need to
16598 * compute the set of GRANT and REVOKE commands necessary to get from the
16599 * default privileges of an object to its initial privileges as recorded
16600 * in pg_init_privs.
16601 *
16602 * At restore time, we apply these commands after having called
16603 * binary_upgrade_set_record_init_privs(true). That tells the backend to
16604 * copy the results into pg_init_privs. This is how we preserve the
16605 * contents of that catalog across binary upgrades.
16606 */
16607 if (dopt->binary_upgrade && privtype == 'e' &&
16608 initprivs && *initprivs != '\0')
16609 {
16610 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16611 if (!buildACLCommands(name, subname, nspname, type,
16612 initprivs, acldefault, owner,
16613 "", fout->remoteVersion, sql))
16614 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16615 initprivs, acldefault, name, type);
16616 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16617 }
16618
16619 /*
16620 * Now figure the GRANT and REVOKE commands needed to get to the object's
16621 * actual current ACL, starting from the initprivs if given, else from the
16622 * object-type-specific default. Also, while buildACLCommands will assume
16623 * that a NULL/empty acls string means it needn't do anything, what that
16624 * actually represents is the object-type-specific default; so we need to
16625 * substitute the acldefault string to get the right results in that case.
16626 */
16627 if (initprivs && *initprivs != '\0')
16628 {
16629 baseacls = initprivs;
16630 if (acls == NULL || *acls == '\0')
16631 acls = acldefault;
16632 }
16633 else
16635
16636 if (!buildACLCommands(name, subname, nspname, type,
16637 acls, baseacls, owner,
16638 "", fout->remoteVersion, sql))
16639 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16640 acls, baseacls, name, type);
16641
16642 if (sql->len > 0)
16643 {
16645 DumpId aclDeps[2];
16646 int nDeps = 0;
16647
16648 if (tag)
16650 else if (subname)
16651 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16652 else
16653 appendPQExpBuffer(tagbuf, "%s %s", type, name);
16654
16655 aclDeps[nDeps++] = objDumpId;
16656 if (altDumpId != InvalidDumpId)
16657 aclDeps[nDeps++] = altDumpId;
16658
16660
16662 ARCHIVE_OPTS(.tag = tagbuf->data,
16663 .namespace = nspname,
16664 .owner = owner,
16665 .description = "ACL",
16666 .section = SECTION_NONE,
16667 .createStmt = sql->data,
16668 .deps = aclDeps,
16669 .nDeps = nDeps));
16670
16672 }
16673
16674 destroyPQExpBuffer(sql);
16675
16676 return aclDumpId;
16677}
16678
16679/*
16680 * dumpSecLabel
16681 *
16682 * This routine is used to dump any security labels associated with the
16683 * object handed to this routine. The routine takes the object type
16684 * and object name (ready to print, except for schema decoration), plus
16685 * the namespace and owner of the object (for labeling the ArchiveEntry),
16686 * plus catalog ID and subid which are the lookup key for pg_seclabel,
16687 * plus the dump ID for the object (for setting a dependency).
16688 * If a matching pg_seclabel entry is found, it is dumped.
16689 *
16690 * Note: although this routine takes a dumpId for dependency purposes,
16691 * that purpose is just to mark the dependency in the emitted dump file
16692 * for possible future use by pg_restore. We do NOT use it for determining
16693 * ordering of the label in the dump file, because this routine is called
16694 * after dependency sorting occurs. This routine should be called just after
16695 * calling ArchiveEntry() for the specified object.
16696 */
16697static void
16698dumpSecLabel(Archive *fout, const char *type, const char *name,
16699 const char *namespace, const char *owner,
16700 CatalogId catalogId, int subid, DumpId dumpId)
16701{
16702 DumpOptions *dopt = fout->dopt;
16703 SecLabelItem *labels;
16704 int nlabels;
16705 int i;
16706 PQExpBuffer query;
16707
16708 /* do nothing, if --no-security-labels is supplied */
16709 if (dopt->no_security_labels)
16710 return;
16711
16712 /*
16713 * Security labels are schema not data ... except large object labels are
16714 * data
16715 */
16716 if (strcmp(type, "LARGE OBJECT") != 0)
16717 {
16718 if (!dopt->dumpSchema)
16719 return;
16720 }
16721 else
16722 {
16723 /* We do dump large object security labels in binary-upgrade mode */
16724 if (!dopt->dumpData && !dopt->binary_upgrade)
16725 return;
16726 }
16727
16728 /* Search for security labels associated with catalogId, using table */
16729 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16730
16731 query = createPQExpBuffer();
16732
16733 for (i = 0; i < nlabels; i++)
16734 {
16735 /*
16736 * Ignore label entries for which the subid doesn't match.
16737 */
16738 if (labels[i].objsubid != subid)
16739 continue;
16740
16741 appendPQExpBuffer(query,
16742 "SECURITY LABEL FOR %s ON %s ",
16743 fmtId(labels[i].provider), type);
16744 if (namespace && *namespace)
16745 appendPQExpBuffer(query, "%s.", fmtId(namespace));
16746 appendPQExpBuffer(query, "%s IS ", name);
16747 appendStringLiteralAH(query, labels[i].label, fout);
16748 appendPQExpBufferStr(query, ";\n");
16749 }
16750
16751 if (query->len > 0)
16752 {
16754
16755 appendPQExpBuffer(tag, "%s %s", type, name);
16757 ARCHIVE_OPTS(.tag = tag->data,
16758 .namespace = namespace,
16759 .owner = owner,
16760 .description = "SECURITY LABEL",
16761 .section = SECTION_NONE,
16762 .createStmt = query->data,
16763 .deps = &dumpId,
16764 .nDeps = 1));
16765 destroyPQExpBuffer(tag);
16766 }
16767
16768 destroyPQExpBuffer(query);
16769}
16770
16771/*
16772 * dumpTableSecLabel
16773 *
16774 * As above, but dump security label for both the specified table (or view)
16775 * and its columns.
16776 */
16777static void
16779{
16780 DumpOptions *dopt = fout->dopt;
16781 SecLabelItem *labels;
16782 int nlabels;
16783 int i;
16784 PQExpBuffer query;
16785 PQExpBuffer target;
16786
16787 /* do nothing, if --no-security-labels is supplied */
16788 if (dopt->no_security_labels)
16789 return;
16790
16791 /* SecLabel are SCHEMA not data */
16792 if (!dopt->dumpSchema)
16793 return;
16794
16795 /* Search for comments associated with relation, using table */
16796 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16797 tbinfo->dobj.catId.oid,
16798 &labels);
16799
16800 /* If security labels exist, build SECURITY LABEL statements */
16801 if (nlabels <= 0)
16802 return;
16803
16804 query = createPQExpBuffer();
16805 target = createPQExpBuffer();
16806
16807 for (i = 0; i < nlabels; i++)
16808 {
16809 const char *colname;
16810 const char *provider = labels[i].provider;
16811 const char *label = labels[i].label;
16812 int objsubid = labels[i].objsubid;
16813
16814 resetPQExpBuffer(target);
16815 if (objsubid == 0)
16816 {
16817 appendPQExpBuffer(target, "%s %s", reltypename,
16819 }
16820 else
16821 {
16822 colname = getAttrName(objsubid, tbinfo);
16823 /* first fmtXXX result must be consumed before calling again */
16824 appendPQExpBuffer(target, "COLUMN %s",
16826 appendPQExpBuffer(target, ".%s", fmtId(colname));
16827 }
16828 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16829 fmtId(provider), target->data);
16831 appendPQExpBufferStr(query, ";\n");
16832 }
16833 if (query->len > 0)
16834 {
16835 resetPQExpBuffer(target);
16836 appendPQExpBuffer(target, "%s %s", reltypename,
16837 fmtId(tbinfo->dobj.name));
16839 ARCHIVE_OPTS(.tag = target->data,
16840 .namespace = tbinfo->dobj.namespace->dobj.name,
16841 .owner = tbinfo->rolname,
16842 .description = "SECURITY LABEL",
16843 .section = SECTION_NONE,
16844 .createStmt = query->data,
16845 .deps = &(tbinfo->dobj.dumpId),
16846 .nDeps = 1));
16847 }
16848 destroyPQExpBuffer(query);
16849 destroyPQExpBuffer(target);
16850}
16851
16852/*
16853 * findSecLabels
16854 *
16855 * Find the security label(s), if any, associated with the given object.
16856 * All the objsubid values associated with the given classoid/objoid are
16857 * found with one search.
16858 */
16859static int
16861{
16863 SecLabelItem *low;
16864 SecLabelItem *high;
16865 int nmatch;
16866
16867 if (nseclabels <= 0) /* no labels, so no match is possible */
16868 {
16869 *items = NULL;
16870 return 0;
16871 }
16872
16873 /*
16874 * Do binary search to find some item matching the object.
16875 */
16876 low = &seclabels[0];
16877 high = &seclabels[nseclabels - 1];
16878 while (low <= high)
16879 {
16880 middle = low + (high - low) / 2;
16881
16882 if (classoid < middle->classoid)
16883 high = middle - 1;
16884 else if (classoid > middle->classoid)
16885 low = middle + 1;
16886 else if (objoid < middle->objoid)
16887 high = middle - 1;
16888 else if (objoid > middle->objoid)
16889 low = middle + 1;
16890 else
16891 break; /* found a match */
16892 }
16893
16894 if (low > high) /* no matches */
16895 {
16896 *items = NULL;
16897 return 0;
16898 }
16899
16900 /*
16901 * Now determine how many items match the object. The search loop
16902 * invariant still holds: only items between low and high inclusive could
16903 * match.
16904 */
16905 nmatch = 1;
16906 while (middle > low)
16907 {
16908 if (classoid != middle[-1].classoid ||
16909 objoid != middle[-1].objoid)
16910 break;
16911 middle--;
16912 nmatch++;
16913 }
16914
16915 *items = middle;
16916
16917 middle += nmatch;
16918 while (middle <= high)
16919 {
16920 if (classoid != middle->classoid ||
16921 objoid != middle->objoid)
16922 break;
16923 middle++;
16924 nmatch++;
16925 }
16926
16927 return nmatch;
16928}
16929
16930/*
16931 * collectSecLabels
16932 *
16933 * Construct a table of all security labels available for database objects;
16934 * also set the has-seclabel component flag for each relevant object.
16935 *
16936 * The table is sorted by classoid/objid/objsubid for speed in lookup.
16937 */
16938static void
16940{
16941 PGresult *res;
16942 PQExpBuffer query;
16943 int i_label;
16944 int i_provider;
16945 int i_classoid;
16946 int i_objoid;
16947 int i_objsubid;
16948 int ntups;
16949 int i;
16950 DumpableObject *dobj;
16951
16952 query = createPQExpBuffer();
16953
16955 "SELECT label, provider, classoid, objoid, objsubid "
16956 "FROM pg_catalog.pg_seclabels "
16957 "ORDER BY classoid, objoid, objsubid");
16958
16959 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16960
16961 /* Construct lookup table containing OIDs in numeric form */
16962 i_label = PQfnumber(res, "label");
16963 i_provider = PQfnumber(res, "provider");
16964 i_classoid = PQfnumber(res, "classoid");
16965 i_objoid = PQfnumber(res, "objoid");
16966 i_objsubid = PQfnumber(res, "objsubid");
16967
16968 ntups = PQntuples(res);
16969
16971 nseclabels = 0;
16972 dobj = NULL;
16973
16974 for (i = 0; i < ntups; i++)
16975 {
16976 CatalogId objId;
16977 int subid;
16978
16979 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16980 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16981 subid = atoi(PQgetvalue(res, i, i_objsubid));
16982
16983 /* We needn't remember labels that don't match any dumpable object */
16984 if (dobj == NULL ||
16985 dobj->catId.tableoid != objId.tableoid ||
16986 dobj->catId.oid != objId.oid)
16987 dobj = findObjectByCatalogId(objId);
16988 if (dobj == NULL)
16989 continue;
16990
16991 /*
16992 * Labels on columns of composite types are linked to the type's
16993 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16994 * in the type's own DumpableObject.
16995 */
16996 if (subid != 0 && dobj->objType == DO_TABLE &&
16997 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16998 {
17000
17001 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
17002 if (cTypeInfo)
17003 cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
17004 }
17005 else
17006 dobj->components |= DUMP_COMPONENT_SECLABEL;
17007
17011 seclabels[nseclabels].objoid = objId.oid;
17012 seclabels[nseclabels].objsubid = subid;
17013 nseclabels++;
17014 }
17015
17016 PQclear(res);
17017 destroyPQExpBuffer(query);
17018}
17019
17020/*
17021 * dumpTable
17022 * write out to fout the declarations (not data) of a user-defined table
17023 */
17024static void
17026{
17027 DumpOptions *dopt = fout->dopt;
17029 char *namecopy;
17030
17031 /* Do nothing if not dumping schema */
17032 if (!dopt->dumpSchema)
17033 return;
17034
17035 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17036 {
17037 if (tbinfo->relkind == RELKIND_SEQUENCE)
17039 else
17041 }
17042
17043 /* Handle the ACL here */
17044 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
17045 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
17046 {
17047 const char *objtype;
17048
17049 switch (tbinfo->relkind)
17050 {
17051 case RELKIND_SEQUENCE:
17052 objtype = "SEQUENCE";
17053 break;
17054 case RELKIND_PROPGRAPH:
17055 objtype = "PROPERTY GRAPH";
17056 break;
17057 default:
17058 objtype = "TABLE";
17059 break;
17060 }
17061
17063 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
17064 objtype, namecopy, NULL,
17065 tbinfo->dobj.namespace->dobj.name,
17066 NULL, tbinfo->rolname, &tbinfo->dacl);
17067 }
17068
17069 /*
17070 * Handle column ACLs, if any. Note: we pull these with a separate query
17071 * rather than trying to fetch them during getTableAttrs, so that we won't
17072 * miss ACLs on system columns. Doing it this way also allows us to dump
17073 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
17074 */
17075 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
17076 {
17078 PGresult *res;
17079 int i;
17080
17082 {
17083 /* Set up query for column ACLs */
17085 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
17086
17087 if (fout->remoteVersion >= 90600)
17088 {
17089 /*
17090 * In principle we should call acldefault('c', relowner) to
17091 * get the default ACL for a column. However, we don't
17092 * currently store the numeric OID of the relowner in
17093 * TableInfo. We could convert the owner name using regrole,
17094 * but that creates a risk of failure due to concurrent role
17095 * renames. Given that the default ACL for columns is empty
17096 * and is likely to stay that way, it's not worth extra cycles
17097 * and risk to avoid hard-wiring that knowledge here.
17098 */
17100 "SELECT at.attname, "
17101 "at.attacl, "
17102 "'{}' AS acldefault, "
17103 "pip.privtype, pip.initprivs "
17104 "FROM pg_catalog.pg_attribute at "
17105 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
17106 "(at.attrelid = pip.objoid "
17107 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
17108 "AND at.attnum = pip.objsubid) "
17109 "WHERE at.attrelid = $1 AND "
17110 "NOT at.attisdropped "
17111 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
17112 "ORDER BY at.attnum");
17113 }
17114 else
17115 {
17117 "SELECT attname, attacl, '{}' AS acldefault, "
17118 "NULL AS privtype, NULL AS initprivs "
17119 "FROM pg_catalog.pg_attribute "
17120 "WHERE attrelid = $1 AND NOT attisdropped "
17121 "AND attacl IS NOT NULL "
17122 "ORDER BY attnum");
17123 }
17124
17125 ExecuteSqlStatement(fout, query->data);
17126
17128 }
17129
17130 printfPQExpBuffer(query,
17131 "EXECUTE getColumnACLs('%u')",
17132 tbinfo->dobj.catId.oid);
17133
17134 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17135
17136 for (i = 0; i < PQntuples(res); i++)
17137 {
17138 char *attname = PQgetvalue(res, i, 0);
17139 char *attacl = PQgetvalue(res, i, 1);
17140 char *acldefault = PQgetvalue(res, i, 2);
17141 char privtype = *(PQgetvalue(res, i, 3));
17142 char *initprivs = PQgetvalue(res, i, 4);
17144 char *attnamecopy;
17145
17146 coldacl.acl = attacl;
17147 coldacl.acldefault = acldefault;
17148 coldacl.privtype = privtype;
17149 coldacl.initprivs = initprivs;
17151
17152 /*
17153 * Column's GRANT type is always TABLE. Each column ACL depends
17154 * on the table-level ACL, since we can restore column ACLs in
17155 * parallel but the table-level ACL has to be done first.
17156 */
17157 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
17158 "TABLE", namecopy, attnamecopy,
17159 tbinfo->dobj.namespace->dobj.name,
17160 NULL, tbinfo->rolname, &coldacl);
17162 }
17163 PQclear(res);
17164 destroyPQExpBuffer(query);
17165 }
17166
17167 free(namecopy);
17168}
17169
17170/*
17171 * Create the AS clause for a view or materialized view. The semicolon is
17172 * stripped because a materialized view must add a WITH NO DATA clause.
17173 *
17174 * This returns a new buffer which must be freed by the caller.
17175 */
17176static PQExpBuffer
17178{
17181 PGresult *res;
17182 int len;
17183
17184 /* Fetch the view definition */
17185 appendPQExpBuffer(query,
17186 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17187 tbinfo->dobj.catId.oid);
17188
17189 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17190
17191 if (PQntuples(res) != 1)
17192 {
17193 if (PQntuples(res) < 1)
17194 pg_fatal("query to obtain definition of view \"%s\" returned no data",
17195 tbinfo->dobj.name);
17196 else
17197 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17198 tbinfo->dobj.name);
17199 }
17200
17201 len = PQgetlength(res, 0, 0);
17202
17203 if (len == 0)
17204 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17205 tbinfo->dobj.name);
17206
17207 /* Strip off the trailing semicolon so that other things may follow. */
17208 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17209 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17210
17211 PQclear(res);
17212 destroyPQExpBuffer(query);
17213
17214 return result;
17215}
17216
17217/*
17218 * Create a dummy AS clause for a view. This is used when the real view
17219 * definition has to be postponed because of circular dependencies.
17220 * We must duplicate the view's external properties -- column names and types
17221 * (including collation) -- so that it works for subsequent references.
17222 *
17223 * This returns a new buffer which must be freed by the caller.
17224 */
17225static PQExpBuffer
17227{
17229 int j;
17230
17231 appendPQExpBufferStr(result, "SELECT");
17232
17233 for (j = 0; j < tbinfo->numatts; j++)
17234 {
17235 if (j > 0)
17238
17239 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17240
17241 /*
17242 * Must add collation if not default for the type, because CREATE OR
17243 * REPLACE VIEW won't change it
17244 */
17245 if (OidIsValid(tbinfo->attcollation[j]))
17246 {
17247 CollInfo *coll;
17248
17249 coll = findCollationByOid(tbinfo->attcollation[j]);
17250 if (coll)
17251 appendPQExpBuffer(result, " COLLATE %s",
17252 fmtQualifiedDumpable(coll));
17253 }
17254
17255 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17256 }
17257
17258 return result;
17259}
17260
17261/*
17262 * dumpTableSchema
17263 * write the declaration (not data) of one user-defined table or view
17264 */
17265static void
17267{
17268 DumpOptions *dopt = fout->dopt;
17272 char *qrelname;
17273 char *qualrelname;
17274 int numParents;
17275 TableInfo **parents;
17276 int actual_atts; /* number of attrs in this CREATE statement */
17277 const char *reltypename;
17278 char *storage;
17279 int j,
17280 k;
17281
17282 /* We had better have loaded per-column details about this table */
17283 Assert(tbinfo->interesting);
17284
17285 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17287
17288 if (tbinfo->hasoids)
17289 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17290 qrelname);
17291
17292 if (dopt->binary_upgrade)
17294
17295 /* Is it a table or a view? */
17296 if (tbinfo->relkind == RELKIND_VIEW)
17297 {
17299
17300 /*
17301 * Note: keep this code in sync with the is_view case in dumpRule()
17302 */
17303
17304 reltypename = "VIEW";
17305
17306 if (dopt->binary_upgrade)
17308 tbinfo->dobj.catId.oid);
17309
17310 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17311
17312 if (tbinfo->dummy_view)
17314 else
17315 {
17316 if (nonemptyReloptions(tbinfo->reloptions))
17317 {
17318 appendPQExpBufferStr(q, " WITH (");
17319 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17320 appendPQExpBufferChar(q, ')');
17321 }
17323 }
17324 appendPQExpBuffer(q, " AS\n%s", result->data);
17326
17327 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17328 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17329 appendPQExpBufferStr(q, ";\n");
17330 }
17331 else if (tbinfo->relkind == RELKIND_PROPGRAPH)
17332 {
17334 PGresult *res;
17335 int len;
17336
17337 reltypename = "PROPERTY GRAPH";
17338
17339 if (dopt->binary_upgrade)
17341 tbinfo->dobj.catId.oid);
17342
17343 appendPQExpBuffer(query,
17344 "SELECT pg_catalog.pg_get_propgraphdef('%u'::pg_catalog.oid) AS pgdef",
17345 tbinfo->dobj.catId.oid);
17346
17347 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17348
17349 if (PQntuples(res) != 1)
17350 {
17351 if (PQntuples(res) < 1)
17352 pg_fatal("query to obtain definition of property graph \"%s\" returned no data",
17353 tbinfo->dobj.name);
17354 else
17355 pg_fatal("query to obtain definition of property graph \"%s\" returned more than one definition",
17356 tbinfo->dobj.name);
17357 }
17358
17359 len = PQgetlength(res, 0, 0);
17360
17361 if (len == 0)
17362 pg_fatal("definition of property graph \"%s\" appears to be empty (length zero)",
17363 tbinfo->dobj.name);
17364
17365 appendPQExpBufferStr(q, PQgetvalue(res, 0, 0));
17366
17367 PQclear(res);
17368 destroyPQExpBuffer(query);
17369
17370 appendPQExpBufferStr(q, ";\n");
17371 }
17372 else
17373 {
17374 char *partkeydef = NULL;
17375 char *ftoptions = NULL;
17376 char *srvname = NULL;
17377 const char *foreign = "";
17378
17379 /*
17380 * Set reltypename, and collect any relkind-specific data that we
17381 * didn't fetch during getTables().
17382 */
17383 switch (tbinfo->relkind)
17384 {
17386 {
17388 PGresult *res;
17389
17390 reltypename = "TABLE";
17391
17392 /* retrieve partition key definition */
17393 appendPQExpBuffer(query,
17394 "SELECT pg_get_partkeydef('%u')",
17395 tbinfo->dobj.catId.oid);
17396 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17397 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17398 PQclear(res);
17399 destroyPQExpBuffer(query);
17400 break;
17401 }
17403 {
17405 PGresult *res;
17406 int i_srvname;
17407 int i_ftoptions;
17408
17409 reltypename = "FOREIGN TABLE";
17410
17411 /* retrieve name of foreign server and generic options */
17412 appendPQExpBuffer(query,
17413 "SELECT fs.srvname, "
17414 "pg_catalog.array_to_string(ARRAY("
17415 "SELECT pg_catalog.quote_ident(option_name) || "
17416 "' ' || pg_catalog.quote_literal(option_value) "
17417 "FROM pg_catalog.pg_options_to_table(ftoptions) "
17418 "ORDER BY option_name"
17419 "), E',\n ') AS ftoptions "
17420 "FROM pg_catalog.pg_foreign_table ft "
17421 "JOIN pg_catalog.pg_foreign_server fs "
17422 "ON (fs.oid = ft.ftserver) "
17423 "WHERE ft.ftrelid = '%u'",
17424 tbinfo->dobj.catId.oid);
17425 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17426 i_srvname = PQfnumber(res, "srvname");
17427 i_ftoptions = PQfnumber(res, "ftoptions");
17430 PQclear(res);
17431 destroyPQExpBuffer(query);
17432
17433 foreign = "FOREIGN ";
17434 break;
17435 }
17436 case RELKIND_MATVIEW:
17437 reltypename = "MATERIALIZED VIEW";
17438 break;
17439 default:
17440 reltypename = "TABLE";
17441 break;
17442 }
17443
17444 numParents = tbinfo->numParents;
17445 parents = tbinfo->parents;
17446
17447 if (dopt->binary_upgrade)
17449 tbinfo->dobj.catId.oid);
17450
17451 /*
17452 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17453 * ignore it when dumping if it was set in this case.
17454 */
17455 appendPQExpBuffer(q, "CREATE %s%s %s",
17456 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17457 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17458 "UNLOGGED " : "",
17460 qualrelname);
17461
17462 /*
17463 * Attach to type, if reloftype; except in case of a binary upgrade,
17464 * we dump the table normally and attach it to the type afterward.
17465 */
17466 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17467 appendPQExpBuffer(q, " OF %s",
17468 getFormattedTypeName(fout, tbinfo->reloftype,
17469 zeroIsError));
17470
17471 if (tbinfo->relkind != RELKIND_MATVIEW)
17472 {
17473 /* Dump the attributes */
17474 actual_atts = 0;
17475 for (j = 0; j < tbinfo->numatts; j++)
17476 {
17477 /*
17478 * Normally, dump if it's locally defined in this table, and
17479 * not dropped. But for binary upgrade, we'll dump all the
17480 * columns, and then fix up the dropped and nonlocal cases
17481 * below.
17482 */
17483 if (shouldPrintColumn(dopt, tbinfo, j))
17484 {
17485 bool print_default;
17486 bool print_notnull;
17487
17488 /*
17489 * Default value --- suppress if to be printed separately
17490 * or not at all.
17491 */
17492 print_default = (tbinfo->attrdefs[j] != NULL &&
17493 tbinfo->attrdefs[j]->dobj.dump &&
17494 !tbinfo->attrdefs[j]->separate);
17495
17496 /*
17497 * Not Null constraint --- print it if it is locally
17498 * defined, or if binary upgrade. (In the latter case, we
17499 * reset conislocal below.)
17500 */
17501 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17502 (tbinfo->notnull_islocal[j] ||
17503 dopt->binary_upgrade ||
17504 tbinfo->ispartition));
17505
17506 /*
17507 * Skip column if fully defined by reloftype, except in
17508 * binary upgrade
17509 */
17510 if (OidIsValid(tbinfo->reloftype) &&
17512 !dopt->binary_upgrade)
17513 continue;
17514
17515 /* Format properly if not first attr */
17516 if (actual_atts == 0)
17517 appendPQExpBufferStr(q, " (");
17518 else
17519 appendPQExpBufferChar(q, ',');
17520 appendPQExpBufferStr(q, "\n ");
17521 actual_atts++;
17522
17523 /* Attribute name */
17524 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17525
17526 if (tbinfo->attisdropped[j])
17527 {
17528 /*
17529 * ALTER TABLE DROP COLUMN clears
17530 * pg_attribute.atttypid, so we will not have gotten a
17531 * valid type name; insert INTEGER as a stopgap. We'll
17532 * clean things up later.
17533 */
17534 appendPQExpBufferStr(q, " INTEGER /* dummy */");
17535 /* and skip to the next column */
17536 continue;
17537 }
17538
17539 /*
17540 * Attribute type; print it except when creating a typed
17541 * table ('OF type_name'), but in binary-upgrade mode,
17542 * print it in that case too.
17543 */
17544 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17545 {
17546 appendPQExpBuffer(q, " %s",
17547 tbinfo->atttypnames[j]);
17548 }
17549
17550 if (print_default)
17551 {
17552 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17553 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17554 tbinfo->attrdefs[j]->adef_expr);
17555 else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17556 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17557 tbinfo->attrdefs[j]->adef_expr);
17558 else
17559 appendPQExpBuffer(q, " DEFAULT %s",
17560 tbinfo->attrdefs[j]->adef_expr);
17561 }
17562
17563 if (print_notnull)
17564 {
17565 if (tbinfo->notnull_constrs[j][0] == '\0')
17566 appendPQExpBufferStr(q, " NOT NULL");
17567 else
17568 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17569 fmtId(tbinfo->notnull_constrs[j]));
17570
17571 if (tbinfo->notnull_noinh[j])
17572 appendPQExpBufferStr(q, " NO INHERIT");
17573 }
17574
17575 /* Add collation if not default for the type */
17576 if (OidIsValid(tbinfo->attcollation[j]))
17577 {
17578 CollInfo *coll;
17579
17580 coll = findCollationByOid(tbinfo->attcollation[j]);
17581 if (coll)
17582 appendPQExpBuffer(q, " COLLATE %s",
17583 fmtQualifiedDumpable(coll));
17584 }
17585 }
17586
17587 /*
17588 * On the other hand, if we choose not to print a column
17589 * (likely because it is created by inheritance), but the
17590 * column has a locally-defined not-null constraint, we need
17591 * to dump the constraint as a standalone object.
17592 *
17593 * This syntax isn't SQL-conforming, but if you wanted
17594 * standard output you wouldn't be creating non-standard
17595 * objects to begin with.
17596 */
17597 if (!shouldPrintColumn(dopt, tbinfo, j) &&
17598 !tbinfo->attisdropped[j] &&
17599 tbinfo->notnull_constrs[j] != NULL &&
17600 tbinfo->notnull_islocal[j])
17601 {
17602 /* Format properly if not first attr */
17603 if (actual_atts == 0)
17604 appendPQExpBufferStr(q, " (");
17605 else
17606 appendPQExpBufferChar(q, ',');
17607 appendPQExpBufferStr(q, "\n ");
17608 actual_atts++;
17609
17610 if (tbinfo->notnull_constrs[j][0] == '\0')
17611 appendPQExpBuffer(q, "NOT NULL %s",
17612 fmtId(tbinfo->attnames[j]));
17613 else
17614 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17615 tbinfo->notnull_constrs[j],
17616 fmtId(tbinfo->attnames[j]));
17617
17618 if (tbinfo->notnull_noinh[j])
17619 appendPQExpBufferStr(q, " NO INHERIT");
17620 }
17621 }
17622
17623 /*
17624 * Add non-inherited CHECK constraints, if any.
17625 *
17626 * For partitions, we need to include check constraints even if
17627 * they're not defined locally, because the ALTER TABLE ATTACH
17628 * PARTITION that we'll emit later expects the constraint to be
17629 * there. (No need to fix conislocal: ATTACH PARTITION does that)
17630 */
17631 for (j = 0; j < tbinfo->ncheck; j++)
17632 {
17633 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17634
17635 if (constr->separate ||
17636 (!constr->conislocal && !tbinfo->ispartition))
17637 continue;
17638
17639 if (actual_atts == 0)
17640 appendPQExpBufferStr(q, " (\n ");
17641 else
17642 appendPQExpBufferStr(q, ",\n ");
17643
17644 appendPQExpBuffer(q, "CONSTRAINT %s ",
17645 fmtId(constr->dobj.name));
17646 appendPQExpBufferStr(q, constr->condef);
17647
17648 actual_atts++;
17649 }
17650
17651 if (actual_atts)
17652 appendPQExpBufferStr(q, "\n)");
17653 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17654 {
17655 /*
17656 * No attributes? we must have a parenthesized attribute list,
17657 * even though empty, when not using the OF TYPE syntax.
17658 */
17659 appendPQExpBufferStr(q, " (\n)");
17660 }
17661
17662 /*
17663 * Emit the INHERITS clause (not for partitions), except in
17664 * binary-upgrade mode.
17665 */
17666 if (numParents > 0 && !tbinfo->ispartition &&
17667 !dopt->binary_upgrade)
17668 {
17669 appendPQExpBufferStr(q, "\nINHERITS (");
17670 for (k = 0; k < numParents; k++)
17671 {
17672 TableInfo *parentRel = parents[k];
17673
17674 if (k > 0)
17675 appendPQExpBufferStr(q, ", ");
17677 }
17678 appendPQExpBufferChar(q, ')');
17679 }
17680
17681 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17682 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17683
17684 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17685 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17686 }
17687
17688 if (nonemptyReloptions(tbinfo->reloptions) ||
17689 nonemptyReloptions(tbinfo->toast_reloptions))
17690 {
17691 bool addcomma = false;
17692
17693 appendPQExpBufferStr(q, "\nWITH (");
17694 if (nonemptyReloptions(tbinfo->reloptions))
17695 {
17696 addcomma = true;
17697 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17698 }
17699 if (nonemptyReloptions(tbinfo->toast_reloptions))
17700 {
17701 if (addcomma)
17702 appendPQExpBufferStr(q, ", ");
17703 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17704 fout);
17705 }
17706 appendPQExpBufferChar(q, ')');
17707 }
17708
17709 /* Dump generic options if any */
17710 if (ftoptions && ftoptions[0])
17711 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17712
17713 /*
17714 * For materialized views, create the AS clause just like a view. At
17715 * this point, we always mark the view as not populated.
17716 */
17717 if (tbinfo->relkind == RELKIND_MATVIEW)
17718 {
17720
17722 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17723 result->data);
17725 }
17726 else
17727 appendPQExpBufferStr(q, ";\n");
17728
17729 /* Materialized views can depend on extensions */
17730 if (tbinfo->relkind == RELKIND_MATVIEW)
17732 "pg_catalog.pg_class",
17733 "MATERIALIZED VIEW",
17734 qualrelname);
17735
17736 /*
17737 * in binary upgrade mode, update the catalog with any missing values
17738 * that might be present.
17739 */
17740 if (dopt->binary_upgrade)
17741 {
17742 for (j = 0; j < tbinfo->numatts; j++)
17743 {
17744 if (tbinfo->attmissingval[j][0] != '\0')
17745 {
17746 appendPQExpBufferStr(q, "\n-- set missing value.\n");
17748 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17750 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17751 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17752 appendPQExpBufferChar(q, ',');
17753 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17754 appendPQExpBufferStr(q, ");\n\n");
17755 }
17756 }
17757 }
17758
17759 /*
17760 * To create binary-compatible heap files, we have to ensure the same
17761 * physical column order, including dropped columns, as in the
17762 * original. Therefore, we create dropped columns above and drop them
17763 * here, also updating their attlen/attalign values so that the
17764 * dropped column can be skipped properly. (We do not bother with
17765 * restoring the original attbyval setting.) Also, inheritance
17766 * relationships are set up by doing ALTER TABLE INHERIT rather than
17767 * using an INHERITS clause --- the latter would possibly mess up the
17768 * column order. That also means we have to take care about setting
17769 * attislocal correctly, plus fix up any inherited CHECK constraints.
17770 * Analogously, we set up typed tables using ALTER TABLE / OF here.
17771 *
17772 * We process foreign and partitioned tables here, even though they
17773 * lack heap storage, because they can participate in inheritance
17774 * relationships and we want this stuff to be consistent across the
17775 * inheritance tree. We can exclude indexes, toast tables, sequences
17776 * and matviews, even though they have storage, because we don't
17777 * support altering or dropping columns in them, nor can they be part
17778 * of inheritance trees.
17779 */
17780 if (dopt->binary_upgrade &&
17781 (tbinfo->relkind == RELKIND_RELATION ||
17782 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17783 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17784 {
17785 bool firstitem;
17786 bool firstitem_extra;
17787
17788 /*
17789 * Drop any dropped columns. Merge the pg_attribute manipulations
17790 * into a single SQL command, so that we don't cause repeated
17791 * relcache flushes on the target table. Otherwise we risk O(N^2)
17792 * relcache bloat while dropping N columns.
17793 */
17794 resetPQExpBuffer(extra);
17795 firstitem = true;
17796 for (j = 0; j < tbinfo->numatts; j++)
17797 {
17798 if (tbinfo->attisdropped[j])
17799 {
17800 if (firstitem)
17801 {
17802 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17803 "UPDATE pg_catalog.pg_attribute\n"
17804 "SET attlen = v.dlen, "
17805 "attalign = v.dalign, "
17806 "attbyval = false\n"
17807 "FROM (VALUES ");
17808 firstitem = false;
17809 }
17810 else
17811 appendPQExpBufferStr(q, ",\n ");
17812 appendPQExpBufferChar(q, '(');
17813 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17814 appendPQExpBuffer(q, ", %d, '%c')",
17815 tbinfo->attlen[j],
17816 tbinfo->attalign[j]);
17817 /* The ALTER ... DROP COLUMN commands must come after */
17818 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17820 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17821 fmtId(tbinfo->attnames[j]));
17822 }
17823 }
17824 if (!firstitem)
17825 {
17826 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17827 "WHERE attrelid = ");
17829 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17830 " AND attname = v.dname;\n");
17831 /* Now we can issue the actual DROP COLUMN commands */
17832 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17833 }
17834
17835 /*
17836 * Fix up inherited columns. As above, do the pg_attribute
17837 * manipulations in a single SQL command.
17838 */
17839 firstitem = true;
17840 for (j = 0; j < tbinfo->numatts; j++)
17841 {
17842 if (!tbinfo->attisdropped[j] &&
17843 !tbinfo->attislocal[j])
17844 {
17845 if (firstitem)
17846 {
17847 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17848 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17849 "SET attislocal = false\n"
17850 "WHERE attrelid = ");
17852 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17853 " AND attname IN (");
17854 firstitem = false;
17855 }
17856 else
17857 appendPQExpBufferStr(q, ", ");
17858 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17859 }
17860 }
17861 if (!firstitem)
17862 appendPQExpBufferStr(q, ");\n");
17863
17864 /*
17865 * Fix up not-null constraints that come from inheritance. As
17866 * above, do the pg_constraint manipulations in a single SQL
17867 * command. (Actually, two in special cases, if we're doing an
17868 * upgrade from < 18).
17869 */
17870 firstitem = true;
17871 firstitem_extra = true;
17872 resetPQExpBuffer(extra);
17873 for (j = 0; j < tbinfo->numatts; j++)
17874 {
17875 /*
17876 * If a not-null constraint comes from inheritance, reset
17877 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17878 * below. Special hack: in versions < 18, columns with no
17879 * local definition need their constraint to be matched by
17880 * column number in conkeys instead of by constraint name,
17881 * because the latter is not available. (We distinguish the
17882 * case because the constraint name is the empty string.)
17883 */
17884 if (tbinfo->notnull_constrs[j] != NULL &&
17885 !tbinfo->notnull_islocal[j])
17886 {
17887 if (tbinfo->notnull_constrs[j][0] != '\0')
17888 {
17889 if (firstitem)
17890 {
17891 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17892 "SET conislocal = false\n"
17893 "WHERE contype = 'n' AND conrelid = ");
17895 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17896 "conname IN (");
17897 firstitem = false;
17898 }
17899 else
17900 appendPQExpBufferStr(q, ", ");
17901 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17902 }
17903 else
17904 {
17905 if (firstitem_extra)
17906 {
17907 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17908 "SET conislocal = false\n"
17909 "WHERE contype = 'n' AND conrelid = ");
17911 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17912 "conkey IN (");
17913 firstitem_extra = false;
17914 }
17915 else
17916 appendPQExpBufferStr(extra, ", ");
17917 appendPQExpBuffer(extra, "'{%d}'", j + 1);
17918 }
17919 }
17920 }
17921 if (!firstitem)
17922 appendPQExpBufferStr(q, ");\n");
17923 if (!firstitem_extra)
17924 appendPQExpBufferStr(extra, ");\n");
17925
17926 if (extra->len > 0)
17927 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17928
17929 /*
17930 * Add inherited CHECK constraints, if any.
17931 *
17932 * For partitions, they were already dumped, and conislocal
17933 * doesn't need fixing.
17934 *
17935 * As above, issue only one direct manipulation of pg_constraint.
17936 * Although it is tempting to merge the ALTER ADD CONSTRAINT
17937 * commands into one as well, refrain for now due to concern about
17938 * possible backend memory bloat if there are many such
17939 * constraints.
17940 */
17941 resetPQExpBuffer(extra);
17942 firstitem = true;
17943 for (k = 0; k < tbinfo->ncheck; k++)
17944 {
17945 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17946
17947 if (constr->separate || constr->conislocal || tbinfo->ispartition)
17948 continue;
17949
17950 if (firstitem)
17951 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17952 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17954 fmtId(constr->dobj.name),
17955 constr->condef);
17956 /* Update pg_constraint after all the ALTER TABLEs */
17957 if (firstitem)
17958 {
17959 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17960 "SET conislocal = false\n"
17961 "WHERE contype = 'c' AND conrelid = ");
17963 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17964 appendPQExpBufferStr(extra, " AND conname IN (");
17965 firstitem = false;
17966 }
17967 else
17968 appendPQExpBufferStr(extra, ", ");
17969 appendStringLiteralAH(extra, constr->dobj.name, fout);
17970 }
17971 if (!firstitem)
17972 {
17973 appendPQExpBufferStr(extra, ");\n");
17974 appendBinaryPQExpBuffer(q, extra->data, extra->len);
17975 }
17976
17977 if (numParents > 0 && !tbinfo->ispartition)
17978 {
17979 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17980 for (k = 0; k < numParents; k++)
17981 {
17982 TableInfo *parentRel = parents[k];
17983
17984 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17987 }
17988 }
17989
17990 if (OidIsValid(tbinfo->reloftype))
17991 {
17992 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17993 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17995 getFormattedTypeName(fout, tbinfo->reloftype,
17996 zeroIsError));
17997 }
17998 }
17999
18000 /*
18001 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
18002 * relminmxid of all vacuumable relations. (While vacuum.c processes
18003 * TOAST tables semi-independently, here we see them only as children
18004 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
18005 * child toast table is handled below.)
18006 */
18007 if (dopt->binary_upgrade &&
18008 (tbinfo->relkind == RELKIND_RELATION ||
18009 tbinfo->relkind == RELKIND_MATVIEW))
18010 {
18011 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
18012 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18013 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18014 "WHERE oid = ",
18015 tbinfo->frozenxid, tbinfo->minmxid);
18017 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18018
18019 if (tbinfo->toast_oid)
18020 {
18021 /*
18022 * The toast table will have the same OID at restore, so we
18023 * can safely target it by OID.
18024 */
18025 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
18026 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
18027 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
18028 "WHERE oid = '%u';\n",
18029 tbinfo->toast_frozenxid,
18030 tbinfo->toast_minmxid, tbinfo->toast_oid);
18031 }
18032 }
18033
18034 /*
18035 * In binary_upgrade mode, restore matviews' populated status by
18036 * poking pg_class directly. This is pretty ugly, but we can't use
18037 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
18038 * matview is not populated even though this matview is; in any case,
18039 * we want to transfer the matview's heap storage, not run REFRESH.
18040 */
18041 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
18042 tbinfo->relispopulated)
18043 {
18044 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
18045 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
18046 "SET relispopulated = 't'\n"
18047 "WHERE oid = ");
18049 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
18050 }
18051
18052 /*
18053 * Dump additional per-column properties that we can't handle in the
18054 * main CREATE TABLE command.
18055 */
18056 for (j = 0; j < tbinfo->numatts; j++)
18057 {
18058 /* None of this applies to dropped columns */
18059 if (tbinfo->attisdropped[j])
18060 continue;
18061
18062 /*
18063 * Dump per-column statistics information. We only issue an ALTER
18064 * TABLE statement if the attstattarget entry for this column is
18065 * not the default value.
18066 */
18067 if (tbinfo->attstattarget[j] >= 0)
18068 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
18070 fmtId(tbinfo->attnames[j]),
18071 tbinfo->attstattarget[j]);
18072
18073 /*
18074 * Dump per-column storage information. The statement is only
18075 * dumped if the storage has been changed from the type's default.
18076 */
18077 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
18078 {
18079 switch (tbinfo->attstorage[j])
18080 {
18081 case TYPSTORAGE_PLAIN:
18082 storage = "PLAIN";
18083 break;
18085 storage = "EXTERNAL";
18086 break;
18088 storage = "EXTENDED";
18089 break;
18090 case TYPSTORAGE_MAIN:
18091 storage = "MAIN";
18092 break;
18093 default:
18094 storage = NULL;
18095 }
18096
18097 /*
18098 * Only dump the statement if it's a storage type we recognize
18099 */
18100 if (storage != NULL)
18101 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
18103 fmtId(tbinfo->attnames[j]),
18104 storage);
18105 }
18106
18107 /*
18108 * Dump per-column compression, if it's been set.
18109 */
18110 if (!dopt->no_toast_compression)
18111 {
18112 const char *cmname;
18113
18114 switch (tbinfo->attcompression[j])
18115 {
18116 case 'p':
18117 cmname = "pglz";
18118 break;
18119 case 'l':
18120 cmname = "lz4";
18121 break;
18122 default:
18123 cmname = NULL;
18124 break;
18125 }
18126
18127 if (cmname != NULL)
18128 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
18130 fmtId(tbinfo->attnames[j]),
18131 cmname);
18132 }
18133
18134 /*
18135 * Dump per-column attributes.
18136 */
18137 if (tbinfo->attoptions[j][0] != '\0')
18138 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
18140 fmtId(tbinfo->attnames[j]),
18141 tbinfo->attoptions[j]);
18142
18143 /*
18144 * Dump per-column fdw options.
18145 */
18146 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
18147 tbinfo->attfdwoptions[j][0] != '\0')
18149 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
18150 " %s\n"
18151 ");\n",
18153 fmtId(tbinfo->attnames[j]),
18154 tbinfo->attfdwoptions[j]);
18155 } /* end loop over columns */
18156
18158 free(ftoptions);
18159 free(srvname);
18160 }
18161
18162 /*
18163 * dump properties we only have ALTER TABLE syntax for
18164 */
18165 if ((tbinfo->relkind == RELKIND_RELATION ||
18166 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
18167 tbinfo->relkind == RELKIND_MATVIEW) &&
18168 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
18169 {
18170 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
18171 {
18172 /* nothing to do, will be set when the index is dumped */
18173 }
18174 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
18175 {
18176 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
18177 qualrelname);
18178 }
18179 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
18180 {
18181 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
18182 qualrelname);
18183 }
18184 }
18185
18186 if (tbinfo->forcerowsec)
18187 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
18188 qualrelname);
18189
18190 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
18191
18192 if (dopt->binary_upgrade)
18195 tbinfo->dobj.namespace->dobj.name);
18196
18197 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18198 {
18199 char *tablespace = NULL;
18200 char *tableam = NULL;
18201
18202 /*
18203 * _selectTablespace() relies on tablespace-enabled objects in the
18204 * default tablespace to have a tablespace of "" (empty string) versus
18205 * non-tablespace-enabled objects to have a tablespace of NULL.
18206 * getTables() sets tbinfo->reltablespace to "" for the default
18207 * tablespace (not NULL).
18208 */
18209 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
18210 tablespace = tbinfo->reltablespace;
18211
18212 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
18214 tableam = tbinfo->amname;
18215
18216 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18217 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18218 .namespace = tbinfo->dobj.namespace->dobj.name,
18219 .tablespace = tablespace,
18220 .tableam = tableam,
18221 .relkind = tbinfo->relkind,
18222 .owner = tbinfo->rolname,
18223 .description = reltypename,
18224 .section = tbinfo->postponed_def ?
18226 .createStmt = q->data,
18227 .dropStmt = delq->data));
18228 }
18229
18230 /* Dump Table Comments */
18231 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18233
18234 /* Dump Table Security Labels */
18235 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18237
18238 /*
18239 * Dump comments for not-null constraints that aren't to be dumped
18240 * separately (those are processed by collectComments/dumpComment).
18241 */
18242 if (!fout->dopt->no_comments && dopt->dumpSchema &&
18243 fout->remoteVersion >= 180000)
18244 {
18246 PQExpBuffer tag = NULL;
18247
18248 for (j = 0; j < tbinfo->numatts; j++)
18249 {
18250 if (tbinfo->notnull_constrs[j] != NULL &&
18251 tbinfo->notnull_comment[j] != NULL)
18252 {
18253 if (comment == NULL)
18254 {
18256 tag = createPQExpBuffer();
18257 }
18258 else
18259 {
18261 resetPQExpBuffer(tag);
18262 }
18263
18264 appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18265 fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18266 appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18268
18269 appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18270 fmtId(tbinfo->notnull_constrs[j]), qrelname);
18271
18273 ARCHIVE_OPTS(.tag = tag->data,
18274 .namespace = tbinfo->dobj.namespace->dobj.name,
18275 .owner = tbinfo->rolname,
18276 .description = "COMMENT",
18277 .section = SECTION_NONE,
18278 .createStmt = comment->data,
18279 .deps = &(tbinfo->dobj.dumpId),
18280 .nDeps = 1));
18281 }
18282 }
18283
18285 destroyPQExpBuffer(tag);
18286 }
18287
18288 /* Dump comments on inlined table constraints */
18289 for (j = 0; j < tbinfo->ncheck; j++)
18290 {
18291 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18292
18293 if (constr->separate || !constr->conislocal)
18294 continue;
18295
18296 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18298 }
18299
18302 destroyPQExpBuffer(extra);
18303 free(qrelname);
18305}
18306
18307/*
18308 * dumpTableAttach
18309 * write to fout the commands to attach a child partition
18310 *
18311 * Child partitions are always made by creating them separately
18312 * and then using ATTACH PARTITION, rather than using
18313 * CREATE TABLE ... PARTITION OF. This is important for preserving
18314 * any possible discrepancy in column layout, to allow assigning the
18315 * correct tablespace if different, and so that it's possible to restore
18316 * a partition without restoring its parent. (You'll get an error from
18317 * the ATTACH PARTITION command, but that can be ignored, or skipped
18318 * using "pg_restore -L" if you prefer.) The last point motivates
18319 * treating ATTACH PARTITION as a completely separate ArchiveEntry
18320 * rather than emitting it within the child partition's ArchiveEntry.
18321 */
18322static void
18324{
18325 DumpOptions *dopt = fout->dopt;
18326 PQExpBuffer q;
18327 PGresult *res;
18328 char *partbound;
18329
18330 /* Do nothing if not dumping schema */
18331 if (!dopt->dumpSchema)
18332 return;
18333
18334 q = createPQExpBuffer();
18335
18337 {
18338 /* Set up query for partbound details */
18340 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18341
18343 "SELECT pg_get_expr(c.relpartbound, c.oid) "
18344 "FROM pg_class c "
18345 "WHERE c.oid = $1");
18346
18348
18350 }
18351
18353 "EXECUTE dumpTableAttach('%u')",
18354 attachinfo->partitionTbl->dobj.catId.oid);
18355
18357 partbound = PQgetvalue(res, 0, 0);
18358
18359 /* Perform ALTER TABLE on the parent */
18361 "ALTER TABLE ONLY %s ",
18362 fmtQualifiedDumpable(attachinfo->parentTbl));
18364 "ATTACH PARTITION %s %s;\n",
18365 fmtQualifiedDumpable(attachinfo->partitionTbl),
18366 partbound);
18367
18368 /*
18369 * There is no point in creating a drop query as the drop is done by table
18370 * drop. (If you think to change this, see also _printTocEntry().)
18371 * Although this object doesn't really have ownership as such, set the
18372 * owner field anyway to ensure that the command is run by the correct
18373 * role at restore time.
18374 */
18375 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18376 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18377 .namespace = attachinfo->dobj.namespace->dobj.name,
18378 .owner = attachinfo->partitionTbl->rolname,
18379 .description = "TABLE ATTACH",
18380 .section = SECTION_PRE_DATA,
18381 .createStmt = q->data));
18382
18383 PQclear(res);
18385}
18386
18387/*
18388 * dumpAttrDef --- dump an attribute's default-value declaration
18389 */
18390static void
18392{
18393 DumpOptions *dopt = fout->dopt;
18394 TableInfo *tbinfo = adinfo->adtable;
18395 int adnum = adinfo->adnum;
18396 PQExpBuffer q;
18398 char *qualrelname;
18399 char *tag;
18400 char *foreign;
18401
18402 /* Do nothing if not dumping schema */
18403 if (!dopt->dumpSchema)
18404 return;
18405
18406 /* Skip if not "separate"; it was dumped in the table's definition */
18407 if (!adinfo->separate)
18408 return;
18409
18410 q = createPQExpBuffer();
18412
18414
18415 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18416
18418 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18419 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18420 adinfo->adef_expr);
18421
18422 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18424 fmtId(tbinfo->attnames[adnum - 1]));
18425
18426 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18427
18428 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18429 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18430 ARCHIVE_OPTS(.tag = tag,
18431 .namespace = tbinfo->dobj.namespace->dobj.name,
18432 .owner = tbinfo->rolname,
18433 .description = "DEFAULT",
18434 .section = SECTION_PRE_DATA,
18435 .createStmt = q->data,
18436 .dropStmt = delq->data));
18437
18438 free(tag);
18442}
18443
18444/*
18445 * getAttrName: extract the correct name for an attribute
18446 *
18447 * The array tblInfo->attnames[] only provides names of user attributes;
18448 * if a system attribute number is supplied, we have to fake it.
18449 * We also do a little bit of bounds checking for safety's sake.
18450 */
18451static const char *
18452getAttrName(int attrnum, const TableInfo *tblInfo)
18453{
18454 if (attrnum > 0 && attrnum <= tblInfo->numatts)
18455 return tblInfo->attnames[attrnum - 1];
18456 switch (attrnum)
18457 {
18459 return "ctid";
18461 return "xmin";
18463 return "cmin";
18465 return "xmax";
18467 return "cmax";
18469 return "tableoid";
18470 }
18471 pg_fatal("invalid column number %d for table \"%s\"",
18472 attrnum, tblInfo->dobj.name);
18473 return NULL; /* keep compiler quiet */
18474}
18475
18476/*
18477 * dumpIndex
18478 * write out to fout a user-defined index
18479 */
18480static void
18482{
18483 DumpOptions *dopt = fout->dopt;
18484 TableInfo *tbinfo = indxinfo->indextable;
18485 bool is_constraint = (indxinfo->indexconstraint != 0);
18486 PQExpBuffer q;
18488 char *qindxname;
18489 char *qqindxname;
18490
18491 /* Do nothing if not dumping schema */
18492 if (!dopt->dumpSchema)
18493 return;
18494
18495 q = createPQExpBuffer();
18497
18498 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18500
18501 /*
18502 * If there's an associated constraint, don't dump the index per se, but
18503 * do dump any comment for it. (This is safe because dependency ordering
18504 * will have ensured the constraint is emitted first.) Note that the
18505 * emitted comment has to be shown as depending on the constraint, not the
18506 * index, in such cases.
18507 */
18508 if (!is_constraint)
18509 {
18510 char *indstatcols = indxinfo->indstatcols;
18511 char *indstatvals = indxinfo->indstatvals;
18512 char **indstatcolsarray = NULL;
18513 char **indstatvalsarray = NULL;
18514 int nstatcols = 0;
18515 int nstatvals = 0;
18516
18517 if (dopt->binary_upgrade)
18519 indxinfo->dobj.catId.oid);
18520
18521 /* Plain secondary index */
18522 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18523
18524 /*
18525 * Append ALTER TABLE commands as needed to set properties that we
18526 * only have ALTER TABLE syntax for. Keep this in sync with the
18527 * similar code in dumpConstraint!
18528 */
18529
18530 /* If the index is clustered, we need to record that. */
18531 if (indxinfo->indisclustered)
18532 {
18533 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18535 /* index name is not qualified in this syntax */
18536 appendPQExpBuffer(q, " ON %s;\n",
18537 qindxname);
18538 }
18539
18540 /*
18541 * If the index has any statistics on some of its columns, generate
18542 * the associated ALTER INDEX queries.
18543 */
18544 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18545 {
18546 int j;
18547
18548 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18549 pg_fatal("could not parse index statistic columns");
18550 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18551 pg_fatal("could not parse index statistic values");
18552 if (nstatcols != nstatvals)
18553 pg_fatal("mismatched number of columns and values for index statistics");
18554
18555 for (j = 0; j < nstatcols; j++)
18556 {
18557 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18558
18559 /*
18560 * Note that this is a column number, so no quotes should be
18561 * used.
18562 */
18563 appendPQExpBuffer(q, "ALTER COLUMN %s ",
18565 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18567 }
18568 }
18569
18570 /* Indexes can depend on extensions */
18572 "pg_catalog.pg_class",
18573 "INDEX", qqindxname);
18574
18575 /* If the index defines identity, we need to record that. */
18576 if (indxinfo->indisreplident)
18577 {
18578 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18580 /* index name is not qualified in this syntax */
18581 appendPQExpBuffer(q, " INDEX %s;\n",
18582 qindxname);
18583 }
18584
18585 /*
18586 * If this index is a member of a partitioned index, the backend will
18587 * not allow us to drop it separately, so don't try. It will go away
18588 * automatically when we drop either the index's table or the
18589 * partitioned index. (If, in a selective restore with --clean, we
18590 * drop neither of those, then this index will not be dropped either.
18591 * But that's fine, and even if you think it's not, the backend won't
18592 * let us do differently.)
18593 */
18594 if (indxinfo->parentidx == 0)
18595 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18596
18597 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18598 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18599 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18600 .namespace = tbinfo->dobj.namespace->dobj.name,
18601 .tablespace = indxinfo->tablespace,
18602 .owner = tbinfo->rolname,
18603 .description = "INDEX",
18604 .section = SECTION_POST_DATA,
18605 .createStmt = q->data,
18606 .dropStmt = delq->data));
18607
18610 }
18611
18612 /* Dump Index Comments */
18613 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18614 dumpComment(fout, "INDEX", qindxname,
18615 tbinfo->dobj.namespace->dobj.name,
18616 tbinfo->rolname,
18617 indxinfo->dobj.catId, 0,
18618 is_constraint ? indxinfo->indexconstraint :
18619 indxinfo->dobj.dumpId);
18620
18623 free(qindxname);
18625}
18626
18627/*
18628 * dumpIndexAttach
18629 * write out to fout a partitioned-index attachment clause
18630 */
18631static void
18633{
18634 /* Do nothing if not dumping schema */
18635 if (!fout->dopt->dumpSchema)
18636 return;
18637
18638 if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18639 {
18641
18642 appendPQExpBuffer(q, "ALTER INDEX %s ",
18643 fmtQualifiedDumpable(attachinfo->parentIdx));
18644 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18645 fmtQualifiedDumpable(attachinfo->partitionIdx));
18646
18647 /*
18648 * There is no need for a dropStmt since the drop is done implicitly
18649 * when we drop either the index's table or the partitioned index.
18650 * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18651 * there's no way to do it anyway. (If you think to change this,
18652 * consider also what to do with --if-exists.)
18653 *
18654 * Although this object doesn't really have ownership as such, set the
18655 * owner field anyway to ensure that the command is run by the correct
18656 * role at restore time.
18657 */
18658 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18659 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18660 .namespace = attachinfo->dobj.namespace->dobj.name,
18661 .owner = attachinfo->parentIdx->indextable->rolname,
18662 .description = "INDEX ATTACH",
18663 .section = SECTION_POST_DATA,
18664 .createStmt = q->data));
18665
18667 }
18668}
18669
18670/*
18671 * dumpStatisticsExt
18672 * write out to fout an extended statistics object
18673 */
18674static void
18676{
18677 DumpOptions *dopt = fout->dopt;
18678 PQExpBuffer q;
18680 PQExpBuffer query;
18681 char *qstatsextname;
18682 PGresult *res;
18683 char *stxdef;
18684
18685 /* Do nothing if not dumping schema */
18686 if (!dopt->dumpSchema)
18687 return;
18688
18689 q = createPQExpBuffer();
18691 query = createPQExpBuffer();
18692
18694
18695 appendPQExpBuffer(query, "SELECT "
18696 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18697 statsextinfo->dobj.catId.oid);
18698
18699 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18700
18701 stxdef = PQgetvalue(res, 0, 0);
18702
18703 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18704 appendPQExpBuffer(q, "%s;\n", stxdef);
18705
18706 /*
18707 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18708 * for this statistics object is not the default value.
18709 */
18710 if (statsextinfo->stattarget >= 0)
18711 {
18712 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18714 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18715 statsextinfo->stattarget);
18716 }
18717
18718 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18720
18721 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18722 ArchiveEntry(fout, statsextinfo->dobj.catId,
18723 statsextinfo->dobj.dumpId,
18724 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18725 .namespace = statsextinfo->dobj.namespace->dobj.name,
18726 .owner = statsextinfo->rolname,
18727 .description = "STATISTICS",
18728 .section = SECTION_POST_DATA,
18729 .createStmt = q->data,
18730 .dropStmt = delq->data));
18731
18732 /* Dump Statistics Comments */
18733 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18734 dumpComment(fout, "STATISTICS", qstatsextname,
18735 statsextinfo->dobj.namespace->dobj.name,
18736 statsextinfo->rolname,
18737 statsextinfo->dobj.catId, 0,
18738 statsextinfo->dobj.dumpId);
18739
18740 PQclear(res);
18743 destroyPQExpBuffer(query);
18745}
18746
18747/*
18748 * dumpStatisticsExtStats
18749 * write out to fout the stats for an extended statistics object
18750 */
18751static void
18753{
18754 DumpOptions *dopt = fout->dopt;
18755 PQExpBuffer query;
18756 PGresult *res;
18757 int nstats;
18758
18759 /* Do nothing if not dumping statistics */
18760 if (!dopt->dumpStatistics)
18761 return;
18762
18764 {
18766
18767 /*---------
18768 * Set up query for details about extended statistics objects.
18769 *
18770 * The query depends on the backend version:
18771 * - In v19 and newer versions, query directly the pg_stats_ext*
18772 * catalogs.
18773 * - In v18 and older versions, ndistinct and dependencies have a
18774 * different format that needs translation.
18775 * - In v14 and older versions, inherited does not exist.
18776 * - In v11 and older versions, there is no pg_stats_ext, hence
18777 * the logic joins pg_statistic_ext and pg_namespace.
18778 *---------
18779 */
18780
18782 "PREPARE getExtStatsStats(pg_catalog.name, pg_catalog.name) AS\n"
18783 "SELECT ");
18784
18785 /*
18786 * Versions 15 and newer have inherited stats.
18787 *
18788 * Create this column in all versions because we need to order by it
18789 * later.
18790 */
18791 if (fout->remoteVersion >= 150000)
18792 appendPQExpBufferStr(pq, "e.inherited, ");
18793 else
18794 appendPQExpBufferStr(pq, "false AS inherited, ");
18795
18796 /*--------
18797 * The ndistinct and dependencies formats changed in v19, so
18798 * everything before that needs to be translated.
18799 *
18800 * The ndistinct translation converts this kind of data:
18801 * {"3, 4": 11, "3, 6": 11, "4, 6": 11, "3, 4, 6": 11}
18802 *
18803 * to this:
18804 * [ {"attributes": [3,4], "ndistinct": 11},
18805 * {"attributes": [3,6], "ndistinct": 11},
18806 * {"attributes": [4,6], "ndistinct": 11},
18807 * {"attributes": [3,4,6], "ndistinct": 11} ]
18808 *
18809 * The dependencies translation converts this kind of data:
18810 * {"3 => 4": 1.000000, "3 => 6": 1.000000,
18811 * "4 => 6": 1.000000, "3, 4 => 6": 1.000000,
18812 * "3, 6 => 4": 1.000000}
18813 *
18814 * to this:
18815 * [ {"attributes": [3], "dependency": 4, "degree": 1.000000},
18816 * {"attributes": [3], "dependency": 6, "degree": 1.000000},
18817 * {"attributes": [4], "dependency": 6, "degree": 1.000000},
18818 * {"attributes": [3,4], "dependency": 6, "degree": 1.000000},
18819 * {"attributes": [3,6], "dependency": 4, "degree": 1.000000} ]
18820 *--------
18821 */
18822 if (fout->remoteVersion >= 190000)
18823 appendPQExpBufferStr(pq, "e.n_distinct, e.dependencies, ");
18824 else
18826 "( "
18827 "SELECT json_agg( "
18828 " json_build_object( "
18830 " string_to_array(kv.key, ', ')::integer[], "
18832 " kv.value::bigint )) "
18833 "FROM json_each_text(e.n_distinct::text::json) AS kv"
18834 ") AS n_distinct, "
18835 "( "
18836 "SELECT json_agg( "
18837 " json_build_object( "
18839 " string_to_array( "
18840 " split_part(kv.key, ' => ', 1), "
18841 " ', ')::integer[], "
18843 " split_part(kv.key, ' => ', 2)::integer, "
18845 " kv.value::double precision )) "
18846 "FROM json_each_text(e.dependencies::text::json) AS kv "
18847 ") AS dependencies, ");
18848
18849 /* MCV was introduced v13 */
18850 if (fout->remoteVersion >= 130000)
18852 "e.most_common_vals, e.most_common_freqs, "
18853 "e.most_common_base_freqs, ");
18854 else
18856 "NULL AS most_common_vals, NULL AS most_common_freqs, "
18857 "NULL AS most_common_base_freqs, ");
18858
18859 /* Expressions were introduced in v14 */
18860 if (fout->remoteVersion >= 140000)
18861 {
18862 /*
18863 * There is no ordering column in pg_stats_ext_exprs. However, we
18864 * can rely on the unnesting of pg_statistic_ext_data.stxdexpr to
18865 * maintain the desired order of expression elements.
18866 */
18868 "( "
18869 "SELECT jsonb_pretty(jsonb_agg("
18870 "nullif(j.obj, '{}'::jsonb))) "
18871 "FROM pg_stats_ext_exprs AS ee "
18872 "CROSS JOIN LATERAL jsonb_strip_nulls("
18873 " jsonb_build_object( "
18874 " 'null_frac', ee.null_frac::text, "
18875 " 'avg_width', ee.avg_width::text, "
18876 " 'n_distinct', ee.n_distinct::text, "
18877 " 'most_common_vals', ee.most_common_vals::text, "
18878 " 'most_common_freqs', ee.most_common_freqs::text, "
18879 " 'histogram_bounds', ee.histogram_bounds::text, "
18880 " 'correlation', ee.correlation::text, "
18881 " 'most_common_elems', ee.most_common_elems::text, "
18882 " 'most_common_elem_freqs', ee.most_common_elem_freqs::text, "
18883 " 'elem_count_histogram', ee.elem_count_histogram::text");
18884
18885 /* These three have been added to pg_stats_ext_exprs in v19. */
18886 if (fout->remoteVersion >= 190000)
18888 ", "
18889 " 'range_length_histogram', ee.range_length_histogram::text, "
18890 " 'range_empty_frac', ee.range_empty_frac::text, "
18891 " 'range_bounds_histogram', ee.range_bounds_histogram::text");
18892
18894 " )) AS j(obj)"
18895 "WHERE ee.statistics_schemaname = $1 "
18896 "AND ee.statistics_name = $2 ");
18897 /* Inherited expressions introduced in v15 */
18898 if (fout->remoteVersion >= 150000)
18899 appendPQExpBufferStr(pq, "AND ee.inherited = e.inherited");
18900
18901 appendPQExpBufferStr(pq, ") AS exprs ");
18902 }
18903 else
18904 appendPQExpBufferStr(pq, "NULL AS exprs ");
18905
18906 /* pg_stats_ext introduced in v12 */
18907 if (fout->remoteVersion >= 120000)
18909 "FROM pg_catalog.pg_stats_ext AS e "
18910 "WHERE e.statistics_schemaname = $1 "
18911 "AND e.statistics_name = $2 ");
18912 else
18914 "FROM ( "
18915 "SELECT s.stxndistinct AS n_distinct, "
18916 " s.stxdependencies AS dependencies "
18917 "FROM pg_catalog.pg_statistic_ext AS s "
18918 "JOIN pg_catalog.pg_namespace AS n "
18919 "ON n.oid = s.stxnamespace "
18920 "WHERE n.nspname = $1 "
18921 "AND s.stxname = $2 "
18922 ") AS e ");
18923
18924 /* we always have an inherited column, but it may be a constant */
18925 appendPQExpBufferStr(pq, "ORDER BY inherited");
18926
18927 ExecuteSqlStatement(fout, pq->data);
18928
18930
18932 }
18933
18934 query = createPQExpBuffer();
18935
18936 appendPQExpBufferStr(query, "EXECUTE getExtStatsStats(");
18937 appendStringLiteralAH(query, statsextinfo->dobj.namespace->dobj.name, fout);
18938 appendPQExpBufferStr(query, "::pg_catalog.name, ");
18939 appendStringLiteralAH(query, statsextinfo->dobj.name, fout);
18940 appendPQExpBufferStr(query, "::pg_catalog.name)");
18941
18942 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18943
18944 destroyPQExpBuffer(query);
18945
18946 nstats = PQntuples(res);
18947
18948 if (nstats > 0)
18949 {
18951
18952 int i_inherited = PQfnumber(res, "inherited");
18953 int i_ndistinct = PQfnumber(res, "n_distinct");
18954 int i_dependencies = PQfnumber(res, "dependencies");
18955 int i_mcv = PQfnumber(res, "most_common_vals");
18956 int i_mcf = PQfnumber(res, "most_common_freqs");
18957 int i_mcbf = PQfnumber(res, "most_common_base_freqs");
18958 int i_exprs = PQfnumber(res, "exprs");
18959
18960 for (int i = 0; i < nstats; i++)
18961 {
18962 TableInfo *tbinfo = statsextinfo->stattable;
18963
18964 if (PQgetisnull(res, i, i_inherited))
18965 pg_fatal("inherited cannot be NULL");
18966
18968 "SELECT * FROM pg_catalog.pg_restore_extended_stats(\n");
18969 appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
18971
18972 /* Relation information */
18973 appendPQExpBufferStr(out, "\t'schemaname', ");
18974 appendStringLiteralAH(out, tbinfo->dobj.namespace->dobj.name, fout);
18975 appendPQExpBufferStr(out, ",\n\t'relname', ");
18976 appendStringLiteralAH(out, tbinfo->dobj.name, fout);
18977
18978 /* Extended statistics information */
18979 appendPQExpBufferStr(out, ",\n\t'statistics_schemaname', ");
18980 appendStringLiteralAH(out, statsextinfo->dobj.namespace->dobj.name, fout);
18981 appendPQExpBufferStr(out, ",\n\t'statistics_name', ");
18982 appendStringLiteralAH(out, statsextinfo->dobj.name, fout);
18983 appendNamedArgument(out, fout, "inherited", "boolean",
18984 PQgetvalue(res, i, i_inherited));
18985
18986 if (!PQgetisnull(res, i, i_ndistinct))
18987 appendNamedArgument(out, fout, "n_distinct", "pg_ndistinct",
18988 PQgetvalue(res, i, i_ndistinct));
18989
18990 if (!PQgetisnull(res, i, i_dependencies))
18991 appendNamedArgument(out, fout, "dependencies", "pg_dependencies",
18992 PQgetvalue(res, i, i_dependencies));
18993
18994 if (!PQgetisnull(res, i, i_mcv))
18995 appendNamedArgument(out, fout, "most_common_vals", "text[]",
18996 PQgetvalue(res, i, i_mcv));
18997
18998 if (!PQgetisnull(res, i, i_mcf))
18999 appendNamedArgument(out, fout, "most_common_freqs", "double precision[]",
19000 PQgetvalue(res, i, i_mcf));
19001
19002 if (!PQgetisnull(res, i, i_mcbf))
19003 appendNamedArgument(out, fout, "most_common_base_freqs", "double precision[]",
19004 PQgetvalue(res, i, i_mcbf));
19005
19006 if (!PQgetisnull(res, i, i_exprs))
19007 appendNamedArgument(out, fout, "exprs", "jsonb",
19008 PQgetvalue(res, i, i_exprs));
19009
19010 appendPQExpBufferStr(out, "\n);\n");
19011 }
19012
19014 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
19015 .namespace = statsextinfo->dobj.namespace->dobj.name,
19016 .owner = statsextinfo->rolname,
19017 .description = "EXTENDED STATISTICS DATA",
19018 .section = SECTION_POST_DATA,
19019 .createStmt = out->data,
19020 .deps = &statsextinfo->dobj.dumpId,
19021 .nDeps = 1));
19022 destroyPQExpBuffer(out);
19023 }
19024 PQclear(res);
19025}
19026
19027/*
19028 * dumpConstraint
19029 * write out to fout a user-defined constraint
19030 */
19031static void
19033{
19034 DumpOptions *dopt = fout->dopt;
19035 TableInfo *tbinfo = coninfo->contable;
19036 PQExpBuffer q;
19038 char *tag = NULL;
19039 char *foreign;
19040
19041 /* Do nothing if not dumping schema */
19042 if (!dopt->dumpSchema)
19043 return;
19044
19045 q = createPQExpBuffer();
19047
19048 foreign = tbinfo &&
19049 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
19050
19051 if (coninfo->contype == 'p' ||
19052 coninfo->contype == 'u' ||
19053 coninfo->contype == 'x')
19054 {
19055 /* Index-related constraint */
19057 int k;
19058
19060
19061 if (indxinfo == NULL)
19062 pg_fatal("missing index for constraint \"%s\"",
19063 coninfo->dobj.name);
19064
19065 if (dopt->binary_upgrade)
19067 indxinfo->dobj.catId.oid);
19068
19069 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
19071 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
19072 fmtId(coninfo->dobj.name));
19073
19074 if (coninfo->condef)
19075 {
19076 /* pg_get_constraintdef should have provided everything */
19077 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
19078 }
19079 else
19080 {
19082 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
19083
19084 /*
19085 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
19086 * indexes. Being able to create this was fixed, but we need to
19087 * make the index distinct in order to be able to restore the
19088 * dump.
19089 */
19090 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
19091 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
19092 appendPQExpBufferStr(q, " (");
19093 for (k = 0; k < indxinfo->indnkeyattrs; k++)
19094 {
19095 int indkey = (int) indxinfo->indkeys[k];
19096 const char *attname;
19097
19099 break;
19101
19102 appendPQExpBuffer(q, "%s%s",
19103 (k == 0) ? "" : ", ",
19104 fmtId(attname));
19105 }
19106 if (coninfo->conperiod)
19107 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
19108
19109 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
19110 appendPQExpBufferStr(q, ") INCLUDE (");
19111
19112 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
19113 {
19114 int indkey = (int) indxinfo->indkeys[k];
19115 const char *attname;
19116
19118 break;
19120
19121 appendPQExpBuffer(q, "%s%s",
19122 (k == indxinfo->indnkeyattrs) ? "" : ", ",
19123 fmtId(attname));
19124 }
19125
19126 appendPQExpBufferChar(q, ')');
19127
19128 if (nonemptyReloptions(indxinfo->indreloptions))
19129 {
19130 appendPQExpBufferStr(q, " WITH (");
19131 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
19132 appendPQExpBufferChar(q, ')');
19133 }
19134
19135 if (coninfo->condeferrable)
19136 {
19137 appendPQExpBufferStr(q, " DEFERRABLE");
19138 if (coninfo->condeferred)
19139 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
19140 }
19141
19142 appendPQExpBufferStr(q, ";\n");
19143 }
19144
19145 /*
19146 * Append ALTER TABLE commands as needed to set properties that we
19147 * only have ALTER TABLE syntax for. Keep this in sync with the
19148 * similar code in dumpIndex!
19149 */
19150
19151 /* If the index is clustered, we need to record that. */
19152 if (indxinfo->indisclustered)
19153 {
19154 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
19156 /* index name is not qualified in this syntax */
19157 appendPQExpBuffer(q, " ON %s;\n",
19158 fmtId(indxinfo->dobj.name));
19159 }
19160
19161 /* If the index defines identity, we need to record that. */
19162 if (indxinfo->indisreplident)
19163 {
19164 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
19166 /* index name is not qualified in this syntax */
19167 appendPQExpBuffer(q, " INDEX %s;\n",
19168 fmtId(indxinfo->dobj.name));
19169 }
19170
19171 /* Indexes can depend on extensions */
19173 "pg_catalog.pg_class", "INDEX",
19175
19176 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
19178 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19179 fmtId(coninfo->dobj.name));
19180
19181 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19182
19183 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19184 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19185 ARCHIVE_OPTS(.tag = tag,
19186 .namespace = tbinfo->dobj.namespace->dobj.name,
19187 .tablespace = indxinfo->tablespace,
19188 .owner = tbinfo->rolname,
19189 .description = "CONSTRAINT",
19190 .section = SECTION_POST_DATA,
19191 .createStmt = q->data,
19192 .dropStmt = delq->data));
19193 }
19194 else if (coninfo->contype == 'f')
19195 {
19196 char *only;
19197
19198 /*
19199 * Foreign keys on partitioned tables are always declared as
19200 * inheriting to partitions; for all other cases, emit them as
19201 * applying ONLY directly to the named table, because that's how they
19202 * work for regular inherited tables.
19203 */
19204 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
19205
19206 /*
19207 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
19208 * current table data is not processed
19209 */
19210 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
19212 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19213 fmtId(coninfo->dobj.name),
19214 coninfo->condef);
19215
19216 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
19218 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19219 fmtId(coninfo->dobj.name));
19220
19221 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19222
19223 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19224 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19225 ARCHIVE_OPTS(.tag = tag,
19226 .namespace = tbinfo->dobj.namespace->dobj.name,
19227 .owner = tbinfo->rolname,
19228 .description = "FK CONSTRAINT",
19229 .section = SECTION_POST_DATA,
19230 .createStmt = q->data,
19231 .dropStmt = delq->data));
19232 }
19233 else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
19234 {
19235 /* CHECK or invalid not-null constraint on a table */
19236
19237 /* Ignore if not to be dumped separately, or if it was inherited */
19238 if (coninfo->separate && coninfo->conislocal)
19239 {
19240 const char *keyword;
19241
19242 if (coninfo->contype == 'c')
19243 keyword = "CHECK CONSTRAINT";
19244 else
19245 keyword = "CONSTRAINT";
19246
19247 /* not ONLY since we want it to propagate to children */
19248 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
19250 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19251 fmtId(coninfo->dobj.name),
19252 coninfo->condef);
19253
19254 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
19256 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19257 fmtId(coninfo->dobj.name));
19258
19259 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
19260
19261 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19262 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19263 ARCHIVE_OPTS(.tag = tag,
19264 .namespace = tbinfo->dobj.namespace->dobj.name,
19265 .owner = tbinfo->rolname,
19266 .description = keyword,
19267 .section = SECTION_POST_DATA,
19268 .createStmt = q->data,
19269 .dropStmt = delq->data));
19270 }
19271 }
19272 else if (tbinfo == NULL)
19273 {
19274 /* CHECK, NOT NULL constraint on a domain */
19275 TypeInfo *tyinfo = coninfo->condomain;
19276
19277 Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
19278
19279 /* Ignore if not to be dumped separately */
19280 if (coninfo->separate)
19281 {
19282 const char *keyword;
19283
19284 if (coninfo->contype == 'c')
19285 keyword = "CHECK CONSTRAINT";
19286 else
19287 keyword = "CONSTRAINT";
19288
19289 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
19291 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
19292 fmtId(coninfo->dobj.name),
19293 coninfo->condef);
19294
19295 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
19297 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
19298 fmtId(coninfo->dobj.name));
19299
19300 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
19301
19302 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19303 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
19304 ARCHIVE_OPTS(.tag = tag,
19305 .namespace = tyinfo->dobj.namespace->dobj.name,
19306 .owner = tyinfo->rolname,
19307 .description = keyword,
19308 .section = SECTION_POST_DATA,
19309 .createStmt = q->data,
19310 .dropStmt = delq->data));
19311
19312 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19313 {
19315 char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
19316
19317 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
19318 fmtId(coninfo->dobj.name));
19319
19321 tyinfo->dobj.namespace->dobj.name,
19322 tyinfo->rolname,
19323 coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
19325 free(qtypname);
19326 }
19327 }
19328 }
19329 else
19330 {
19331 pg_fatal("unrecognized constraint type: %c",
19332 coninfo->contype);
19333 }
19334
19335 /* Dump Constraint Comments --- only works for table constraints */
19336 if (tbinfo && coninfo->separate &&
19337 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19339
19340 free(tag);
19343}
19344
19345/*
19346 * dumpTableConstraintComment --- dump a constraint's comment if any
19347 *
19348 * This is split out because we need the function in two different places
19349 * depending on whether the constraint is dumped as part of CREATE TABLE
19350 * or as a separate ALTER command.
19351 */
19352static void
19354{
19355 TableInfo *tbinfo = coninfo->contable;
19357 char *qtabname;
19358
19359 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19360
19361 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
19362 fmtId(coninfo->dobj.name));
19363
19364 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19366 tbinfo->dobj.namespace->dobj.name,
19367 tbinfo->rolname,
19368 coninfo->dobj.catId, 0,
19369 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
19370
19372 free(qtabname);
19373}
19374
19375static inline SeqType
19377{
19378 for (int i = 0; i < lengthof(SeqTypeNames); i++)
19379 {
19380 if (strcmp(SeqTypeNames[i], name) == 0)
19381 return (SeqType) i;
19382 }
19383
19384 pg_fatal("unrecognized sequence type: %s", name);
19385 return (SeqType) 0; /* keep compiler quiet */
19386}
19387
19388/*
19389 * bsearch() comparator for SequenceItem
19390 */
19391static int
19392SequenceItemCmp(const void *p1, const void *p2)
19393{
19394 SequenceItem v1 = *((const SequenceItem *) p1);
19395 SequenceItem v2 = *((const SequenceItem *) p2);
19396
19397 return pg_cmp_u32(v1.oid, v2.oid);
19398}
19399
19400/*
19401 * collectSequences
19402 *
19403 * Construct a table of sequence information. This table is sorted by OID for
19404 * speed in lookup.
19405 */
19406static void
19408{
19409 PGresult *res;
19410 const char *query;
19411
19412 /*
19413 * Before Postgres 10, sequence metadata is in the sequence itself. With
19414 * some extra effort, we might be able to use the sorted table for those
19415 * versions, but for now it seems unlikely to be worth it.
19416 *
19417 * Since version 18, we can gather the sequence data in this query with
19418 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
19419 */
19420 if (fout->remoteVersion < 100000)
19421 return;
19422 else if (fout->remoteVersion < 180000 ||
19424 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19425 "seqstart, seqincrement, "
19426 "seqmax, seqmin, "
19427 "seqcache, seqcycle, "
19428 "NULL, 'f' "
19429 "FROM pg_catalog.pg_sequence "
19430 "ORDER BY seqrelid";
19431 else
19432 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
19433 "seqstart, seqincrement, "
19434 "seqmax, seqmin, "
19435 "seqcache, seqcycle, "
19436 "last_value, is_called "
19437 "FROM pg_catalog.pg_sequence, "
19438 "pg_get_sequence_data(seqrelid) "
19439 "ORDER BY seqrelid;";
19440
19441 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
19442
19443 nsequences = PQntuples(res);
19445
19446 for (int i = 0; i < nsequences; i++)
19447 {
19448 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
19450 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
19451 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
19452 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
19453 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
19454 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
19455 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
19456 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
19457 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
19458 sequences[i].null_seqtuple = (PQgetisnull(res, i, 8) || PQgetisnull(res, i, 9));
19459 }
19460
19461 PQclear(res);
19462}
19463
19464/*
19465 * dumpSequence
19466 * write the declaration (not data) of one user-defined sequence
19467 */
19468static void
19470{
19471 DumpOptions *dopt = fout->dopt;
19473 bool is_ascending;
19478 char *qseqname;
19479 TableInfo *owning_tab = NULL;
19480
19481 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
19482
19483 /*
19484 * For versions >= 10, the sequence information is gathered in a sorted
19485 * table before any calls to dumpSequence(). See collectSequences() for
19486 * more information.
19487 */
19488 if (fout->remoteVersion >= 100000)
19489 {
19490 SequenceItem key = {0};
19491
19493
19494 key.oid = tbinfo->dobj.catId.oid;
19496 sizeof(SequenceItem), SequenceItemCmp);
19497 }
19498 else
19499 {
19500 PGresult *res;
19501
19502 /*
19503 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19504 *
19505 * Note: it might seem that 'bigint' potentially needs to be
19506 * schema-qualified, but actually that's a keyword.
19507 */
19508 appendPQExpBuffer(query,
19509 "SELECT 'bigint' AS sequence_type, "
19510 "start_value, increment_by, max_value, min_value, "
19511 "cache_value, is_cycled FROM %s",
19513
19514 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19515
19516 if (PQntuples(res) != 1)
19517 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19518 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19519 PQntuples(res)),
19520 tbinfo->dobj.name, PQntuples(res));
19521
19523 seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19524 seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19525 seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19526 seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19527 seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19528 seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19529 seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19530
19531 PQclear(res);
19532 }
19533
19534 /* Calculate default limits for a sequence of this type */
19535 is_ascending = (seq->incby >= 0);
19536 if (seq->seqtype == SEQTYPE_SMALLINT)
19537 {
19540 }
19541 else if (seq->seqtype == SEQTYPE_INTEGER)
19542 {
19545 }
19546 else if (seq->seqtype == SEQTYPE_BIGINT)
19547 {
19550 }
19551 else
19552 {
19553 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19554 default_minv = default_maxv = 0; /* keep compiler quiet */
19555 }
19556
19557 /*
19558 * Identity sequences are not to be dropped separately.
19559 */
19560 if (!tbinfo->is_identity_sequence)
19561 {
19562 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19564 }
19565
19566 resetPQExpBuffer(query);
19567
19568 if (dopt->binary_upgrade)
19569 {
19571 tbinfo->dobj.catId.oid);
19572
19573 /*
19574 * In older PG versions a sequence will have a pg_type entry, but v14
19575 * and up don't use that, so don't attempt to preserve the type OID.
19576 */
19577 }
19578
19579 if (tbinfo->is_identity_sequence)
19580 {
19581 owning_tab = findTableByOid(tbinfo->owning_tab);
19582
19583 appendPQExpBuffer(query,
19584 "ALTER TABLE %s ",
19585 fmtQualifiedDumpable(owning_tab));
19586 appendPQExpBuffer(query,
19587 "ALTER COLUMN %s ADD GENERATED ",
19588 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19589 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19590 appendPQExpBufferStr(query, "ALWAYS");
19591 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19592 appendPQExpBufferStr(query, "BY DEFAULT");
19593 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19595
19596 /*
19597 * Emit persistence option only if it's different from the owning
19598 * table's. This avoids using this new syntax unnecessarily.
19599 */
19600 if (tbinfo->relpersistence != owning_tab->relpersistence)
19601 appendPQExpBuffer(query, " %s\n",
19602 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19603 "UNLOGGED" : "LOGGED");
19604 }
19605 else
19606 {
19607 appendPQExpBuffer(query,
19608 "CREATE %sSEQUENCE %s\n",
19609 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19610 "UNLOGGED " : "",
19612
19613 if (seq->seqtype != SEQTYPE_BIGINT)
19614 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19615 }
19616
19617 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19618
19619 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19620
19621 if (seq->minv != default_minv)
19622 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19623 else
19624 appendPQExpBufferStr(query, " NO MINVALUE\n");
19625
19626 if (seq->maxv != default_maxv)
19627 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19628 else
19629 appendPQExpBufferStr(query, " NO MAXVALUE\n");
19630
19631 appendPQExpBuffer(query,
19632 " CACHE " INT64_FORMAT "%s",
19633 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19634
19635 if (tbinfo->is_identity_sequence)
19636 appendPQExpBufferStr(query, "\n);\n");
19637 else
19638 appendPQExpBufferStr(query, ";\n");
19639
19640 /* binary_upgrade: no need to clear TOAST table oid */
19641
19642 if (dopt->binary_upgrade)
19644 "SEQUENCE", qseqname,
19645 tbinfo->dobj.namespace->dobj.name);
19646
19647 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19648 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19649 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19650 .namespace = tbinfo->dobj.namespace->dobj.name,
19651 .owner = tbinfo->rolname,
19652 .description = "SEQUENCE",
19653 .section = SECTION_PRE_DATA,
19654 .createStmt = query->data,
19655 .dropStmt = delqry->data));
19656
19657 /*
19658 * If the sequence is owned by a table column, emit the ALTER for it as a
19659 * separate TOC entry immediately following the sequence's own entry. It's
19660 * OK to do this rather than using full sorting logic, because the
19661 * dependency that tells us it's owned will have forced the table to be
19662 * created first. We can't just include the ALTER in the TOC entry
19663 * because it will fail if we haven't reassigned the sequence owner to
19664 * match the table's owner.
19665 *
19666 * We need not schema-qualify the table reference because both sequence
19667 * and table must be in the same schema.
19668 */
19669 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19670 {
19671 owning_tab = findTableByOid(tbinfo->owning_tab);
19672
19673 if (owning_tab == NULL)
19674 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19675 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19676
19677 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19678 {
19679 resetPQExpBuffer(query);
19680 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19682 appendPQExpBuffer(query, " OWNED BY %s",
19683 fmtQualifiedDumpable(owning_tab));
19684 appendPQExpBuffer(query, ".%s;\n",
19685 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19686
19687 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19689 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19690 .namespace = tbinfo->dobj.namespace->dobj.name,
19691 .owner = tbinfo->rolname,
19692 .description = "SEQUENCE OWNED BY",
19693 .section = SECTION_PRE_DATA,
19694 .createStmt = query->data,
19695 .deps = &(tbinfo->dobj.dumpId),
19696 .nDeps = 1));
19697 }
19698 }
19699
19700 /* Dump Sequence Comments and Security Labels */
19701 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19702 dumpComment(fout, "SEQUENCE", qseqname,
19703 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19704 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19705
19706 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19707 dumpSecLabel(fout, "SEQUENCE", qseqname,
19708 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19709 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19710
19711 if (fout->remoteVersion < 100000)
19712 pg_free(seq);
19713 destroyPQExpBuffer(query);
19715 free(qseqname);
19716}
19717
19718/*
19719 * dumpSequenceData
19720 * write the data of one user-defined sequence
19721 */
19722static void
19724{
19725 TableInfo *tbinfo = tdinfo->tdtable;
19726 int64 last;
19727 bool called;
19728 PQExpBuffer query;
19729
19730 /* needn't bother if not dumping sequence data */
19731 if (!fout->dopt->dumpData && !fout->dopt->sequence_data)
19732 return;
19733
19734 query = createPQExpBuffer();
19735
19736 /*
19737 * For versions >= 18, the sequence information is gathered in the sorted
19738 * array before any calls to dumpSequenceData(). See collectSequences()
19739 * for more information.
19740 *
19741 * For older versions, we have to query the sequence relations
19742 * individually.
19743 */
19744 if (fout->remoteVersion < 180000)
19745 {
19746 PGresult *res;
19747
19748 appendPQExpBuffer(query,
19749 "SELECT last_value, is_called FROM %s",
19751
19752 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19753
19754 if (PQntuples(res) != 1)
19755 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19756 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19757 PQntuples(res)),
19758 tbinfo->dobj.name, PQntuples(res));
19759
19760 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19761 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19762
19763 PQclear(res);
19764 }
19765 else
19766 {
19767 SequenceItem key = {0};
19768 SequenceItem *entry;
19769
19771 Assert(tbinfo->dobj.catId.oid);
19772
19773 key.oid = tbinfo->dobj.catId.oid;
19774 entry = bsearch(&key, sequences, nsequences,
19775 sizeof(SequenceItem), SequenceItemCmp);
19776
19777 if (entry->null_seqtuple)
19778 pg_fatal("failed to get data for sequence \"%s\"; user may lack "
19779 "SELECT privilege on the sequence or the sequence may "
19780 "have been concurrently dropped",
19781 tbinfo->dobj.name);
19782
19783 last = entry->last_value;
19784 called = entry->is_called;
19785 }
19786
19787 resetPQExpBuffer(query);
19788 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19790 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19791 last, (called ? "true" : "false"));
19792
19793 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19795 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19796 .namespace = tbinfo->dobj.namespace->dobj.name,
19797 .owner = tbinfo->rolname,
19798 .description = "SEQUENCE SET",
19799 .section = SECTION_DATA,
19800 .createStmt = query->data,
19801 .deps = &(tbinfo->dobj.dumpId),
19802 .nDeps = 1));
19803
19804 destroyPQExpBuffer(query);
19805}
19806
19807/*
19808 * dumpTrigger
19809 * write the declaration of one user-defined table trigger
19810 */
19811static void
19813{
19814 DumpOptions *dopt = fout->dopt;
19815 TableInfo *tbinfo = tginfo->tgtable;
19816 PQExpBuffer query;
19820 char *qtabname;
19821 char *tag;
19822
19823 /* Do nothing if not dumping schema */
19824 if (!dopt->dumpSchema)
19825 return;
19826
19827 query = createPQExpBuffer();
19831
19832 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19833
19834 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19836
19837 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19838 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19839
19840 /* Triggers can depend on extensions */
19842 "pg_catalog.pg_trigger", "TRIGGER",
19844
19845 if (tginfo->tgispartition)
19846 {
19847 Assert(tbinfo->ispartition);
19848
19849 /*
19850 * Partition triggers only appear here because their 'tgenabled' flag
19851 * differs from its parent's. The trigger is created already, so
19852 * remove the CREATE and replace it with an ALTER. (Clear out the
19853 * DROP query too, so that pg_dump --create does not cause errors.)
19854 */
19855 resetPQExpBuffer(query);
19857 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19858 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19860 switch (tginfo->tgenabled)
19861 {
19862 case 'f':
19863 case 'D':
19864 appendPQExpBufferStr(query, "DISABLE");
19865 break;
19866 case 't':
19867 case 'O':
19868 appendPQExpBufferStr(query, "ENABLE");
19869 break;
19870 case 'R':
19871 appendPQExpBufferStr(query, "ENABLE REPLICA");
19872 break;
19873 case 'A':
19874 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19875 break;
19876 }
19877 appendPQExpBuffer(query, " TRIGGER %s;\n",
19878 fmtId(tginfo->dobj.name));
19879 }
19880 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19881 {
19882 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19883 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19885 switch (tginfo->tgenabled)
19886 {
19887 case 'D':
19888 case 'f':
19889 appendPQExpBufferStr(query, "DISABLE");
19890 break;
19891 case 'A':
19892 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19893 break;
19894 case 'R':
19895 appendPQExpBufferStr(query, "ENABLE REPLICA");
19896 break;
19897 default:
19898 appendPQExpBufferStr(query, "ENABLE");
19899 break;
19900 }
19901 appendPQExpBuffer(query, " TRIGGER %s;\n",
19902 fmtId(tginfo->dobj.name));
19903 }
19904
19905 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19906 fmtId(tginfo->dobj.name));
19907
19908 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19909
19910 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19911 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19912 ARCHIVE_OPTS(.tag = tag,
19913 .namespace = tbinfo->dobj.namespace->dobj.name,
19914 .owner = tbinfo->rolname,
19915 .description = "TRIGGER",
19916 .section = SECTION_POST_DATA,
19917 .createStmt = query->data,
19918 .dropStmt = delqry->data));
19919
19920 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19922 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19923 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19924
19925 free(tag);
19926 destroyPQExpBuffer(query);
19930 free(qtabname);
19931}
19932
19933/*
19934 * dumpEventTrigger
19935 * write the declaration of one user-defined event trigger
19936 */
19937static void
19939{
19940 DumpOptions *dopt = fout->dopt;
19941 PQExpBuffer query;
19943 char *qevtname;
19944
19945 /* Do nothing if not dumping schema */
19946 if (!dopt->dumpSchema)
19947 return;
19948
19949 query = createPQExpBuffer();
19951
19952 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19953
19954 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19956 appendPQExpBufferStr(query, " ON ");
19957 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19958
19959 if (strcmp("", evtinfo->evttags) != 0)
19960 {
19961 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19962 appendPQExpBufferStr(query, evtinfo->evttags);
19963 appendPQExpBufferChar(query, ')');
19964 }
19965
19966 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19967 appendPQExpBufferStr(query, evtinfo->evtfname);
19968 appendPQExpBufferStr(query, "();\n");
19969
19970 if (evtinfo->evtenabled != 'O')
19971 {
19972 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19973 qevtname);
19974 switch (evtinfo->evtenabled)
19975 {
19976 case 'D':
19977 appendPQExpBufferStr(query, "DISABLE");
19978 break;
19979 case 'A':
19980 appendPQExpBufferStr(query, "ENABLE ALWAYS");
19981 break;
19982 case 'R':
19983 appendPQExpBufferStr(query, "ENABLE REPLICA");
19984 break;
19985 default:
19986 appendPQExpBufferStr(query, "ENABLE");
19987 break;
19988 }
19989 appendPQExpBufferStr(query, ";\n");
19990 }
19991
19992 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19993 qevtname);
19994
19995 if (dopt->binary_upgrade)
19997 "EVENT TRIGGER", qevtname, NULL);
19998
19999 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20000 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
20001 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
20002 .owner = evtinfo->evtowner,
20003 .description = "EVENT TRIGGER",
20004 .section = SECTION_POST_DATA,
20005 .createStmt = query->data,
20006 .dropStmt = delqry->data));
20007
20008 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20009 dumpComment(fout, "EVENT TRIGGER", qevtname,
20010 NULL, evtinfo->evtowner,
20011 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20012
20013 if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
20014 dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
20015 NULL, evtinfo->evtowner,
20016 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
20017
20018 destroyPQExpBuffer(query);
20020 free(qevtname);
20021}
20022
20023/*
20024 * dumpRule
20025 * Dump a rule
20026 */
20027static void
20029{
20030 DumpOptions *dopt = fout->dopt;
20031 TableInfo *tbinfo = rinfo->ruletable;
20032 bool is_view;
20033 PQExpBuffer query;
20034 PQExpBuffer cmd;
20037 char *qtabname;
20038 PGresult *res;
20039 char *tag;
20040
20041 /* Do nothing if not dumping schema */
20042 if (!dopt->dumpSchema)
20043 return;
20044
20045 /*
20046 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
20047 * we do not want to dump it as a separate object.
20048 */
20049 if (!rinfo->separate)
20050 return;
20051
20052 /*
20053 * If it's an ON SELECT rule, we want to print it as a view definition,
20054 * instead of a rule.
20055 */
20056 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
20057
20058 query = createPQExpBuffer();
20059 cmd = createPQExpBuffer();
20062
20063 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
20064
20065 if (is_view)
20066 {
20068
20069 /*
20070 * We need OR REPLACE here because we'll be replacing a dummy view.
20071 * Otherwise this should look largely like the regular view dump code.
20072 */
20073 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
20075 if (nonemptyReloptions(tbinfo->reloptions))
20076 {
20077 appendPQExpBufferStr(cmd, " WITH (");
20078 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
20079 appendPQExpBufferChar(cmd, ')');
20080 }
20082 appendPQExpBuffer(cmd, " AS\n%s", result->data);
20084 if (tbinfo->checkoption != NULL)
20085 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
20086 tbinfo->checkoption);
20087 appendPQExpBufferStr(cmd, ";\n");
20088 }
20089 else
20090 {
20091 /* In the rule case, just print pg_get_ruledef's result verbatim */
20092 appendPQExpBuffer(query,
20093 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
20094 rinfo->dobj.catId.oid);
20095
20096 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20097
20098 if (PQntuples(res) != 1)
20099 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
20100 rinfo->dobj.name, tbinfo->dobj.name);
20101
20102 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
20103
20104 PQclear(res);
20105 }
20106
20107 /*
20108 * Add the command to alter the rules replication firing semantics if it
20109 * differs from the default.
20110 */
20111 if (rinfo->ev_enabled != 'O')
20112 {
20113 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
20114 switch (rinfo->ev_enabled)
20115 {
20116 case 'A':
20117 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
20118 fmtId(rinfo->dobj.name));
20119 break;
20120 case 'R':
20121 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
20122 fmtId(rinfo->dobj.name));
20123 break;
20124 case 'D':
20125 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
20126 fmtId(rinfo->dobj.name));
20127 break;
20128 }
20129 }
20130
20131 if (is_view)
20132 {
20133 /*
20134 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
20135 * REPLACE VIEW to replace the rule with something with minimal
20136 * dependencies.
20137 */
20139
20140 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
20143 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
20145 }
20146 else
20147 {
20148 appendPQExpBuffer(delcmd, "DROP RULE %s ",
20149 fmtId(rinfo->dobj.name));
20150 appendPQExpBuffer(delcmd, "ON %s;\n",
20152 }
20153
20154 appendPQExpBuffer(ruleprefix, "RULE %s ON",
20155 fmtId(rinfo->dobj.name));
20156
20157 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
20158
20159 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
20160 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
20161 ARCHIVE_OPTS(.tag = tag,
20162 .namespace = tbinfo->dobj.namespace->dobj.name,
20163 .owner = tbinfo->rolname,
20164 .description = "RULE",
20165 .section = SECTION_POST_DATA,
20166 .createStmt = cmd->data,
20167 .dropStmt = delcmd->data));
20168
20169 /* Dump rule comments */
20170 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
20172 tbinfo->dobj.namespace->dobj.name,
20173 tbinfo->rolname,
20174 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
20175
20176 free(tag);
20177 destroyPQExpBuffer(query);
20178 destroyPQExpBuffer(cmd);
20181 free(qtabname);
20182}
20183
20184/*
20185 * getExtensionMembership --- obtain extension membership data
20186 *
20187 * We need to identify objects that are extension members as soon as they're
20188 * loaded, so that we can correctly determine whether they need to be dumped.
20189 * Generally speaking, extension member objects will get marked as *not* to
20190 * be dumped, as they will be recreated by the single CREATE EXTENSION
20191 * command. However, in binary upgrade mode we still need to dump the members
20192 * individually.
20193 */
20194void
20196 int numExtensions)
20197{
20198 PQExpBuffer query;
20199 PGresult *res;
20200 int ntups,
20201 i;
20202 int i_classid,
20203 i_objid,
20204 i_refobjid;
20205 ExtensionInfo *ext;
20206
20207 /* Nothing to do if no extensions */
20208 if (numExtensions == 0)
20209 return;
20210
20211 query = createPQExpBuffer();
20212
20213 /* refclassid constraint is redundant but may speed the search */
20214 appendPQExpBufferStr(query, "SELECT "
20215 "classid, objid, refobjid "
20216 "FROM pg_depend "
20217 "WHERE refclassid = 'pg_extension'::regclass "
20218 "AND deptype = 'e' "
20219 "ORDER BY 3");
20220
20221 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20222
20223 ntups = PQntuples(res);
20224
20225 i_classid = PQfnumber(res, "classid");
20226 i_objid = PQfnumber(res, "objid");
20227 i_refobjid = PQfnumber(res, "refobjid");
20228
20229 /*
20230 * Since we ordered the SELECT by referenced ID, we can expect that
20231 * multiple entries for the same extension will appear together; this
20232 * saves on searches.
20233 */
20234 ext = NULL;
20235
20236 for (i = 0; i < ntups; i++)
20237 {
20238 CatalogId objId;
20239 Oid extId;
20240
20241 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20242 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20244
20245 if (ext == NULL ||
20246 ext->dobj.catId.oid != extId)
20248
20249 if (ext == NULL)
20250 {
20251 /* shouldn't happen */
20252 pg_log_warning("could not find referenced extension %u", extId);
20253 continue;
20254 }
20255
20256 recordExtensionMembership(objId, ext);
20257 }
20258
20259 PQclear(res);
20260
20261 destroyPQExpBuffer(query);
20262}
20263
20264/*
20265 * processExtensionTables --- deal with extension configuration tables
20266 *
20267 * There are two parts to this process:
20268 *
20269 * 1. Identify and create dump records for extension configuration tables.
20270 *
20271 * Extensions can mark tables as "configuration", which means that the user
20272 * is able and expected to modify those tables after the extension has been
20273 * loaded. For these tables, we dump out only the data- the structure is
20274 * expected to be handled at CREATE EXTENSION time, including any indexes or
20275 * foreign keys, which brings us to-
20276 *
20277 * 2. Record FK dependencies between configuration tables.
20278 *
20279 * Due to the FKs being created at CREATE EXTENSION time and therefore before
20280 * the data is loaded, we have to work out what the best order for reloading
20281 * the data is, to avoid FK violations when the tables are restored. This is
20282 * not perfect- we can't handle circular dependencies and if any exist they
20283 * will cause an invalid dump to be produced (though at least all of the data
20284 * is included for a user to manually restore). This is currently documented
20285 * but perhaps we can provide a better solution in the future.
20286 */
20287void
20289 int numExtensions)
20290{
20291 DumpOptions *dopt = fout->dopt;
20292 PQExpBuffer query;
20293 PGresult *res;
20294 int ntups,
20295 i;
20296 int i_conrelid,
20298
20299 /* Nothing to do if no extensions */
20300 if (numExtensions == 0)
20301 return;
20302
20303 /*
20304 * Identify extension configuration tables and create TableDataInfo
20305 * objects for them, ensuring their data will be dumped even though the
20306 * tables themselves won't be.
20307 *
20308 * Note that we create TableDataInfo objects even in schema-only mode, ie,
20309 * user data in a configuration table is treated like schema data. This
20310 * seems appropriate since system data in a config table would get
20311 * reloaded by CREATE EXTENSION. If the extension is not listed in the
20312 * list of extensions to be included, none of its data is dumped.
20313 */
20314 for (i = 0; i < numExtensions; i++)
20315 {
20317 char *extconfig = curext->extconfig;
20318 char *extcondition = curext->extcondition;
20319 char **extconfigarray = NULL;
20320 char **extconditionarray = NULL;
20321 int nconfigitems = 0;
20322 int nconditionitems = 0;
20323
20324 /*
20325 * Check if this extension is listed as to include in the dump. If
20326 * not, any table data associated with it is discarded.
20327 */
20330 curext->dobj.catId.oid))
20331 continue;
20332
20333 /*
20334 * Check if this extension is listed as to exclude in the dump. If
20335 * yes, any table data associated with it is discarded.
20336 */
20339 curext->dobj.catId.oid))
20340 continue;
20341
20342 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
20343 {
20344 int j;
20345
20346 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
20347 pg_fatal("could not parse %s array", "extconfig");
20348 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
20349 pg_fatal("could not parse %s array", "extcondition");
20351 pg_fatal("mismatched number of configurations and conditions for extension");
20352
20353 for (j = 0; j < nconfigitems; j++)
20354 {
20357 bool dumpobj =
20358 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
20359
20361 if (configtbl == NULL)
20362 continue;
20363
20364 /*
20365 * Tables of not-to-be-dumped extensions shouldn't be dumped
20366 * unless the table or its schema is explicitly included
20367 */
20368 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
20369 {
20370 /* check table explicitly requested */
20371 if (table_include_oids.head != NULL &&
20373 configtbloid))
20374 dumpobj = true;
20375
20376 /* check table's schema explicitly requested */
20377 if (configtbl->dobj.namespace->dobj.dump &
20379 dumpobj = true;
20380 }
20381
20382 /* check table excluded by an exclusion switch */
20383 if (table_exclude_oids.head != NULL &&
20385 configtbloid))
20386 dumpobj = false;
20387
20388 /* check schema excluded by an exclusion switch */
20390 configtbl->dobj.namespace->dobj.catId.oid))
20391 dumpobj = false;
20392
20393 if (dumpobj)
20394 {
20396 if (configtbl->dataObj != NULL)
20397 {
20398 if (strlen(extconditionarray[j]) > 0)
20399 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
20400 }
20401 }
20402 }
20403 }
20404 if (extconfigarray)
20408 }
20409
20410 /*
20411 * Now that all the TableDataInfo objects have been created for all the
20412 * extensions, check their FK dependencies and register them to try and
20413 * dump the data out in an order that they can be restored in.
20414 *
20415 * Note that this is not a problem for user tables as their FKs are
20416 * recreated after the data has been loaded.
20417 */
20418
20419 query = createPQExpBuffer();
20420
20421 printfPQExpBuffer(query,
20422 "SELECT conrelid, confrelid "
20423 "FROM pg_constraint "
20424 "JOIN pg_depend ON (objid = confrelid) "
20425 "WHERE contype = 'f' "
20426 "AND refclassid = 'pg_extension'::regclass "
20427 "AND classid = 'pg_class'::regclass;");
20428
20429 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20430 ntups = PQntuples(res);
20431
20432 i_conrelid = PQfnumber(res, "conrelid");
20433 i_confrelid = PQfnumber(res, "confrelid");
20434
20435 /* Now get the dependencies and register them */
20436 for (i = 0; i < ntups; i++)
20437 {
20438 Oid conrelid,
20439 confrelid;
20441 *contable;
20442
20443 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
20444 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
20445 contable = findTableByOid(conrelid);
20446 reftable = findTableByOid(confrelid);
20447
20448 if (reftable == NULL ||
20449 reftable->dataObj == NULL ||
20450 contable == NULL ||
20451 contable->dataObj == NULL)
20452 continue;
20453
20454 /*
20455 * Make referencing TABLE_DATA object depend on the referenced table's
20456 * TABLE_DATA object.
20457 */
20458 addObjectDependency(&contable->dataObj->dobj,
20459 reftable->dataObj->dobj.dumpId);
20460 }
20461 PQclear(res);
20462 destroyPQExpBuffer(query);
20463}
20464
20465/*
20466 * getDependencies --- obtain available dependency data
20467 */
20468static void
20470{
20471 PQExpBuffer query;
20472 PGresult *res;
20473 int ntups,
20474 i;
20475 int i_classid,
20476 i_objid,
20478 i_refobjid,
20479 i_deptype;
20480 DumpableObject *dobj,
20481 *refdobj;
20482
20483 pg_log_info("reading dependency data");
20484
20485 query = createPQExpBuffer();
20486
20487 /*
20488 * Messy query to collect the dependency data we need. Note that we
20489 * ignore the sub-object column, so that dependencies of or on a column
20490 * look the same as dependencies of or on a whole table.
20491 *
20492 * PIN dependencies aren't interesting, and EXTENSION dependencies were
20493 * already processed by getExtensionMembership.
20494 */
20495 appendPQExpBufferStr(query, "SELECT "
20496 "classid, objid, refclassid, refobjid, deptype "
20497 "FROM pg_depend "
20498 "WHERE deptype != 'p' AND deptype != 'e'\n");
20499
20500 /*
20501 * Since we don't treat pg_amop entries as separate DumpableObjects, we
20502 * have to translate their dependencies into dependencies of their parent
20503 * opfamily. Ignore internal dependencies though, as those will point to
20504 * their parent opclass, which we needn't consider here (and if we did,
20505 * it'd just result in circular dependencies). Also, "loose" opfamily
20506 * entries will have dependencies on their parent opfamily, which we
20507 * should drop since they'd likewise become useless self-dependencies.
20508 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20509 */
20510 appendPQExpBufferStr(query, "UNION ALL\n"
20511 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20512 "FROM pg_depend d, pg_amop o "
20513 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20514 "classid = 'pg_amop'::regclass AND objid = o.oid "
20515 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20516
20517 /* Likewise for pg_amproc entries */
20518 appendPQExpBufferStr(query, "UNION ALL\n"
20519 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20520 "FROM pg_depend d, pg_amproc p "
20521 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20522 "classid = 'pg_amproc'::regclass AND objid = p.oid "
20523 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20524
20525 /*
20526 * Translate dependencies of pg_propgraph_element entries into
20527 * dependencies of their parent pg_class entry.
20528 */
20529 if (fout->remoteVersion >= 190000)
20530 appendPQExpBufferStr(query, "UNION ALL\n"
20531 "SELECT 'pg_class'::regclass AS classid, pgepgid AS objid, refclassid, refobjid, deptype "
20532 "FROM pg_depend d, pg_propgraph_element pge "
20533 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20534 "classid = 'pg_propgraph_element'::regclass AND objid = pge.oid\n");
20535
20536 /* Sort the output for efficiency below */
20537 appendPQExpBufferStr(query, "ORDER BY 1,2");
20538
20539 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20540
20541 ntups = PQntuples(res);
20542
20543 i_classid = PQfnumber(res, "classid");
20544 i_objid = PQfnumber(res, "objid");
20545 i_refclassid = PQfnumber(res, "refclassid");
20546 i_refobjid = PQfnumber(res, "refobjid");
20547 i_deptype = PQfnumber(res, "deptype");
20548
20549 /*
20550 * Since we ordered the SELECT by referencing ID, we can expect that
20551 * multiple entries for the same object will appear together; this saves
20552 * on searches.
20553 */
20554 dobj = NULL;
20555
20556 for (i = 0; i < ntups; i++)
20557 {
20558 CatalogId objId;
20560 char deptype;
20561
20562 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20563 objId.oid = atooid(PQgetvalue(res, i, i_objid));
20564 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20565 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20566 deptype = *(PQgetvalue(res, i, i_deptype));
20567
20568 if (dobj == NULL ||
20569 dobj->catId.tableoid != objId.tableoid ||
20570 dobj->catId.oid != objId.oid)
20571 dobj = findObjectByCatalogId(objId);
20572
20573 /*
20574 * Failure to find objects mentioned in pg_depend is not unexpected,
20575 * since for example we don't collect info about TOAST tables.
20576 */
20577 if (dobj == NULL)
20578 {
20579#ifdef NOT_USED
20580 pg_log_warning("no referencing object %u %u",
20581 objId.tableoid, objId.oid);
20582#endif
20583 continue;
20584 }
20585
20587
20588 if (refdobj == NULL)
20589 {
20590#ifdef NOT_USED
20591 pg_log_warning("no referenced object %u %u",
20592 refobjId.tableoid, refobjId.oid);
20593#endif
20594 continue;
20595 }
20596
20597 /*
20598 * For 'x' dependencies, mark the object for later; we still add the
20599 * normal dependency, for possible ordering purposes. Currently
20600 * pg_dump_sort.c knows to put extensions ahead of all object types
20601 * that could possibly depend on them, but this is safer.
20602 */
20603 if (deptype == 'x')
20604 dobj->depends_on_ext = true;
20605
20606 /*
20607 * Ordinarily, table rowtypes have implicit dependencies on their
20608 * tables. However, for a composite type the implicit dependency goes
20609 * the other way in pg_depend; which is the right thing for DROP but
20610 * it doesn't produce the dependency ordering we need. So in that one
20611 * case, we reverse the direction of the dependency.
20612 */
20613 if (deptype == 'i' &&
20614 dobj->objType == DO_TABLE &&
20615 refdobj->objType == DO_TYPE)
20617 else
20618 /* normal case */
20620 }
20621
20622 PQclear(res);
20623
20624 destroyPQExpBuffer(query);
20625}
20626
20627
20628/*
20629 * createBoundaryObjects - create dummy DumpableObjects to represent
20630 * dump section boundaries.
20631 */
20632static DumpableObject *
20634{
20636
20638
20639 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20640 dobjs[0].catId = nilCatalogId;
20641 AssignDumpId(dobjs + 0);
20642 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20643
20644 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20645 dobjs[1].catId = nilCatalogId;
20646 AssignDumpId(dobjs + 1);
20647 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20648
20649 return dobjs;
20650}
20651
20652/*
20653 * addBoundaryDependencies - add dependencies as needed to enforce the dump
20654 * section boundaries.
20655 */
20656static void
20659{
20662 int i;
20663
20664 for (i = 0; i < numObjs; i++)
20665 {
20666 DumpableObject *dobj = dobjs[i];
20667
20668 /*
20669 * The classification of object types here must match the SECTION_xxx
20670 * values assigned during subsequent ArchiveEntry calls!
20671 */
20672 switch (dobj->objType)
20673 {
20674 case DO_NAMESPACE:
20675 case DO_EXTENSION:
20676 case DO_TYPE:
20677 case DO_SHELL_TYPE:
20678 case DO_FUNC:
20679 case DO_AGG:
20680 case DO_OPERATOR:
20681 case DO_ACCESS_METHOD:
20682 case DO_OPCLASS:
20683 case DO_OPFAMILY:
20684 case DO_COLLATION:
20685 case DO_CONVERSION:
20686 case DO_TABLE:
20687 case DO_TABLE_ATTACH:
20688 case DO_ATTRDEF:
20689 case DO_PROCLANG:
20690 case DO_CAST:
20691 case DO_DUMMY_TYPE:
20692 case DO_TSPARSER:
20693 case DO_TSDICT:
20694 case DO_TSTEMPLATE:
20695 case DO_TSCONFIG:
20696 case DO_FDW:
20697 case DO_FOREIGN_SERVER:
20698 case DO_TRANSFORM:
20699 /* Pre-data objects: must come before the pre-data boundary */
20701 break;
20702 case DO_TABLE_DATA:
20703 case DO_SEQUENCE_SET:
20704 case DO_LARGE_OBJECT:
20706 /* Data objects: must come between the boundaries */
20709 break;
20710 case DO_INDEX:
20711 case DO_INDEX_ATTACH:
20712 case DO_STATSEXT:
20713 case DO_REFRESH_MATVIEW:
20714 case DO_TRIGGER:
20715 case DO_EVENT_TRIGGER:
20716 case DO_DEFAULT_ACL:
20717 case DO_POLICY:
20718 case DO_PUBLICATION:
20719 case DO_PUBLICATION_REL:
20721 case DO_SUBSCRIPTION:
20723 /* Post-data objects: must come after the post-data boundary */
20725 break;
20726 case DO_RULE:
20727 /* Rules are post-data, but only if dumped separately */
20728 if (((RuleInfo *) dobj)->separate)
20729 addObjectDependency(dobj, postDataBound->dumpId);
20730 break;
20731 case DO_CONSTRAINT:
20732 case DO_FK_CONSTRAINT:
20733 /* Constraints are post-data, but only if dumped separately */
20734 if (((ConstraintInfo *) dobj)->separate)
20735 addObjectDependency(dobj, postDataBound->dumpId);
20736 break;
20738 /* nothing to do */
20739 break;
20741 /* must come after the pre-data boundary */
20742 addObjectDependency(dobj, preDataBound->dumpId);
20743 break;
20744 case DO_REL_STATS:
20745 /* stats section varies by parent object type, DATA or POST */
20746 if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20747 {
20748 addObjectDependency(dobj, preDataBound->dumpId);
20749 addObjectDependency(postDataBound, dobj->dumpId);
20750 }
20751 else
20752 addObjectDependency(dobj, postDataBound->dumpId);
20753 break;
20754 }
20755 }
20756}
20757
20758
20759/*
20760 * BuildArchiveDependencies - create dependency data for archive TOC entries
20761 *
20762 * The raw dependency data obtained by getDependencies() is not terribly
20763 * useful in an archive dump, because in many cases there are dependency
20764 * chains linking through objects that don't appear explicitly in the dump.
20765 * For example, a view will depend on its _RETURN rule while the _RETURN rule
20766 * will depend on other objects --- but the rule will not appear as a separate
20767 * object in the dump. We need to adjust the view's dependencies to include
20768 * whatever the rule depends on that is included in the dump.
20769 *
20770 * Just to make things more complicated, there are also "special" dependencies
20771 * such as the dependency of a TABLE DATA item on its TABLE, which we must
20772 * not rearrange because pg_restore knows that TABLE DATA only depends on
20773 * its table. In these cases we must leave the dependencies strictly as-is
20774 * even if they refer to not-to-be-dumped objects.
20775 *
20776 * To handle this, the convention is that "special" dependencies are created
20777 * during ArchiveEntry calls, and an archive TOC item that has any such
20778 * entries will not be touched here. Otherwise, we recursively search the
20779 * DumpableObject data structures to build the correct dependencies for each
20780 * archive TOC item.
20781 */
20782static void
20784{
20786 TocEntry *te;
20787
20788 /* Scan all TOC entries in the archive */
20789 for (te = AH->toc->next; te != AH->toc; te = te->next)
20790 {
20791 DumpableObject *dobj;
20792 DumpId *dependencies;
20793 int nDeps;
20794 int allocDeps;
20795
20796 /* No need to process entries that will not be dumped */
20797 if (te->reqs == 0)
20798 continue;
20799 /* Ignore entries that already have "special" dependencies */
20800 if (te->nDeps > 0)
20801 continue;
20802 /* Otherwise, look up the item's original DumpableObject, if any */
20803 dobj = findObjectByDumpId(te->dumpId);
20804 if (dobj == NULL)
20805 continue;
20806 /* No work if it has no dependencies */
20807 if (dobj->nDeps <= 0)
20808 continue;
20809 /* Set up work array */
20810 allocDeps = 64;
20811 dependencies = pg_malloc_array(DumpId, allocDeps);
20812 nDeps = 0;
20813 /* Recursively find all dumpable dependencies */
20814 findDumpableDependencies(AH, dobj,
20815 &dependencies, &nDeps, &allocDeps);
20816 /* And save 'em ... */
20817 if (nDeps > 0)
20818 {
20819 dependencies = pg_realloc_array(dependencies, DumpId, nDeps);
20820 te->dependencies = dependencies;
20821 te->nDeps = nDeps;
20822 }
20823 else
20824 free(dependencies);
20825 }
20826}
20827
20828/* Recursive search subroutine for BuildArchiveDependencies */
20829static void
20831 DumpId **dependencies, int *nDeps, int *allocDeps)
20832{
20833 int i;
20834
20835 /*
20836 * Ignore section boundary objects: if we search through them, we'll
20837 * report lots of bogus dependencies.
20838 */
20839 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20841 return;
20842
20843 for (i = 0; i < dobj->nDeps; i++)
20844 {
20845 DumpId depid = dobj->dependencies[i];
20846
20847 if (TocIDRequired(AH, depid) != 0)
20848 {
20849 /* Object will be dumped, so just reference it as a dependency */
20850 if (*nDeps >= *allocDeps)
20851 {
20852 *allocDeps *= 2;
20853 *dependencies = pg_realloc_array(*dependencies, DumpId, *allocDeps);
20854 }
20855 (*dependencies)[*nDeps] = depid;
20856 (*nDeps)++;
20857 }
20858 else
20859 {
20860 /*
20861 * Object will not be dumped, so recursively consider its deps. We
20862 * rely on the assumption that sortDumpableObjects already broke
20863 * any dependency loops, else we might recurse infinitely.
20864 */
20866
20867 if (otherdobj)
20869 dependencies, nDeps, allocDeps);
20870 }
20871 }
20872}
20873
20874
20875/*
20876 * getFormattedTypeName - retrieve a nicely-formatted type name for the
20877 * given type OID.
20878 *
20879 * This does not guarantee to schema-qualify the output, so it should not
20880 * be used to create the target object name for CREATE or ALTER commands.
20881 *
20882 * Note that the result is cached and must not be freed by the caller.
20883 */
20884static const char *
20886{
20888 char *result;
20889 PQExpBuffer query;
20890 PGresult *res;
20891
20892 if (oid == 0)
20893 {
20894 if ((opts & zeroAsStar) != 0)
20895 return "*";
20896 else if ((opts & zeroAsNone) != 0)
20897 return "NONE";
20898 }
20899
20900 /* see if we have the result cached in the type's TypeInfo record */
20901 typeInfo = findTypeByOid(oid);
20902 if (typeInfo && typeInfo->ftypname)
20903 return typeInfo->ftypname;
20904
20905 query = createPQExpBuffer();
20906 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20907 oid);
20908
20909 res = ExecuteSqlQueryForSingleRow(fout, query->data);
20910
20911 /* result of format_type is already quoted */
20912 result = pg_strdup(PQgetvalue(res, 0, 0));
20913
20914 PQclear(res);
20915 destroyPQExpBuffer(query);
20916
20917 /*
20918 * Cache the result for re-use in later requests, if possible. If we
20919 * don't have a TypeInfo for the type, the string will be leaked once the
20920 * caller is done with it ... but that case really should not happen, so
20921 * leaking if it does seems acceptable.
20922 */
20923 if (typeInfo)
20924 typeInfo->ftypname = result;
20925
20926 return result;
20927}
20928
20929/*
20930 * Return a column list clause for the given relation.
20931 *
20932 * Special case: if there are no undropped columns in the relation, return
20933 * "", not an invalid "()" column list.
20934 */
20935static const char *
20937{
20938 int numatts = ti->numatts;
20939 char **attnames = ti->attnames;
20940 bool *attisdropped = ti->attisdropped;
20941 char *attgenerated = ti->attgenerated;
20942 bool needComma;
20943 int i;
20944
20945 appendPQExpBufferChar(buffer, '(');
20946 needComma = false;
20947 for (i = 0; i < numatts; i++)
20948 {
20949 if (attisdropped[i])
20950 continue;
20951 if (attgenerated[i])
20952 continue;
20953 if (needComma)
20954 appendPQExpBufferStr(buffer, ", ");
20955 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20956 needComma = true;
20957 }
20958
20959 if (!needComma)
20960 return ""; /* no undropped columns */
20961
20962 appendPQExpBufferChar(buffer, ')');
20963 return buffer->data;
20964}
20965
20966/*
20967 * Check if a reloptions array is nonempty.
20968 */
20969static bool
20970nonemptyReloptions(const char *reloptions)
20971{
20972 /* Don't want to print it if it's just "{}" */
20973 return (reloptions != NULL && strlen(reloptions) > 2);
20974}
20975
20976/*
20977 * Format a reloptions array and append it to the given buffer.
20978 *
20979 * "prefix" is prepended to the option names; typically it's "" or "toast.".
20980 */
20981static void
20982appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20983 const char *prefix, Archive *fout)
20984{
20985 bool res;
20986
20987 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20988 fout->std_strings);
20989 if (!res)
20990 pg_log_warning("could not parse %s array", "reloptions");
20991}
20992
20993/*
20994 * read_dump_filters - retrieve object identifier patterns from file
20995 *
20996 * Parse the specified filter file for include and exclude patterns, and add
20997 * them to the relevant lists. If the filename is "-" then filters will be
20998 * read from STDIN rather than a file.
20999 */
21000static void
21002{
21004 char *objname;
21006 FilterObjectType objtype;
21007
21009
21010 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
21011 {
21013 {
21014 switch (objtype)
21015 {
21017 break;
21024 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21025 "include",
21026 filter_object_type_name(objtype));
21027 exit_nicely(1);
21028 break; /* unreachable */
21029
21032 break;
21035 break;
21038 dopt->include_everything = false;
21039 break;
21042 dopt->include_everything = false;
21043 break;
21046 objname);
21047 dopt->include_everything = false;
21048 break;
21049 }
21050 }
21052 {
21053 switch (objtype)
21054 {
21056 break;
21062 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
21063 "exclude",
21064 filter_object_type_name(objtype));
21065 exit_nicely(1);
21066 break;
21067
21070 break;
21073 objname);
21074 break;
21077 objname);
21078 break;
21081 break;
21084 break;
21087 objname);
21088 break;
21089 }
21090 }
21091 else
21092 {
21094 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
21095 }
21096
21097 if (objname)
21098 free(objname);
21099 }
21100
21102}
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
DumpId createDumpId(void)
Definition common.c:748
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
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:109
#define PG_INT32_MAX
Definition c.h:673
#define ngettext(s, p, n)
Definition c.h:1270
#define INT64_FORMAT
Definition c.h:634
#define Assert(condition)
Definition c.h:943
#define PG_TEXTDOMAIN(domain)
Definition c.h:1303
int64_t int64
Definition c.h:621
#define PG_INT16_MIN
Definition c.h:669
#define CppAsString2(x)
Definition c.h:506
int32_t int32
Definition c.h:620
#define PG_INT64_MAX
Definition c.h:676
#define PG_INT64_MIN
Definition c.h:675
uint32_t uint32
Definition c.h:624
#define lengthof(array)
Definition c.h:873
#define PG_INT32_MIN
Definition c.h:672
#define StaticAssertDecl(condition, errmessage)
Definition c.h:1008
#define PG_INT16_MAX
Definition c.h:670
#define OidIsValid(objectId)
Definition c.h:858
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 @177 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
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 RestoreArchive(Archive *AHX, bool append_data)
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 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:1647
static const CatalogId nilCatalogId
Definition pg_dump.c:191
static void dumpEncoding(Archive *AH)
Definition pg_dump.c:3825
void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:8388
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:16570
static SimpleStringList schema_include_patterns
Definition pg_dump.c:167
static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
Definition pg_dump.c:18391
ExtensionInfo * getExtensions(Archive *fout, int *numExtensions)
Definition pg_dump.c:6215
static void selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
Definition pg_dump.c:2199
static void collectBinaryUpgradeClassOids(Archive *fout)
Definition pg_dump.c:5915
static PQExpBuffer createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17226
static void dumpUserMappings(Archive *fout, const char *servername, const char *namespace, const char *owner, CatalogId catalogId, DumpId dumpId)
Definition pg_dump.c:16384
static void dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
Definition pg_dump.c:5015
static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs)
Definition pg_dump.c:20657
void getPublicationNamespaces(Archive *fout)
Definition pg_dump.c:4804
static void dumpSearchPath(Archive *AH)
Definition pg_dump.c:3874
static int ncomments
Definition pg_dump.c:203
static void selectDumpableTable(TableInfo *tbinfo, Archive *fout)
Definition pg_dump.c:2068
static DumpableObject * createBoundaryObjects(void)
Definition pg_dump.c:20633
static char * convertTSFunction(Archive *fout, Oid funcOid)
Definition pg_dump.c:14554
static void dumpDatabase(Archive *fout)
Definition pg_dump.c:3272
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:5728
static Oid get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
Definition pg_dump.c:5773
static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
Definition pg_dump.c:12016
static bool forcePartitionRootLoad(const TableInfo *tbinfo)
Definition pg_dump.c:2829
static void dumpCast(Archive *fout, const CastInfo *cast)
Definition pg_dump.c:14030
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:18481
void getPartitioningInfo(Archive *fout)
Definition pg_dump.c:7877
static int nbinaryUpgradeClassOids
Definition pg_dump.c:211
static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12609
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:11246
static SimpleOidList extension_include_oids
Definition pg_dump.c:186
static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
Definition pg_dump.c:15953
static void dumpAgg(Archive *fout, const AggInfo *agginfo)
Definition pg_dump.c:15529
static int extra_float_digits
Definition pg_dump.c:195
static int SequenceItemCmp(const void *p1, const void *p2)
Definition pg_dump.c:19392
static void dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
Definition pg_dump.c:11533
static void dumpTableComment(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition pg_dump.c:11559
static SimpleStringList extension_include_patterns
Definition pg_dump.c:185
static void selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
Definition pg_dump.c:1982
InhInfo * getInherits(Archive *fout, int *numInherits)
Definition pg_dump.c:7821
void getForeignDataWrappers(Archive *fout)
Definition pg_dump.c:10519
static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
Definition pg_dump.c:19812
static void binary_upgrade_set_type_oids_by_rel(Archive *fout, PQExpBuffer upgrade_buffer, const TableInfo *tbinfo)
Definition pg_dump.c:5884
static void dumpTable(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17025
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:17177
static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
Definition pg_dump.c:18675
void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:4226
static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12387
void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition pg_dump.c:20195
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:11098
static char * getFormattedOperatorName(const char *oproid)
Definition pg_dump.c:14524
static char * format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
Definition pg_dump.c:13579
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:3850
static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
Definition pg_dump.c:19032
static void dumpType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12216
static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
Definition pg_dump.c:18323
void getTypes(Archive *fout)
Definition pg_dump.c:6290
static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
Definition pg_dump.c:14576
static void dumpOpr(Archive *fout, const OprInfo *oprinfo)
Definition pg_dump.c:14264
static void selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
Definition pg_dump.c:2324
static void selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:2306
static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:19723
static void dumpFunc(Archive *fout, const FuncInfo *finfo)
Definition pg_dump.c:13608
static void selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
Definition pg_dump.c:2152
static void BuildArchiveDependencies(Archive *fout)
Definition pg_dump.c:20783
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:7200
static const char *const SeqTypeNames[]
Definition pg_dump.c:119
void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:7756
static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
Definition pg_dump.c:3026
static int nsequences
Definition pg_dump.c:215
static const char * getAttrName(int attrnum, const TableInfo *tblInfo)
Definition pg_dump.c:18452
static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
Definition pg_dump.c:16284
static RoleNameItem * rolenames
Definition pg_dump.c:198
static void collectRoleNames(Archive *fout)
Definition pg_dump.c:10834
static PGresult * fetchAttributeStats(Archive *fout)
Definition pg_dump.c:11132
static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, const char *prefix, Archive *fout)
Definition pg_dump.c:20982
void getOpclasses(Archive *fout)
Definition pg_dump.c:6736
void getForeignServers(Archive *fout)
Definition pg_dump.c:10613
void getFuncs(Archive *fout)
Definition pg_dump.c:7005
static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:2857
static void prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
Definition pg_dump.c:1907
static bool dosync
Definition pg_dump.c:152
static int dumpTableData_copy(Archive *fout, const void *dcontext)
Definition pg_dump.c:2364
#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:20885
static void getDependencies(Archive *fout)
Definition pg_dump.c:20469
static void buildMatViewRefreshDependencies(Archive *fout)
Definition pg_dump.c:3116
void getTSDictionaries(Archive *fout)
Definition pg_dump.c:10335
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:5804
#define DUMP_DEFAULT_ROWS_PER_INSERT
Definition pg_dump.c:224
void getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:4884
static SeqType parse_sequence_type(const char *name)
Definition pg_dump.c:19376
static const char * getRoleName(const char *roleoid_str)
Definition pg_dump.c:10798
static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
Definition pg_dump.c:13378
static SequenceItem * sequences
Definition pg_dump.c:214
static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
Definition pg_dump.c:2972
static int findComments(Oid classoid, Oid objoid, CommentItem **items)
Definition pg_dump.c:11657
static SimpleStringList foreign_servers_include_patterns
Definition pg_dump.c:182
static void selectDumpableCast(CastInfo *cast, Archive *fout)
Definition pg_dump.c:2174
void getCasts(Archive *fout)
Definition pg_dump.c:9137
static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
Definition pg_dump.c:4682
static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
Definition pg_dump.c:4398
void getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
Definition pg_dump.c:7937
static void setupDumpWorker(Archive *AH)
Definition pg_dump.c:1580
static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
Definition pg_dump.c:8547
void getTSConfigurations(Archive *fout)
Definition pg_dump.c:10460
static int nrolenames
Definition pg_dump.c:199
static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
Definition pg_dump.c:16860
static SimpleStringList table_include_patterns_and_children
Definition pg_dump.c:173
static char * convertRegProcReference(const char *proc)
Definition pg_dump.c:14483
static void getAdditionalACLs(Archive *fout)
Definition pg_dump.c:10869
static void getTableDataFKConstraints(void)
Definition pg_dump.c:3231
static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
Definition pg_dump.c:3007
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:6662
void getConversions(Archive *fout)
Definition pg_dump.c:6600
void getRules(Archive *fout)
Definition pg_dump.c:8682
static void dumpDomain(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12858
void getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
Definition pg_dump.c:9331
static void collectComments(Archive *fout)
Definition pg_dump.c:11734
static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
Definition pg_dump.c:8570
static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
Definition pg_dump.c:14925
static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
Definition pg_dump.c:16478
static void selectDumpableObject(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:2342
static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
Definition pg_dump.c:16033
static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
Definition pg_dump.c:18632
static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
Definition pg_dump.c:5494
void getCollations(Archive *fout)
Definition pg_dump.c:6534
static char * format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
Definition pg_dump.c:13556
static int strict_names
Definition pg_dump.c:157
static void dumpTransform(Archive *fout, const TransformInfo *transform)
Definition pg_dump.c:14135
void getAggregates(Archive *fout)
Definition pg_dump.c:6864
static void dumpLO(Archive *fout, const LoInfo *loinfo)
Definition pg_dump.c:4090
void getNamespaces(Archive *fout)
Definition pg_dump.c:6083
static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
Definition pg_dump.c:5058
void getPublications(Archive *fout)
Definition pg_dump.c:4516
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:6039
static void dumpDumpableObject(Archive *fout, DumpableObject *dobj)
Definition pg_dump.c:11819
static void getLOs(Archive *fout)
Definition pg_dump.c:3936
static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf, const char *dbname, Oid dboid)
Definition pg_dump.c:3781
void getTSParsers(Archive *fout)
Definition pg_dump.c:10261
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:19353
static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12545
static void selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
Definition pg_dump.c:2232
static const char * fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
Definition pg_dump.c:20936
static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names, bool with_child_tables)
Definition pg_dump.c:1811
static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj, DumpId **dependencies, int *nDeps, int *allocDeps)
Definition pg_dump.c:20830
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:10119
static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:17266
static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
Definition pg_dump.c:15889
static void expand_foreign_server_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids)
Definition pg_dump.c:1759
TableInfo * getTables(Archive *fout, int *numTables)
Definition pg_dump.c:7278
static void dumpRule(Archive *fout, const RuleInfo *rinfo)
Definition pg_dump.c:20028
static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:13083
static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
Definition pg_dump.c:12247
static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
Definition pg_dump.c:12093
#define fmtQualifiedDumpable(obj)
Definition pg_dump.c:236
static bool nonemptyReloptions(const char *reloptions)
Definition pg_dump.c:20970
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:18752
void getExtendedStatistics(Archive *fout)
Definition pg_dump.c:8306
static NamespaceInfo * findNamespace(Oid nsoid)
Definition pg_dump.c:6197
static char * get_synchronized_snapshot(Archive *fout)
Definition pg_dump.c:1595
static int dumpLOs(Archive *fout, const void *arg)
Definition pg_dump.c:4180
static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
Definition pg_dump.c:5563
static void appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname, const char *argtype, const char *argval)
Definition pg_dump.c:11114
void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], int numExtensions)
Definition pg_dump.c:20288
static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
Definition pg_dump.c:19938
static int BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
Definition pg_dump.c:5899
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:10998
void getDefaultACLs(Archive *fout)
Definition pg_dump.c:10701
static SimpleStringList tabledata_exclude_patterns
Definition pg_dump.c:178
static void dumpConversion(Archive *fout, const ConvInfo *convinfo)
Definition pg_dump.c:15401
static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
Definition pg_dump.c:16211
static void dumpProcLang(Archive *fout, const ProcLangInfo *plang)
Definition pg_dump.c:13424
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:16698
void getSubscriptions(Archive *fout)
Definition pg_dump.c:5159
static void collectSecLabels(Archive *fout)
Definition pg_dump.c:16939
static void selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
Definition pg_dump.c:2267
static void collectSequences(Archive *fout)
Definition pg_dump.c:19407
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:8779
void getTransforms(Archive *fout)
Definition pg_dump.c:9247
void getEventTriggers(Archive *fout)
Definition pg_dump.c:8975
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode)
Definition pg_dump.c:1609
static void read_dump_filters(const char *filename, DumpOptions *dopt)
Definition pg_dump.c:21001
static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
Definition pg_dump.c:16091
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:9226
static bool checkExtensionMembership(DumpableObject *dobj, Archive *fout)
Definition pg_dump.c:1932
static CommentItem * comments
Definition pg_dump.c:202
static int dumpTableData_insert(Archive *fout, const void *dcontext)
Definition pg_dump.c:2535
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:5949
void getTSTemplates(Archive *fout)
Definition pg_dump.c:10401
static void set_restrict_relation_kind(Archive *AH, const char *value)
Definition pg_dump.c:5138
static char * format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
Definition pg_dump.c:15497
void getProcLangs(Archive *fout)
Definition pg_dump.c:9053
static void dumpSequence(Archive *fout, const TableInfo *tbinfo)
Definition pg_dump.c:19469
bool shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
Definition pg_dump.c:10246
static TableInfo * getRootTableInfo(const TableInfo *tbinfo)
Definition pg_dump.c:2804
void getSubscriptionRelations(Archive *fout)
Definition pg_dump.c:5408
void getOperators(Archive *fout)
Definition pg_dump.c:6458
static SimpleOidList foreign_servers_include_oids
Definition pg_dump.c:183
static void dumpCollation(Archive *fout, const CollInfo *collinfo)
Definition pg_dump.c:15144
static void dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
Definition pg_dump.c:16778
static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo, PGresult *res)
Definition pg_dump.c:13289
static void expand_extension_name_patterns(Archive *fout, SimpleStringList *patterns, SimpleOidList *oids, bool strict_names)
Definition pg_dump.c:1706
void getOpfamilies(Archive *fout)
Definition pg_dump.c:6799
static void selectDumpableType(TypeInfo *tyinfo, Archive *fout)
Definition pg_dump.c:2107
static SimpleOidList schema_include_oids
Definition pg_dump.c:168
static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
Definition pg_dump.c:14644
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:125
static Archive * fout
Definition pg_dumpall.c:139
static int no_statistics
Definition pg_dumpall.c:116
static int no_data
Definition pg_dumpall.c:114
static int no_schema
Definition pg_dumpall.c:115
static char * filename
Definition pg_dumpall.c:133
static int with_statistics
Definition pg_dumpall.c:121
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 minRemoteVersion
Definition pg_backup.h:237
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 isStandby
Definition pg_backup.h:235
int maxRemoteVersion
Definition pg_backup.h:238
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
int verbose
Definition pg_backup.h:232
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:831
#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