Move routines to manipulate WAL into PostgreSQL::Test::Cluster
[pgsql.git] / src / bin / pg_dump / pg_dump.c
blob8f73a5df9568b8706cd20b72925e49190c5d72e6
1 /*-------------------------------------------------------------------------
3 * pg_dump.c
4 * pg_dump is a utility for dumping out a postgres database
5 * into a script file.
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
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
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.
25 * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
27 * IDENTIFICATION
28 * src/bin/pg_dump/pg_dump.c
30 *-------------------------------------------------------------------------
32 #include "postgres_fe.h"
34 #include <unistd.h>
35 #include <ctype.h>
36 #include <limits.h>
37 #ifdef HAVE_TERMIOS_H
38 #include <termios.h>
39 #endif
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_default_acl_d.h"
51 #include "catalog/pg_largeobject_d.h"
52 #include "catalog/pg_proc_d.h"
53 #include "catalog/pg_subscription_d.h"
54 #include "catalog/pg_type_d.h"
55 #include "common/connect.h"
56 #include "common/int.h"
57 #include "common/relpath.h"
58 #include "compress_io.h"
59 #include "dumputils.h"
60 #include "fe_utils/option_utils.h"
61 #include "fe_utils/string_utils.h"
62 #include "filter.h"
63 #include "getopt_long.h"
64 #include "libpq/libpq-fs.h"
65 #include "parallel.h"
66 #include "pg_backup_db.h"
67 #include "pg_backup_utils.h"
68 #include "pg_dump.h"
69 #include "storage/block.h"
71 typedef struct
73 Oid roleoid; /* role's OID */
74 const char *rolename; /* role's name */
75 } RoleNameItem;
77 typedef struct
79 const char *descr; /* comment for an object */
80 Oid classoid; /* object class (catalog OID) */
81 Oid objoid; /* object OID */
82 int objsubid; /* subobject (table column #) */
83 } CommentItem;
85 typedef struct
87 const char *provider; /* label provider of this security label */
88 const char *label; /* security label for an object */
89 Oid classoid; /* object class (catalog OID) */
90 Oid objoid; /* object OID */
91 int objsubid; /* subobject (table column #) */
92 } SecLabelItem;
94 typedef struct
96 Oid oid; /* object OID */
97 char relkind; /* object kind */
98 RelFileNumber relfilenumber; /* object filenode */
99 Oid toast_oid; /* toast table OID */
100 RelFileNumber toast_relfilenumber; /* toast table filenode */
101 Oid toast_index_oid; /* toast table index OID */
102 RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
103 } BinaryUpgradeClassOidItem;
105 /* sequence types */
106 typedef enum SeqType
108 SEQTYPE_SMALLINT,
109 SEQTYPE_INTEGER,
110 SEQTYPE_BIGINT,
111 } SeqType;
113 static const char *const SeqTypeNames[] =
115 [SEQTYPE_SMALLINT] = "smallint",
116 [SEQTYPE_INTEGER] = "integer",
117 [SEQTYPE_BIGINT] = "bigint",
120 StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
121 "array length mismatch");
123 typedef struct
125 Oid oid; /* sequence OID */
126 SeqType seqtype; /* data type of sequence */
127 bool cycled; /* whether sequence cycles */
128 int64 minv; /* minimum value */
129 int64 maxv; /* maximum value */
130 int64 startv; /* start value */
131 int64 incby; /* increment value */
132 int64 cache; /* cache size */
133 int64 last_value; /* last value of sequence */
134 bool is_called; /* whether nextval advances before returning */
135 } SequenceItem;
137 typedef enum OidOptions
139 zeroIsError = 1,
140 zeroAsStar = 2,
141 zeroAsNone = 4,
142 } OidOptions;
144 /* global decls */
145 static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
147 static Oid g_last_builtin_oid; /* value of the last builtin oid */
149 /* The specified names/patterns should to match at least one entity */
150 static int strict_names = 0;
152 static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
155 * Object inclusion/exclusion lists
157 * The string lists record the patterns given by command-line switches,
158 * which we then convert to lists of OIDs of matching objects.
160 static SimpleStringList schema_include_patterns = {NULL, NULL};
161 static SimpleOidList schema_include_oids = {NULL, NULL};
162 static SimpleStringList schema_exclude_patterns = {NULL, NULL};
163 static SimpleOidList schema_exclude_oids = {NULL, NULL};
165 static SimpleStringList table_include_patterns = {NULL, NULL};
166 static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
167 static SimpleOidList table_include_oids = {NULL, NULL};
168 static SimpleStringList table_exclude_patterns = {NULL, NULL};
169 static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
170 static SimpleOidList table_exclude_oids = {NULL, NULL};
171 static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
172 static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
173 static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
175 static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
176 static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
178 static SimpleStringList extension_include_patterns = {NULL, NULL};
179 static SimpleOidList extension_include_oids = {NULL, NULL};
181 static SimpleStringList extension_exclude_patterns = {NULL, NULL};
182 static SimpleOidList extension_exclude_oids = {NULL, NULL};
184 static const CatalogId nilCatalogId = {0, 0};
186 /* override for standard extra_float_digits setting */
187 static bool have_extra_float_digits = false;
188 static int extra_float_digits;
190 /* sorted table of role names */
191 static RoleNameItem *rolenames = NULL;
192 static int nrolenames = 0;
194 /* sorted table of comments */
195 static CommentItem *comments = NULL;
196 static int ncomments = 0;
198 /* sorted table of security labels */
199 static SecLabelItem *seclabels = NULL;
200 static int nseclabels = 0;
202 /* sorted table of pg_class information for binary upgrade */
203 static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
204 static int nbinaryUpgradeClassOids = 0;
206 /* sorted table of sequences */
207 static SequenceItem *sequences = NULL;
208 static int nsequences = 0;
211 * The default number of rows per INSERT when
212 * --inserts is specified without --rows-per-insert
214 #define DUMP_DEFAULT_ROWS_PER_INSERT 1
217 * Maximum number of large objects to group into a single ArchiveEntry.
218 * At some point we might want to make this user-controllable, but for now
219 * a hard-wired setting will suffice.
221 #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
224 * Macro for producing quoted, schema-qualified name of a dumpable object.
226 #define fmtQualifiedDumpable(obj) \
227 fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
228 (obj)->dobj.name)
230 static void help(const char *progname);
231 static void setup_connection(Archive *AH,
232 const char *dumpencoding, const char *dumpsnapshot,
233 char *use_role);
234 static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
235 static void expand_schema_name_patterns(Archive *fout,
236 SimpleStringList *patterns,
237 SimpleOidList *oids,
238 bool strict_names);
239 static void expand_extension_name_patterns(Archive *fout,
240 SimpleStringList *patterns,
241 SimpleOidList *oids,
242 bool strict_names);
243 static void expand_foreign_server_name_patterns(Archive *fout,
244 SimpleStringList *patterns,
245 SimpleOidList *oids);
246 static void expand_table_name_patterns(Archive *fout,
247 SimpleStringList *patterns,
248 SimpleOidList *oids,
249 bool strict_names,
250 bool with_child_tables);
251 static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
252 const char *pattern);
254 static NamespaceInfo *findNamespace(Oid nsoid);
255 static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
256 static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
257 static const char *getRoleName(const char *roleoid_str);
258 static void collectRoleNames(Archive *fout);
259 static void getAdditionalACLs(Archive *fout);
260 static void dumpCommentExtended(Archive *fout, const char *type,
261 const char *name, const char *namespace,
262 const char *owner, CatalogId catalogId,
263 int subid, DumpId dumpId,
264 const char *initdb_comment);
265 static inline void dumpComment(Archive *fout, const char *type,
266 const char *name, const char *namespace,
267 const char *owner, CatalogId catalogId,
268 int subid, DumpId dumpId);
269 static int findComments(Oid classoid, Oid objoid, CommentItem **items);
270 static void collectComments(Archive *fout);
271 static void dumpSecLabel(Archive *fout, const char *type, const char *name,
272 const char *namespace, const char *owner,
273 CatalogId catalogId, int subid, DumpId dumpId);
274 static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
275 static void collectSecLabels(Archive *fout);
276 static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
277 static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
278 static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
279 static void dumpType(Archive *fout, const TypeInfo *tyinfo);
280 static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
281 static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
282 static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
283 static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
284 static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
285 static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
286 static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
287 PGresult *res);
288 static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
289 static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
290 static void dumpFunc(Archive *fout, const FuncInfo *finfo);
291 static void dumpCast(Archive *fout, const CastInfo *cast);
292 static void dumpTransform(Archive *fout, const TransformInfo *transform);
293 static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
294 static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
295 static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
296 static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
297 static void dumpCollation(Archive *fout, const CollInfo *collinfo);
298 static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
299 static void dumpRule(Archive *fout, const RuleInfo *rinfo);
300 static void dumpAgg(Archive *fout, const AggInfo *agginfo);
301 static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
302 static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
303 static void dumpTable(Archive *fout, const TableInfo *tbinfo);
304 static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
305 static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
306 static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
307 static void collectSequences(Archive *fout);
308 static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
309 static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
310 static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
311 static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
312 static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
313 static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
314 static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
315 static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
316 static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
317 static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
318 static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
319 static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
320 static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
321 static void dumpUserMappings(Archive *fout,
322 const char *servername, const char *namespace,
323 const char *owner, CatalogId catalogId, DumpId dumpId);
324 static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
326 static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
327 const char *type, const char *name, const char *subname,
328 const char *nspname, const char *tag, const char *owner,
329 const DumpableAcl *dacl);
331 static void getDependencies(Archive *fout);
332 static void BuildArchiveDependencies(Archive *fout);
333 static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
334 DumpId **dependencies, int *nDeps, int *allocDeps);
336 static DumpableObject *createBoundaryObjects(void);
337 static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
338 DumpableObject *boundaryObjs);
340 static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
341 static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
342 static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
343 static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
344 static void buildMatViewRefreshDependencies(Archive *fout);
345 static void getTableDataFKConstraints(void);
346 static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
347 TableInfo *tbinfo, int j,
348 int i_notnull_name, int i_notnull_noinherit,
349 int i_notnull_islocal);
350 static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
351 bool is_agg);
352 static char *format_function_signature(Archive *fout,
353 const FuncInfo *finfo, bool honor_quotes);
354 static char *convertRegProcReference(const char *proc);
355 static char *getFormattedOperatorName(const char *oproid);
356 static char *convertTSFunction(Archive *fout, Oid funcOid);
357 static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
358 static void getLOs(Archive *fout);
359 static void dumpLO(Archive *fout, const LoInfo *loinfo);
360 static int dumpLOs(Archive *fout, const void *arg);
361 static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
362 static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
363 static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
364 static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
365 static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
366 static void dumpDatabase(Archive *fout);
367 static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
368 const char *dbname, Oid dboid);
369 static void dumpEncoding(Archive *AH);
370 static void dumpStdStrings(Archive *AH);
371 static void dumpSearchPath(Archive *AH);
372 static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
373 PQExpBuffer upgrade_buffer,
374 Oid pg_type_oid,
375 bool force_array_type,
376 bool include_multirange_type);
377 static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
378 PQExpBuffer upgrade_buffer,
379 const TableInfo *tbinfo);
380 static void collectBinaryUpgradeClassOids(Archive *fout);
381 static void binary_upgrade_set_pg_class_oids(Archive *fout,
382 PQExpBuffer upgrade_buffer,
383 Oid pg_class_oid);
384 static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
385 const DumpableObject *dobj,
386 const char *objtype,
387 const char *objname,
388 const char *objnamespace);
389 static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
390 static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
391 static bool nonemptyReloptions(const char *reloptions);
392 static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
393 const char *prefix, Archive *fout);
394 static char *get_synchronized_snapshot(Archive *fout);
395 static void set_restrict_relation_kind(Archive *AH, const char *value);
396 static void setupDumpWorker(Archive *AH);
397 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
398 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
399 static void read_dump_filters(const char *filename, DumpOptions *dopt);
403 main(int argc, char **argv)
405 int c;
406 const char *filename = NULL;
407 const char *format = "p";
408 TableInfo *tblinfo;
409 int numTables;
410 DumpableObject **dobjs;
411 int numObjs;
412 DumpableObject *boundaryObjs;
413 int i;
414 int optindex;
415 RestoreOptions *ropt;
416 Archive *fout; /* the script file */
417 bool g_verbose = false;
418 const char *dumpencoding = NULL;
419 const char *dumpsnapshot = NULL;
420 char *use_role = NULL;
421 int numWorkers = 1;
422 int plainText = 0;
423 ArchiveFormat archiveFormat = archUnknown;
424 ArchiveMode archiveMode;
425 pg_compress_specification compression_spec = {0};
426 char *compression_detail = NULL;
427 char *compression_algorithm_str = "none";
428 char *error_detail = NULL;
429 bool user_compression_defined = false;
430 DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
431 bool data_only = false;
432 bool schema_only = false;
434 static DumpOptions dopt;
436 static struct option long_options[] = {
437 {"data-only", no_argument, NULL, 'a'},
438 {"blobs", no_argument, NULL, 'b'},
439 {"large-objects", no_argument, NULL, 'b'},
440 {"no-blobs", no_argument, NULL, 'B'},
441 {"no-large-objects", no_argument, NULL, 'B'},
442 {"clean", no_argument, NULL, 'c'},
443 {"create", no_argument, NULL, 'C'},
444 {"dbname", required_argument, NULL, 'd'},
445 {"extension", required_argument, NULL, 'e'},
446 {"file", required_argument, NULL, 'f'},
447 {"format", required_argument, NULL, 'F'},
448 {"host", required_argument, NULL, 'h'},
449 {"jobs", 1, NULL, 'j'},
450 {"no-reconnect", no_argument, NULL, 'R'},
451 {"no-owner", no_argument, NULL, 'O'},
452 {"port", required_argument, NULL, 'p'},
453 {"schema", required_argument, NULL, 'n'},
454 {"exclude-schema", required_argument, NULL, 'N'},
455 {"schema-only", no_argument, NULL, 's'},
456 {"superuser", required_argument, NULL, 'S'},
457 {"table", required_argument, NULL, 't'},
458 {"exclude-table", required_argument, NULL, 'T'},
459 {"no-password", no_argument, NULL, 'w'},
460 {"password", no_argument, NULL, 'W'},
461 {"username", required_argument, NULL, 'U'},
462 {"verbose", no_argument, NULL, 'v'},
463 {"no-privileges", no_argument, NULL, 'x'},
464 {"no-acl", no_argument, NULL, 'x'},
465 {"compress", required_argument, NULL, 'Z'},
466 {"encoding", required_argument, NULL, 'E'},
467 {"help", no_argument, NULL, '?'},
468 {"version", no_argument, NULL, 'V'},
471 * the following options don't have an equivalent short option letter
473 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
474 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
475 {"column-inserts", no_argument, &dopt.column_inserts, 1},
476 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
477 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
478 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
479 {"exclude-table-data", required_argument, NULL, 4},
480 {"extra-float-digits", required_argument, NULL, 8},
481 {"if-exists", no_argument, &dopt.if_exists, 1},
482 {"inserts", no_argument, NULL, 9},
483 {"lock-wait-timeout", required_argument, NULL, 2},
484 {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
485 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
486 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
487 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
488 {"role", required_argument, NULL, 3},
489 {"section", required_argument, NULL, 5},
490 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
491 {"snapshot", required_argument, NULL, 6},
492 {"strict-names", no_argument, &strict_names, 1},
493 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
494 {"no-comments", no_argument, &dopt.no_comments, 1},
495 {"no-publications", no_argument, &dopt.no_publications, 1},
496 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
497 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
498 {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
499 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
500 {"no-sync", no_argument, NULL, 7},
501 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
502 {"rows-per-insert", required_argument, NULL, 10},
503 {"include-foreign-data", required_argument, NULL, 11},
504 {"table-and-children", required_argument, NULL, 12},
505 {"exclude-table-and-children", required_argument, NULL, 13},
506 {"exclude-table-data-and-children", required_argument, NULL, 14},
507 {"sync-method", required_argument, NULL, 15},
508 {"filter", required_argument, NULL, 16},
509 {"exclude-extension", required_argument, NULL, 17},
511 {NULL, 0, NULL, 0}
514 pg_logging_init(argv[0]);
515 pg_logging_set_level(PG_LOG_WARNING);
516 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
519 * Initialize what we need for parallel execution, especially for thread
520 * support on Windows.
522 init_parallel_dump_utils();
524 progname = get_progname(argv[0]);
526 if (argc > 1)
528 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
530 help(progname);
531 exit_nicely(0);
533 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
535 puts("pg_dump (PostgreSQL) " PG_VERSION);
536 exit_nicely(0);
540 InitDumpOptions(&dopt);
542 while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
543 long_options, &optindex)) != -1)
545 switch (c)
547 case 'a': /* Dump data only */
548 data_only = true;
549 break;
551 case 'b': /* Dump LOs */
552 dopt.outputLOs = true;
553 break;
555 case 'B': /* Don't dump LOs */
556 dopt.dontOutputLOs = true;
557 break;
559 case 'c': /* clean (i.e., drop) schema prior to create */
560 dopt.outputClean = 1;
561 break;
563 case 'C': /* Create DB */
564 dopt.outputCreateDB = 1;
565 break;
567 case 'd': /* database name */
568 dopt.cparams.dbname = pg_strdup(optarg);
569 break;
571 case 'e': /* include extension(s) */
572 simple_string_list_append(&extension_include_patterns, optarg);
573 dopt.include_everything = false;
574 break;
576 case 'E': /* Dump encoding */
577 dumpencoding = pg_strdup(optarg);
578 break;
580 case 'f':
581 filename = pg_strdup(optarg);
582 break;
584 case 'F':
585 format = pg_strdup(optarg);
586 break;
588 case 'h': /* server host */
589 dopt.cparams.pghost = pg_strdup(optarg);
590 break;
592 case 'j': /* number of dump jobs */
593 if (!option_parse_int(optarg, "-j/--jobs", 1,
594 PG_MAX_JOBS,
595 &numWorkers))
596 exit_nicely(1);
597 break;
599 case 'n': /* include schema(s) */
600 simple_string_list_append(&schema_include_patterns, optarg);
601 dopt.include_everything = false;
602 break;
604 case 'N': /* exclude schema(s) */
605 simple_string_list_append(&schema_exclude_patterns, optarg);
606 break;
608 case 'O': /* Don't reconnect to match owner */
609 dopt.outputNoOwner = 1;
610 break;
612 case 'p': /* server port */
613 dopt.cparams.pgport = pg_strdup(optarg);
614 break;
616 case 'R':
617 /* no-op, still accepted for backwards compatibility */
618 break;
620 case 's': /* dump schema only */
621 schema_only = true;
622 break;
624 case 'S': /* Username for superuser in plain text output */
625 dopt.outputSuperuser = pg_strdup(optarg);
626 break;
628 case 't': /* include table(s) */
629 simple_string_list_append(&table_include_patterns, optarg);
630 dopt.include_everything = false;
631 break;
633 case 'T': /* exclude table(s) */
634 simple_string_list_append(&table_exclude_patterns, optarg);
635 break;
637 case 'U':
638 dopt.cparams.username = pg_strdup(optarg);
639 break;
641 case 'v': /* verbose */
642 g_verbose = true;
643 pg_logging_increase_verbosity();
644 break;
646 case 'w':
647 dopt.cparams.promptPassword = TRI_NO;
648 break;
650 case 'W':
651 dopt.cparams.promptPassword = TRI_YES;
652 break;
654 case 'x': /* skip ACL dump */
655 dopt.aclsSkip = true;
656 break;
658 case 'Z': /* Compression */
659 parse_compress_options(optarg, &compression_algorithm_str,
660 &compression_detail);
661 user_compression_defined = true;
662 break;
664 case 0:
665 /* This covers the long options. */
666 break;
668 case 2: /* lock-wait-timeout */
669 dopt.lockWaitTimeout = pg_strdup(optarg);
670 break;
672 case 3: /* SET ROLE */
673 use_role = pg_strdup(optarg);
674 break;
676 case 4: /* exclude table(s) data */
677 simple_string_list_append(&tabledata_exclude_patterns, optarg);
678 break;
680 case 5: /* section */
681 set_dump_section(optarg, &dopt.dumpSections);
682 break;
684 case 6: /* snapshot */
685 dumpsnapshot = pg_strdup(optarg);
686 break;
688 case 7: /* no-sync */
689 dosync = false;
690 break;
692 case 8:
693 have_extra_float_digits = true;
694 if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
695 &extra_float_digits))
696 exit_nicely(1);
697 break;
699 case 9: /* inserts */
702 * dump_inserts also stores --rows-per-insert, careful not to
703 * overwrite that.
705 if (dopt.dump_inserts == 0)
706 dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
707 break;
709 case 10: /* rows per insert */
710 if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
711 &dopt.dump_inserts))
712 exit_nicely(1);
713 break;
715 case 11: /* include foreign data */
716 simple_string_list_append(&foreign_servers_include_patterns,
717 optarg);
718 break;
720 case 12: /* include table(s) and their children */
721 simple_string_list_append(&table_include_patterns_and_children,
722 optarg);
723 dopt.include_everything = false;
724 break;
726 case 13: /* exclude table(s) and their children */
727 simple_string_list_append(&table_exclude_patterns_and_children,
728 optarg);
729 break;
731 case 14: /* exclude data of table(s) and children */
732 simple_string_list_append(&tabledata_exclude_patterns_and_children,
733 optarg);
734 break;
736 case 15:
737 if (!parse_sync_method(optarg, &sync_method))
738 exit_nicely(1);
739 break;
741 case 16: /* read object filters from file */
742 read_dump_filters(optarg, &dopt);
743 break;
745 case 17: /* exclude extension(s) */
746 simple_string_list_append(&extension_exclude_patterns,
747 optarg);
748 break;
750 default:
751 /* getopt_long already emitted a complaint */
752 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
753 exit_nicely(1);
758 * Non-option argument specifies database name as long as it wasn't
759 * already specified with -d / --dbname
761 if (optind < argc && dopt.cparams.dbname == NULL)
762 dopt.cparams.dbname = argv[optind++];
764 /* Complain if any arguments remain */
765 if (optind < argc)
767 pg_log_error("too many command-line arguments (first is \"%s\")",
768 argv[optind]);
769 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
770 exit_nicely(1);
773 /* --column-inserts implies --inserts */
774 if (dopt.column_inserts && dopt.dump_inserts == 0)
775 dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
778 * Binary upgrade mode implies dumping sequence data even in schema-only
779 * mode. This is not exposed as a separate option, but kept separate
780 * internally for clarity.
782 if (dopt.binary_upgrade)
783 dopt.sequence_data = 1;
785 if (data_only && schema_only)
786 pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
788 if (schema_only && foreign_servers_include_patterns.head != NULL)
789 pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
791 if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
792 pg_fatal("option --include-foreign-data is not supported with parallel backup");
794 if (data_only && dopt.outputClean)
795 pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
797 if (dopt.if_exists && !dopt.outputClean)
798 pg_fatal("option --if-exists requires option -c/--clean");
800 /* set derivative flags */
801 dopt.dumpSchema = (!data_only);
802 dopt.dumpData = (!schema_only);
805 * --inserts are already implied above if --column-inserts or
806 * --rows-per-insert were specified.
808 if (dopt.do_nothing && dopt.dump_inserts == 0)
809 pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
811 /* Identify archive format to emit */
812 archiveFormat = parseArchiveFormat(format, &archiveMode);
814 /* archiveFormat specific setup */
815 if (archiveFormat == archNull)
816 plainText = 1;
819 * Custom and directory formats are compressed by default with gzip when
820 * available, not the others. If gzip is not available, no compression is
821 * done by default.
823 if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
824 !user_compression_defined)
826 #ifdef HAVE_LIBZ
827 compression_algorithm_str = "gzip";
828 #else
829 compression_algorithm_str = "none";
830 #endif
834 * Compression options
836 if (!parse_compress_algorithm(compression_algorithm_str,
837 &compression_algorithm))
838 pg_fatal("unrecognized compression algorithm: \"%s\"",
839 compression_algorithm_str);
841 parse_compress_specification(compression_algorithm, compression_detail,
842 &compression_spec);
843 error_detail = validate_compress_specification(&compression_spec);
844 if (error_detail != NULL)
845 pg_fatal("invalid compression specification: %s",
846 error_detail);
848 error_detail = supports_compression(compression_spec);
849 if (error_detail != NULL)
850 pg_fatal("%s", error_detail);
853 * Disable support for zstd workers for now - these are based on
854 * threading, and it's unclear how it interacts with parallel dumps on
855 * platforms where that relies on threads too (e.g. Windows).
857 if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
858 pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
859 "workers");
862 * If emitting an archive format, we always want to emit a DATABASE item,
863 * in case --create is specified at pg_restore time.
865 if (!plainText)
866 dopt.outputCreateDB = 1;
868 /* Parallel backup only in the directory archive format so far */
869 if (archiveFormat != archDirectory && numWorkers > 1)
870 pg_fatal("parallel backup only supported by the directory format");
872 /* Open the output file */
873 fout = CreateArchive(filename, archiveFormat, compression_spec,
874 dosync, archiveMode, setupDumpWorker, sync_method);
876 /* Make dump options accessible right away */
877 SetArchiveOptions(fout, &dopt, NULL);
879 /* Register the cleanup hook */
880 on_exit_close_archive(fout);
882 /* Let the archiver know how noisy to be */
883 fout->verbose = g_verbose;
887 * We allow the server to be back to 9.2, and up to any minor release of
888 * our own major version. (See also version check in pg_dumpall.c.)
890 fout->minRemoteVersion = 90200;
891 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
893 fout->numWorkers = numWorkers;
896 * Open the database using the Archiver, so it knows about it. Errors mean
897 * death.
899 ConnectDatabase(fout, &dopt.cparams, false);
900 setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
903 * On hot standbys, never try to dump unlogged table data, since it will
904 * just throw an error.
906 if (fout->isStandby)
907 dopt.no_unlogged_table_data = true;
910 * Find the last built-in OID, if needed (prior to 8.1)
912 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
914 g_last_builtin_oid = FirstNormalObjectId - 1;
916 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
918 /* Expand schema selection patterns into OID lists */
919 if (schema_include_patterns.head != NULL)
921 expand_schema_name_patterns(fout, &schema_include_patterns,
922 &schema_include_oids,
923 strict_names);
924 if (schema_include_oids.head == NULL)
925 pg_fatal("no matching schemas were found");
927 expand_schema_name_patterns(fout, &schema_exclude_patterns,
928 &schema_exclude_oids,
929 false);
930 /* non-matching exclusion patterns aren't an error */
932 /* Expand table selection patterns into OID lists */
933 expand_table_name_patterns(fout, &table_include_patterns,
934 &table_include_oids,
935 strict_names, false);
936 expand_table_name_patterns(fout, &table_include_patterns_and_children,
937 &table_include_oids,
938 strict_names, true);
939 if ((table_include_patterns.head != NULL ||
940 table_include_patterns_and_children.head != NULL) &&
941 table_include_oids.head == NULL)
942 pg_fatal("no matching tables were found");
944 expand_table_name_patterns(fout, &table_exclude_patterns,
945 &table_exclude_oids,
946 false, false);
947 expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
948 &table_exclude_oids,
949 false, true);
951 expand_table_name_patterns(fout, &tabledata_exclude_patterns,
952 &tabledata_exclude_oids,
953 false, false);
954 expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
955 &tabledata_exclude_oids,
956 false, true);
958 expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
959 &foreign_servers_include_oids);
961 /* non-matching exclusion patterns aren't an error */
963 /* Expand extension selection patterns into OID lists */
964 if (extension_include_patterns.head != NULL)
966 expand_extension_name_patterns(fout, &extension_include_patterns,
967 &extension_include_oids,
968 strict_names);
969 if (extension_include_oids.head == NULL)
970 pg_fatal("no matching extensions were found");
972 expand_extension_name_patterns(fout, &extension_exclude_patterns,
973 &extension_exclude_oids,
974 false);
975 /* non-matching exclusion patterns aren't an error */
978 * Dumping LOs is the default for dumps where an inclusion switch is not
979 * used (an "include everything" dump). -B can be used to exclude LOs
980 * from those dumps. -b can be used to include LOs even when an inclusion
981 * switch is used.
983 * -s means "schema only" and LOs are data, not schema, so we never
984 * include LOs when -s is used.
986 if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
987 dopt.outputLOs = true;
990 * Collect role names so we can map object owner OIDs to names.
992 collectRoleNames(fout);
995 * Now scan the database and create DumpableObject structs for all the
996 * objects we intend to dump.
998 tblinfo = getSchemaData(fout, &numTables);
1000 if (dopt.dumpData)
1002 getTableData(&dopt, tblinfo, numTables, 0);
1003 buildMatViewRefreshDependencies(fout);
1004 if (!dopt.dumpSchema)
1005 getTableDataFKConstraints();
1008 if (!dopt.dumpData && dopt.sequence_data)
1009 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1012 * In binary-upgrade mode, we do not have to worry about the actual LO
1013 * data or the associated metadata that resides in the pg_largeobject and
1014 * pg_largeobject_metadata tables, respectively.
1016 * However, we do need to collect LO information as there may be comments
1017 * or other information on LOs that we do need to dump out.
1019 if (dopt.outputLOs || dopt.binary_upgrade)
1020 getLOs(fout);
1023 * Collect dependency data to assist in ordering the objects.
1025 getDependencies(fout);
1028 * Collect ACLs, comments, and security labels, if wanted.
1030 if (!dopt.aclsSkip)
1031 getAdditionalACLs(fout);
1032 if (!dopt.no_comments)
1033 collectComments(fout);
1034 if (!dopt.no_security_labels)
1035 collectSecLabels(fout);
1037 /* For binary upgrade mode, collect required pg_class information. */
1038 if (dopt.binary_upgrade)
1039 collectBinaryUpgradeClassOids(fout);
1041 /* Collect sequence information. */
1042 collectSequences(fout);
1044 /* Lastly, create dummy objects to represent the section boundaries */
1045 boundaryObjs = createBoundaryObjects();
1047 /* Get pointers to all the known DumpableObjects */
1048 getDumpableObjects(&dobjs, &numObjs);
1051 * Add dummy dependencies to enforce the dump section ordering.
1053 addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1056 * Sort the objects into a safe dump order (no forward references).
1058 * We rely on dependency information to help us determine a safe order, so
1059 * the initial sort is mostly for cosmetic purposes: we sort by name to
1060 * ensure that logically identical schemas will dump identically.
1062 sortDumpableObjectsByTypeName(dobjs, numObjs);
1064 sortDumpableObjects(dobjs, numObjs,
1065 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1068 * Create archive TOC entries for all the objects to be dumped, in a safe
1069 * order.
1073 * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1075 dumpEncoding(fout);
1076 dumpStdStrings(fout);
1077 dumpSearchPath(fout);
1079 /* The database items are always next, unless we don't want them at all */
1080 if (dopt.outputCreateDB)
1081 dumpDatabase(fout);
1083 /* Now the rearrangeable objects. */
1084 for (i = 0; i < numObjs; i++)
1085 dumpDumpableObject(fout, dobjs[i]);
1088 * Set up options info to ensure we dump what we want.
1090 ropt = NewRestoreOptions();
1091 ropt->filename = filename;
1093 /* if you change this list, see dumpOptionsFromRestoreOptions */
1094 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1095 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1096 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1097 ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1098 ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1099 ropt->dropSchema = dopt.outputClean;
1100 ropt->dumpData = dopt.dumpData;
1101 ropt->dumpSchema = dopt.dumpSchema;
1102 ropt->if_exists = dopt.if_exists;
1103 ropt->column_inserts = dopt.column_inserts;
1104 ropt->dumpSections = dopt.dumpSections;
1105 ropt->aclsSkip = dopt.aclsSkip;
1106 ropt->superuser = dopt.outputSuperuser;
1107 ropt->createDB = dopt.outputCreateDB;
1108 ropt->noOwner = dopt.outputNoOwner;
1109 ropt->noTableAm = dopt.outputNoTableAm;
1110 ropt->noTablespace = dopt.outputNoTablespaces;
1111 ropt->disable_triggers = dopt.disable_triggers;
1112 ropt->use_setsessauth = dopt.use_setsessauth;
1113 ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1114 ropt->dump_inserts = dopt.dump_inserts;
1115 ropt->no_comments = dopt.no_comments;
1116 ropt->no_publications = dopt.no_publications;
1117 ropt->no_security_labels = dopt.no_security_labels;
1118 ropt->no_subscriptions = dopt.no_subscriptions;
1119 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1120 ropt->include_everything = dopt.include_everything;
1121 ropt->enable_row_security = dopt.enable_row_security;
1122 ropt->sequence_data = dopt.sequence_data;
1123 ropt->binary_upgrade = dopt.binary_upgrade;
1125 ropt->compression_spec = compression_spec;
1127 ropt->suppressDumpWarnings = true; /* We've already shown them */
1129 SetArchiveOptions(fout, &dopt, ropt);
1131 /* Mark which entries should be output */
1132 ProcessArchiveRestoreOptions(fout);
1135 * The archive's TOC entries are now marked as to which ones will actually
1136 * be output, so we can set up their dependency lists properly. This isn't
1137 * necessary for plain-text output, though.
1139 if (!plainText)
1140 BuildArchiveDependencies(fout);
1143 * And finally we can do the actual output.
1145 * Note: for non-plain-text output formats, the output file is written
1146 * inside CloseArchive(). This is, um, bizarre; but not worth changing
1147 * right now.
1149 if (plainText)
1150 RestoreArchive(fout);
1152 CloseArchive(fout);
1154 exit_nicely(0);
1158 static void
1159 help(const char *progname)
1161 printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1162 printf(_("Usage:\n"));
1163 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1165 printf(_("\nGeneral options:\n"));
1166 printf(_(" -f, --file=FILENAME output file or directory name\n"));
1167 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1168 " plain text (default))\n"));
1169 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1170 printf(_(" -v, --verbose verbose mode\n"));
1171 printf(_(" -V, --version output version information, then exit\n"));
1172 printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1173 " compress as specified\n"));
1174 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1175 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1176 printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1177 printf(_(" -?, --help show this help, then exit\n"));
1179 printf(_("\nOptions controlling the output content:\n"));
1180 printf(_(" -a, --data-only dump only the data, not the schema\n"));
1181 printf(_(" -b, --large-objects include large objects in dump\n"));
1182 printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1183 printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1184 printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1185 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1186 printf(_(" -C, --create include commands to create database in dump\n"));
1187 printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1188 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1189 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1190 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1191 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1192 " plain-text format\n"));
1193 printf(_(" -s, --schema-only dump only the schema, no data\n"));
1194 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1195 printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1196 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1197 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1198 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1199 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1200 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1201 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1202 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1203 " access to)\n"));
1204 printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1205 printf(_(" --exclude-table-and-children=PATTERN\n"
1206 " do NOT dump the specified table(s), including\n"
1207 " child and partition tables\n"));
1208 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1209 printf(_(" --exclude-table-data-and-children=PATTERN\n"
1210 " do NOT dump data for the specified table(s),\n"
1211 " including child and partition tables\n"));
1212 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1213 printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1214 " based on expressions in FILENAME\n"));
1215 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1216 printf(_(" --include-foreign-data=PATTERN\n"
1217 " include data of foreign tables on foreign\n"
1218 " servers matching PATTERN\n"));
1219 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1220 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1221 printf(_(" --no-comments do not dump comment commands\n"));
1222 printf(_(" --no-publications do not dump publications\n"));
1223 printf(_(" --no-security-labels do not dump security label assignments\n"));
1224 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1225 printf(_(" --no-table-access-method do not dump table access methods\n"));
1226 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1227 printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1228 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1229 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1230 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1231 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1232 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1233 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1234 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1235 printf(_(" --strict-names require table and/or schema include patterns to\n"
1236 " match at least one entity each\n"));
1237 printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1238 " child and partition tables\n"));
1239 printf(_(" --use-set-session-authorization\n"
1240 " use SET SESSION AUTHORIZATION commands instead of\n"
1241 " ALTER OWNER commands to set ownership\n"));
1243 printf(_("\nConnection options:\n"));
1244 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1245 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1246 printf(_(" -p, --port=PORT database server port number\n"));
1247 printf(_(" -U, --username=NAME connect as specified database user\n"));
1248 printf(_(" -w, --no-password never prompt for password\n"));
1249 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1250 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1252 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1253 "variable value is used.\n\n"));
1254 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1255 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1258 static void
1259 setup_connection(Archive *AH, const char *dumpencoding,
1260 const char *dumpsnapshot, char *use_role)
1262 DumpOptions *dopt = AH->dopt;
1263 PGconn *conn = GetConnection(AH);
1264 const char *std_strings;
1266 PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1269 * Set the client encoding if requested.
1271 if (dumpencoding)
1273 if (PQsetClientEncoding(conn, dumpencoding) < 0)
1274 pg_fatal("invalid client encoding \"%s\" specified",
1275 dumpencoding);
1279 * Get the active encoding and the standard_conforming_strings setting, so
1280 * we know how to escape strings.
1282 AH->encoding = PQclientEncoding(conn);
1284 std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1285 AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1288 * Set the role if requested. In a parallel dump worker, we'll be passed
1289 * use_role == NULL, but AH->use_role is already set (if user specified it
1290 * originally) and we should use that.
1292 if (!use_role && AH->use_role)
1293 use_role = AH->use_role;
1295 /* Set the role if requested */
1296 if (use_role)
1298 PQExpBuffer query = createPQExpBuffer();
1300 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1301 ExecuteSqlStatement(AH, query->data);
1302 destroyPQExpBuffer(query);
1304 /* save it for possible later use by parallel workers */
1305 if (!AH->use_role)
1306 AH->use_role = pg_strdup(use_role);
1309 /* Set the datestyle to ISO to ensure the dump's portability */
1310 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1312 /* Likewise, avoid using sql_standard intervalstyle */
1313 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1316 * Use an explicitly specified extra_float_digits if it has been provided.
1317 * Otherwise, set extra_float_digits so that we can dump float data
1318 * exactly (given correctly implemented float I/O code, anyway).
1320 if (have_extra_float_digits)
1322 PQExpBuffer q = createPQExpBuffer();
1324 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1325 extra_float_digits);
1326 ExecuteSqlStatement(AH, q->data);
1327 destroyPQExpBuffer(q);
1329 else
1330 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1333 * Disable synchronized scanning, to prevent unpredictable changes in row
1334 * ordering across a dump and reload.
1336 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1339 * Disable timeouts if supported.
1341 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1342 if (AH->remoteVersion >= 90300)
1343 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1344 if (AH->remoteVersion >= 90600)
1345 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1346 if (AH->remoteVersion >= 170000)
1347 ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1350 * Quote all identifiers, if requested.
1352 if (quote_all_identifiers)
1353 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1356 * Adjust row-security mode, if supported.
1358 if (AH->remoteVersion >= 90500)
1360 if (dopt->enable_row_security)
1361 ExecuteSqlStatement(AH, "SET row_security = on");
1362 else
1363 ExecuteSqlStatement(AH, "SET row_security = off");
1367 * For security reasons, we restrict the expansion of non-system views and
1368 * access to foreign tables during the pg_dump process. This restriction
1369 * is adjusted when dumping foreign table data.
1371 set_restrict_relation_kind(AH, "view, foreign-table");
1374 * Initialize prepared-query state to "nothing prepared". We do this here
1375 * so that a parallel dump worker will have its own state.
1377 AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1380 * Start transaction-snapshot mode transaction to dump consistent data.
1382 ExecuteSqlStatement(AH, "BEGIN");
1385 * To support the combination of serializable_deferrable with the jobs
1386 * option we use REPEATABLE READ for the worker connections that are
1387 * passed a snapshot. As long as the snapshot is acquired in a
1388 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1389 * REPEATABLE READ transaction provides the appropriate integrity
1390 * guarantees. This is a kluge, but safe for back-patching.
1392 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1393 ExecuteSqlStatement(AH,
1394 "SET TRANSACTION ISOLATION LEVEL "
1395 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1396 else
1397 ExecuteSqlStatement(AH,
1398 "SET TRANSACTION ISOLATION LEVEL "
1399 "REPEATABLE READ, READ ONLY");
1402 * If user specified a snapshot to use, select that. In a parallel dump
1403 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1404 * is already set (if the server can handle it) and we should use that.
1406 if (dumpsnapshot)
1407 AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1409 if (AH->sync_snapshot_id)
1411 PQExpBuffer query = createPQExpBuffer();
1413 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1414 appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1415 ExecuteSqlStatement(AH, query->data);
1416 destroyPQExpBuffer(query);
1418 else if (AH->numWorkers > 1)
1420 if (AH->isStandby && AH->remoteVersion < 100000)
1421 pg_fatal("parallel dumps from standby servers are not supported by this server version");
1422 AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1426 /* Set up connection for a parallel worker process */
1427 static void
1428 setupDumpWorker(Archive *AH)
1431 * We want to re-select all the same values the leader connection is
1432 * using. We'll have inherited directly-usable values in
1433 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1434 * inherited encoding value back to a string to pass to setup_connection.
1436 setup_connection(AH,
1437 pg_encoding_to_char(AH->encoding),
1438 NULL,
1439 NULL);
1442 static char *
1443 get_synchronized_snapshot(Archive *fout)
1445 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1446 char *result;
1447 PGresult *res;
1449 res = ExecuteSqlQueryForSingleRow(fout, query);
1450 result = pg_strdup(PQgetvalue(res, 0, 0));
1451 PQclear(res);
1453 return result;
1456 static ArchiveFormat
1457 parseArchiveFormat(const char *format, ArchiveMode *mode)
1459 ArchiveFormat archiveFormat;
1461 *mode = archModeWrite;
1463 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1465 /* This is used by pg_dumpall, and is not documented */
1466 archiveFormat = archNull;
1467 *mode = archModeAppend;
1469 else if (pg_strcasecmp(format, "c") == 0)
1470 archiveFormat = archCustom;
1471 else if (pg_strcasecmp(format, "custom") == 0)
1472 archiveFormat = archCustom;
1473 else if (pg_strcasecmp(format, "d") == 0)
1474 archiveFormat = archDirectory;
1475 else if (pg_strcasecmp(format, "directory") == 0)
1476 archiveFormat = archDirectory;
1477 else if (pg_strcasecmp(format, "p") == 0)
1478 archiveFormat = archNull;
1479 else if (pg_strcasecmp(format, "plain") == 0)
1480 archiveFormat = archNull;
1481 else if (pg_strcasecmp(format, "t") == 0)
1482 archiveFormat = archTar;
1483 else if (pg_strcasecmp(format, "tar") == 0)
1484 archiveFormat = archTar;
1485 else
1486 pg_fatal("invalid output format \"%s\" specified", format);
1487 return archiveFormat;
1491 * Find the OIDs of all schemas matching the given list of patterns,
1492 * and append them to the given OID list.
1494 static void
1495 expand_schema_name_patterns(Archive *fout,
1496 SimpleStringList *patterns,
1497 SimpleOidList *oids,
1498 bool strict_names)
1500 PQExpBuffer query;
1501 PGresult *res;
1502 SimpleStringListCell *cell;
1503 int i;
1505 if (patterns->head == NULL)
1506 return; /* nothing to do */
1508 query = createPQExpBuffer();
1511 * The loop below runs multiple SELECTs might sometimes result in
1512 * duplicate entries in the OID list, but we don't care.
1515 for (cell = patterns->head; cell; cell = cell->next)
1517 PQExpBufferData dbbuf;
1518 int dotcnt;
1520 appendPQExpBufferStr(query,
1521 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1522 initPQExpBuffer(&dbbuf);
1523 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1524 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1525 &dotcnt);
1526 if (dotcnt > 1)
1527 pg_fatal("improper qualified name (too many dotted names): %s",
1528 cell->val);
1529 else if (dotcnt == 1)
1530 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1531 termPQExpBuffer(&dbbuf);
1533 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1534 if (strict_names && PQntuples(res) == 0)
1535 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1537 for (i = 0; i < PQntuples(res); i++)
1539 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1542 PQclear(res);
1543 resetPQExpBuffer(query);
1546 destroyPQExpBuffer(query);
1550 * Find the OIDs of all extensions matching the given list of patterns,
1551 * and append them to the given OID list.
1553 static void
1554 expand_extension_name_patterns(Archive *fout,
1555 SimpleStringList *patterns,
1556 SimpleOidList *oids,
1557 bool strict_names)
1559 PQExpBuffer query;
1560 PGresult *res;
1561 SimpleStringListCell *cell;
1562 int i;
1564 if (patterns->head == NULL)
1565 return; /* nothing to do */
1567 query = createPQExpBuffer();
1570 * The loop below runs multiple SELECTs might sometimes result in
1571 * duplicate entries in the OID list, but we don't care.
1573 for (cell = patterns->head; cell; cell = cell->next)
1575 int dotcnt;
1577 appendPQExpBufferStr(query,
1578 "SELECT oid FROM pg_catalog.pg_extension e\n");
1579 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1580 false, NULL, "e.extname", NULL, NULL, NULL,
1581 &dotcnt);
1582 if (dotcnt > 0)
1583 pg_fatal("improper qualified name (too many dotted names): %s",
1584 cell->val);
1586 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1587 if (strict_names && PQntuples(res) == 0)
1588 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1590 for (i = 0; i < PQntuples(res); i++)
1592 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1595 PQclear(res);
1596 resetPQExpBuffer(query);
1599 destroyPQExpBuffer(query);
1603 * Find the OIDs of all foreign servers matching the given list of patterns,
1604 * and append them to the given OID list.
1606 static void
1607 expand_foreign_server_name_patterns(Archive *fout,
1608 SimpleStringList *patterns,
1609 SimpleOidList *oids)
1611 PQExpBuffer query;
1612 PGresult *res;
1613 SimpleStringListCell *cell;
1614 int i;
1616 if (patterns->head == NULL)
1617 return; /* nothing to do */
1619 query = createPQExpBuffer();
1622 * The loop below runs multiple SELECTs might sometimes result in
1623 * duplicate entries in the OID list, but we don't care.
1626 for (cell = patterns->head; cell; cell = cell->next)
1628 int dotcnt;
1630 appendPQExpBufferStr(query,
1631 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1632 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1633 false, NULL, "s.srvname", NULL, NULL, NULL,
1634 &dotcnt);
1635 if (dotcnt > 0)
1636 pg_fatal("improper qualified name (too many dotted names): %s",
1637 cell->val);
1639 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1640 if (PQntuples(res) == 0)
1641 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1643 for (i = 0; i < PQntuples(res); i++)
1644 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1646 PQclear(res);
1647 resetPQExpBuffer(query);
1650 destroyPQExpBuffer(query);
1654 * Find the OIDs of all tables matching the given list of patterns,
1655 * and append them to the given OID list. See also expand_dbname_patterns()
1656 * in pg_dumpall.c
1658 static void
1659 expand_table_name_patterns(Archive *fout,
1660 SimpleStringList *patterns, SimpleOidList *oids,
1661 bool strict_names, bool with_child_tables)
1663 PQExpBuffer query;
1664 PGresult *res;
1665 SimpleStringListCell *cell;
1666 int i;
1668 if (patterns->head == NULL)
1669 return; /* nothing to do */
1671 query = createPQExpBuffer();
1674 * this might sometimes result in duplicate entries in the OID list, but
1675 * we don't care.
1678 for (cell = patterns->head; cell; cell = cell->next)
1680 PQExpBufferData dbbuf;
1681 int dotcnt;
1684 * Query must remain ABSOLUTELY devoid of unqualified names. This
1685 * would be unnecessary given a pg_table_is_visible() variant taking a
1686 * search_path argument.
1688 * For with_child_tables, we start with the basic query's results and
1689 * recursively search the inheritance tree to add child tables.
1691 if (with_child_tables)
1693 appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1696 appendPQExpBuffer(query,
1697 "SELECT c.oid"
1698 "\nFROM pg_catalog.pg_class c"
1699 "\n LEFT JOIN pg_catalog.pg_namespace n"
1700 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1701 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1702 "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1703 RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1704 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1705 RELKIND_PARTITIONED_TABLE);
1706 initPQExpBuffer(&dbbuf);
1707 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1708 false, "n.nspname", "c.relname", NULL,
1709 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1710 &dotcnt);
1711 if (dotcnt > 2)
1712 pg_fatal("improper relation name (too many dotted names): %s",
1713 cell->val);
1714 else if (dotcnt == 2)
1715 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1716 termPQExpBuffer(&dbbuf);
1718 if (with_child_tables)
1720 appendPQExpBuffer(query, "UNION"
1721 "\nSELECT i.inhrelid"
1722 "\nFROM partition_tree p"
1723 "\n JOIN pg_catalog.pg_inherits i"
1724 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1725 "\n)"
1726 "\nSELECT relid FROM partition_tree");
1729 ExecuteSqlStatement(fout, "RESET search_path");
1730 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1731 PQclear(ExecuteSqlQueryForSingleRow(fout,
1732 ALWAYS_SECURE_SEARCH_PATH_SQL));
1733 if (strict_names && PQntuples(res) == 0)
1734 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1736 for (i = 0; i < PQntuples(res); i++)
1738 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1741 PQclear(res);
1742 resetPQExpBuffer(query);
1745 destroyPQExpBuffer(query);
1749 * Verifies that the connected database name matches the given database name,
1750 * and if not, dies with an error about the given pattern.
1752 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1754 static void
1755 prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1757 const char *db;
1759 db = PQdb(conn);
1760 if (db == NULL)
1761 pg_fatal("You are currently not connected to a database.");
1763 if (strcmp(db, dbname) != 0)
1764 pg_fatal("cross-database references are not implemented: %s",
1765 pattern);
1769 * checkExtensionMembership
1770 * Determine whether object is an extension member, and if so,
1771 * record an appropriate dependency and set the object's dump flag.
1773 * It's important to call this for each object that could be an extension
1774 * member. Generally, we integrate this with determining the object's
1775 * to-be-dumped-ness, since extension membership overrides other rules for that.
1777 * Returns true if object is an extension member, else false.
1779 static bool
1780 checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1782 ExtensionInfo *ext = findOwningExtension(dobj->catId);
1784 if (ext == NULL)
1785 return false;
1787 dobj->ext_member = true;
1789 /* Record dependency so that getDependencies needn't deal with that */
1790 addObjectDependency(dobj, ext->dobj.dumpId);
1793 * In 9.6 and above, mark the member object to have any non-initial ACLs
1794 * dumped. (Any initial ACLs will be removed later, using data from
1795 * pg_init_privs, so that we'll dump only the delta from the extension's
1796 * initial setup.)
1798 * Prior to 9.6, we do not include any extension member components.
1800 * In binary upgrades, we still dump all components of the members
1801 * individually, since the idea is to exactly reproduce the database
1802 * contents rather than replace the extension contents with something
1803 * different.
1805 * Note: it might be interesting someday to implement storage and delta
1806 * dumping of extension members' RLS policies and/or security labels.
1807 * However there is a pitfall for RLS policies: trying to dump them
1808 * requires getting a lock on their tables, and the calling user might not
1809 * have privileges for that. We need no lock to examine a table's ACLs,
1810 * so the current feature doesn't have a problem of that sort.
1812 if (fout->dopt->binary_upgrade)
1813 dobj->dump = ext->dobj.dump;
1814 else
1816 if (fout->remoteVersion < 90600)
1817 dobj->dump = DUMP_COMPONENT_NONE;
1818 else
1819 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1822 return true;
1826 * selectDumpableNamespace: policy-setting subroutine
1827 * Mark a namespace as to be dumped or not
1829 static void
1830 selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1833 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1834 * and (for --clean) a DROP SCHEMA statement. (In the absence of
1835 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1837 nsinfo->create = true;
1840 * If specific tables are being dumped, do not dump any complete
1841 * namespaces. If specific namespaces are being dumped, dump just those
1842 * namespaces. Otherwise, dump all non-system namespaces.
1844 if (table_include_oids.head != NULL)
1845 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1846 else if (schema_include_oids.head != NULL)
1847 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1848 simple_oid_list_member(&schema_include_oids,
1849 nsinfo->dobj.catId.oid) ?
1850 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1851 else if (fout->remoteVersion >= 90600 &&
1852 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1855 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1856 * they are interesting (and not the original ACLs which were set at
1857 * initdb time, see pg_init_privs).
1859 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1861 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1862 strcmp(nsinfo->dobj.name, "information_schema") == 0)
1864 /* Other system schemas don't get dumped */
1865 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1867 else if (strcmp(nsinfo->dobj.name, "public") == 0)
1870 * The public schema is a strange beast that sits in a sort of
1871 * no-mans-land between being a system object and a user object.
1872 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1873 * a comment and an indication of ownership. If the owner is the
1874 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1875 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1877 nsinfo->create = false;
1878 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1879 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1880 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1881 nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1884 * Also, make like it has a comment even if it doesn't; this is so
1885 * that we'll emit a command to drop the comment, if appropriate.
1886 * (Without this, we'd not call dumpCommentExtended for it.)
1888 nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1890 else
1891 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1894 * In any case, a namespace can be excluded by an exclusion switch
1896 if (nsinfo->dobj.dump_contains &&
1897 simple_oid_list_member(&schema_exclude_oids,
1898 nsinfo->dobj.catId.oid))
1899 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1902 * If the schema belongs to an extension, allow extension membership to
1903 * override the dump decision for the schema itself. However, this does
1904 * not change dump_contains, so this won't change what we do with objects
1905 * within the schema. (If they belong to the extension, they'll get
1906 * suppressed by it, otherwise not.)
1908 (void) checkExtensionMembership(&nsinfo->dobj, fout);
1912 * selectDumpableTable: policy-setting subroutine
1913 * Mark a table as to be dumped or not
1915 static void
1916 selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1918 if (checkExtensionMembership(&tbinfo->dobj, fout))
1919 return; /* extension membership overrides all else */
1922 * If specific tables are being dumped, dump just those tables; else, dump
1923 * according to the parent namespace's dump flag.
1925 if (table_include_oids.head != NULL)
1926 tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1927 tbinfo->dobj.catId.oid) ?
1928 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1929 else
1930 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1933 * In any case, a table can be excluded by an exclusion switch
1935 if (tbinfo->dobj.dump &&
1936 simple_oid_list_member(&table_exclude_oids,
1937 tbinfo->dobj.catId.oid))
1938 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1942 * selectDumpableType: policy-setting subroutine
1943 * Mark a type as to be dumped or not
1945 * If it's a table's rowtype or an autogenerated array type, we also apply a
1946 * special type code to facilitate sorting into the desired order. (We don't
1947 * want to consider those to be ordinary types because that would bring tables
1948 * up into the datatype part of the dump order.) We still set the object's
1949 * dump flag; that's not going to cause the dummy type to be dumped, but we
1950 * need it so that casts involving such types will be dumped correctly -- see
1951 * dumpCast. This means the flag should be set the same as for the underlying
1952 * object (the table or base type).
1954 static void
1955 selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1957 /* skip complex types, except for standalone composite types */
1958 if (OidIsValid(tyinfo->typrelid) &&
1959 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1961 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1963 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1964 if (tytable != NULL)
1965 tyinfo->dobj.dump = tytable->dobj.dump;
1966 else
1967 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1968 return;
1971 /* skip auto-generated array and multirange types */
1972 if (tyinfo->isArray || tyinfo->isMultirange)
1974 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1977 * Fall through to set the dump flag; we assume that the subsequent
1978 * rules will do the same thing as they would for the array's base
1979 * type or multirange's range type. (We cannot reliably look up the
1980 * base type here, since getTypes may not have processed it yet.)
1984 if (checkExtensionMembership(&tyinfo->dobj, fout))
1985 return; /* extension membership overrides all else */
1987 /* Dump based on if the contents of the namespace are being dumped */
1988 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1992 * selectDumpableDefaultACL: policy-setting subroutine
1993 * Mark a default ACL as to be dumped or not
1995 * For per-schema default ACLs, dump if the schema is to be dumped.
1996 * Otherwise dump if we are dumping "everything". Note that dumpSchema
1997 * and aclsSkip are checked separately.
1999 static void
2000 selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2002 /* Default ACLs can't be extension members */
2004 if (dinfo->dobj.namespace)
2005 /* default ACLs are considered part of the namespace */
2006 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2007 else
2008 dinfo->dobj.dump = dopt->include_everything ?
2009 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2013 * selectDumpableCast: policy-setting subroutine
2014 * Mark a cast as to be dumped or not
2016 * Casts do not belong to any particular namespace (since they haven't got
2017 * names), nor do they have identifiable owners. To distinguish user-defined
2018 * casts from built-in ones, we must resort to checking whether the cast's
2019 * OID is in the range reserved for initdb.
2021 static void
2022 selectDumpableCast(CastInfo *cast, Archive *fout)
2024 if (checkExtensionMembership(&cast->dobj, fout))
2025 return; /* extension membership overrides all else */
2028 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2029 * support ACLs currently.
2031 if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2032 cast->dobj.dump = DUMP_COMPONENT_NONE;
2033 else
2034 cast->dobj.dump = fout->dopt->include_everything ?
2035 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2039 * selectDumpableProcLang: policy-setting subroutine
2040 * Mark a procedural language as to be dumped or not
2042 * Procedural languages do not belong to any particular namespace. To
2043 * identify built-in languages, we must resort to checking whether the
2044 * language's OID is in the range reserved for initdb.
2046 static void
2047 selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2049 if (checkExtensionMembership(&plang->dobj, fout))
2050 return; /* extension membership overrides all else */
2053 * Only include procedural languages when we are dumping everything.
2055 * For from-initdb procedural languages, only include ACLs, as we do for
2056 * the pg_catalog namespace. We need this because procedural languages do
2057 * not live in any namespace.
2059 if (!fout->dopt->include_everything)
2060 plang->dobj.dump = DUMP_COMPONENT_NONE;
2061 else
2063 if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2064 plang->dobj.dump = fout->remoteVersion < 90600 ?
2065 DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2066 else
2067 plang->dobj.dump = DUMP_COMPONENT_ALL;
2072 * selectDumpableAccessMethod: policy-setting subroutine
2073 * Mark an access method as to be dumped or not
2075 * Access methods do not belong to any particular namespace. To identify
2076 * built-in access methods, we must resort to checking whether the
2077 * method's OID is in the range reserved for initdb.
2079 static void
2080 selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2082 if (checkExtensionMembership(&method->dobj, fout))
2083 return; /* extension membership overrides all else */
2086 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2087 * they do not support ACLs currently.
2089 if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2090 method->dobj.dump = DUMP_COMPONENT_NONE;
2091 else
2092 method->dobj.dump = fout->dopt->include_everything ?
2093 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2097 * selectDumpableExtension: policy-setting subroutine
2098 * Mark an extension as to be dumped or not
2100 * Built-in extensions should be skipped except for checking ACLs, since we
2101 * assume those will already be installed in the target database. We identify
2102 * such extensions by their having OIDs in the range reserved for initdb.
2103 * We dump all user-added extensions by default. No extensions are dumped
2104 * if include_everything is false (i.e., a --schema or --table switch was
2105 * given), except if --extension specifies a list of extensions to dump.
2107 static void
2108 selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2111 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2112 * change permissions on their member objects, if they wish to, and have
2113 * those changes preserved.
2115 if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2116 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2117 else
2119 /* check if there is a list of extensions to dump */
2120 if (extension_include_oids.head != NULL)
2121 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2122 simple_oid_list_member(&extension_include_oids,
2123 extinfo->dobj.catId.oid) ?
2124 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2125 else
2126 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2127 dopt->include_everything ?
2128 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2130 /* check that the extension is not explicitly excluded */
2131 if (extinfo->dobj.dump &&
2132 simple_oid_list_member(&extension_exclude_oids,
2133 extinfo->dobj.catId.oid))
2134 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2139 * selectDumpablePublicationObject: policy-setting subroutine
2140 * Mark a publication object as to be dumped or not
2142 * A publication can have schemas and tables which have schemas, but those are
2143 * ignored in decision making, because publications are only dumped when we are
2144 * dumping everything.
2146 static void
2147 selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2149 if (checkExtensionMembership(dobj, fout))
2150 return; /* extension membership overrides all else */
2152 dobj->dump = fout->dopt->include_everything ?
2153 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2157 * selectDumpableStatisticsObject: policy-setting subroutine
2158 * Mark an extended statistics object as to be dumped or not
2160 * We dump an extended statistics object if the schema it's in and the table
2161 * it's for are being dumped. (This'll need more thought if statistics
2162 * objects ever support cross-table stats.)
2164 static void
2165 selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2167 if (checkExtensionMembership(&sobj->dobj, fout))
2168 return; /* extension membership overrides all else */
2170 sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2171 if (sobj->stattable == NULL ||
2172 !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2173 sobj->dobj.dump = DUMP_COMPONENT_NONE;
2177 * selectDumpableObject: policy-setting subroutine
2178 * Mark a generic dumpable object as to be dumped or not
2180 * Use this only for object types without a special-case routine above.
2182 static void
2183 selectDumpableObject(DumpableObject *dobj, Archive *fout)
2185 if (checkExtensionMembership(dobj, fout))
2186 return; /* extension membership overrides all else */
2189 * Default policy is to dump if parent namespace is dumpable, or for
2190 * non-namespace-associated items, dump if we're dumping "everything".
2192 if (dobj->namespace)
2193 dobj->dump = dobj->namespace->dobj.dump_contains;
2194 else
2195 dobj->dump = fout->dopt->include_everything ?
2196 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2200 * Dump a table's contents for loading using the COPY command
2201 * - this routine is called by the Archiver when it wants the table
2202 * to be dumped.
2204 static int
2205 dumpTableData_copy(Archive *fout, const void *dcontext)
2207 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2208 TableInfo *tbinfo = tdinfo->tdtable;
2209 const char *classname = tbinfo->dobj.name;
2210 PQExpBuffer q = createPQExpBuffer();
2213 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2214 * which uses it already.
2216 PQExpBuffer clistBuf = createPQExpBuffer();
2217 PGconn *conn = GetConnection(fout);
2218 PGresult *res;
2219 int ret;
2220 char *copybuf;
2221 const char *column_list;
2223 pg_log_info("dumping contents of table \"%s.%s\"",
2224 tbinfo->dobj.namespace->dobj.name, classname);
2227 * Specify the column list explicitly so that we have no possibility of
2228 * retrieving data in the wrong column order. (The default column
2229 * ordering of COPY will not be what we want in certain corner cases
2230 * involving ADD COLUMN and inheritance.)
2232 column_list = fmtCopyColumnList(tbinfo, clistBuf);
2235 * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2236 * a filter condition was specified. For other cases a simple COPY
2237 * suffices.
2239 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2241 /* Temporary allows to access to foreign tables to dump data */
2242 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2243 set_restrict_relation_kind(fout, "view");
2245 appendPQExpBufferStr(q, "COPY (SELECT ");
2246 /* klugery to get rid of parens in column list */
2247 if (strlen(column_list) > 2)
2249 appendPQExpBufferStr(q, column_list + 1);
2250 q->data[q->len - 1] = ' ';
2252 else
2253 appendPQExpBufferStr(q, "* ");
2255 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2256 fmtQualifiedDumpable(tbinfo),
2257 tdinfo->filtercond ? tdinfo->filtercond : "");
2259 else
2261 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2262 fmtQualifiedDumpable(tbinfo),
2263 column_list);
2265 res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2266 PQclear(res);
2267 destroyPQExpBuffer(clistBuf);
2269 for (;;)
2271 ret = PQgetCopyData(conn, &copybuf, 0);
2273 if (ret < 0)
2274 break; /* done or error */
2276 if (copybuf)
2278 WriteData(fout, copybuf, ret);
2279 PQfreemem(copybuf);
2282 /* ----------
2283 * THROTTLE:
2285 * There was considerable discussion in late July, 2000 regarding
2286 * slowing down pg_dump when backing up large tables. Users with both
2287 * slow & fast (multi-processor) machines experienced performance
2288 * degradation when doing a backup.
2290 * Initial attempts based on sleeping for a number of ms for each ms
2291 * of work were deemed too complex, then a simple 'sleep in each loop'
2292 * implementation was suggested. The latter failed because the loop
2293 * was too tight. Finally, the following was implemented:
2295 * If throttle is non-zero, then
2296 * See how long since the last sleep.
2297 * Work out how long to sleep (based on ratio).
2298 * If sleep is more than 100ms, then
2299 * sleep
2300 * reset timer
2301 * EndIf
2302 * EndIf
2304 * where the throttle value was the number of ms to sleep per ms of
2305 * work. The calculation was done in each loop.
2307 * Most of the hard work is done in the backend, and this solution
2308 * still did not work particularly well: on slow machines, the ratio
2309 * was 50:1, and on medium paced machines, 1:1, and on fast
2310 * multi-processor machines, it had little or no effect, for reasons
2311 * that were unclear.
2313 * Further discussion ensued, and the proposal was dropped.
2315 * For those people who want this feature, it can be implemented using
2316 * gettimeofday in each loop, calculating the time since last sleep,
2317 * multiplying that by the sleep ratio, then if the result is more
2318 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2319 * function to sleep for a subsecond period ie.
2321 * select(0, NULL, NULL, NULL, &tvi);
2323 * This will return after the interval specified in the structure tvi.
2324 * Finally, call gettimeofday again to save the 'last sleep time'.
2325 * ----------
2328 archprintf(fout, "\\.\n\n\n");
2330 if (ret == -2)
2332 /* copy data transfer failed */
2333 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2334 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2335 pg_log_error_detail("Command was: %s", q->data);
2336 exit_nicely(1);
2339 /* Check command status and return to normal libpq state */
2340 res = PQgetResult(conn);
2341 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2343 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2344 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2345 pg_log_error_detail("Command was: %s", q->data);
2346 exit_nicely(1);
2348 PQclear(res);
2350 /* Do this to ensure we've pumped libpq back to idle state */
2351 if (PQgetResult(conn) != NULL)
2352 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2353 classname);
2355 destroyPQExpBuffer(q);
2357 /* Revert back the setting */
2358 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2359 set_restrict_relation_kind(fout, "view, foreign-table");
2361 return 1;
2365 * Dump table data using INSERT commands.
2367 * Caution: when we restore from an archive file direct to database, the
2368 * INSERT commands emitted by this function have to be parsed by
2369 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2370 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2372 static int
2373 dumpTableData_insert(Archive *fout, const void *dcontext)
2375 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2376 TableInfo *tbinfo = tdinfo->tdtable;
2377 DumpOptions *dopt = fout->dopt;
2378 PQExpBuffer q = createPQExpBuffer();
2379 PQExpBuffer insertStmt = NULL;
2380 char *attgenerated;
2381 PGresult *res;
2382 int nfields,
2384 int rows_per_statement = dopt->dump_inserts;
2385 int rows_this_statement = 0;
2387 /* Temporary allows to access to foreign tables to dump data */
2388 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2389 set_restrict_relation_kind(fout, "view");
2392 * If we're going to emit INSERTs with column names, the most efficient
2393 * way to deal with generated columns is to exclude them entirely. For
2394 * INSERTs without column names, we have to emit DEFAULT rather than the
2395 * actual column value --- but we can save a few cycles by fetching nulls
2396 * rather than the uninteresting-to-us value.
2398 attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2399 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2400 nfields = 0;
2401 for (i = 0; i < tbinfo->numatts; i++)
2403 if (tbinfo->attisdropped[i])
2404 continue;
2405 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2406 continue;
2407 if (nfields > 0)
2408 appendPQExpBufferStr(q, ", ");
2409 if (tbinfo->attgenerated[i])
2410 appendPQExpBufferStr(q, "NULL");
2411 else
2412 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2413 attgenerated[nfields] = tbinfo->attgenerated[i];
2414 nfields++;
2416 /* Servers before 9.4 will complain about zero-column SELECT */
2417 if (nfields == 0)
2418 appendPQExpBufferStr(q, "NULL");
2419 appendPQExpBuffer(q, " FROM ONLY %s",
2420 fmtQualifiedDumpable(tbinfo));
2421 if (tdinfo->filtercond)
2422 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2424 ExecuteSqlStatement(fout, q->data);
2426 while (1)
2428 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2429 PGRES_TUPLES_OK);
2431 /* cross-check field count, allowing for dummy NULL if any */
2432 if (nfields != PQnfields(res) &&
2433 !(nfields == 0 && PQnfields(res) == 1))
2434 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2435 tbinfo->dobj.name);
2438 * First time through, we build as much of the INSERT statement as
2439 * possible in "insertStmt", which we can then just print for each
2440 * statement. If the table happens to have zero dumpable columns then
2441 * this will be a complete statement, otherwise it will end in
2442 * "VALUES" and be ready to have the row's column values printed.
2444 if (insertStmt == NULL)
2446 TableInfo *targettab;
2448 insertStmt = createPQExpBuffer();
2451 * When load-via-partition-root is set or forced, get the root
2452 * table name for the partition table, so that we can reload data
2453 * through the root table.
2455 if (tbinfo->ispartition &&
2456 (dopt->load_via_partition_root ||
2457 forcePartitionRootLoad(tbinfo)))
2458 targettab = getRootTableInfo(tbinfo);
2459 else
2460 targettab = tbinfo;
2462 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2463 fmtQualifiedDumpable(targettab));
2465 /* corner case for zero-column table */
2466 if (nfields == 0)
2468 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2470 else
2472 /* append the list of column names if required */
2473 if (dopt->column_inserts)
2475 appendPQExpBufferChar(insertStmt, '(');
2476 for (int field = 0; field < nfields; field++)
2478 if (field > 0)
2479 appendPQExpBufferStr(insertStmt, ", ");
2480 appendPQExpBufferStr(insertStmt,
2481 fmtId(PQfname(res, field)));
2483 appendPQExpBufferStr(insertStmt, ") ");
2486 if (tbinfo->needs_override)
2487 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2489 appendPQExpBufferStr(insertStmt, "VALUES");
2493 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2495 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2496 if (rows_this_statement == 0)
2497 archputs(insertStmt->data, fout);
2500 * If it is zero-column table then we've already written the
2501 * complete statement, which will mean we've disobeyed
2502 * --rows-per-insert when it's set greater than 1. We do support
2503 * a way to make this multi-row with: SELECT UNION ALL SELECT
2504 * UNION ALL ... but that's non-standard so we should avoid it
2505 * given that using INSERTs is mostly only ever needed for
2506 * cross-database exports.
2508 if (nfields == 0)
2509 continue;
2511 /* Emit a row heading */
2512 if (rows_per_statement == 1)
2513 archputs(" (", fout);
2514 else if (rows_this_statement > 0)
2515 archputs(",\n\t(", fout);
2516 else
2517 archputs("\n\t(", fout);
2519 for (int field = 0; field < nfields; field++)
2521 if (field > 0)
2522 archputs(", ", fout);
2523 if (attgenerated[field])
2525 archputs("DEFAULT", fout);
2526 continue;
2528 if (PQgetisnull(res, tuple, field))
2530 archputs("NULL", fout);
2531 continue;
2534 /* XXX This code is partially duplicated in ruleutils.c */
2535 switch (PQftype(res, field))
2537 case INT2OID:
2538 case INT4OID:
2539 case INT8OID:
2540 case OIDOID:
2541 case FLOAT4OID:
2542 case FLOAT8OID:
2543 case NUMERICOID:
2546 * These types are printed without quotes unless
2547 * they contain values that aren't accepted by the
2548 * scanner unquoted (e.g., 'NaN'). Note that
2549 * strtod() and friends might accept NaN, so we
2550 * can't use that to test.
2552 * In reality we only need to defend against
2553 * infinity and NaN, so we need not get too crazy
2554 * about pattern matching here.
2556 const char *s = PQgetvalue(res, tuple, field);
2558 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2559 archputs(s, fout);
2560 else
2561 archprintf(fout, "'%s'", s);
2563 break;
2565 case BITOID:
2566 case VARBITOID:
2567 archprintf(fout, "B'%s'",
2568 PQgetvalue(res, tuple, field));
2569 break;
2571 case BOOLOID:
2572 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2573 archputs("true", fout);
2574 else
2575 archputs("false", fout);
2576 break;
2578 default:
2579 /* All other types are printed as string literals. */
2580 resetPQExpBuffer(q);
2581 appendStringLiteralAH(q,
2582 PQgetvalue(res, tuple, field),
2583 fout);
2584 archputs(q->data, fout);
2585 break;
2589 /* Terminate the row ... */
2590 archputs(")", fout);
2592 /* ... and the statement, if the target no. of rows is reached */
2593 if (++rows_this_statement >= rows_per_statement)
2595 if (dopt->do_nothing)
2596 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2597 else
2598 archputs(";\n", fout);
2599 /* Reset the row counter */
2600 rows_this_statement = 0;
2604 if (PQntuples(res) <= 0)
2606 PQclear(res);
2607 break;
2609 PQclear(res);
2612 /* Terminate any statements that didn't make the row count. */
2613 if (rows_this_statement > 0)
2615 if (dopt->do_nothing)
2616 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2617 else
2618 archputs(";\n", fout);
2621 archputs("\n\n", fout);
2623 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2625 destroyPQExpBuffer(q);
2626 if (insertStmt != NULL)
2627 destroyPQExpBuffer(insertStmt);
2628 free(attgenerated);
2630 /* Revert back the setting */
2631 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2632 set_restrict_relation_kind(fout, "view, foreign-table");
2634 return 1;
2638 * getRootTableInfo:
2639 * get the root TableInfo for the given partition table.
2641 static TableInfo *
2642 getRootTableInfo(const TableInfo *tbinfo)
2644 TableInfo *parentTbinfo;
2646 Assert(tbinfo->ispartition);
2647 Assert(tbinfo->numParents == 1);
2649 parentTbinfo = tbinfo->parents[0];
2650 while (parentTbinfo->ispartition)
2652 Assert(parentTbinfo->numParents == 1);
2653 parentTbinfo = parentTbinfo->parents[0];
2656 return parentTbinfo;
2660 * forcePartitionRootLoad
2661 * Check if we must force load_via_partition_root for this partition.
2663 * This is required if any level of ancestral partitioned table has an
2664 * unsafe partitioning scheme.
2666 static bool
2667 forcePartitionRootLoad(const TableInfo *tbinfo)
2669 TableInfo *parentTbinfo;
2671 Assert(tbinfo->ispartition);
2672 Assert(tbinfo->numParents == 1);
2674 parentTbinfo = tbinfo->parents[0];
2675 if (parentTbinfo->unsafe_partitions)
2676 return true;
2677 while (parentTbinfo->ispartition)
2679 Assert(parentTbinfo->numParents == 1);
2680 parentTbinfo = parentTbinfo->parents[0];
2681 if (parentTbinfo->unsafe_partitions)
2682 return true;
2685 return false;
2689 * dumpTableData -
2690 * dump the contents of a single table
2692 * Actually, this just makes an ArchiveEntry for the table contents.
2694 static void
2695 dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2697 DumpOptions *dopt = fout->dopt;
2698 TableInfo *tbinfo = tdinfo->tdtable;
2699 PQExpBuffer copyBuf = createPQExpBuffer();
2700 PQExpBuffer clistBuf = createPQExpBuffer();
2701 DataDumperPtr dumpFn;
2702 char *tdDefn = NULL;
2703 char *copyStmt;
2704 const char *copyFrom;
2706 /* We had better have loaded per-column details about this table */
2707 Assert(tbinfo->interesting);
2710 * When load-via-partition-root is set or forced, get the root table name
2711 * for the partition table, so that we can reload data through the root
2712 * table. Then construct a comment to be inserted into the TOC entry's
2713 * defn field, so that such cases can be identified reliably.
2715 if (tbinfo->ispartition &&
2716 (dopt->load_via_partition_root ||
2717 forcePartitionRootLoad(tbinfo)))
2719 TableInfo *parentTbinfo;
2721 parentTbinfo = getRootTableInfo(tbinfo);
2722 copyFrom = fmtQualifiedDumpable(parentTbinfo);
2723 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2724 copyFrom);
2725 tdDefn = pg_strdup(copyBuf->data);
2727 else
2728 copyFrom = fmtQualifiedDumpable(tbinfo);
2730 if (dopt->dump_inserts == 0)
2732 /* Dump/restore using COPY */
2733 dumpFn = dumpTableData_copy;
2734 /* must use 2 steps here 'cause fmtId is nonreentrant */
2735 printfPQExpBuffer(copyBuf, "COPY %s ",
2736 copyFrom);
2737 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2738 fmtCopyColumnList(tbinfo, clistBuf));
2739 copyStmt = copyBuf->data;
2741 else
2743 /* Restore using INSERT */
2744 dumpFn = dumpTableData_insert;
2745 copyStmt = NULL;
2749 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2750 * dependency on its table as "special" and pass it to ArchiveEntry now.
2751 * See comments for BuildArchiveDependencies.
2753 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2755 TocEntry *te;
2757 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2758 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2759 .namespace = tbinfo->dobj.namespace->dobj.name,
2760 .owner = tbinfo->rolname,
2761 .description = "TABLE DATA",
2762 .section = SECTION_DATA,
2763 .createStmt = tdDefn,
2764 .copyStmt = copyStmt,
2765 .deps = &(tbinfo->dobj.dumpId),
2766 .nDeps = 1,
2767 .dumpFn = dumpFn,
2768 .dumpArg = tdinfo));
2771 * Set the TocEntry's dataLength in case we are doing a parallel dump
2772 * and want to order dump jobs by table size. We choose to measure
2773 * dataLength in table pages (including TOAST pages) during dump, so
2774 * no scaling is needed.
2776 * However, relpages is declared as "integer" in pg_class, and hence
2777 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2778 * Cast so that we get the right interpretation of table sizes
2779 * exceeding INT_MAX pages.
2781 te->dataLength = (BlockNumber) tbinfo->relpages;
2782 te->dataLength += (BlockNumber) tbinfo->toastpages;
2785 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2786 * and instead we'd better worry about integer overflow. Clamp to
2787 * INT_MAX if the correct result exceeds that.
2789 if (sizeof(te->dataLength) == 4 &&
2790 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2791 te->dataLength < 0))
2792 te->dataLength = INT_MAX;
2795 destroyPQExpBuffer(copyBuf);
2796 destroyPQExpBuffer(clistBuf);
2800 * refreshMatViewData -
2801 * load or refresh the contents of a single materialized view
2803 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2804 * statement.
2806 static void
2807 refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2809 TableInfo *tbinfo = tdinfo->tdtable;
2810 PQExpBuffer q;
2812 /* If the materialized view is not flagged as populated, skip this. */
2813 if (!tbinfo->relispopulated)
2814 return;
2816 q = createPQExpBuffer();
2818 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2819 fmtQualifiedDumpable(tbinfo));
2821 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2822 ArchiveEntry(fout,
2823 tdinfo->dobj.catId, /* catalog ID */
2824 tdinfo->dobj.dumpId, /* dump ID */
2825 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2826 .namespace = tbinfo->dobj.namespace->dobj.name,
2827 .owner = tbinfo->rolname,
2828 .description = "MATERIALIZED VIEW DATA",
2829 .section = SECTION_POST_DATA,
2830 .createStmt = q->data,
2831 .deps = tdinfo->dobj.dependencies,
2832 .nDeps = tdinfo->dobj.nDeps));
2834 destroyPQExpBuffer(q);
2838 * getTableData -
2839 * set up dumpable objects representing the contents of tables
2841 static void
2842 getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2844 int i;
2846 for (i = 0; i < numTables; i++)
2848 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2849 (!relkind || tblinfo[i].relkind == relkind))
2850 makeTableDataInfo(dopt, &(tblinfo[i]));
2855 * Make a dumpable object for the data of this specific table
2857 * Note: we make a TableDataInfo if and only if we are going to dump the
2858 * table data; the "dump" field in such objects isn't very interesting.
2860 static void
2861 makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2863 TableDataInfo *tdinfo;
2866 * Nothing to do if we already decided to dump the table. This will
2867 * happen for "config" tables.
2869 if (tbinfo->dataObj != NULL)
2870 return;
2872 /* Skip VIEWs (no data to dump) */
2873 if (tbinfo->relkind == RELKIND_VIEW)
2874 return;
2875 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2876 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2877 (foreign_servers_include_oids.head == NULL ||
2878 !simple_oid_list_member(&foreign_servers_include_oids,
2879 tbinfo->foreign_server)))
2880 return;
2881 /* Skip partitioned tables (data in partitions) */
2882 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2883 return;
2885 /* Don't dump data in unlogged tables, if so requested */
2886 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2887 dopt->no_unlogged_table_data)
2888 return;
2890 /* Check that the data is not explicitly excluded */
2891 if (simple_oid_list_member(&tabledata_exclude_oids,
2892 tbinfo->dobj.catId.oid))
2893 return;
2895 /* OK, let's dump it */
2896 tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2898 if (tbinfo->relkind == RELKIND_MATVIEW)
2899 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2900 else if (tbinfo->relkind == RELKIND_SEQUENCE)
2901 tdinfo->dobj.objType = DO_SEQUENCE_SET;
2902 else
2903 tdinfo->dobj.objType = DO_TABLE_DATA;
2906 * Note: use tableoid 0 so that this object won't be mistaken for
2907 * something that pg_depend entries apply to.
2909 tdinfo->dobj.catId.tableoid = 0;
2910 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2911 AssignDumpId(&tdinfo->dobj);
2912 tdinfo->dobj.name = tbinfo->dobj.name;
2913 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2914 tdinfo->tdtable = tbinfo;
2915 tdinfo->filtercond = NULL; /* might get set later */
2916 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2918 /* A TableDataInfo contains data, of course */
2919 tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
2921 tbinfo->dataObj = tdinfo;
2923 /* Make sure that we'll collect per-column info for this table. */
2924 tbinfo->interesting = true;
2928 * The refresh for a materialized view must be dependent on the refresh for
2929 * any materialized view that this one is dependent on.
2931 * This must be called after all the objects are created, but before they are
2932 * sorted.
2934 static void
2935 buildMatViewRefreshDependencies(Archive *fout)
2937 PQExpBuffer query;
2938 PGresult *res;
2939 int ntups,
2941 int i_classid,
2942 i_objid,
2943 i_refobjid;
2945 /* No Mat Views before 9.3. */
2946 if (fout->remoteVersion < 90300)
2947 return;
2949 query = createPQExpBuffer();
2951 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2952 "( "
2953 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2954 "FROM pg_depend d1 "
2955 "JOIN pg_class c1 ON c1.oid = d1.objid "
2956 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2957 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2958 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2959 "AND d2.objid = r1.oid "
2960 "AND d2.refobjid <> d1.objid "
2961 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2962 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2963 CppAsString2(RELKIND_VIEW) ") "
2964 "WHERE d1.classid = 'pg_class'::regclass "
2965 "UNION "
2966 "SELECT w.objid, d3.refobjid, c3.relkind "
2967 "FROM w "
2968 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2969 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2970 "AND d3.objid = r3.oid "
2971 "AND d3.refobjid <> w.refobjid "
2972 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2973 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2974 CppAsString2(RELKIND_VIEW) ") "
2975 ") "
2976 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2977 "FROM w "
2978 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2980 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2982 ntups = PQntuples(res);
2984 i_classid = PQfnumber(res, "classid");
2985 i_objid = PQfnumber(res, "objid");
2986 i_refobjid = PQfnumber(res, "refobjid");
2988 for (i = 0; i < ntups; i++)
2990 CatalogId objId;
2991 CatalogId refobjId;
2992 DumpableObject *dobj;
2993 DumpableObject *refdobj;
2994 TableInfo *tbinfo;
2995 TableInfo *reftbinfo;
2997 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2998 objId.oid = atooid(PQgetvalue(res, i, i_objid));
2999 refobjId.tableoid = objId.tableoid;
3000 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3002 dobj = findObjectByCatalogId(objId);
3003 if (dobj == NULL)
3004 continue;
3006 Assert(dobj->objType == DO_TABLE);
3007 tbinfo = (TableInfo *) dobj;
3008 Assert(tbinfo->relkind == RELKIND_MATVIEW);
3009 dobj = (DumpableObject *) tbinfo->dataObj;
3010 if (dobj == NULL)
3011 continue;
3012 Assert(dobj->objType == DO_REFRESH_MATVIEW);
3014 refdobj = findObjectByCatalogId(refobjId);
3015 if (refdobj == NULL)
3016 continue;
3018 Assert(refdobj->objType == DO_TABLE);
3019 reftbinfo = (TableInfo *) refdobj;
3020 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3021 refdobj = (DumpableObject *) reftbinfo->dataObj;
3022 if (refdobj == NULL)
3023 continue;
3024 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3026 addObjectDependency(dobj, refdobj->dumpId);
3028 if (!reftbinfo->relispopulated)
3029 tbinfo->relispopulated = false;
3032 PQclear(res);
3034 destroyPQExpBuffer(query);
3038 * getTableDataFKConstraints -
3039 * add dump-order dependencies reflecting foreign key constraints
3041 * This code is executed only in a data-only dump --- in schema+data dumps
3042 * we handle foreign key issues by not creating the FK constraints until
3043 * after the data is loaded. In a data-only dump, however, we want to
3044 * order the table data objects in such a way that a table's referenced
3045 * tables are restored first. (In the presence of circular references or
3046 * self-references this may be impossible; we'll detect and complain about
3047 * that during the dependency sorting step.)
3049 static void
3050 getTableDataFKConstraints(void)
3052 DumpableObject **dobjs;
3053 int numObjs;
3054 int i;
3056 /* Search through all the dumpable objects for FK constraints */
3057 getDumpableObjects(&dobjs, &numObjs);
3058 for (i = 0; i < numObjs; i++)
3060 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3062 ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3063 TableInfo *ftable;
3065 /* Not interesting unless both tables are to be dumped */
3066 if (cinfo->contable == NULL ||
3067 cinfo->contable->dataObj == NULL)
3068 continue;
3069 ftable = findTableByOid(cinfo->confrelid);
3070 if (ftable == NULL ||
3071 ftable->dataObj == NULL)
3072 continue;
3075 * Okay, make referencing table's TABLE_DATA object depend on the
3076 * referenced table's TABLE_DATA object.
3078 addObjectDependency(&cinfo->contable->dataObj->dobj,
3079 ftable->dataObj->dobj.dumpId);
3082 free(dobjs);
3087 * dumpDatabase:
3088 * dump the database definition
3090 static void
3091 dumpDatabase(Archive *fout)
3093 DumpOptions *dopt = fout->dopt;
3094 PQExpBuffer dbQry = createPQExpBuffer();
3095 PQExpBuffer delQry = createPQExpBuffer();
3096 PQExpBuffer creaQry = createPQExpBuffer();
3097 PQExpBuffer labelq = createPQExpBuffer();
3098 PGconn *conn = GetConnection(fout);
3099 PGresult *res;
3100 int i_tableoid,
3101 i_oid,
3102 i_datname,
3103 i_datdba,
3104 i_encoding,
3105 i_datlocprovider,
3106 i_collate,
3107 i_ctype,
3108 i_datlocale,
3109 i_daticurules,
3110 i_frozenxid,
3111 i_minmxid,
3112 i_datacl,
3113 i_acldefault,
3114 i_datistemplate,
3115 i_datconnlimit,
3116 i_datcollversion,
3117 i_tablespace;
3118 CatalogId dbCatId;
3119 DumpId dbDumpId;
3120 DumpableAcl dbdacl;
3121 const char *datname,
3122 *dba,
3123 *encoding,
3124 *datlocprovider,
3125 *collate,
3126 *ctype,
3127 *locale,
3128 *icurules,
3129 *datistemplate,
3130 *datconnlimit,
3131 *tablespace;
3132 uint32 frozenxid,
3133 minmxid;
3134 char *qdatname;
3136 pg_log_info("saving database definition");
3139 * Fetch the database-level properties for this database.
3141 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3142 "datdba, "
3143 "pg_encoding_to_char(encoding) AS encoding, "
3144 "datcollate, datctype, datfrozenxid, "
3145 "datacl, acldefault('d', datdba) AS acldefault, "
3146 "datistemplate, datconnlimit, ");
3147 if (fout->remoteVersion >= 90300)
3148 appendPQExpBufferStr(dbQry, "datminmxid, ");
3149 else
3150 appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3151 if (fout->remoteVersion >= 170000)
3152 appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3153 else if (fout->remoteVersion >= 150000)
3154 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3155 else
3156 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3157 if (fout->remoteVersion >= 160000)
3158 appendPQExpBufferStr(dbQry, "daticurules, ");
3159 else
3160 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3161 appendPQExpBufferStr(dbQry,
3162 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3163 "shobj_description(oid, 'pg_database') AS description "
3164 "FROM pg_database "
3165 "WHERE datname = current_database()");
3167 res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3169 i_tableoid = PQfnumber(res, "tableoid");
3170 i_oid = PQfnumber(res, "oid");
3171 i_datname = PQfnumber(res, "datname");
3172 i_datdba = PQfnumber(res, "datdba");
3173 i_encoding = PQfnumber(res, "encoding");
3174 i_datlocprovider = PQfnumber(res, "datlocprovider");
3175 i_collate = PQfnumber(res, "datcollate");
3176 i_ctype = PQfnumber(res, "datctype");
3177 i_datlocale = PQfnumber(res, "datlocale");
3178 i_daticurules = PQfnumber(res, "daticurules");
3179 i_frozenxid = PQfnumber(res, "datfrozenxid");
3180 i_minmxid = PQfnumber(res, "datminmxid");
3181 i_datacl = PQfnumber(res, "datacl");
3182 i_acldefault = PQfnumber(res, "acldefault");
3183 i_datistemplate = PQfnumber(res, "datistemplate");
3184 i_datconnlimit = PQfnumber(res, "datconnlimit");
3185 i_datcollversion = PQfnumber(res, "datcollversion");
3186 i_tablespace = PQfnumber(res, "tablespace");
3188 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3189 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3190 datname = PQgetvalue(res, 0, i_datname);
3191 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3192 encoding = PQgetvalue(res, 0, i_encoding);
3193 datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3194 collate = PQgetvalue(res, 0, i_collate);
3195 ctype = PQgetvalue(res, 0, i_ctype);
3196 if (!PQgetisnull(res, 0, i_datlocale))
3197 locale = PQgetvalue(res, 0, i_datlocale);
3198 else
3199 locale = NULL;
3200 if (!PQgetisnull(res, 0, i_daticurules))
3201 icurules = PQgetvalue(res, 0, i_daticurules);
3202 else
3203 icurules = NULL;
3204 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3205 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3206 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3207 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3208 datistemplate = PQgetvalue(res, 0, i_datistemplate);
3209 datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3210 tablespace = PQgetvalue(res, 0, i_tablespace);
3212 qdatname = pg_strdup(fmtId(datname));
3215 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3216 * to preserve that), as well as the encoding, locale, and tablespace
3217 * since those can't be altered later. Other DB properties are left to
3218 * the DATABASE PROPERTIES entry, so that they can be applied after
3219 * reconnecting to the target DB.
3221 * For binary upgrade, we use the FILE_COPY strategy because testing has
3222 * shown it to be faster. When the server is in binary upgrade mode, it
3223 * will also skip the checkpoints this strategy ordinarily performs.
3225 if (dopt->binary_upgrade)
3227 appendPQExpBuffer(creaQry,
3228 "CREATE DATABASE %s WITH TEMPLATE = template0 "
3229 "OID = %u STRATEGY = FILE_COPY",
3230 qdatname, dbCatId.oid);
3232 else
3234 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3235 qdatname);
3237 if (strlen(encoding) > 0)
3239 appendPQExpBufferStr(creaQry, " ENCODING = ");
3240 appendStringLiteralAH(creaQry, encoding, fout);
3243 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3244 if (datlocprovider[0] == 'b')
3245 appendPQExpBufferStr(creaQry, "builtin");
3246 else if (datlocprovider[0] == 'c')
3247 appendPQExpBufferStr(creaQry, "libc");
3248 else if (datlocprovider[0] == 'i')
3249 appendPQExpBufferStr(creaQry, "icu");
3250 else
3251 pg_fatal("unrecognized locale provider: %s",
3252 datlocprovider);
3254 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3256 appendPQExpBufferStr(creaQry, " LOCALE = ");
3257 appendStringLiteralAH(creaQry, collate, fout);
3259 else
3261 if (strlen(collate) > 0)
3263 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3264 appendStringLiteralAH(creaQry, collate, fout);
3266 if (strlen(ctype) > 0)
3268 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3269 appendStringLiteralAH(creaQry, ctype, fout);
3272 if (locale)
3274 if (datlocprovider[0] == 'b')
3275 appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3276 else
3277 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3279 appendStringLiteralAH(creaQry, locale, fout);
3282 if (icurules)
3284 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3285 appendStringLiteralAH(creaQry, icurules, fout);
3289 * For binary upgrade, carry over the collation version. For normal
3290 * dump/restore, omit the version, so that it is computed upon restore.
3292 if (dopt->binary_upgrade)
3294 if (!PQgetisnull(res, 0, i_datcollversion))
3296 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3297 appendStringLiteralAH(creaQry,
3298 PQgetvalue(res, 0, i_datcollversion),
3299 fout);
3304 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3305 * thing; the decision whether to specify a tablespace should be left till
3306 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3307 * label the DATABASE entry with the tablespace and let the normal
3308 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3309 * attention to default_tablespace, so that won't work.
3311 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3312 !dopt->outputNoTablespaces)
3313 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3314 fmtId(tablespace));
3315 appendPQExpBufferStr(creaQry, ";\n");
3317 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3318 qdatname);
3320 dbDumpId = createDumpId();
3322 ArchiveEntry(fout,
3323 dbCatId, /* catalog ID */
3324 dbDumpId, /* dump ID */
3325 ARCHIVE_OPTS(.tag = datname,
3326 .owner = dba,
3327 .description = "DATABASE",
3328 .section = SECTION_PRE_DATA,
3329 .createStmt = creaQry->data,
3330 .dropStmt = delQry->data));
3332 /* Compute correct tag for archive entry */
3333 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3335 /* Dump DB comment if any */
3338 * 8.2 and up keep comments on shared objects in a shared table, so we
3339 * cannot use the dumpComment() code used for other database objects.
3340 * Be careful that the ArchiveEntry parameters match that function.
3342 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3344 if (comment && *comment && !dopt->no_comments)
3346 resetPQExpBuffer(dbQry);
3349 * Generates warning when loaded into a differently-named
3350 * database.
3352 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3353 appendStringLiteralAH(dbQry, comment, fout);
3354 appendPQExpBufferStr(dbQry, ";\n");
3356 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3357 ARCHIVE_OPTS(.tag = labelq->data,
3358 .owner = dba,
3359 .description = "COMMENT",
3360 .section = SECTION_NONE,
3361 .createStmt = dbQry->data,
3362 .deps = &dbDumpId,
3363 .nDeps = 1));
3367 /* Dump DB security label, if enabled */
3368 if (!dopt->no_security_labels)
3370 PGresult *shres;
3371 PQExpBuffer seclabelQry;
3373 seclabelQry = createPQExpBuffer();
3375 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3376 shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3377 resetPQExpBuffer(seclabelQry);
3378 emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3379 if (seclabelQry->len > 0)
3380 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3381 ARCHIVE_OPTS(.tag = labelq->data,
3382 .owner = dba,
3383 .description = "SECURITY LABEL",
3384 .section = SECTION_NONE,
3385 .createStmt = seclabelQry->data,
3386 .deps = &dbDumpId,
3387 .nDeps = 1));
3388 destroyPQExpBuffer(seclabelQry);
3389 PQclear(shres);
3393 * Dump ACL if any. Note that we do not support initial privileges
3394 * (pg_init_privs) on databases.
3396 dbdacl.privtype = 0;
3397 dbdacl.initprivs = NULL;
3399 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3400 qdatname, NULL, NULL,
3401 NULL, dba, &dbdacl);
3404 * Now construct a DATABASE PROPERTIES archive entry to restore any
3405 * non-default database-level properties. (The reason this must be
3406 * separate is that we cannot put any additional commands into the TOC
3407 * entry that has CREATE DATABASE. pg_restore would execute such a group
3408 * in an implicit transaction block, and the backend won't allow CREATE
3409 * DATABASE in that context.)
3411 resetPQExpBuffer(creaQry);
3412 resetPQExpBuffer(delQry);
3414 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3415 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3416 qdatname, datconnlimit);
3418 if (strcmp(datistemplate, "t") == 0)
3420 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3421 qdatname);
3424 * The backend won't accept DROP DATABASE on a template database. We
3425 * can deal with that by removing the template marking before the DROP
3426 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3427 * since no such command is currently supported, fake it with a direct
3428 * UPDATE on pg_database.
3430 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3431 "SET datistemplate = false WHERE datname = ");
3432 appendStringLiteralAH(delQry, datname, fout);
3433 appendPQExpBufferStr(delQry, ";\n");
3437 * We do not restore pg_database.dathasloginevt because it is set
3438 * automatically on login event trigger creation.
3441 /* Add database-specific SET options */
3442 dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3445 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3446 * entry, too, for lack of a better place.
3448 if (dopt->binary_upgrade)
3450 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3451 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3452 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3453 "WHERE datname = ",
3454 frozenxid, minmxid);
3455 appendStringLiteralAH(creaQry, datname, fout);
3456 appendPQExpBufferStr(creaQry, ";\n");
3459 if (creaQry->len > 0)
3460 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3461 ARCHIVE_OPTS(.tag = datname,
3462 .owner = dba,
3463 .description = "DATABASE PROPERTIES",
3464 .section = SECTION_PRE_DATA,
3465 .createStmt = creaQry->data,
3466 .dropStmt = delQry->data,
3467 .deps = &dbDumpId));
3470 * pg_largeobject comes from the old system intact, so set its
3471 * relfrozenxids, relminmxids and relfilenode.
3473 if (dopt->binary_upgrade)
3475 PGresult *lo_res;
3476 PQExpBuffer loFrozenQry = createPQExpBuffer();
3477 PQExpBuffer loOutQry = createPQExpBuffer();
3478 PQExpBuffer loHorizonQry = createPQExpBuffer();
3479 int ii_relfrozenxid,
3480 ii_relfilenode,
3481 ii_oid,
3482 ii_relminmxid;
3485 * pg_largeobject
3487 if (fout->remoteVersion >= 90300)
3488 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3489 "FROM pg_catalog.pg_class\n"
3490 "WHERE oid IN (%u, %u);\n",
3491 LargeObjectRelationId, LargeObjectLOidPNIndexId);
3492 else
3493 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3494 "FROM pg_catalog.pg_class\n"
3495 "WHERE oid IN (%u, %u);\n",
3496 LargeObjectRelationId, LargeObjectLOidPNIndexId);
3498 lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3500 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3501 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3502 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3503 ii_oid = PQfnumber(lo_res, "oid");
3505 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3506 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3507 for (int i = 0; i < PQntuples(lo_res); ++i)
3509 Oid oid;
3510 RelFileNumber relfilenumber;
3512 appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3513 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3514 "WHERE oid = %u;\n",
3515 atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3516 atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3517 atooid(PQgetvalue(lo_res, i, ii_oid)));
3519 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3520 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3522 if (oid == LargeObjectRelationId)
3523 appendPQExpBuffer(loOutQry,
3524 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3525 relfilenumber);
3526 else if (oid == LargeObjectLOidPNIndexId)
3527 appendPQExpBuffer(loOutQry,
3528 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3529 relfilenumber);
3532 appendPQExpBufferStr(loOutQry,
3533 "TRUNCATE pg_catalog.pg_largeobject;\n");
3534 appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3536 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3537 ARCHIVE_OPTS(.tag = "pg_largeobject",
3538 .description = "pg_largeobject",
3539 .section = SECTION_PRE_DATA,
3540 .createStmt = loOutQry->data));
3542 PQclear(lo_res);
3544 destroyPQExpBuffer(loFrozenQry);
3545 destroyPQExpBuffer(loHorizonQry);
3546 destroyPQExpBuffer(loOutQry);
3549 PQclear(res);
3551 free(qdatname);
3552 destroyPQExpBuffer(dbQry);
3553 destroyPQExpBuffer(delQry);
3554 destroyPQExpBuffer(creaQry);
3555 destroyPQExpBuffer(labelq);
3559 * Collect any database-specific or role-and-database-specific SET options
3560 * for this database, and append them to outbuf.
3562 static void
3563 dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3564 const char *dbname, Oid dboid)
3566 PGconn *conn = GetConnection(AH);
3567 PQExpBuffer buf = createPQExpBuffer();
3568 PGresult *res;
3570 /* First collect database-specific options */
3571 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3572 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3573 dboid);
3575 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3577 for (int i = 0; i < PQntuples(res); i++)
3578 makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3579 "DATABASE", dbname, NULL, NULL,
3580 outbuf);
3582 PQclear(res);
3584 /* Now look for role-and-database-specific options */
3585 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3586 "FROM pg_db_role_setting s, pg_roles r "
3587 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3588 dboid);
3590 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3592 for (int i = 0; i < PQntuples(res); i++)
3593 makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3594 "ROLE", PQgetvalue(res, i, 0),
3595 "DATABASE", dbname,
3596 outbuf);
3598 PQclear(res);
3600 destroyPQExpBuffer(buf);
3604 * dumpEncoding: put the correct encoding into the archive
3606 static void
3607 dumpEncoding(Archive *AH)
3609 const char *encname = pg_encoding_to_char(AH->encoding);
3610 PQExpBuffer qry = createPQExpBuffer();
3612 pg_log_info("saving encoding = %s", encname);
3614 appendPQExpBufferStr(qry, "SET client_encoding = ");
3615 appendStringLiteralAH(qry, encname, AH);
3616 appendPQExpBufferStr(qry, ";\n");
3618 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3619 ARCHIVE_OPTS(.tag = "ENCODING",
3620 .description = "ENCODING",
3621 .section = SECTION_PRE_DATA,
3622 .createStmt = qry->data));
3624 destroyPQExpBuffer(qry);
3629 * dumpStdStrings: put the correct escape string behavior into the archive
3631 static void
3632 dumpStdStrings(Archive *AH)
3634 const char *stdstrings = AH->std_strings ? "on" : "off";
3635 PQExpBuffer qry = createPQExpBuffer();
3637 pg_log_info("saving \"standard_conforming_strings = %s\"",
3638 stdstrings);
3640 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3641 stdstrings);
3643 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3644 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3645 .description = "STDSTRINGS",
3646 .section = SECTION_PRE_DATA,
3647 .createStmt = qry->data));
3649 destroyPQExpBuffer(qry);
3653 * dumpSearchPath: record the active search_path in the archive
3655 static void
3656 dumpSearchPath(Archive *AH)
3658 PQExpBuffer qry = createPQExpBuffer();
3659 PQExpBuffer path = createPQExpBuffer();
3660 PGresult *res;
3661 char **schemanames = NULL;
3662 int nschemanames = 0;
3663 int i;
3666 * We use the result of current_schemas(), not the search_path GUC,
3667 * because that might contain wildcards such as "$user", which won't
3668 * necessarily have the same value during restore. Also, this way avoids
3669 * listing schemas that may appear in search_path but not actually exist,
3670 * which seems like a prudent exclusion.
3672 res = ExecuteSqlQueryForSingleRow(AH,
3673 "SELECT pg_catalog.current_schemas(false)");
3675 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3676 pg_fatal("could not parse result of current_schemas()");
3679 * We use set_config(), not a simple "SET search_path" command, because
3680 * the latter has less-clean behavior if the search path is empty. While
3681 * that's likely to get fixed at some point, it seems like a good idea to
3682 * be as backwards-compatible as possible in what we put into archives.
3684 for (i = 0; i < nschemanames; i++)
3686 if (i > 0)
3687 appendPQExpBufferStr(path, ", ");
3688 appendPQExpBufferStr(path, fmtId(schemanames[i]));
3691 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3692 appendStringLiteralAH(qry, path->data, AH);
3693 appendPQExpBufferStr(qry, ", false);\n");
3695 pg_log_info("saving \"search_path = %s\"", path->data);
3697 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3698 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3699 .description = "SEARCHPATH",
3700 .section = SECTION_PRE_DATA,
3701 .createStmt = qry->data));
3703 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3704 AH->searchpath = pg_strdup(qry->data);
3706 free(schemanames);
3707 PQclear(res);
3708 destroyPQExpBuffer(qry);
3709 destroyPQExpBuffer(path);
3714 * getLOs:
3715 * Collect schema-level data about large objects
3717 static void
3718 getLOs(Archive *fout)
3720 DumpOptions *dopt = fout->dopt;
3721 PQExpBuffer loQry = createPQExpBuffer();
3722 PGresult *res;
3723 int ntups;
3724 int i;
3725 int n;
3726 int i_oid;
3727 int i_lomowner;
3728 int i_lomacl;
3729 int i_acldefault;
3731 pg_log_info("reading large objects");
3734 * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3735 * with the same owner/ACL appear together.
3737 appendPQExpBufferStr(loQry,
3738 "SELECT oid, lomowner, lomacl, "
3739 "acldefault('L', lomowner) AS acldefault "
3740 "FROM pg_largeobject_metadata "
3741 "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3743 res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3745 i_oid = PQfnumber(res, "oid");
3746 i_lomowner = PQfnumber(res, "lomowner");
3747 i_lomacl = PQfnumber(res, "lomacl");
3748 i_acldefault = PQfnumber(res, "acldefault");
3750 ntups = PQntuples(res);
3753 * Group the blobs into suitably-sized groups that have the same owner and
3754 * ACL setting, and build a metadata and a data DumpableObject for each
3755 * group. (If we supported initprivs for blobs, we'd have to insist that
3756 * groups also share initprivs settings, since the DumpableObject only has
3757 * room for one.) i is the index of the first tuple in the current group,
3758 * and n is the number of tuples we include in the group.
3760 for (i = 0; i < ntups; i += n)
3762 Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3763 char *thisowner = PQgetvalue(res, i, i_lomowner);
3764 char *thisacl = PQgetvalue(res, i, i_lomacl);
3765 LoInfo *loinfo;
3766 DumpableObject *lodata;
3767 char namebuf[64];
3769 /* Scan to find first tuple not to be included in group */
3770 n = 1;
3771 while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3773 if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3774 strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3775 break;
3776 n++;
3779 /* Build the metadata DumpableObject */
3780 loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3782 loinfo->dobj.objType = DO_LARGE_OBJECT;
3783 loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3784 loinfo->dobj.catId.oid = thisoid;
3785 AssignDumpId(&loinfo->dobj);
3787 if (n > 1)
3788 snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3789 atooid(PQgetvalue(res, i + n - 1, i_oid)));
3790 else
3791 snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3792 loinfo->dobj.name = pg_strdup(namebuf);
3793 loinfo->dacl.acl = pg_strdup(thisacl);
3794 loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3795 loinfo->dacl.privtype = 0;
3796 loinfo->dacl.initprivs = NULL;
3797 loinfo->rolname = getRoleName(thisowner);
3798 loinfo->numlos = n;
3799 loinfo->looids[0] = thisoid;
3800 /* Collect OIDs of the remaining blobs in this group */
3801 for (int k = 1; k < n; k++)
3803 CatalogId extraID;
3805 loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3807 /* Make sure we can look up loinfo by any of the blobs' OIDs */
3808 extraID.tableoid = LargeObjectRelationId;
3809 extraID.oid = loinfo->looids[k];
3810 recordAdditionalCatalogID(extraID, &loinfo->dobj);
3813 /* LOs have data */
3814 loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3816 /* Mark whether LO group has a non-empty ACL */
3817 if (!PQgetisnull(res, i, i_lomacl))
3818 loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3821 * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3822 * as it will be copied by pg_upgrade, which simply copies the
3823 * pg_largeobject table. We *do* however dump out anything but the
3824 * data, as pg_upgrade copies just pg_largeobject, but not
3825 * pg_largeobject_metadata, after the dump is restored.
3827 if (dopt->binary_upgrade)
3828 loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3831 * Create a "BLOBS" data item for the group, too. This is just a
3832 * placeholder for sorting; it carries no data now.
3834 lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3835 lodata->objType = DO_LARGE_OBJECT_DATA;
3836 lodata->catId = nilCatalogId;
3837 AssignDumpId(lodata);
3838 lodata->name = pg_strdup(namebuf);
3839 lodata->components |= DUMP_COMPONENT_DATA;
3840 /* Set up explicit dependency from data to metadata */
3841 lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3842 lodata->dependencies[0] = loinfo->dobj.dumpId;
3843 lodata->nDeps = lodata->allocDeps = 1;
3846 PQclear(res);
3847 destroyPQExpBuffer(loQry);
3851 * dumpLO
3853 * dump the definition (metadata) of the given large object group
3855 static void
3856 dumpLO(Archive *fout, const LoInfo *loinfo)
3858 PQExpBuffer cquery = createPQExpBuffer();
3861 * The "definition" is just a newline-separated list of OIDs. We need to
3862 * put something into the dropStmt too, but it can just be a comment.
3864 for (int i = 0; i < loinfo->numlos; i++)
3865 appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3867 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3868 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3869 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3870 .owner = loinfo->rolname,
3871 .description = "BLOB METADATA",
3872 .section = SECTION_DATA,
3873 .createStmt = cquery->data,
3874 .dropStmt = "-- dummy"));
3877 * Dump per-blob comments and seclabels if any. We assume these are rare
3878 * enough that it's okay to generate retail TOC entries for them.
3880 if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3881 DUMP_COMPONENT_SECLABEL))
3883 for (int i = 0; i < loinfo->numlos; i++)
3885 CatalogId catId;
3886 char namebuf[32];
3888 /* Build identifying info for this blob */
3889 catId.tableoid = loinfo->dobj.catId.tableoid;
3890 catId.oid = loinfo->looids[i];
3891 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3893 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3894 dumpComment(fout, "LARGE OBJECT", namebuf,
3895 NULL, loinfo->rolname,
3896 catId, 0, loinfo->dobj.dumpId);
3898 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3899 dumpSecLabel(fout, "LARGE OBJECT", namebuf,
3900 NULL, loinfo->rolname,
3901 catId, 0, loinfo->dobj.dumpId);
3906 * Dump the ACLs if any (remember that all blobs in the group will have
3907 * the same ACL). If there's just one blob, dump a simple ACL entry; if
3908 * there's more, make a "LARGE OBJECTS" entry that really contains only
3909 * the ACL for the first blob. _printTocEntry() will be cued by the tag
3910 * string to emit a mutated version for each blob.
3912 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
3914 char namebuf[32];
3916 /* Build identifying info for the first blob */
3917 snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
3919 if (loinfo->numlos > 1)
3921 char tagbuf[64];
3923 snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
3924 loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
3926 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3927 "LARGE OBJECT", namebuf, NULL, NULL,
3928 tagbuf, loinfo->rolname, &loinfo->dacl);
3930 else
3932 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3933 "LARGE OBJECT", namebuf, NULL, NULL,
3934 NULL, loinfo->rolname, &loinfo->dacl);
3938 destroyPQExpBuffer(cquery);
3942 * dumpLOs:
3943 * dump the data contents of the large objects in the given group
3945 static int
3946 dumpLOs(Archive *fout, const void *arg)
3948 const LoInfo *loinfo = (const LoInfo *) arg;
3949 PGconn *conn = GetConnection(fout);
3950 char buf[LOBBUFSIZE];
3952 pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
3954 for (int i = 0; i < loinfo->numlos; i++)
3956 Oid loOid = loinfo->looids[i];
3957 int loFd;
3958 int cnt;
3960 /* Open the LO */
3961 loFd = lo_open(conn, loOid, INV_READ);
3962 if (loFd == -1)
3963 pg_fatal("could not open large object %u: %s",
3964 loOid, PQerrorMessage(conn));
3966 StartLO(fout, loOid);
3968 /* Now read it in chunks, sending data to archive */
3971 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3972 if (cnt < 0)
3973 pg_fatal("error reading large object %u: %s",
3974 loOid, PQerrorMessage(conn));
3976 WriteData(fout, buf, cnt);
3977 } while (cnt > 0);
3979 lo_close(conn, loFd);
3981 EndLO(fout, loOid);
3984 return 1;
3988 * getPolicies
3989 * get information about all RLS policies on dumpable tables.
3991 void
3992 getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3994 PQExpBuffer query;
3995 PQExpBuffer tbloids;
3996 PGresult *res;
3997 PolicyInfo *polinfo;
3998 int i_oid;
3999 int i_tableoid;
4000 int i_polrelid;
4001 int i_polname;
4002 int i_polcmd;
4003 int i_polpermissive;
4004 int i_polroles;
4005 int i_polqual;
4006 int i_polwithcheck;
4007 int i,
4009 ntups;
4011 /* No policies before 9.5 */
4012 if (fout->remoteVersion < 90500)
4013 return;
4015 query = createPQExpBuffer();
4016 tbloids = createPQExpBuffer();
4019 * Identify tables of interest, and check which ones have RLS enabled.
4021 appendPQExpBufferChar(tbloids, '{');
4022 for (i = 0; i < numTables; i++)
4024 TableInfo *tbinfo = &tblinfo[i];
4026 /* Ignore row security on tables not to be dumped */
4027 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4028 continue;
4030 /* It can't have RLS or policies if it's not a table */
4031 if (tbinfo->relkind != RELKIND_RELATION &&
4032 tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4033 continue;
4035 /* Add it to the list of table OIDs to be probed below */
4036 if (tbloids->len > 1) /* do we have more than the '{'? */
4037 appendPQExpBufferChar(tbloids, ',');
4038 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4040 /* Is RLS enabled? (That's separate from whether it has policies) */
4041 if (tbinfo->rowsec)
4043 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4046 * We represent RLS being enabled on a table by creating a
4047 * PolicyInfo object with null polname.
4049 * Note: use tableoid 0 so that this object won't be mistaken for
4050 * something that pg_depend entries apply to.
4052 polinfo = pg_malloc(sizeof(PolicyInfo));
4053 polinfo->dobj.objType = DO_POLICY;
4054 polinfo->dobj.catId.tableoid = 0;
4055 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4056 AssignDumpId(&polinfo->dobj);
4057 polinfo->dobj.namespace = tbinfo->dobj.namespace;
4058 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4059 polinfo->poltable = tbinfo;
4060 polinfo->polname = NULL;
4061 polinfo->polcmd = '\0';
4062 polinfo->polpermissive = 0;
4063 polinfo->polroles = NULL;
4064 polinfo->polqual = NULL;
4065 polinfo->polwithcheck = NULL;
4068 appendPQExpBufferChar(tbloids, '}');
4071 * Now, read all RLS policies belonging to the tables of interest, and
4072 * create PolicyInfo objects for them. (Note that we must filter the
4073 * results server-side not locally, because we dare not apply pg_get_expr
4074 * to tables we don't have lock on.)
4076 pg_log_info("reading row-level security policies");
4078 printfPQExpBuffer(query,
4079 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4080 if (fout->remoteVersion >= 100000)
4081 appendPQExpBufferStr(query, "pol.polpermissive, ");
4082 else
4083 appendPQExpBufferStr(query, "'t' as polpermissive, ");
4084 appendPQExpBuffer(query,
4085 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4086 " 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, "
4087 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4088 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4089 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4090 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4091 tbloids->data);
4093 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4095 ntups = PQntuples(res);
4096 if (ntups > 0)
4098 i_oid = PQfnumber(res, "oid");
4099 i_tableoid = PQfnumber(res, "tableoid");
4100 i_polrelid = PQfnumber(res, "polrelid");
4101 i_polname = PQfnumber(res, "polname");
4102 i_polcmd = PQfnumber(res, "polcmd");
4103 i_polpermissive = PQfnumber(res, "polpermissive");
4104 i_polroles = PQfnumber(res, "polroles");
4105 i_polqual = PQfnumber(res, "polqual");
4106 i_polwithcheck = PQfnumber(res, "polwithcheck");
4108 polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4110 for (j = 0; j < ntups; j++)
4112 Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4113 TableInfo *tbinfo = findTableByOid(polrelid);
4115 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4117 polinfo[j].dobj.objType = DO_POLICY;
4118 polinfo[j].dobj.catId.tableoid =
4119 atooid(PQgetvalue(res, j, i_tableoid));
4120 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4121 AssignDumpId(&polinfo[j].dobj);
4122 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4123 polinfo[j].poltable = tbinfo;
4124 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4125 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4127 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4128 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4130 if (PQgetisnull(res, j, i_polroles))
4131 polinfo[j].polroles = NULL;
4132 else
4133 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4135 if (PQgetisnull(res, j, i_polqual))
4136 polinfo[j].polqual = NULL;
4137 else
4138 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4140 if (PQgetisnull(res, j, i_polwithcheck))
4141 polinfo[j].polwithcheck = NULL;
4142 else
4143 polinfo[j].polwithcheck
4144 = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4148 PQclear(res);
4150 destroyPQExpBuffer(query);
4151 destroyPQExpBuffer(tbloids);
4155 * dumpPolicy
4156 * dump the definition of the given policy
4158 static void
4159 dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4161 DumpOptions *dopt = fout->dopt;
4162 TableInfo *tbinfo = polinfo->poltable;
4163 PQExpBuffer query;
4164 PQExpBuffer delqry;
4165 PQExpBuffer polprefix;
4166 char *qtabname;
4167 const char *cmd;
4168 char *tag;
4170 /* Do nothing if not dumping schema */
4171 if (!dopt->dumpSchema)
4172 return;
4175 * If polname is NULL, then this record is just indicating that ROW LEVEL
4176 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4177 * ROW LEVEL SECURITY.
4179 if (polinfo->polname == NULL)
4181 query = createPQExpBuffer();
4183 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4184 fmtQualifiedDumpable(tbinfo));
4187 * We must emit the ROW SECURITY object's dependency on its table
4188 * explicitly, because it will not match anything in pg_depend (unlike
4189 * the case for other PolicyInfo objects).
4191 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4192 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4193 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4194 .namespace = polinfo->dobj.namespace->dobj.name,
4195 .owner = tbinfo->rolname,
4196 .description = "ROW SECURITY",
4197 .section = SECTION_POST_DATA,
4198 .createStmt = query->data,
4199 .deps = &(tbinfo->dobj.dumpId),
4200 .nDeps = 1));
4202 destroyPQExpBuffer(query);
4203 return;
4206 if (polinfo->polcmd == '*')
4207 cmd = "";
4208 else if (polinfo->polcmd == 'r')
4209 cmd = " FOR SELECT";
4210 else if (polinfo->polcmd == 'a')
4211 cmd = " FOR INSERT";
4212 else if (polinfo->polcmd == 'w')
4213 cmd = " FOR UPDATE";
4214 else if (polinfo->polcmd == 'd')
4215 cmd = " FOR DELETE";
4216 else
4217 pg_fatal("unexpected policy command type: %c",
4218 polinfo->polcmd);
4220 query = createPQExpBuffer();
4221 delqry = createPQExpBuffer();
4222 polprefix = createPQExpBuffer();
4224 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4226 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4228 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4229 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4231 if (polinfo->polroles != NULL)
4232 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4234 if (polinfo->polqual != NULL)
4235 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4237 if (polinfo->polwithcheck != NULL)
4238 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4240 appendPQExpBufferStr(query, ";\n");
4242 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4243 appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4245 appendPQExpBuffer(polprefix, "POLICY %s ON",
4246 fmtId(polinfo->polname));
4248 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4250 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4251 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4252 ARCHIVE_OPTS(.tag = tag,
4253 .namespace = polinfo->dobj.namespace->dobj.name,
4254 .owner = tbinfo->rolname,
4255 .description = "POLICY",
4256 .section = SECTION_POST_DATA,
4257 .createStmt = query->data,
4258 .dropStmt = delqry->data));
4260 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4261 dumpComment(fout, polprefix->data, qtabname,
4262 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4263 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4265 free(tag);
4266 destroyPQExpBuffer(query);
4267 destroyPQExpBuffer(delqry);
4268 destroyPQExpBuffer(polprefix);
4269 free(qtabname);
4273 * getPublications
4274 * get information about publications
4276 void
4277 getPublications(Archive *fout)
4279 DumpOptions *dopt = fout->dopt;
4280 PQExpBuffer query;
4281 PGresult *res;
4282 PublicationInfo *pubinfo;
4283 int i_tableoid;
4284 int i_oid;
4285 int i_pubname;
4286 int i_pubowner;
4287 int i_puballtables;
4288 int i_pubinsert;
4289 int i_pubupdate;
4290 int i_pubdelete;
4291 int i_pubtruncate;
4292 int i_pubviaroot;
4293 int i_pubgencols;
4294 int i,
4295 ntups;
4297 if (dopt->no_publications || fout->remoteVersion < 100000)
4298 return;
4300 query = createPQExpBuffer();
4302 /* Get the publications. */
4303 appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4304 "p.pubowner, p.puballtables, p.pubinsert, "
4305 "p.pubupdate, p.pubdelete, ");
4307 if (fout->remoteVersion >= 110000)
4308 appendPQExpBufferStr(query, "p.pubtruncate, ");
4309 else
4310 appendPQExpBufferStr(query, "false AS pubtruncate, ");
4312 if (fout->remoteVersion >= 130000)
4313 appendPQExpBufferStr(query, "p.pubviaroot, ");
4314 else
4315 appendPQExpBufferStr(query, "false AS pubviaroot, ");
4317 if (fout->remoteVersion >= 180000)
4318 appendPQExpBufferStr(query, "p.pubgencols ");
4319 else
4320 appendPQExpBufferStr(query, "false AS pubgencols ");
4322 appendPQExpBufferStr(query, "FROM pg_publication p");
4324 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4326 ntups = PQntuples(res);
4328 if (ntups == 0)
4329 goto cleanup;
4331 i_tableoid = PQfnumber(res, "tableoid");
4332 i_oid = PQfnumber(res, "oid");
4333 i_pubname = PQfnumber(res, "pubname");
4334 i_pubowner = PQfnumber(res, "pubowner");
4335 i_puballtables = PQfnumber(res, "puballtables");
4336 i_pubinsert = PQfnumber(res, "pubinsert");
4337 i_pubupdate = PQfnumber(res, "pubupdate");
4338 i_pubdelete = PQfnumber(res, "pubdelete");
4339 i_pubtruncate = PQfnumber(res, "pubtruncate");
4340 i_pubviaroot = PQfnumber(res, "pubviaroot");
4341 i_pubgencols = PQfnumber(res, "pubgencols");
4343 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4345 for (i = 0; i < ntups; i++)
4347 pubinfo[i].dobj.objType = DO_PUBLICATION;
4348 pubinfo[i].dobj.catId.tableoid =
4349 atooid(PQgetvalue(res, i, i_tableoid));
4350 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4351 AssignDumpId(&pubinfo[i].dobj);
4352 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4353 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4354 pubinfo[i].puballtables =
4355 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4356 pubinfo[i].pubinsert =
4357 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4358 pubinfo[i].pubupdate =
4359 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4360 pubinfo[i].pubdelete =
4361 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4362 pubinfo[i].pubtruncate =
4363 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4364 pubinfo[i].pubviaroot =
4365 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4366 pubinfo[i].pubgencols =
4367 (strcmp(PQgetvalue(res, i, i_pubgencols), "t") == 0);
4369 /* Decide whether we want to dump it */
4370 selectDumpableObject(&(pubinfo[i].dobj), fout);
4373 cleanup:
4374 PQclear(res);
4376 destroyPQExpBuffer(query);
4380 * dumpPublication
4381 * dump the definition of the given publication
4383 static void
4384 dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4386 DumpOptions *dopt = fout->dopt;
4387 PQExpBuffer delq;
4388 PQExpBuffer query;
4389 char *qpubname;
4390 bool first = true;
4392 /* Do nothing if not dumping schema */
4393 if (!dopt->dumpSchema)
4394 return;
4396 delq = createPQExpBuffer();
4397 query = createPQExpBuffer();
4399 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4401 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4402 qpubname);
4404 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4405 qpubname);
4407 if (pubinfo->puballtables)
4408 appendPQExpBufferStr(query, " FOR ALL TABLES");
4410 appendPQExpBufferStr(query, " WITH (publish = '");
4411 if (pubinfo->pubinsert)
4413 appendPQExpBufferStr(query, "insert");
4414 first = false;
4417 if (pubinfo->pubupdate)
4419 if (!first)
4420 appendPQExpBufferStr(query, ", ");
4422 appendPQExpBufferStr(query, "update");
4423 first = false;
4426 if (pubinfo->pubdelete)
4428 if (!first)
4429 appendPQExpBufferStr(query, ", ");
4431 appendPQExpBufferStr(query, "delete");
4432 first = false;
4435 if (pubinfo->pubtruncate)
4437 if (!first)
4438 appendPQExpBufferStr(query, ", ");
4440 appendPQExpBufferStr(query, "truncate");
4441 first = false;
4444 appendPQExpBufferChar(query, '\'');
4446 if (pubinfo->pubviaroot)
4447 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4449 if (pubinfo->pubgencols)
4450 appendPQExpBufferStr(query, ", publish_generated_columns = true");
4452 appendPQExpBufferStr(query, ");\n");
4454 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4455 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4456 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4457 .owner = pubinfo->rolname,
4458 .description = "PUBLICATION",
4459 .section = SECTION_POST_DATA,
4460 .createStmt = query->data,
4461 .dropStmt = delq->data));
4463 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4464 dumpComment(fout, "PUBLICATION", qpubname,
4465 NULL, pubinfo->rolname,
4466 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4468 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4469 dumpSecLabel(fout, "PUBLICATION", qpubname,
4470 NULL, pubinfo->rolname,
4471 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4473 destroyPQExpBuffer(delq);
4474 destroyPQExpBuffer(query);
4475 free(qpubname);
4479 * getPublicationNamespaces
4480 * get information about publication membership for dumpable schemas.
4482 void
4483 getPublicationNamespaces(Archive *fout)
4485 PQExpBuffer query;
4486 PGresult *res;
4487 PublicationSchemaInfo *pubsinfo;
4488 DumpOptions *dopt = fout->dopt;
4489 int i_tableoid;
4490 int i_oid;
4491 int i_pnpubid;
4492 int i_pnnspid;
4493 int i,
4495 ntups;
4497 if (dopt->no_publications || fout->remoteVersion < 150000)
4498 return;
4500 query = createPQExpBuffer();
4502 /* Collect all publication membership info. */
4503 appendPQExpBufferStr(query,
4504 "SELECT tableoid, oid, pnpubid, pnnspid "
4505 "FROM pg_catalog.pg_publication_namespace");
4506 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4508 ntups = PQntuples(res);
4510 i_tableoid = PQfnumber(res, "tableoid");
4511 i_oid = PQfnumber(res, "oid");
4512 i_pnpubid = PQfnumber(res, "pnpubid");
4513 i_pnnspid = PQfnumber(res, "pnnspid");
4515 /* this allocation may be more than we need */
4516 pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4517 j = 0;
4519 for (i = 0; i < ntups; i++)
4521 Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4522 Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4523 PublicationInfo *pubinfo;
4524 NamespaceInfo *nspinfo;
4527 * Ignore any entries for which we aren't interested in either the
4528 * publication or the rel.
4530 pubinfo = findPublicationByOid(pnpubid);
4531 if (pubinfo == NULL)
4532 continue;
4533 nspinfo = findNamespaceByOid(pnnspid);
4534 if (nspinfo == NULL)
4535 continue;
4538 * We always dump publication namespaces unless the corresponding
4539 * namespace is excluded from the dump.
4541 if (nspinfo->dobj.dump == DUMP_COMPONENT_NONE)
4542 continue;
4544 /* OK, make a DumpableObject for this relationship */
4545 pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4546 pubsinfo[j].dobj.catId.tableoid =
4547 atooid(PQgetvalue(res, i, i_tableoid));
4548 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4549 AssignDumpId(&pubsinfo[j].dobj);
4550 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4551 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4552 pubsinfo[j].publication = pubinfo;
4553 pubsinfo[j].pubschema = nspinfo;
4555 /* Decide whether we want to dump it */
4556 selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4558 j++;
4561 PQclear(res);
4562 destroyPQExpBuffer(query);
4566 * getPublicationTables
4567 * get information about publication membership for dumpable tables.
4569 void
4570 getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4572 PQExpBuffer query;
4573 PGresult *res;
4574 PublicationRelInfo *pubrinfo;
4575 DumpOptions *dopt = fout->dopt;
4576 int i_tableoid;
4577 int i_oid;
4578 int i_prpubid;
4579 int i_prrelid;
4580 int i_prrelqual;
4581 int i_prattrs;
4582 int i,
4584 ntups;
4586 if (dopt->no_publications || fout->remoteVersion < 100000)
4587 return;
4589 query = createPQExpBuffer();
4591 /* Collect all publication membership info. */
4592 if (fout->remoteVersion >= 150000)
4593 appendPQExpBufferStr(query,
4594 "SELECT tableoid, oid, prpubid, prrelid, "
4595 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4596 "(CASE\n"
4597 " WHEN pr.prattrs IS NOT NULL THEN\n"
4598 " (SELECT array_agg(attname)\n"
4599 " FROM\n"
4600 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4601 " pg_catalog.pg_attribute\n"
4602 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4603 " ELSE NULL END) prattrs "
4604 "FROM pg_catalog.pg_publication_rel pr");
4605 else
4606 appendPQExpBufferStr(query,
4607 "SELECT tableoid, oid, prpubid, prrelid, "
4608 "NULL AS prrelqual, NULL AS prattrs "
4609 "FROM pg_catalog.pg_publication_rel");
4610 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4612 ntups = PQntuples(res);
4614 i_tableoid = PQfnumber(res, "tableoid");
4615 i_oid = PQfnumber(res, "oid");
4616 i_prpubid = PQfnumber(res, "prpubid");
4617 i_prrelid = PQfnumber(res, "prrelid");
4618 i_prrelqual = PQfnumber(res, "prrelqual");
4619 i_prattrs = PQfnumber(res, "prattrs");
4621 /* this allocation may be more than we need */
4622 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4623 j = 0;
4625 for (i = 0; i < ntups; i++)
4627 Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4628 Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4629 PublicationInfo *pubinfo;
4630 TableInfo *tbinfo;
4633 * Ignore any entries for which we aren't interested in either the
4634 * publication or the rel.
4636 pubinfo = findPublicationByOid(prpubid);
4637 if (pubinfo == NULL)
4638 continue;
4639 tbinfo = findTableByOid(prrelid);
4640 if (tbinfo == NULL)
4641 continue;
4644 * Ignore publication membership of tables whose definitions are not
4645 * to be dumped.
4647 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4648 continue;
4650 /* OK, make a DumpableObject for this relationship */
4651 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4652 pubrinfo[j].dobj.catId.tableoid =
4653 atooid(PQgetvalue(res, i, i_tableoid));
4654 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4655 AssignDumpId(&pubrinfo[j].dobj);
4656 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4657 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4658 pubrinfo[j].publication = pubinfo;
4659 pubrinfo[j].pubtable = tbinfo;
4660 if (PQgetisnull(res, i, i_prrelqual))
4661 pubrinfo[j].pubrelqual = NULL;
4662 else
4663 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4665 if (!PQgetisnull(res, i, i_prattrs))
4667 char **attnames;
4668 int nattnames;
4669 PQExpBuffer attribs;
4671 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4672 &attnames, &nattnames))
4673 pg_fatal("could not parse %s array", "prattrs");
4674 attribs = createPQExpBuffer();
4675 for (int k = 0; k < nattnames; k++)
4677 if (k > 0)
4678 appendPQExpBufferStr(attribs, ", ");
4680 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4682 pubrinfo[j].pubrattrs = attribs->data;
4684 else
4685 pubrinfo[j].pubrattrs = NULL;
4687 /* Decide whether we want to dump it */
4688 selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4690 j++;
4693 PQclear(res);
4694 destroyPQExpBuffer(query);
4698 * dumpPublicationNamespace
4699 * dump the definition of the given publication schema mapping.
4701 static void
4702 dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4704 DumpOptions *dopt = fout->dopt;
4705 NamespaceInfo *schemainfo = pubsinfo->pubschema;
4706 PublicationInfo *pubinfo = pubsinfo->publication;
4707 PQExpBuffer query;
4708 char *tag;
4710 /* Do nothing if not dumping schema */
4711 if (!dopt->dumpSchema)
4712 return;
4714 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4716 query = createPQExpBuffer();
4718 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4719 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4722 * There is no point in creating drop query as the drop is done by schema
4723 * drop.
4725 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4726 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4727 ARCHIVE_OPTS(.tag = tag,
4728 .namespace = schemainfo->dobj.name,
4729 .owner = pubinfo->rolname,
4730 .description = "PUBLICATION TABLES IN SCHEMA",
4731 .section = SECTION_POST_DATA,
4732 .createStmt = query->data));
4734 /* These objects can't currently have comments or seclabels */
4736 free(tag);
4737 destroyPQExpBuffer(query);
4741 * dumpPublicationTable
4742 * dump the definition of the given publication table mapping
4744 static void
4745 dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4747 DumpOptions *dopt = fout->dopt;
4748 PublicationInfo *pubinfo = pubrinfo->publication;
4749 TableInfo *tbinfo = pubrinfo->pubtable;
4750 PQExpBuffer query;
4751 char *tag;
4753 /* Do nothing if not dumping schema */
4754 if (!dopt->dumpSchema)
4755 return;
4757 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4759 query = createPQExpBuffer();
4761 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4762 fmtId(pubinfo->dobj.name));
4763 appendPQExpBuffer(query, " %s",
4764 fmtQualifiedDumpable(tbinfo));
4766 if (pubrinfo->pubrattrs)
4767 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4769 if (pubrinfo->pubrelqual)
4772 * It's necessary to add parentheses around the expression because
4773 * pg_get_expr won't supply the parentheses for things like WHERE
4774 * TRUE.
4776 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4778 appendPQExpBufferStr(query, ";\n");
4781 * There is no point in creating a drop query as the drop is done by table
4782 * drop. (If you think to change this, see also _printTocEntry().)
4783 * Although this object doesn't really have ownership as such, set the
4784 * owner field anyway to ensure that the command is run by the correct
4785 * role at restore time.
4787 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4788 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4789 ARCHIVE_OPTS(.tag = tag,
4790 .namespace = tbinfo->dobj.namespace->dobj.name,
4791 .owner = pubinfo->rolname,
4792 .description = "PUBLICATION TABLE",
4793 .section = SECTION_POST_DATA,
4794 .createStmt = query->data));
4796 /* These objects can't currently have comments or seclabels */
4798 free(tag);
4799 destroyPQExpBuffer(query);
4803 * Is the currently connected user a superuser?
4805 static bool
4806 is_superuser(Archive *fout)
4808 ArchiveHandle *AH = (ArchiveHandle *) fout;
4809 const char *val;
4811 val = PQparameterStatus(AH->connection, "is_superuser");
4813 if (val && strcmp(val, "on") == 0)
4814 return true;
4816 return false;
4820 * Set the given value to restrict_nonsystem_relation_kind value. Since
4821 * restrict_nonsystem_relation_kind is introduced in minor version releases,
4822 * the setting query is effective only where available.
4824 static void
4825 set_restrict_relation_kind(Archive *AH, const char *value)
4827 PQExpBuffer query = createPQExpBuffer();
4828 PGresult *res;
4830 appendPQExpBuffer(query,
4831 "SELECT set_config(name, '%s', false) "
4832 "FROM pg_settings "
4833 "WHERE name = 'restrict_nonsystem_relation_kind'",
4834 value);
4835 res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4837 PQclear(res);
4838 destroyPQExpBuffer(query);
4842 * getSubscriptions
4843 * get information about subscriptions
4845 void
4846 getSubscriptions(Archive *fout)
4848 DumpOptions *dopt = fout->dopt;
4849 PQExpBuffer query;
4850 PGresult *res;
4851 SubscriptionInfo *subinfo;
4852 int i_tableoid;
4853 int i_oid;
4854 int i_subname;
4855 int i_subowner;
4856 int i_subbinary;
4857 int i_substream;
4858 int i_subtwophasestate;
4859 int i_subdisableonerr;
4860 int i_subpasswordrequired;
4861 int i_subrunasowner;
4862 int i_subconninfo;
4863 int i_subslotname;
4864 int i_subsynccommit;
4865 int i_subpublications;
4866 int i_suborigin;
4867 int i_suboriginremotelsn;
4868 int i_subenabled;
4869 int i_subfailover;
4870 int i,
4871 ntups;
4873 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4874 return;
4876 if (!is_superuser(fout))
4878 int n;
4880 res = ExecuteSqlQuery(fout,
4881 "SELECT count(*) FROM pg_subscription "
4882 "WHERE subdbid = (SELECT oid FROM pg_database"
4883 " WHERE datname = current_database())",
4884 PGRES_TUPLES_OK);
4885 n = atoi(PQgetvalue(res, 0, 0));
4886 if (n > 0)
4887 pg_log_warning("subscriptions not dumped because current user is not a superuser");
4888 PQclear(res);
4889 return;
4892 query = createPQExpBuffer();
4894 /* Get the subscriptions in current database. */
4895 appendPQExpBufferStr(query,
4896 "SELECT s.tableoid, s.oid, s.subname,\n"
4897 " s.subowner,\n"
4898 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4899 " s.subpublications,\n");
4901 if (fout->remoteVersion >= 140000)
4902 appendPQExpBufferStr(query, " s.subbinary,\n");
4903 else
4904 appendPQExpBufferStr(query, " false AS subbinary,\n");
4906 if (fout->remoteVersion >= 140000)
4907 appendPQExpBufferStr(query, " s.substream,\n");
4908 else
4909 appendPQExpBufferStr(query, " 'f' AS substream,\n");
4911 if (fout->remoteVersion >= 150000)
4912 appendPQExpBufferStr(query,
4913 " s.subtwophasestate,\n"
4914 " s.subdisableonerr,\n");
4915 else
4916 appendPQExpBuffer(query,
4917 " '%c' AS subtwophasestate,\n"
4918 " false AS subdisableonerr,\n",
4919 LOGICALREP_TWOPHASE_STATE_DISABLED);
4921 if (fout->remoteVersion >= 160000)
4922 appendPQExpBufferStr(query,
4923 " s.subpasswordrequired,\n"
4924 " s.subrunasowner,\n"
4925 " s.suborigin,\n");
4926 else
4927 appendPQExpBuffer(query,
4928 " 't' AS subpasswordrequired,\n"
4929 " 't' AS subrunasowner,\n"
4930 " '%s' AS suborigin,\n",
4931 LOGICALREP_ORIGIN_ANY);
4933 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4934 appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
4935 " s.subenabled,\n");
4936 else
4937 appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
4938 " false AS subenabled,\n");
4940 if (fout->remoteVersion >= 170000)
4941 appendPQExpBufferStr(query,
4942 " s.subfailover\n");
4943 else
4944 appendPQExpBuffer(query,
4945 " false AS subfailover\n");
4947 appendPQExpBufferStr(query,
4948 "FROM pg_subscription s\n");
4950 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4951 appendPQExpBufferStr(query,
4952 "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
4953 " ON o.external_id = 'pg_' || s.oid::text \n");
4955 appendPQExpBufferStr(query,
4956 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
4957 " WHERE datname = current_database())");
4959 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4961 ntups = PQntuples(res);
4964 * Get subscription fields. We don't include subskiplsn in the dump as
4965 * after restoring the dump this value may no longer be relevant.
4967 i_tableoid = PQfnumber(res, "tableoid");
4968 i_oid = PQfnumber(res, "oid");
4969 i_subname = PQfnumber(res, "subname");
4970 i_subowner = PQfnumber(res, "subowner");
4971 i_subenabled = PQfnumber(res, "subenabled");
4972 i_subbinary = PQfnumber(res, "subbinary");
4973 i_substream = PQfnumber(res, "substream");
4974 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
4975 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
4976 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
4977 i_subrunasowner = PQfnumber(res, "subrunasowner");
4978 i_subfailover = PQfnumber(res, "subfailover");
4979 i_subconninfo = PQfnumber(res, "subconninfo");
4980 i_subslotname = PQfnumber(res, "subslotname");
4981 i_subsynccommit = PQfnumber(res, "subsynccommit");
4982 i_subpublications = PQfnumber(res, "subpublications");
4983 i_suborigin = PQfnumber(res, "suborigin");
4984 i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
4986 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
4988 for (i = 0; i < ntups; i++)
4990 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
4991 subinfo[i].dobj.catId.tableoid =
4992 atooid(PQgetvalue(res, i, i_tableoid));
4993 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4994 AssignDumpId(&subinfo[i].dobj);
4995 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
4996 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
4998 subinfo[i].subenabled =
4999 (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5000 subinfo[i].subbinary =
5001 (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5002 subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5003 subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5004 subinfo[i].subdisableonerr =
5005 (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5006 subinfo[i].subpasswordrequired =
5007 (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5008 subinfo[i].subrunasowner =
5009 (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5010 subinfo[i].subfailover =
5011 (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5012 subinfo[i].subconninfo =
5013 pg_strdup(PQgetvalue(res, i, i_subconninfo));
5014 if (PQgetisnull(res, i, i_subslotname))
5015 subinfo[i].subslotname = NULL;
5016 else
5017 subinfo[i].subslotname =
5018 pg_strdup(PQgetvalue(res, i, i_subslotname));
5019 subinfo[i].subsynccommit =
5020 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5021 subinfo[i].subpublications =
5022 pg_strdup(PQgetvalue(res, i, i_subpublications));
5023 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5024 if (PQgetisnull(res, i, i_suboriginremotelsn))
5025 subinfo[i].suboriginremotelsn = NULL;
5026 else
5027 subinfo[i].suboriginremotelsn =
5028 pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5030 /* Decide whether we want to dump it */
5031 selectDumpableObject(&(subinfo[i].dobj), fout);
5033 PQclear(res);
5035 destroyPQExpBuffer(query);
5039 * getSubscriptionTables
5040 * Get information about subscription membership for dumpable tables. This
5041 * will be used only in binary-upgrade mode for PG17 or later versions.
5043 void
5044 getSubscriptionTables(Archive *fout)
5046 DumpOptions *dopt = fout->dopt;
5047 SubscriptionInfo *subinfo = NULL;
5048 SubRelInfo *subrinfo;
5049 PGresult *res;
5050 int i_srsubid;
5051 int i_srrelid;
5052 int i_srsubstate;
5053 int i_srsublsn;
5054 int ntups;
5055 Oid last_srsubid = InvalidOid;
5057 if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5058 fout->remoteVersion < 170000)
5059 return;
5061 res = ExecuteSqlQuery(fout,
5062 "SELECT srsubid, srrelid, srsubstate, srsublsn "
5063 "FROM pg_catalog.pg_subscription_rel "
5064 "ORDER BY srsubid",
5065 PGRES_TUPLES_OK);
5066 ntups = PQntuples(res);
5067 if (ntups == 0)
5068 goto cleanup;
5070 /* Get pg_subscription_rel attributes */
5071 i_srsubid = PQfnumber(res, "srsubid");
5072 i_srrelid = PQfnumber(res, "srrelid");
5073 i_srsubstate = PQfnumber(res, "srsubstate");
5074 i_srsublsn = PQfnumber(res, "srsublsn");
5076 subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5077 for (int i = 0; i < ntups; i++)
5079 Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5080 Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5081 TableInfo *tblinfo;
5084 * If we switched to a new subscription, check if the subscription
5085 * exists.
5087 if (cur_srsubid != last_srsubid)
5089 subinfo = findSubscriptionByOid(cur_srsubid);
5090 if (subinfo == NULL)
5091 pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5093 last_srsubid = cur_srsubid;
5096 tblinfo = findTableByOid(relid);
5097 if (tblinfo == NULL)
5098 pg_fatal("failed sanity check, table with OID %u not found",
5099 relid);
5101 /* OK, make a DumpableObject for this relationship */
5102 subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5103 subrinfo[i].dobj.catId.tableoid = relid;
5104 subrinfo[i].dobj.catId.oid = cur_srsubid;
5105 AssignDumpId(&subrinfo[i].dobj);
5106 subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5107 subrinfo[i].tblinfo = tblinfo;
5108 subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5109 if (PQgetisnull(res, i, i_srsublsn))
5110 subrinfo[i].srsublsn = NULL;
5111 else
5112 subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5114 subrinfo[i].subinfo = subinfo;
5116 /* Decide whether we want to dump it */
5117 selectDumpableObject(&(subrinfo[i].dobj), fout);
5120 cleanup:
5121 PQclear(res);
5125 * dumpSubscriptionTable
5126 * Dump the definition of the given subscription table mapping. This will be
5127 * used only in binary-upgrade mode for PG17 or later versions.
5129 static void
5130 dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5132 DumpOptions *dopt = fout->dopt;
5133 SubscriptionInfo *subinfo = subrinfo->subinfo;
5134 PQExpBuffer query;
5135 char *tag;
5137 /* Do nothing if not dumping schema */
5138 if (!dopt->dumpSchema)
5139 return;
5141 Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5143 tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5145 query = createPQExpBuffer();
5147 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5150 * binary_upgrade_add_sub_rel_state will add the subscription relation
5151 * to pg_subscription_rel table. This will be used only in
5152 * binary-upgrade mode.
5154 appendPQExpBufferStr(query,
5155 "\n-- For binary upgrade, must preserve the subscriber table.\n");
5156 appendPQExpBufferStr(query,
5157 "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5158 appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5159 appendPQExpBuffer(query,
5160 ", %u, '%c'",
5161 subrinfo->tblinfo->dobj.catId.oid,
5162 subrinfo->srsubstate);
5164 if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5165 appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5166 else
5167 appendPQExpBuffer(query, ", NULL");
5169 appendPQExpBufferStr(query, ");\n");
5173 * There is no point in creating a drop query as the drop is done by table
5174 * drop. (If you think to change this, see also _printTocEntry().)
5175 * Although this object doesn't really have ownership as such, set the
5176 * owner field anyway to ensure that the command is run by the correct
5177 * role at restore time.
5179 if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5180 ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5181 ARCHIVE_OPTS(.tag = tag,
5182 .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5183 .owner = subinfo->rolname,
5184 .description = "SUBSCRIPTION TABLE",
5185 .section = SECTION_POST_DATA,
5186 .createStmt = query->data));
5188 /* These objects can't currently have comments or seclabels */
5190 free(tag);
5191 destroyPQExpBuffer(query);
5195 * dumpSubscription
5196 * dump the definition of the given subscription
5198 static void
5199 dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5201 DumpOptions *dopt = fout->dopt;
5202 PQExpBuffer delq;
5203 PQExpBuffer query;
5204 PQExpBuffer publications;
5205 char *qsubname;
5206 char **pubnames = NULL;
5207 int npubnames = 0;
5208 int i;
5210 /* Do nothing if not dumping schema */
5211 if (!dopt->dumpSchema)
5212 return;
5214 delq = createPQExpBuffer();
5215 query = createPQExpBuffer();
5217 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5219 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5220 qsubname);
5222 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5223 qsubname);
5224 appendStringLiteralAH(query, subinfo->subconninfo, fout);
5226 /* Build list of quoted publications and append them to query. */
5227 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5228 pg_fatal("could not parse %s array", "subpublications");
5230 publications = createPQExpBuffer();
5231 for (i = 0; i < npubnames; i++)
5233 if (i > 0)
5234 appendPQExpBufferStr(publications, ", ");
5236 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5239 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5240 if (subinfo->subslotname)
5241 appendStringLiteralAH(query, subinfo->subslotname, fout);
5242 else
5243 appendPQExpBufferStr(query, "NONE");
5245 if (subinfo->subbinary)
5246 appendPQExpBufferStr(query, ", binary = true");
5248 if (subinfo->substream == LOGICALREP_STREAM_ON)
5249 appendPQExpBufferStr(query, ", streaming = on");
5250 else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5251 appendPQExpBufferStr(query, ", streaming = parallel");
5252 else
5253 appendPQExpBufferStr(query, ", streaming = off");
5255 if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5256 appendPQExpBufferStr(query, ", two_phase = on");
5258 if (subinfo->subdisableonerr)
5259 appendPQExpBufferStr(query, ", disable_on_error = true");
5261 if (!subinfo->subpasswordrequired)
5262 appendPQExpBuffer(query, ", password_required = false");
5264 if (subinfo->subrunasowner)
5265 appendPQExpBufferStr(query, ", run_as_owner = true");
5267 if (subinfo->subfailover)
5268 appendPQExpBufferStr(query, ", failover = true");
5270 if (strcmp(subinfo->subsynccommit, "off") != 0)
5271 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5273 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5274 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5276 appendPQExpBufferStr(query, ");\n");
5279 * In binary-upgrade mode, we allow the replication to continue after the
5280 * upgrade.
5282 if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5284 if (subinfo->suboriginremotelsn)
5287 * Preserve the remote_lsn for the subscriber's replication
5288 * origin. This value is required to start the replication from
5289 * the position before the upgrade. This value will be stale if
5290 * the publisher gets upgraded before the subscriber node.
5291 * However, this shouldn't be a problem as the upgrade of the
5292 * publisher ensures that all the transactions were replicated
5293 * before upgrading it.
5295 appendPQExpBufferStr(query,
5296 "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5297 appendPQExpBufferStr(query,
5298 "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5299 appendStringLiteralAH(query, subinfo->dobj.name, fout);
5300 appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5303 if (subinfo->subenabled)
5306 * Enable the subscription to allow the replication to continue
5307 * after the upgrade.
5309 appendPQExpBufferStr(query,
5310 "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5311 appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5315 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5316 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5317 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5318 .owner = subinfo->rolname,
5319 .description = "SUBSCRIPTION",
5320 .section = SECTION_POST_DATA,
5321 .createStmt = query->data,
5322 .dropStmt = delq->data));
5324 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5325 dumpComment(fout, "SUBSCRIPTION", qsubname,
5326 NULL, subinfo->rolname,
5327 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5329 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5330 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5331 NULL, subinfo->rolname,
5332 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5334 destroyPQExpBuffer(publications);
5335 free(pubnames);
5337 destroyPQExpBuffer(delq);
5338 destroyPQExpBuffer(query);
5339 free(qsubname);
5343 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5344 * the object needs.
5346 static void
5347 append_depends_on_extension(Archive *fout,
5348 PQExpBuffer create,
5349 const DumpableObject *dobj,
5350 const char *catalog,
5351 const char *keyword,
5352 const char *objname)
5354 if (dobj->depends_on_ext)
5356 char *nm;
5357 PGresult *res;
5358 PQExpBuffer query;
5359 int ntups;
5360 int i_extname;
5361 int i;
5363 /* dodge fmtId() non-reentrancy */
5364 nm = pg_strdup(objname);
5366 query = createPQExpBuffer();
5367 appendPQExpBuffer(query,
5368 "SELECT e.extname "
5369 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5370 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5371 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5372 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5373 catalog,
5374 dobj->catId.oid);
5375 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5376 ntups = PQntuples(res);
5377 i_extname = PQfnumber(res, "extname");
5378 for (i = 0; i < ntups; i++)
5380 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5381 keyword, nm,
5382 fmtId(PQgetvalue(res, i, i_extname)));
5385 PQclear(res);
5386 destroyPQExpBuffer(query);
5387 pg_free(nm);
5391 static Oid
5392 get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5395 * If the old version didn't assign an array type, but the new version
5396 * does, we must select an unused type OID to assign. This currently only
5397 * happens for domains, when upgrading pre-v11 to v11 and up.
5399 * Note: local state here is kind of ugly, but we must have some, since we
5400 * mustn't choose the same unused OID more than once.
5402 static Oid next_possible_free_oid = FirstNormalObjectId;
5403 PGresult *res;
5404 bool is_dup;
5408 ++next_possible_free_oid;
5409 printfPQExpBuffer(upgrade_query,
5410 "SELECT EXISTS(SELECT 1 "
5411 "FROM pg_catalog.pg_type "
5412 "WHERE oid = '%u'::pg_catalog.oid);",
5413 next_possible_free_oid);
5414 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5415 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5416 PQclear(res);
5417 } while (is_dup);
5419 return next_possible_free_oid;
5422 static void
5423 binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5424 PQExpBuffer upgrade_buffer,
5425 Oid pg_type_oid,
5426 bool force_array_type,
5427 bool include_multirange_type)
5429 PQExpBuffer upgrade_query = createPQExpBuffer();
5430 PGresult *res;
5431 Oid pg_type_array_oid;
5432 Oid pg_type_multirange_oid;
5433 Oid pg_type_multirange_array_oid;
5434 TypeInfo *tinfo;
5436 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5437 appendPQExpBuffer(upgrade_buffer,
5438 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5439 pg_type_oid);
5441 tinfo = findTypeByOid(pg_type_oid);
5442 if (tinfo)
5443 pg_type_array_oid = tinfo->typarray;
5444 else
5445 pg_type_array_oid = InvalidOid;
5447 if (!OidIsValid(pg_type_array_oid) && force_array_type)
5448 pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5450 if (OidIsValid(pg_type_array_oid))
5452 appendPQExpBufferStr(upgrade_buffer,
5453 "\n-- For binary upgrade, must preserve pg_type array oid\n");
5454 appendPQExpBuffer(upgrade_buffer,
5455 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5456 pg_type_array_oid);
5460 * Pre-set the multirange type oid and its own array type oid.
5462 if (include_multirange_type)
5464 if (fout->remoteVersion >= 140000)
5466 printfPQExpBuffer(upgrade_query,
5467 "SELECT t.oid, t.typarray "
5468 "FROM pg_catalog.pg_type t "
5469 "JOIN pg_catalog.pg_range r "
5470 "ON t.oid = r.rngmultitypid "
5471 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5472 pg_type_oid);
5474 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5476 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5477 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5479 PQclear(res);
5481 else
5483 pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5484 pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5487 appendPQExpBufferStr(upgrade_buffer,
5488 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5489 appendPQExpBuffer(upgrade_buffer,
5490 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5491 pg_type_multirange_oid);
5492 appendPQExpBufferStr(upgrade_buffer,
5493 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5494 appendPQExpBuffer(upgrade_buffer,
5495 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5496 pg_type_multirange_array_oid);
5499 destroyPQExpBuffer(upgrade_query);
5502 static void
5503 binary_upgrade_set_type_oids_by_rel(Archive *fout,
5504 PQExpBuffer upgrade_buffer,
5505 const TableInfo *tbinfo)
5507 Oid pg_type_oid = tbinfo->reltype;
5509 if (OidIsValid(pg_type_oid))
5510 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5511 pg_type_oid, false, false);
5515 * bsearch() comparator for BinaryUpgradeClassOidItem
5517 static int
5518 BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5520 BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5521 BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5523 return pg_cmp_u32(v1.oid, v2.oid);
5527 * collectBinaryUpgradeClassOids
5529 * Construct a table of pg_class information required for
5530 * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5531 * lookup.
5533 static void
5534 collectBinaryUpgradeClassOids(Archive *fout)
5536 PGresult *res;
5537 const char *query;
5539 query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5540 "ct.relfilenode, i.indexrelid, cti.relfilenode "
5541 "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5542 "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5543 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5544 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5545 "ORDER BY c.oid;";
5547 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5549 nbinaryUpgradeClassOids = PQntuples(res);
5550 binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5551 pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5553 for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5555 binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5556 binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5557 binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5558 binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5559 binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5560 binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5561 binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5564 PQclear(res);
5567 static void
5568 binary_upgrade_set_pg_class_oids(Archive *fout,
5569 PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5571 BinaryUpgradeClassOidItem key = {0};
5572 BinaryUpgradeClassOidItem *entry;
5574 Assert(binaryUpgradeClassOids);
5577 * Preserve the OID and relfilenumber of the table, table's index, table's
5578 * toast table and toast table's index if any.
5580 * One complexity is that the current table definition might not require
5581 * the creation of a TOAST table, but the old database might have a TOAST
5582 * table that was created earlier, before some wide columns were dropped.
5583 * By setting the TOAST oid we force creation of the TOAST heap and index
5584 * by the new backend, so we can copy the files during binary upgrade
5585 * without worrying about this case.
5587 key.oid = pg_class_oid;
5588 entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5589 sizeof(BinaryUpgradeClassOidItem),
5590 BinaryUpgradeClassOidItemCmp);
5592 appendPQExpBufferStr(upgrade_buffer,
5593 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5595 if (entry->relkind != RELKIND_INDEX &&
5596 entry->relkind != RELKIND_PARTITIONED_INDEX)
5598 appendPQExpBuffer(upgrade_buffer,
5599 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5600 pg_class_oid);
5603 * Not every relation has storage. Also, in a pre-v12 database,
5604 * partitioned tables have a relfilenumber, which should not be
5605 * preserved when upgrading.
5607 if (RelFileNumberIsValid(entry->relfilenumber) &&
5608 entry->relkind != RELKIND_PARTITIONED_TABLE)
5609 appendPQExpBuffer(upgrade_buffer,
5610 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5611 entry->relfilenumber);
5614 * In a pre-v12 database, partitioned tables might be marked as having
5615 * toast tables, but we should ignore them if so.
5617 if (OidIsValid(entry->toast_oid) &&
5618 entry->relkind != RELKIND_PARTITIONED_TABLE)
5620 appendPQExpBuffer(upgrade_buffer,
5621 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5622 entry->toast_oid);
5623 appendPQExpBuffer(upgrade_buffer,
5624 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5625 entry->toast_relfilenumber);
5627 /* every toast table has an index */
5628 appendPQExpBuffer(upgrade_buffer,
5629 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5630 entry->toast_index_oid);
5631 appendPQExpBuffer(upgrade_buffer,
5632 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5633 entry->toast_index_relfilenumber);
5636 else
5638 /* Preserve the OID and relfilenumber of the index */
5639 appendPQExpBuffer(upgrade_buffer,
5640 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5641 pg_class_oid);
5642 appendPQExpBuffer(upgrade_buffer,
5643 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5644 entry->relfilenumber);
5647 appendPQExpBufferChar(upgrade_buffer, '\n');
5651 * If the DumpableObject is a member of an extension, add a suitable
5652 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5654 * For somewhat historical reasons, objname should already be quoted,
5655 * but not objnamespace (if any).
5657 static void
5658 binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5659 const DumpableObject *dobj,
5660 const char *objtype,
5661 const char *objname,
5662 const char *objnamespace)
5664 DumpableObject *extobj = NULL;
5665 int i;
5667 if (!dobj->ext_member)
5668 return;
5671 * Find the parent extension. We could avoid this search if we wanted to
5672 * add a link field to DumpableObject, but the space costs of that would
5673 * be considerable. We assume that member objects could only have a
5674 * direct dependency on their own extension, not any others.
5676 for (i = 0; i < dobj->nDeps; i++)
5678 extobj = findObjectByDumpId(dobj->dependencies[i]);
5679 if (extobj && extobj->objType == DO_EXTENSION)
5680 break;
5681 extobj = NULL;
5683 if (extobj == NULL)
5684 pg_fatal("could not find parent extension for %s %s",
5685 objtype, objname);
5687 appendPQExpBufferStr(upgrade_buffer,
5688 "\n-- For binary upgrade, handle extension membership the hard way\n");
5689 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5690 fmtId(extobj->name),
5691 objtype);
5692 if (objnamespace && *objnamespace)
5693 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5694 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5698 * getNamespaces:
5699 * get information about all namespaces in the system catalogs
5701 void
5702 getNamespaces(Archive *fout)
5704 PGresult *res;
5705 int ntups;
5706 int i;
5707 PQExpBuffer query;
5708 NamespaceInfo *nsinfo;
5709 int i_tableoid;
5710 int i_oid;
5711 int i_nspname;
5712 int i_nspowner;
5713 int i_nspacl;
5714 int i_acldefault;
5716 query = createPQExpBuffer();
5719 * we fetch all namespaces including system ones, so that every object we
5720 * read in can be linked to a containing namespace.
5722 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5723 "n.nspowner, "
5724 "n.nspacl, "
5725 "acldefault('n', n.nspowner) AS acldefault "
5726 "FROM pg_namespace n");
5728 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5730 ntups = PQntuples(res);
5732 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5734 i_tableoid = PQfnumber(res, "tableoid");
5735 i_oid = PQfnumber(res, "oid");
5736 i_nspname = PQfnumber(res, "nspname");
5737 i_nspowner = PQfnumber(res, "nspowner");
5738 i_nspacl = PQfnumber(res, "nspacl");
5739 i_acldefault = PQfnumber(res, "acldefault");
5741 for (i = 0; i < ntups; i++)
5743 const char *nspowner;
5745 nsinfo[i].dobj.objType = DO_NAMESPACE;
5746 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5747 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5748 AssignDumpId(&nsinfo[i].dobj);
5749 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5750 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5751 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5752 nsinfo[i].dacl.privtype = 0;
5753 nsinfo[i].dacl.initprivs = NULL;
5754 nspowner = PQgetvalue(res, i, i_nspowner);
5755 nsinfo[i].nspowner = atooid(nspowner);
5756 nsinfo[i].rolname = getRoleName(nspowner);
5758 /* Decide whether to dump this namespace */
5759 selectDumpableNamespace(&nsinfo[i], fout);
5761 /* Mark whether namespace has an ACL */
5762 if (!PQgetisnull(res, i, i_nspacl))
5763 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5766 * We ignore any pg_init_privs.initprivs entry for the public schema
5767 * and assume a predetermined default, for several reasons. First,
5768 * dropping and recreating the schema removes its pg_init_privs entry,
5769 * but an empty destination database starts with this ACL nonetheless.
5770 * Second, we support dump/reload of public schema ownership changes.
5771 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5772 * initprivs continues to reflect the initial owner. Hence,
5773 * synthesize the value that nspacl will have after the restore's
5774 * ALTER SCHEMA OWNER. Third, this makes the destination database
5775 * match the source's ACL, even if the latter was an initdb-default
5776 * ACL, which changed in v15. An upgrade pulls in changes to most
5777 * system object ACLs that the DBA had not customized. We've made the
5778 * public schema depart from that, because changing its ACL so easily
5779 * breaks applications.
5781 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5783 PQExpBuffer aclarray = createPQExpBuffer();
5784 PQExpBuffer aclitem = createPQExpBuffer();
5786 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5787 appendPQExpBufferChar(aclarray, '{');
5788 quoteAclUserName(aclitem, nsinfo[i].rolname);
5789 appendPQExpBufferStr(aclitem, "=UC/");
5790 quoteAclUserName(aclitem, nsinfo[i].rolname);
5791 appendPGArray(aclarray, aclitem->data);
5792 resetPQExpBuffer(aclitem);
5793 appendPQExpBufferStr(aclitem, "=U/");
5794 quoteAclUserName(aclitem, nsinfo[i].rolname);
5795 appendPGArray(aclarray, aclitem->data);
5796 appendPQExpBufferChar(aclarray, '}');
5798 nsinfo[i].dacl.privtype = 'i';
5799 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5800 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5802 destroyPQExpBuffer(aclarray);
5803 destroyPQExpBuffer(aclitem);
5807 PQclear(res);
5808 destroyPQExpBuffer(query);
5812 * findNamespace:
5813 * given a namespace OID, look up the info read by getNamespaces
5815 static NamespaceInfo *
5816 findNamespace(Oid nsoid)
5818 NamespaceInfo *nsinfo;
5820 nsinfo = findNamespaceByOid(nsoid);
5821 if (nsinfo == NULL)
5822 pg_fatal("schema with OID %u does not exist", nsoid);
5823 return nsinfo;
5827 * getExtensions:
5828 * read all extensions in the system catalogs and return them in the
5829 * ExtensionInfo* structure
5831 * numExtensions is set to the number of extensions read in
5833 ExtensionInfo *
5834 getExtensions(Archive *fout, int *numExtensions)
5836 DumpOptions *dopt = fout->dopt;
5837 PGresult *res;
5838 int ntups;
5839 int i;
5840 PQExpBuffer query;
5841 ExtensionInfo *extinfo = NULL;
5842 int i_tableoid;
5843 int i_oid;
5844 int i_extname;
5845 int i_nspname;
5846 int i_extrelocatable;
5847 int i_extversion;
5848 int i_extconfig;
5849 int i_extcondition;
5851 query = createPQExpBuffer();
5853 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5854 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5855 "FROM pg_extension x "
5856 "JOIN pg_namespace n ON n.oid = x.extnamespace");
5858 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5860 ntups = PQntuples(res);
5861 if (ntups == 0)
5862 goto cleanup;
5864 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5866 i_tableoid = PQfnumber(res, "tableoid");
5867 i_oid = PQfnumber(res, "oid");
5868 i_extname = PQfnumber(res, "extname");
5869 i_nspname = PQfnumber(res, "nspname");
5870 i_extrelocatable = PQfnumber(res, "extrelocatable");
5871 i_extversion = PQfnumber(res, "extversion");
5872 i_extconfig = PQfnumber(res, "extconfig");
5873 i_extcondition = PQfnumber(res, "extcondition");
5875 for (i = 0; i < ntups; i++)
5877 extinfo[i].dobj.objType = DO_EXTENSION;
5878 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5879 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5880 AssignDumpId(&extinfo[i].dobj);
5881 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5882 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5883 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5884 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5885 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5886 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5888 /* Decide whether we want to dump it */
5889 selectDumpableExtension(&(extinfo[i]), dopt);
5892 cleanup:
5893 PQclear(res);
5894 destroyPQExpBuffer(query);
5896 *numExtensions = ntups;
5898 return extinfo;
5902 * getTypes:
5903 * get information about all types in the system catalogs
5905 * NB: this must run after getFuncs() because we assume we can do
5906 * findFuncByOid().
5908 void
5909 getTypes(Archive *fout)
5911 PGresult *res;
5912 int ntups;
5913 int i;
5914 PQExpBuffer query = createPQExpBuffer();
5915 TypeInfo *tyinfo;
5916 ShellTypeInfo *stinfo;
5917 int i_tableoid;
5918 int i_oid;
5919 int i_typname;
5920 int i_typnamespace;
5921 int i_typacl;
5922 int i_acldefault;
5923 int i_typowner;
5924 int i_typelem;
5925 int i_typrelid;
5926 int i_typrelkind;
5927 int i_typtype;
5928 int i_typisdefined;
5929 int i_isarray;
5930 int i_typarray;
5933 * we include even the built-in types because those may be used as array
5934 * elements by user-defined types
5936 * we filter out the built-in types when we dump out the types
5938 * same approach for undefined (shell) types and array types
5940 * Note: as of 8.3 we can reliably detect whether a type is an
5941 * auto-generated array type by checking the element type's typarray.
5942 * (Before that the test is capable of generating false positives.) We
5943 * still check for name beginning with '_', though, so as to avoid the
5944 * cost of the subselect probe for all standard types. This would have to
5945 * be revisited if the backend ever allows renaming of array types.
5947 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
5948 "typnamespace, typacl, "
5949 "acldefault('T', typowner) AS acldefault, "
5950 "typowner, "
5951 "typelem, typrelid, typarray, "
5952 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
5953 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
5954 "typtype, typisdefined, "
5955 "typname[0] = '_' AND typelem != 0 AND "
5956 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
5957 "FROM pg_type");
5959 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5961 ntups = PQntuples(res);
5963 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
5965 i_tableoid = PQfnumber(res, "tableoid");
5966 i_oid = PQfnumber(res, "oid");
5967 i_typname = PQfnumber(res, "typname");
5968 i_typnamespace = PQfnumber(res, "typnamespace");
5969 i_typacl = PQfnumber(res, "typacl");
5970 i_acldefault = PQfnumber(res, "acldefault");
5971 i_typowner = PQfnumber(res, "typowner");
5972 i_typelem = PQfnumber(res, "typelem");
5973 i_typrelid = PQfnumber(res, "typrelid");
5974 i_typrelkind = PQfnumber(res, "typrelkind");
5975 i_typtype = PQfnumber(res, "typtype");
5976 i_typisdefined = PQfnumber(res, "typisdefined");
5977 i_isarray = PQfnumber(res, "isarray");
5978 i_typarray = PQfnumber(res, "typarray");
5980 for (i = 0; i < ntups; i++)
5982 tyinfo[i].dobj.objType = DO_TYPE;
5983 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5984 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5985 AssignDumpId(&tyinfo[i].dobj);
5986 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
5987 tyinfo[i].dobj.namespace =
5988 findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
5989 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
5990 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5991 tyinfo[i].dacl.privtype = 0;
5992 tyinfo[i].dacl.initprivs = NULL;
5993 tyinfo[i].ftypname = NULL; /* may get filled later */
5994 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
5995 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
5996 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
5997 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
5998 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
5999 tyinfo[i].shellType = NULL;
6001 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6002 tyinfo[i].isDefined = true;
6003 else
6004 tyinfo[i].isDefined = false;
6006 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6007 tyinfo[i].isArray = true;
6008 else
6009 tyinfo[i].isArray = false;
6011 tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6013 if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6014 tyinfo[i].isMultirange = true;
6015 else
6016 tyinfo[i].isMultirange = false;
6018 /* Decide whether we want to dump it */
6019 selectDumpableType(&tyinfo[i], fout);
6021 /* Mark whether type has an ACL */
6022 if (!PQgetisnull(res, i, i_typacl))
6023 tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6026 * If it's a domain, fetch info about its constraints, if any
6028 tyinfo[i].nDomChecks = 0;
6029 tyinfo[i].domChecks = NULL;
6030 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6031 tyinfo[i].typtype == TYPTYPE_DOMAIN)
6032 getDomainConstraints(fout, &(tyinfo[i]));
6035 * If it's a base type, make a DumpableObject representing a shell
6036 * definition of the type. We will need to dump that ahead of the I/O
6037 * functions for the type. Similarly, range types need a shell
6038 * definition in case they have a canonicalize function.
6040 * Note: the shell type doesn't have a catId. You might think it
6041 * should copy the base type's catId, but then it might capture the
6042 * pg_depend entries for the type, which we don't want.
6044 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6045 (tyinfo[i].typtype == TYPTYPE_BASE ||
6046 tyinfo[i].typtype == TYPTYPE_RANGE))
6048 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6049 stinfo->dobj.objType = DO_SHELL_TYPE;
6050 stinfo->dobj.catId = nilCatalogId;
6051 AssignDumpId(&stinfo->dobj);
6052 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6053 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6054 stinfo->baseType = &(tyinfo[i]);
6055 tyinfo[i].shellType = stinfo;
6058 * Initially mark the shell type as not to be dumped. We'll only
6059 * dump it if the I/O or canonicalize functions need to be dumped;
6060 * this is taken care of while sorting dependencies.
6062 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6066 PQclear(res);
6068 destroyPQExpBuffer(query);
6072 * getOperators:
6073 * get information about all operators in the system catalogs
6075 void
6076 getOperators(Archive *fout)
6078 PGresult *res;
6079 int ntups;
6080 int i;
6081 PQExpBuffer query = createPQExpBuffer();
6082 OprInfo *oprinfo;
6083 int i_tableoid;
6084 int i_oid;
6085 int i_oprname;
6086 int i_oprnamespace;
6087 int i_oprowner;
6088 int i_oprkind;
6089 int i_oprcode;
6092 * find all operators, including builtin operators; we filter out
6093 * system-defined operators at dump-out time.
6096 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6097 "oprnamespace, "
6098 "oprowner, "
6099 "oprkind, "
6100 "oprcode::oid AS oprcode "
6101 "FROM pg_operator");
6103 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6105 ntups = PQntuples(res);
6107 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6109 i_tableoid = PQfnumber(res, "tableoid");
6110 i_oid = PQfnumber(res, "oid");
6111 i_oprname = PQfnumber(res, "oprname");
6112 i_oprnamespace = PQfnumber(res, "oprnamespace");
6113 i_oprowner = PQfnumber(res, "oprowner");
6114 i_oprkind = PQfnumber(res, "oprkind");
6115 i_oprcode = PQfnumber(res, "oprcode");
6117 for (i = 0; i < ntups; i++)
6119 oprinfo[i].dobj.objType = DO_OPERATOR;
6120 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6121 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6122 AssignDumpId(&oprinfo[i].dobj);
6123 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6124 oprinfo[i].dobj.namespace =
6125 findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6126 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6127 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6128 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6130 /* Decide whether we want to dump it */
6131 selectDumpableObject(&(oprinfo[i].dobj), fout);
6134 PQclear(res);
6136 destroyPQExpBuffer(query);
6140 * getCollations:
6141 * get information about all collations in the system catalogs
6143 void
6144 getCollations(Archive *fout)
6146 PGresult *res;
6147 int ntups;
6148 int i;
6149 PQExpBuffer query;
6150 CollInfo *collinfo;
6151 int i_tableoid;
6152 int i_oid;
6153 int i_collname;
6154 int i_collnamespace;
6155 int i_collowner;
6157 query = createPQExpBuffer();
6160 * find all collations, including builtin collations; we filter out
6161 * system-defined collations at dump-out time.
6164 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6165 "collnamespace, "
6166 "collowner "
6167 "FROM pg_collation");
6169 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6171 ntups = PQntuples(res);
6173 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6175 i_tableoid = PQfnumber(res, "tableoid");
6176 i_oid = PQfnumber(res, "oid");
6177 i_collname = PQfnumber(res, "collname");
6178 i_collnamespace = PQfnumber(res, "collnamespace");
6179 i_collowner = PQfnumber(res, "collowner");
6181 for (i = 0; i < ntups; i++)
6183 collinfo[i].dobj.objType = DO_COLLATION;
6184 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6185 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6186 AssignDumpId(&collinfo[i].dobj);
6187 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6188 collinfo[i].dobj.namespace =
6189 findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6190 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6192 /* Decide whether we want to dump it */
6193 selectDumpableObject(&(collinfo[i].dobj), fout);
6196 PQclear(res);
6198 destroyPQExpBuffer(query);
6202 * getConversions:
6203 * get information about all conversions in the system catalogs
6205 void
6206 getConversions(Archive *fout)
6208 PGresult *res;
6209 int ntups;
6210 int i;
6211 PQExpBuffer query;
6212 ConvInfo *convinfo;
6213 int i_tableoid;
6214 int i_oid;
6215 int i_conname;
6216 int i_connamespace;
6217 int i_conowner;
6219 query = createPQExpBuffer();
6222 * find all conversions, including builtin conversions; we filter out
6223 * system-defined conversions at dump-out time.
6226 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6227 "connamespace, "
6228 "conowner "
6229 "FROM pg_conversion");
6231 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6233 ntups = PQntuples(res);
6235 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6237 i_tableoid = PQfnumber(res, "tableoid");
6238 i_oid = PQfnumber(res, "oid");
6239 i_conname = PQfnumber(res, "conname");
6240 i_connamespace = PQfnumber(res, "connamespace");
6241 i_conowner = PQfnumber(res, "conowner");
6243 for (i = 0; i < ntups; i++)
6245 convinfo[i].dobj.objType = DO_CONVERSION;
6246 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6247 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6248 AssignDumpId(&convinfo[i].dobj);
6249 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6250 convinfo[i].dobj.namespace =
6251 findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6252 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6254 /* Decide whether we want to dump it */
6255 selectDumpableObject(&(convinfo[i].dobj), fout);
6258 PQclear(res);
6260 destroyPQExpBuffer(query);
6264 * getAccessMethods:
6265 * get information about all user-defined access methods
6267 void
6268 getAccessMethods(Archive *fout)
6270 PGresult *res;
6271 int ntups;
6272 int i;
6273 PQExpBuffer query;
6274 AccessMethodInfo *aminfo;
6275 int i_tableoid;
6276 int i_oid;
6277 int i_amname;
6278 int i_amhandler;
6279 int i_amtype;
6281 /* Before 9.6, there are no user-defined access methods */
6282 if (fout->remoteVersion < 90600)
6283 return;
6285 query = createPQExpBuffer();
6287 /* Select all access methods from pg_am table */
6288 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6289 "amhandler::pg_catalog.regproc AS amhandler "
6290 "FROM pg_am");
6292 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6294 ntups = PQntuples(res);
6296 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6298 i_tableoid = PQfnumber(res, "tableoid");
6299 i_oid = PQfnumber(res, "oid");
6300 i_amname = PQfnumber(res, "amname");
6301 i_amhandler = PQfnumber(res, "amhandler");
6302 i_amtype = PQfnumber(res, "amtype");
6304 for (i = 0; i < ntups; i++)
6306 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6307 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6308 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6309 AssignDumpId(&aminfo[i].dobj);
6310 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6311 aminfo[i].dobj.namespace = NULL;
6312 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6313 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6315 /* Decide whether we want to dump it */
6316 selectDumpableAccessMethod(&(aminfo[i]), fout);
6319 PQclear(res);
6321 destroyPQExpBuffer(query);
6326 * getOpclasses:
6327 * get information about all opclasses in the system catalogs
6329 void
6330 getOpclasses(Archive *fout)
6332 PGresult *res;
6333 int ntups;
6334 int i;
6335 PQExpBuffer query = createPQExpBuffer();
6336 OpclassInfo *opcinfo;
6337 int i_tableoid;
6338 int i_oid;
6339 int i_opcname;
6340 int i_opcnamespace;
6341 int i_opcowner;
6344 * find all opclasses, including builtin opclasses; we filter out
6345 * system-defined opclasses at dump-out time.
6348 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6349 "opcnamespace, "
6350 "opcowner "
6351 "FROM pg_opclass");
6353 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6355 ntups = PQntuples(res);
6357 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6359 i_tableoid = PQfnumber(res, "tableoid");
6360 i_oid = PQfnumber(res, "oid");
6361 i_opcname = PQfnumber(res, "opcname");
6362 i_opcnamespace = PQfnumber(res, "opcnamespace");
6363 i_opcowner = PQfnumber(res, "opcowner");
6365 for (i = 0; i < ntups; i++)
6367 opcinfo[i].dobj.objType = DO_OPCLASS;
6368 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6369 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6370 AssignDumpId(&opcinfo[i].dobj);
6371 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6372 opcinfo[i].dobj.namespace =
6373 findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6374 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6376 /* Decide whether we want to dump it */
6377 selectDumpableObject(&(opcinfo[i].dobj), fout);
6380 PQclear(res);
6382 destroyPQExpBuffer(query);
6386 * getOpfamilies:
6387 * get information about all opfamilies in the system catalogs
6389 void
6390 getOpfamilies(Archive *fout)
6392 PGresult *res;
6393 int ntups;
6394 int i;
6395 PQExpBuffer query;
6396 OpfamilyInfo *opfinfo;
6397 int i_tableoid;
6398 int i_oid;
6399 int i_opfname;
6400 int i_opfnamespace;
6401 int i_opfowner;
6403 query = createPQExpBuffer();
6406 * find all opfamilies, including builtin opfamilies; we filter out
6407 * system-defined opfamilies at dump-out time.
6410 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6411 "opfnamespace, "
6412 "opfowner "
6413 "FROM pg_opfamily");
6415 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6417 ntups = PQntuples(res);
6419 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6421 i_tableoid = PQfnumber(res, "tableoid");
6422 i_oid = PQfnumber(res, "oid");
6423 i_opfname = PQfnumber(res, "opfname");
6424 i_opfnamespace = PQfnumber(res, "opfnamespace");
6425 i_opfowner = PQfnumber(res, "opfowner");
6427 for (i = 0; i < ntups; i++)
6429 opfinfo[i].dobj.objType = DO_OPFAMILY;
6430 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6431 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6432 AssignDumpId(&opfinfo[i].dobj);
6433 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6434 opfinfo[i].dobj.namespace =
6435 findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6436 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6438 /* Decide whether we want to dump it */
6439 selectDumpableObject(&(opfinfo[i].dobj), fout);
6442 PQclear(res);
6444 destroyPQExpBuffer(query);
6448 * getAggregates:
6449 * get information about all user-defined aggregates in the system catalogs
6451 void
6452 getAggregates(Archive *fout)
6454 DumpOptions *dopt = fout->dopt;
6455 PGresult *res;
6456 int ntups;
6457 int i;
6458 PQExpBuffer query = createPQExpBuffer();
6459 AggInfo *agginfo;
6460 int i_tableoid;
6461 int i_oid;
6462 int i_aggname;
6463 int i_aggnamespace;
6464 int i_pronargs;
6465 int i_proargtypes;
6466 int i_proowner;
6467 int i_aggacl;
6468 int i_acldefault;
6471 * Find all interesting aggregates. See comment in getFuncs() for the
6472 * rationale behind the filtering logic.
6474 if (fout->remoteVersion >= 90600)
6476 const char *agg_check;
6478 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6479 : "p.proisagg");
6481 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6482 "p.proname AS aggname, "
6483 "p.pronamespace AS aggnamespace, "
6484 "p.pronargs, p.proargtypes, "
6485 "p.proowner, "
6486 "p.proacl AS aggacl, "
6487 "acldefault('f', p.proowner) AS acldefault "
6488 "FROM pg_proc p "
6489 "LEFT JOIN pg_init_privs pip ON "
6490 "(p.oid = pip.objoid "
6491 "AND pip.classoid = 'pg_proc'::regclass "
6492 "AND pip.objsubid = 0) "
6493 "WHERE %s AND ("
6494 "p.pronamespace != "
6495 "(SELECT oid FROM pg_namespace "
6496 "WHERE nspname = 'pg_catalog') OR "
6497 "p.proacl IS DISTINCT FROM pip.initprivs",
6498 agg_check);
6499 if (dopt->binary_upgrade)
6500 appendPQExpBufferStr(query,
6501 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6502 "classid = 'pg_proc'::regclass AND "
6503 "objid = p.oid AND "
6504 "refclassid = 'pg_extension'::regclass AND "
6505 "deptype = 'e')");
6506 appendPQExpBufferChar(query, ')');
6508 else
6510 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6511 "pronamespace AS aggnamespace, "
6512 "pronargs, proargtypes, "
6513 "proowner, "
6514 "proacl AS aggacl, "
6515 "acldefault('f', proowner) AS acldefault "
6516 "FROM pg_proc p "
6517 "WHERE proisagg AND ("
6518 "pronamespace != "
6519 "(SELECT oid FROM pg_namespace "
6520 "WHERE nspname = 'pg_catalog')");
6521 if (dopt->binary_upgrade)
6522 appendPQExpBufferStr(query,
6523 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6524 "classid = 'pg_proc'::regclass AND "
6525 "objid = p.oid AND "
6526 "refclassid = 'pg_extension'::regclass AND "
6527 "deptype = 'e')");
6528 appendPQExpBufferChar(query, ')');
6531 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6533 ntups = PQntuples(res);
6535 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6537 i_tableoid = PQfnumber(res, "tableoid");
6538 i_oid = PQfnumber(res, "oid");
6539 i_aggname = PQfnumber(res, "aggname");
6540 i_aggnamespace = PQfnumber(res, "aggnamespace");
6541 i_pronargs = PQfnumber(res, "pronargs");
6542 i_proargtypes = PQfnumber(res, "proargtypes");
6543 i_proowner = PQfnumber(res, "proowner");
6544 i_aggacl = PQfnumber(res, "aggacl");
6545 i_acldefault = PQfnumber(res, "acldefault");
6547 for (i = 0; i < ntups; i++)
6549 agginfo[i].aggfn.dobj.objType = DO_AGG;
6550 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6551 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6552 AssignDumpId(&agginfo[i].aggfn.dobj);
6553 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6554 agginfo[i].aggfn.dobj.namespace =
6555 findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6556 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6557 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6558 agginfo[i].aggfn.dacl.privtype = 0;
6559 agginfo[i].aggfn.dacl.initprivs = NULL;
6560 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6561 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6562 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6563 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6564 if (agginfo[i].aggfn.nargs == 0)
6565 agginfo[i].aggfn.argtypes = NULL;
6566 else
6568 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6569 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6570 agginfo[i].aggfn.argtypes,
6571 agginfo[i].aggfn.nargs);
6573 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6575 /* Decide whether we want to dump it */
6576 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6578 /* Mark whether aggregate has an ACL */
6579 if (!PQgetisnull(res, i, i_aggacl))
6580 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6583 PQclear(res);
6585 destroyPQExpBuffer(query);
6589 * getFuncs:
6590 * get information about all user-defined functions in the system catalogs
6592 void
6593 getFuncs(Archive *fout)
6595 DumpOptions *dopt = fout->dopt;
6596 PGresult *res;
6597 int ntups;
6598 int i;
6599 PQExpBuffer query = createPQExpBuffer();
6600 FuncInfo *finfo;
6601 int i_tableoid;
6602 int i_oid;
6603 int i_proname;
6604 int i_pronamespace;
6605 int i_proowner;
6606 int i_prolang;
6607 int i_pronargs;
6608 int i_proargtypes;
6609 int i_prorettype;
6610 int i_proacl;
6611 int i_acldefault;
6614 * Find all interesting functions. This is a bit complicated:
6616 * 1. Always exclude aggregates; those are handled elsewhere.
6618 * 2. Always exclude functions that are internally dependent on something
6619 * else, since presumably those will be created as a result of creating
6620 * the something else. This currently acts only to suppress constructor
6621 * functions for range types. Note this is OK only because the
6622 * constructors don't have any dependencies the range type doesn't have;
6623 * otherwise we might not get creation ordering correct.
6625 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6626 * they're members of extensions and we are in binary-upgrade mode then
6627 * include them, since we want to dump extension members individually in
6628 * that mode. Also, if they are used by casts or transforms then we need
6629 * to gather the information about them, though they won't be dumped if
6630 * they are built-in. Also, in 9.6 and up, include functions in
6631 * pg_catalog if they have an ACL different from what's shown in
6632 * pg_init_privs (so we have to join to pg_init_privs; annoying).
6634 if (fout->remoteVersion >= 90600)
6636 const char *not_agg_check;
6638 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6639 : "NOT p.proisagg");
6641 appendPQExpBuffer(query,
6642 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6643 "p.pronargs, p.proargtypes, p.prorettype, "
6644 "p.proacl, "
6645 "acldefault('f', p.proowner) AS acldefault, "
6646 "p.pronamespace, "
6647 "p.proowner "
6648 "FROM pg_proc p "
6649 "LEFT JOIN pg_init_privs pip ON "
6650 "(p.oid = pip.objoid "
6651 "AND pip.classoid = 'pg_proc'::regclass "
6652 "AND pip.objsubid = 0) "
6653 "WHERE %s"
6654 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6655 "WHERE classid = 'pg_proc'::regclass AND "
6656 "objid = p.oid AND deptype = 'i')"
6657 "\n AND ("
6658 "\n pronamespace != "
6659 "(SELECT oid FROM pg_namespace "
6660 "WHERE nspname = 'pg_catalog')"
6661 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6662 "\n WHERE pg_cast.oid > %u "
6663 "\n AND p.oid = pg_cast.castfunc)"
6664 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6665 "\n WHERE pg_transform.oid > %u AND "
6666 "\n (p.oid = pg_transform.trffromsql"
6667 "\n OR p.oid = pg_transform.trftosql))",
6668 not_agg_check,
6669 g_last_builtin_oid,
6670 g_last_builtin_oid);
6671 if (dopt->binary_upgrade)
6672 appendPQExpBufferStr(query,
6673 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6674 "classid = 'pg_proc'::regclass AND "
6675 "objid = p.oid AND "
6676 "refclassid = 'pg_extension'::regclass AND "
6677 "deptype = 'e')");
6678 appendPQExpBufferStr(query,
6679 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6680 appendPQExpBufferChar(query, ')');
6682 else
6684 appendPQExpBuffer(query,
6685 "SELECT tableoid, oid, proname, prolang, "
6686 "pronargs, proargtypes, prorettype, proacl, "
6687 "acldefault('f', proowner) AS acldefault, "
6688 "pronamespace, "
6689 "proowner "
6690 "FROM pg_proc p "
6691 "WHERE NOT proisagg"
6692 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6693 "WHERE classid = 'pg_proc'::regclass AND "
6694 "objid = p.oid AND deptype = 'i')"
6695 "\n AND ("
6696 "\n pronamespace != "
6697 "(SELECT oid FROM pg_namespace "
6698 "WHERE nspname = 'pg_catalog')"
6699 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6700 "\n WHERE pg_cast.oid > '%u'::oid"
6701 "\n AND p.oid = pg_cast.castfunc)",
6702 g_last_builtin_oid);
6704 if (fout->remoteVersion >= 90500)
6705 appendPQExpBuffer(query,
6706 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6707 "\n WHERE pg_transform.oid > '%u'::oid"
6708 "\n AND (p.oid = pg_transform.trffromsql"
6709 "\n OR p.oid = pg_transform.trftosql))",
6710 g_last_builtin_oid);
6712 if (dopt->binary_upgrade)
6713 appendPQExpBufferStr(query,
6714 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6715 "classid = 'pg_proc'::regclass AND "
6716 "objid = p.oid AND "
6717 "refclassid = 'pg_extension'::regclass AND "
6718 "deptype = 'e')");
6719 appendPQExpBufferChar(query, ')');
6722 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6724 ntups = PQntuples(res);
6726 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6728 i_tableoid = PQfnumber(res, "tableoid");
6729 i_oid = PQfnumber(res, "oid");
6730 i_proname = PQfnumber(res, "proname");
6731 i_pronamespace = PQfnumber(res, "pronamespace");
6732 i_proowner = PQfnumber(res, "proowner");
6733 i_prolang = PQfnumber(res, "prolang");
6734 i_pronargs = PQfnumber(res, "pronargs");
6735 i_proargtypes = PQfnumber(res, "proargtypes");
6736 i_prorettype = PQfnumber(res, "prorettype");
6737 i_proacl = PQfnumber(res, "proacl");
6738 i_acldefault = PQfnumber(res, "acldefault");
6740 for (i = 0; i < ntups; i++)
6742 finfo[i].dobj.objType = DO_FUNC;
6743 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6744 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6745 AssignDumpId(&finfo[i].dobj);
6746 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6747 finfo[i].dobj.namespace =
6748 findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6749 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6750 finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6751 finfo[i].dacl.privtype = 0;
6752 finfo[i].dacl.initprivs = NULL;
6753 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6754 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6755 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6756 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6757 if (finfo[i].nargs == 0)
6758 finfo[i].argtypes = NULL;
6759 else
6761 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6762 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6763 finfo[i].argtypes, finfo[i].nargs);
6765 finfo[i].postponed_def = false; /* might get set during sort */
6767 /* Decide whether we want to dump it */
6768 selectDumpableObject(&(finfo[i].dobj), fout);
6770 /* Mark whether function has an ACL */
6771 if (!PQgetisnull(res, i, i_proacl))
6772 finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6775 PQclear(res);
6777 destroyPQExpBuffer(query);
6781 * getTables
6782 * read all the tables (no indexes) in the system catalogs,
6783 * and return them as an array of TableInfo structures
6785 * *numTables is set to the number of tables read in
6787 TableInfo *
6788 getTables(Archive *fout, int *numTables)
6790 DumpOptions *dopt = fout->dopt;
6791 PGresult *res;
6792 int ntups;
6793 int i;
6794 PQExpBuffer query = createPQExpBuffer();
6795 TableInfo *tblinfo;
6796 int i_reltableoid;
6797 int i_reloid;
6798 int i_relname;
6799 int i_relnamespace;
6800 int i_relkind;
6801 int i_reltype;
6802 int i_relowner;
6803 int i_relchecks;
6804 int i_relhasindex;
6805 int i_relhasrules;
6806 int i_relpages;
6807 int i_toastpages;
6808 int i_owning_tab;
6809 int i_owning_col;
6810 int i_reltablespace;
6811 int i_relhasoids;
6812 int i_relhastriggers;
6813 int i_relpersistence;
6814 int i_relispopulated;
6815 int i_relreplident;
6816 int i_relrowsec;
6817 int i_relforcerowsec;
6818 int i_relfrozenxid;
6819 int i_toastfrozenxid;
6820 int i_toastoid;
6821 int i_relminmxid;
6822 int i_toastminmxid;
6823 int i_reloptions;
6824 int i_checkoption;
6825 int i_toastreloptions;
6826 int i_reloftype;
6827 int i_foreignserver;
6828 int i_amname;
6829 int i_is_identity_sequence;
6830 int i_relacl;
6831 int i_acldefault;
6832 int i_ispartition;
6835 * Find all the tables and table-like objects.
6837 * We must fetch all tables in this phase because otherwise we cannot
6838 * correctly identify inherited columns, owned sequences, etc.
6840 * We include system catalogs, so that we can work if a user table is
6841 * defined to inherit from a system catalog (pretty weird, but...)
6843 * Note: in this phase we should collect only a minimal amount of
6844 * information about each table, basically just enough to decide if it is
6845 * interesting. In particular, since we do not yet have lock on any user
6846 * table, we MUST NOT invoke any server-side data collection functions
6847 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
6848 * wrong answers if any concurrent DDL is happening.
6851 appendPQExpBufferStr(query,
6852 "SELECT c.tableoid, c.oid, c.relname, "
6853 "c.relnamespace, c.relkind, c.reltype, "
6854 "c.relowner, "
6855 "c.relchecks, "
6856 "c.relhasindex, c.relhasrules, c.relpages, "
6857 "c.relhastriggers, "
6858 "c.relpersistence, "
6859 "c.reloftype, "
6860 "c.relacl, "
6861 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6862 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
6863 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
6864 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6865 "ELSE 0 END AS foreignserver, "
6866 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
6867 "tc.oid AS toid, "
6868 "tc.relpages AS toastpages, "
6869 "tc.reloptions AS toast_reloptions, "
6870 "d.refobjid AS owning_tab, "
6871 "d.refobjsubid AS owning_col, "
6872 "tsp.spcname AS reltablespace, ");
6874 if (fout->remoteVersion >= 120000)
6875 appendPQExpBufferStr(query,
6876 "false AS relhasoids, ");
6877 else
6878 appendPQExpBufferStr(query,
6879 "c.relhasoids, ");
6881 if (fout->remoteVersion >= 90300)
6882 appendPQExpBufferStr(query,
6883 "c.relispopulated, ");
6884 else
6885 appendPQExpBufferStr(query,
6886 "'t' as relispopulated, ");
6888 if (fout->remoteVersion >= 90400)
6889 appendPQExpBufferStr(query,
6890 "c.relreplident, ");
6891 else
6892 appendPQExpBufferStr(query,
6893 "'d' AS relreplident, ");
6895 if (fout->remoteVersion >= 90500)
6896 appendPQExpBufferStr(query,
6897 "c.relrowsecurity, c.relforcerowsecurity, ");
6898 else
6899 appendPQExpBufferStr(query,
6900 "false AS relrowsecurity, "
6901 "false AS relforcerowsecurity, ");
6903 if (fout->remoteVersion >= 90300)
6904 appendPQExpBufferStr(query,
6905 "c.relminmxid, tc.relminmxid AS tminmxid, ");
6906 else
6907 appendPQExpBufferStr(query,
6908 "0 AS relminmxid, 0 AS tminmxid, ");
6910 if (fout->remoteVersion >= 90300)
6911 appendPQExpBufferStr(query,
6912 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6913 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6914 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
6915 else
6916 appendPQExpBufferStr(query,
6917 "c.reloptions, NULL AS checkoption, ");
6919 if (fout->remoteVersion >= 90600)
6920 appendPQExpBufferStr(query,
6921 "am.amname, ");
6922 else
6923 appendPQExpBufferStr(query,
6924 "NULL AS amname, ");
6926 if (fout->remoteVersion >= 90600)
6927 appendPQExpBufferStr(query,
6928 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
6929 else
6930 appendPQExpBufferStr(query,
6931 "false AS is_identity_sequence, ");
6933 if (fout->remoteVersion >= 100000)
6934 appendPQExpBufferStr(query,
6935 "c.relispartition AS ispartition ");
6936 else
6937 appendPQExpBufferStr(query,
6938 "false AS ispartition ");
6941 * Left join to pg_depend to pick up dependency info linking sequences to
6942 * their owning column, if any (note this dependency is AUTO except for
6943 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
6944 * collect the spcname.
6946 appendPQExpBufferStr(query,
6947 "\nFROM pg_class c\n"
6948 "LEFT JOIN pg_depend d ON "
6949 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
6950 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
6951 "d.objsubid = 0 AND "
6952 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
6953 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
6956 * In 9.6 and up, left join to pg_am to pick up the amname.
6958 if (fout->remoteVersion >= 90600)
6959 appendPQExpBufferStr(query,
6960 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
6963 * We purposefully ignore toast OIDs for partitioned tables; the reason is
6964 * that versions 10 and 11 have them, but later versions do not, so
6965 * emitting them causes the upgrade to fail.
6967 appendPQExpBufferStr(query,
6968 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
6969 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
6970 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
6973 * Restrict to interesting relkinds (in particular, not indexes). Not all
6974 * relkinds are possible in older servers, but it's not worth the trouble
6975 * to emit a version-dependent list.
6977 * Composite-type table entries won't be dumped as such, but we have to
6978 * make a DumpableObject for them so that we can track dependencies of the
6979 * composite type (pg_depend entries for columns of the composite type
6980 * link to the pg_class entry not the pg_type entry).
6982 appendPQExpBufferStr(query,
6983 "WHERE c.relkind IN ("
6984 CppAsString2(RELKIND_RELATION) ", "
6985 CppAsString2(RELKIND_SEQUENCE) ", "
6986 CppAsString2(RELKIND_VIEW) ", "
6987 CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
6988 CppAsString2(RELKIND_MATVIEW) ", "
6989 CppAsString2(RELKIND_FOREIGN_TABLE) ", "
6990 CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
6991 "ORDER BY c.oid");
6993 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6995 ntups = PQntuples(res);
6997 *numTables = ntups;
7000 * Extract data from result and lock dumpable tables. We do the locking
7001 * before anything else, to minimize the window wherein a table could
7002 * disappear under us.
7004 * Note that we have to save info about all tables here, even when dumping
7005 * only one, because we don't yet know which tables might be inheritance
7006 * ancestors of the target table.
7008 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7010 i_reltableoid = PQfnumber(res, "tableoid");
7011 i_reloid = PQfnumber(res, "oid");
7012 i_relname = PQfnumber(res, "relname");
7013 i_relnamespace = PQfnumber(res, "relnamespace");
7014 i_relkind = PQfnumber(res, "relkind");
7015 i_reltype = PQfnumber(res, "reltype");
7016 i_relowner = PQfnumber(res, "relowner");
7017 i_relchecks = PQfnumber(res, "relchecks");
7018 i_relhasindex = PQfnumber(res, "relhasindex");
7019 i_relhasrules = PQfnumber(res, "relhasrules");
7020 i_relpages = PQfnumber(res, "relpages");
7021 i_toastpages = PQfnumber(res, "toastpages");
7022 i_owning_tab = PQfnumber(res, "owning_tab");
7023 i_owning_col = PQfnumber(res, "owning_col");
7024 i_reltablespace = PQfnumber(res, "reltablespace");
7025 i_relhasoids = PQfnumber(res, "relhasoids");
7026 i_relhastriggers = PQfnumber(res, "relhastriggers");
7027 i_relpersistence = PQfnumber(res, "relpersistence");
7028 i_relispopulated = PQfnumber(res, "relispopulated");
7029 i_relreplident = PQfnumber(res, "relreplident");
7030 i_relrowsec = PQfnumber(res, "relrowsecurity");
7031 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7032 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7033 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7034 i_toastoid = PQfnumber(res, "toid");
7035 i_relminmxid = PQfnumber(res, "relminmxid");
7036 i_toastminmxid = PQfnumber(res, "tminmxid");
7037 i_reloptions = PQfnumber(res, "reloptions");
7038 i_checkoption = PQfnumber(res, "checkoption");
7039 i_toastreloptions = PQfnumber(res, "toast_reloptions");
7040 i_reloftype = PQfnumber(res, "reloftype");
7041 i_foreignserver = PQfnumber(res, "foreignserver");
7042 i_amname = PQfnumber(res, "amname");
7043 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7044 i_relacl = PQfnumber(res, "relacl");
7045 i_acldefault = PQfnumber(res, "acldefault");
7046 i_ispartition = PQfnumber(res, "ispartition");
7048 if (dopt->lockWaitTimeout)
7051 * Arrange to fail instead of waiting forever for a table lock.
7053 * NB: this coding assumes that the only queries issued within the
7054 * following loop are LOCK TABLEs; else the timeout may be undesirably
7055 * applied to other things too.
7057 resetPQExpBuffer(query);
7058 appendPQExpBufferStr(query, "SET statement_timeout = ");
7059 appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7060 ExecuteSqlStatement(fout, query->data);
7063 resetPQExpBuffer(query);
7065 for (i = 0; i < ntups; i++)
7067 tblinfo[i].dobj.objType = DO_TABLE;
7068 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7069 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7070 AssignDumpId(&tblinfo[i].dobj);
7071 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7072 tblinfo[i].dobj.namespace =
7073 findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7074 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7075 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7076 tblinfo[i].dacl.privtype = 0;
7077 tblinfo[i].dacl.initprivs = NULL;
7078 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7079 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7080 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7081 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7082 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7083 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7084 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7085 if (PQgetisnull(res, i, i_toastpages))
7086 tblinfo[i].toastpages = 0;
7087 else
7088 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7089 if (PQgetisnull(res, i, i_owning_tab))
7091 tblinfo[i].owning_tab = InvalidOid;
7092 tblinfo[i].owning_col = 0;
7094 else
7096 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7097 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7099 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7100 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7101 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7102 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7103 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7104 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7105 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7106 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7107 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7108 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7109 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7110 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7111 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7112 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7113 if (PQgetisnull(res, i, i_checkoption))
7114 tblinfo[i].checkoption = NULL;
7115 else
7116 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7117 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7118 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7119 tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7120 if (PQgetisnull(res, i, i_amname))
7121 tblinfo[i].amname = NULL;
7122 else
7123 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7124 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7125 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7127 /* other fields were zeroed above */
7130 * Decide whether we want to dump this table.
7132 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7133 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7134 else
7135 selectDumpableTable(&tblinfo[i], fout);
7138 * Now, consider the table "interesting" if we need to dump its
7139 * definition or its data. Later on, we'll skip a lot of data
7140 * collection for uninteresting tables.
7142 * Note: the "interesting" flag will also be set by flagInhTables for
7143 * parents of interesting tables, so that we collect necessary
7144 * inheritance info even when the parents are not themselves being
7145 * dumped. This is the main reason why we need an "interesting" flag
7146 * that's separate from the components-to-dump bitmask.
7148 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7149 (DUMP_COMPONENT_DEFINITION |
7150 DUMP_COMPONENT_DATA)) != 0;
7152 tblinfo[i].dummy_view = false; /* might get set during sort */
7153 tblinfo[i].postponed_def = false; /* might get set during sort */
7155 /* Tables have data */
7156 tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7158 /* Mark whether table has an ACL */
7159 if (!PQgetisnull(res, i, i_relacl))
7160 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7161 tblinfo[i].hascolumnACLs = false; /* may get set later */
7164 * Read-lock target tables to make sure they aren't DROPPED or altered
7165 * in schema before we get around to dumping them.
7167 * Note that we don't explicitly lock parents of the target tables; we
7168 * assume our lock on the child is enough to prevent schema
7169 * alterations to parent tables.
7171 * NOTE: it'd be kinda nice to lock other relations too, not only
7172 * plain or partitioned tables, but the backend doesn't presently
7173 * allow that.
7175 * We only need to lock the table for certain components; see
7176 * pg_dump.h
7178 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7179 (tblinfo[i].relkind == RELKIND_RELATION ||
7180 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7183 * Tables are locked in batches. When dumping from a remote
7184 * server this can save a significant amount of time by reducing
7185 * the number of round trips.
7187 if (query->len == 0)
7188 appendPQExpBuffer(query, "LOCK TABLE %s",
7189 fmtQualifiedDumpable(&tblinfo[i]));
7190 else
7192 appendPQExpBuffer(query, ", %s",
7193 fmtQualifiedDumpable(&tblinfo[i]));
7195 /* Arbitrarily end a batch when query length reaches 100K. */
7196 if (query->len >= 100000)
7198 /* Lock another batch of tables. */
7199 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7200 ExecuteSqlStatement(fout, query->data);
7201 resetPQExpBuffer(query);
7207 if (query->len != 0)
7209 /* Lock the tables in the last batch. */
7210 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7211 ExecuteSqlStatement(fout, query->data);
7214 if (dopt->lockWaitTimeout)
7216 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7219 PQclear(res);
7221 destroyPQExpBuffer(query);
7223 return tblinfo;
7227 * getOwnedSeqs
7228 * identify owned sequences and mark them as dumpable if owning table is
7230 * We used to do this in getTables(), but it's better to do it after the
7231 * index used by findTableByOid() has been set up.
7233 void
7234 getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7236 int i;
7239 * Force sequences that are "owned" by table columns to be dumped whenever
7240 * their owning table is being dumped.
7242 for (i = 0; i < numTables; i++)
7244 TableInfo *seqinfo = &tblinfo[i];
7245 TableInfo *owning_tab;
7247 if (!OidIsValid(seqinfo->owning_tab))
7248 continue; /* not an owned sequence */
7250 owning_tab = findTableByOid(seqinfo->owning_tab);
7251 if (owning_tab == NULL)
7252 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7253 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7256 * For an identity sequence, dump exactly the same components for the
7257 * sequence as for the owning table. This is important because we
7258 * treat the identity sequence as an integral part of the table. For
7259 * example, there is not any DDL command that allows creation of such
7260 * a sequence independently of the table.
7262 * For other owned sequences such as serial sequences, we need to dump
7263 * the components that are being dumped for the table and any
7264 * components that the sequence is explicitly marked with.
7266 * We can't simply use the set of components which are being dumped
7267 * for the table as the table might be in an extension (and only the
7268 * non-extension components, eg: ACLs if changed, security labels, and
7269 * policies, are being dumped) while the sequence is not (and
7270 * therefore the definition and other components should also be
7271 * dumped).
7273 * If the sequence is part of the extension then it should be properly
7274 * marked by checkExtensionMembership() and this will be a no-op as
7275 * the table will be equivalently marked.
7277 if (seqinfo->is_identity_sequence)
7278 seqinfo->dobj.dump = owning_tab->dobj.dump;
7279 else
7280 seqinfo->dobj.dump |= owning_tab->dobj.dump;
7282 /* Make sure that necessary data is available if we're dumping it */
7283 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7285 seqinfo->interesting = true;
7286 owning_tab->interesting = true;
7292 * getInherits
7293 * read all the inheritance information
7294 * from the system catalogs return them in the InhInfo* structure
7296 * numInherits is set to the number of pairs read in
7298 InhInfo *
7299 getInherits(Archive *fout, int *numInherits)
7301 PGresult *res;
7302 int ntups;
7303 int i;
7304 PQExpBuffer query = createPQExpBuffer();
7305 InhInfo *inhinfo;
7307 int i_inhrelid;
7308 int i_inhparent;
7310 /* find all the inheritance information */
7311 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7313 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7315 ntups = PQntuples(res);
7317 *numInherits = ntups;
7319 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7321 i_inhrelid = PQfnumber(res, "inhrelid");
7322 i_inhparent = PQfnumber(res, "inhparent");
7324 for (i = 0; i < ntups; i++)
7326 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7327 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7330 PQclear(res);
7332 destroyPQExpBuffer(query);
7334 return inhinfo;
7338 * getPartitioningInfo
7339 * get information about partitioning
7341 * For the most part, we only collect partitioning info about tables we
7342 * intend to dump. However, this function has to consider all partitioned
7343 * tables in the database, because we need to know about parents of partitions
7344 * we are going to dump even if the parents themselves won't be dumped.
7346 * Specifically, what we need to know is whether each partitioned table
7347 * has an "unsafe" partitioning scheme that requires us to force
7348 * load-via-partition-root mode for its children. Currently the only case
7349 * for which we force that is hash partitioning on enum columns, since the
7350 * hash codes depend on enum value OIDs which won't be replicated across
7351 * dump-and-reload. There are other cases in which load-via-partition-root
7352 * might be necessary, but we expect users to cope with them.
7354 void
7355 getPartitioningInfo(Archive *fout)
7357 PQExpBuffer query;
7358 PGresult *res;
7359 int ntups;
7361 /* hash partitioning didn't exist before v11 */
7362 if (fout->remoteVersion < 110000)
7363 return;
7364 /* needn't bother if not dumping data */
7365 if (!fout->dopt->dumpData)
7366 return;
7368 query = createPQExpBuffer();
7371 * Unsafe partitioning schemes are exactly those for which hash enum_ops
7372 * appears among the partition opclasses. We needn't check partstrat.
7374 * Note that this query may well retrieve info about tables we aren't
7375 * going to dump and hence have no lock on. That's okay since we need not
7376 * invoke any unsafe server-side functions.
7378 appendPQExpBufferStr(query,
7379 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7380 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7381 "ON c.opcmethod = a.oid\n"
7382 "WHERE opcname = 'enum_ops' "
7383 "AND opcnamespace = 'pg_catalog'::regnamespace "
7384 "AND amname = 'hash') = ANY(partclass)");
7386 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7388 ntups = PQntuples(res);
7390 for (int i = 0; i < ntups; i++)
7392 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7393 TableInfo *tbinfo;
7395 tbinfo = findTableByOid(tabrelid);
7396 if (tbinfo == NULL)
7397 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7398 tabrelid);
7399 tbinfo->unsafe_partitions = true;
7402 PQclear(res);
7404 destroyPQExpBuffer(query);
7408 * getIndexes
7409 * get information about every index on a dumpable table
7411 * Note: index data is not returned directly to the caller, but it
7412 * does get entered into the DumpableObject tables.
7414 void
7415 getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7417 PQExpBuffer query = createPQExpBuffer();
7418 PQExpBuffer tbloids = createPQExpBuffer();
7419 PGresult *res;
7420 int ntups;
7421 int curtblindx;
7422 IndxInfo *indxinfo;
7423 int i_tableoid,
7424 i_oid,
7425 i_indrelid,
7426 i_indexname,
7427 i_parentidx,
7428 i_indexdef,
7429 i_indnkeyatts,
7430 i_indnatts,
7431 i_indkey,
7432 i_indisclustered,
7433 i_indisreplident,
7434 i_indnullsnotdistinct,
7435 i_contype,
7436 i_conname,
7437 i_condeferrable,
7438 i_condeferred,
7439 i_conperiod,
7440 i_contableoid,
7441 i_conoid,
7442 i_condef,
7443 i_tablespace,
7444 i_indreloptions,
7445 i_indstatcols,
7446 i_indstatvals;
7449 * We want to perform just one query against pg_index. However, we
7450 * mustn't try to select every row of the catalog and then sort it out on
7451 * the client side, because some of the server-side functions we need
7452 * would be unsafe to apply to tables we don't have lock on. Hence, we
7453 * build an array of the OIDs of tables we care about (and now have lock
7454 * on!), and use a WHERE clause to constrain which rows are selected.
7456 appendPQExpBufferChar(tbloids, '{');
7457 for (int i = 0; i < numTables; i++)
7459 TableInfo *tbinfo = &tblinfo[i];
7461 if (!tbinfo->hasindex)
7462 continue;
7465 * We can ignore indexes of uninteresting tables.
7467 if (!tbinfo->interesting)
7468 continue;
7470 /* OK, we need info for this table */
7471 if (tbloids->len > 1) /* do we have more than the '{'? */
7472 appendPQExpBufferChar(tbloids, ',');
7473 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7475 appendPQExpBufferChar(tbloids, '}');
7477 appendPQExpBufferStr(query,
7478 "SELECT t.tableoid, t.oid, i.indrelid, "
7479 "t.relname AS indexname, "
7480 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7481 "i.indkey, i.indisclustered, "
7482 "c.contype, c.conname, "
7483 "c.condeferrable, c.condeferred, "
7484 "c.tableoid AS contableoid, "
7485 "c.oid AS conoid, "
7486 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7487 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7488 "t.reloptions AS indreloptions, ");
7491 if (fout->remoteVersion >= 90400)
7492 appendPQExpBufferStr(query,
7493 "i.indisreplident, ");
7494 else
7495 appendPQExpBufferStr(query,
7496 "false AS indisreplident, ");
7498 if (fout->remoteVersion >= 110000)
7499 appendPQExpBufferStr(query,
7500 "inh.inhparent AS parentidx, "
7501 "i.indnkeyatts AS indnkeyatts, "
7502 "i.indnatts AS indnatts, "
7503 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7504 " FROM pg_catalog.pg_attribute "
7505 " WHERE attrelid = i.indexrelid AND "
7506 " attstattarget >= 0) AS indstatcols, "
7507 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7508 " FROM pg_catalog.pg_attribute "
7509 " WHERE attrelid = i.indexrelid AND "
7510 " attstattarget >= 0) AS indstatvals, ");
7511 else
7512 appendPQExpBufferStr(query,
7513 "0 AS parentidx, "
7514 "i.indnatts AS indnkeyatts, "
7515 "i.indnatts AS indnatts, "
7516 "'' AS indstatcols, "
7517 "'' AS indstatvals, ");
7519 if (fout->remoteVersion >= 150000)
7520 appendPQExpBufferStr(query,
7521 "i.indnullsnotdistinct, ");
7522 else
7523 appendPQExpBufferStr(query,
7524 "false AS indnullsnotdistinct, ");
7526 if (fout->remoteVersion >= 180000)
7527 appendPQExpBufferStr(query,
7528 "c.conperiod ");
7529 else
7530 appendPQExpBufferStr(query,
7531 "NULL AS conperiod ");
7534 * The point of the messy-looking outer join is to find a constraint that
7535 * is related by an internal dependency link to the index. If we find one,
7536 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7537 * index won't have more than one internal dependency.
7539 * Note: the check on conrelid is redundant, but useful because that
7540 * column is indexed while conindid is not.
7542 if (fout->remoteVersion >= 110000)
7544 appendPQExpBuffer(query,
7545 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7546 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7547 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7548 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7549 "LEFT JOIN pg_catalog.pg_constraint c "
7550 "ON (i.indrelid = c.conrelid AND "
7551 "i.indexrelid = c.conindid AND "
7552 "c.contype IN ('p','u','x')) "
7553 "LEFT JOIN pg_catalog.pg_inherits inh "
7554 "ON (inh.inhrelid = indexrelid) "
7555 "WHERE (i.indisvalid OR t2.relkind = 'p') "
7556 "AND i.indisready "
7557 "ORDER BY i.indrelid, indexname",
7558 tbloids->data);
7560 else
7563 * the test on indisready is necessary in 9.2, and harmless in
7564 * earlier/later versions
7566 appendPQExpBuffer(query,
7567 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7568 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7569 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7570 "LEFT JOIN pg_catalog.pg_constraint c "
7571 "ON (i.indrelid = c.conrelid AND "
7572 "i.indexrelid = c.conindid AND "
7573 "c.contype IN ('p','u','x')) "
7574 "WHERE i.indisvalid AND i.indisready "
7575 "ORDER BY i.indrelid, indexname",
7576 tbloids->data);
7579 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7581 ntups = PQntuples(res);
7583 i_tableoid = PQfnumber(res, "tableoid");
7584 i_oid = PQfnumber(res, "oid");
7585 i_indrelid = PQfnumber(res, "indrelid");
7586 i_indexname = PQfnumber(res, "indexname");
7587 i_parentidx = PQfnumber(res, "parentidx");
7588 i_indexdef = PQfnumber(res, "indexdef");
7589 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7590 i_indnatts = PQfnumber(res, "indnatts");
7591 i_indkey = PQfnumber(res, "indkey");
7592 i_indisclustered = PQfnumber(res, "indisclustered");
7593 i_indisreplident = PQfnumber(res, "indisreplident");
7594 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7595 i_contype = PQfnumber(res, "contype");
7596 i_conname = PQfnumber(res, "conname");
7597 i_condeferrable = PQfnumber(res, "condeferrable");
7598 i_condeferred = PQfnumber(res, "condeferred");
7599 i_conperiod = PQfnumber(res, "conperiod");
7600 i_contableoid = PQfnumber(res, "contableoid");
7601 i_conoid = PQfnumber(res, "conoid");
7602 i_condef = PQfnumber(res, "condef");
7603 i_tablespace = PQfnumber(res, "tablespace");
7604 i_indreloptions = PQfnumber(res, "indreloptions");
7605 i_indstatcols = PQfnumber(res, "indstatcols");
7606 i_indstatvals = PQfnumber(res, "indstatvals");
7608 indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7611 * Outer loop iterates once per table, not once per row. Incrementing of
7612 * j is handled by the inner loop.
7614 curtblindx = -1;
7615 for (int j = 0; j < ntups;)
7617 Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7618 TableInfo *tbinfo = NULL;
7619 int numinds;
7621 /* Count rows for this table */
7622 for (numinds = 1; numinds < ntups - j; numinds++)
7623 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7624 break;
7627 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7628 * order.
7630 while (++curtblindx < numTables)
7632 tbinfo = &tblinfo[curtblindx];
7633 if (tbinfo->dobj.catId.oid == indrelid)
7634 break;
7636 if (curtblindx >= numTables)
7637 pg_fatal("unrecognized table OID %u", indrelid);
7638 /* cross-check that we only got requested tables */
7639 if (!tbinfo->hasindex ||
7640 !tbinfo->interesting)
7641 pg_fatal("unexpected index data for table \"%s\"",
7642 tbinfo->dobj.name);
7644 /* Save data for this table */
7645 tbinfo->indexes = indxinfo + j;
7646 tbinfo->numIndexes = numinds;
7648 for (int c = 0; c < numinds; c++, j++)
7650 char contype;
7652 indxinfo[j].dobj.objType = DO_INDEX;
7653 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7654 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7655 AssignDumpId(&indxinfo[j].dobj);
7656 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7657 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7658 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7659 indxinfo[j].indextable = tbinfo;
7660 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7661 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7662 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7663 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7664 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7665 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7666 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7667 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7668 parseOidArray(PQgetvalue(res, j, i_indkey),
7669 indxinfo[j].indkeys, indxinfo[j].indnattrs);
7670 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7671 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7672 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7673 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7674 indxinfo[j].partattaches = (SimplePtrList)
7676 NULL, NULL
7678 contype = *(PQgetvalue(res, j, i_contype));
7680 if (contype == 'p' || contype == 'u' || contype == 'x')
7683 * If we found a constraint matching the index, create an
7684 * entry for it.
7686 ConstraintInfo *constrinfo;
7688 constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7689 constrinfo->dobj.objType = DO_CONSTRAINT;
7690 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7691 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7692 AssignDumpId(&constrinfo->dobj);
7693 constrinfo->dobj.dump = tbinfo->dobj.dump;
7694 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7695 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7696 constrinfo->contable = tbinfo;
7697 constrinfo->condomain = NULL;
7698 constrinfo->contype = contype;
7699 if (contype == 'x')
7700 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7701 else
7702 constrinfo->condef = NULL;
7703 constrinfo->confrelid = InvalidOid;
7704 constrinfo->conindex = indxinfo[j].dobj.dumpId;
7705 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7706 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7707 constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7708 constrinfo->conislocal = true;
7709 constrinfo->separate = true;
7711 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7713 else
7715 /* Plain secondary index */
7716 indxinfo[j].indexconstraint = 0;
7721 PQclear(res);
7723 destroyPQExpBuffer(query);
7724 destroyPQExpBuffer(tbloids);
7728 * getExtendedStatistics
7729 * get information about extended-statistics objects.
7731 * Note: extended statistics data is not returned directly to the caller, but
7732 * it does get entered into the DumpableObject tables.
7734 void
7735 getExtendedStatistics(Archive *fout)
7737 PQExpBuffer query;
7738 PGresult *res;
7739 StatsExtInfo *statsextinfo;
7740 int ntups;
7741 int i_tableoid;
7742 int i_oid;
7743 int i_stxname;
7744 int i_stxnamespace;
7745 int i_stxowner;
7746 int i_stxrelid;
7747 int i_stattarget;
7748 int i;
7750 /* Extended statistics were new in v10 */
7751 if (fout->remoteVersion < 100000)
7752 return;
7754 query = createPQExpBuffer();
7756 if (fout->remoteVersion < 130000)
7757 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7758 "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
7759 "FROM pg_catalog.pg_statistic_ext");
7760 else
7761 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7762 "stxnamespace, stxowner, stxrelid, stxstattarget "
7763 "FROM pg_catalog.pg_statistic_ext");
7765 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7767 ntups = PQntuples(res);
7769 i_tableoid = PQfnumber(res, "tableoid");
7770 i_oid = PQfnumber(res, "oid");
7771 i_stxname = PQfnumber(res, "stxname");
7772 i_stxnamespace = PQfnumber(res, "stxnamespace");
7773 i_stxowner = PQfnumber(res, "stxowner");
7774 i_stxrelid = PQfnumber(res, "stxrelid");
7775 i_stattarget = PQfnumber(res, "stxstattarget");
7777 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7779 for (i = 0; i < ntups; i++)
7781 statsextinfo[i].dobj.objType = DO_STATSEXT;
7782 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7783 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7784 AssignDumpId(&statsextinfo[i].dobj);
7785 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7786 statsextinfo[i].dobj.namespace =
7787 findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
7788 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
7789 statsextinfo[i].stattable =
7790 findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
7791 if (PQgetisnull(res, i, i_stattarget))
7792 statsextinfo[i].stattarget = -1;
7793 else
7794 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
7796 /* Decide whether we want to dump it */
7797 selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
7800 PQclear(res);
7801 destroyPQExpBuffer(query);
7805 * getConstraints
7807 * Get info about constraints on dumpable tables.
7809 * Currently handles foreign keys only.
7810 * Unique and primary key constraints are handled with indexes,
7811 * while check constraints are processed in getTableAttrs().
7813 void
7814 getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7816 PQExpBuffer query = createPQExpBuffer();
7817 PQExpBuffer tbloids = createPQExpBuffer();
7818 PGresult *res;
7819 int ntups;
7820 int curtblindx;
7821 TableInfo *tbinfo = NULL;
7822 ConstraintInfo *constrinfo;
7823 int i_contableoid,
7824 i_conoid,
7825 i_conrelid,
7826 i_conname,
7827 i_confrelid,
7828 i_conindid,
7829 i_condef;
7832 * We want to perform just one query against pg_constraint. However, we
7833 * mustn't try to select every row of the catalog and then sort it out on
7834 * the client side, because some of the server-side functions we need
7835 * would be unsafe to apply to tables we don't have lock on. Hence, we
7836 * build an array of the OIDs of tables we care about (and now have lock
7837 * on!), and use a WHERE clause to constrain which rows are selected.
7839 appendPQExpBufferChar(tbloids, '{');
7840 for (int i = 0; i < numTables; i++)
7842 TableInfo *tinfo = &tblinfo[i];
7845 * For partitioned tables, foreign keys have no triggers so they must
7846 * be included anyway in case some foreign keys are defined.
7848 if ((!tinfo->hastriggers &&
7849 tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7850 !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7851 continue;
7853 /* OK, we need info for this table */
7854 if (tbloids->len > 1) /* do we have more than the '{'? */
7855 appendPQExpBufferChar(tbloids, ',');
7856 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
7858 appendPQExpBufferChar(tbloids, '}');
7860 appendPQExpBufferStr(query,
7861 "SELECT c.tableoid, c.oid, "
7862 "conrelid, conname, confrelid, ");
7863 if (fout->remoteVersion >= 110000)
7864 appendPQExpBufferStr(query, "conindid, ");
7865 else
7866 appendPQExpBufferStr(query, "0 AS conindid, ");
7867 appendPQExpBuffer(query,
7868 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
7869 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7870 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
7871 "WHERE contype = 'f' ",
7872 tbloids->data);
7873 if (fout->remoteVersion >= 110000)
7874 appendPQExpBufferStr(query,
7875 "AND conparentid = 0 ");
7876 appendPQExpBufferStr(query,
7877 "ORDER BY conrelid, conname");
7879 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7881 ntups = PQntuples(res);
7883 i_contableoid = PQfnumber(res, "tableoid");
7884 i_conoid = PQfnumber(res, "oid");
7885 i_conrelid = PQfnumber(res, "conrelid");
7886 i_conname = PQfnumber(res, "conname");
7887 i_confrelid = PQfnumber(res, "confrelid");
7888 i_conindid = PQfnumber(res, "conindid");
7889 i_condef = PQfnumber(res, "condef");
7891 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7893 curtblindx = -1;
7894 for (int j = 0; j < ntups; j++)
7896 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
7897 TableInfo *reftable;
7900 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7901 * order.
7903 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
7905 while (++curtblindx < numTables)
7907 tbinfo = &tblinfo[curtblindx];
7908 if (tbinfo->dobj.catId.oid == conrelid)
7909 break;
7911 if (curtblindx >= numTables)
7912 pg_fatal("unrecognized table OID %u", conrelid);
7915 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
7916 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7917 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7918 AssignDumpId(&constrinfo[j].dobj);
7919 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7920 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7921 constrinfo[j].contable = tbinfo;
7922 constrinfo[j].condomain = NULL;
7923 constrinfo[j].contype = 'f';
7924 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7925 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
7926 constrinfo[j].conindex = 0;
7927 constrinfo[j].condeferrable = false;
7928 constrinfo[j].condeferred = false;
7929 constrinfo[j].conislocal = true;
7930 constrinfo[j].separate = true;
7933 * Restoring an FK that points to a partitioned table requires that
7934 * all partition indexes have been attached beforehand. Ensure that
7935 * happens by making the constraint depend on each index partition
7936 * attach object.
7938 reftable = findTableByOid(constrinfo[j].confrelid);
7939 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
7941 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
7943 if (indexOid != InvalidOid)
7945 for (int k = 0; k < reftable->numIndexes; k++)
7947 IndxInfo *refidx;
7949 /* not our index? */
7950 if (reftable->indexes[k].dobj.catId.oid != indexOid)
7951 continue;
7953 refidx = &reftable->indexes[k];
7954 addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
7955 break;
7961 PQclear(res);
7963 destroyPQExpBuffer(query);
7964 destroyPQExpBuffer(tbloids);
7968 * addConstrChildIdxDeps
7970 * Recursive subroutine for getConstraints
7972 * Given an object representing a foreign key constraint and an index on the
7973 * partitioned table it references, mark the constraint object as dependent
7974 * on the DO_INDEX_ATTACH object of each index partition, recursively
7975 * drilling down to their partitions if any. This ensures that the FK is not
7976 * restored until the index is fully marked valid.
7978 static void
7979 addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
7981 SimplePtrListCell *cell;
7983 Assert(dobj->objType == DO_FK_CONSTRAINT);
7985 for (cell = refidx->partattaches.head; cell; cell = cell->next)
7987 IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
7989 addObjectDependency(dobj, attach->dobj.dumpId);
7991 if (attach->partitionIdx->partattaches.head != NULL)
7992 addConstrChildIdxDeps(dobj, attach->partitionIdx);
7997 * getDomainConstraints
7999 * Get info about constraints on a domain.
8001 static void
8002 getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8004 int i;
8005 ConstraintInfo *constrinfo;
8006 PQExpBuffer query = createPQExpBuffer();
8007 PGresult *res;
8008 int i_tableoid,
8009 i_oid,
8010 i_conname,
8011 i_consrc;
8012 int ntups;
8014 if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8016 /* Set up query for constraint-specific details */
8017 appendPQExpBufferStr(query,
8018 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8019 "SELECT tableoid, oid, conname, "
8020 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8021 "convalidated "
8022 "FROM pg_catalog.pg_constraint "
8023 "WHERE contypid = $1 AND contype = 'c' "
8024 "ORDER BY conname");
8026 ExecuteSqlStatement(fout, query->data);
8028 fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8031 printfPQExpBuffer(query,
8032 "EXECUTE getDomainConstraints('%u')",
8033 tyinfo->dobj.catId.oid);
8035 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8037 ntups = PQntuples(res);
8039 i_tableoid = PQfnumber(res, "tableoid");
8040 i_oid = PQfnumber(res, "oid");
8041 i_conname = PQfnumber(res, "conname");
8042 i_consrc = PQfnumber(res, "consrc");
8044 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8046 tyinfo->nDomChecks = ntups;
8047 tyinfo->domChecks = constrinfo;
8049 for (i = 0; i < ntups; i++)
8051 bool validated = PQgetvalue(res, i, 4)[0] == 't';
8053 constrinfo[i].dobj.objType = DO_CONSTRAINT;
8054 constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8055 constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8056 AssignDumpId(&constrinfo[i].dobj);
8057 constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8058 constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
8059 constrinfo[i].contable = NULL;
8060 constrinfo[i].condomain = tyinfo;
8061 constrinfo[i].contype = 'c';
8062 constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8063 constrinfo[i].confrelid = InvalidOid;
8064 constrinfo[i].conindex = 0;
8065 constrinfo[i].condeferrable = false;
8066 constrinfo[i].condeferred = false;
8067 constrinfo[i].conislocal = true;
8069 constrinfo[i].separate = !validated;
8072 * Make the domain depend on the constraint, ensuring it won't be
8073 * output till any constraint dependencies are OK. If the constraint
8074 * has not been validated, it's going to be dumped after the domain
8075 * anyway, so this doesn't matter.
8077 if (validated)
8078 addObjectDependency(&tyinfo->dobj,
8079 constrinfo[i].dobj.dumpId);
8082 PQclear(res);
8084 destroyPQExpBuffer(query);
8088 * getRules
8089 * get basic information about every rule in the system
8091 void
8092 getRules(Archive *fout)
8094 PGresult *res;
8095 int ntups;
8096 int i;
8097 PQExpBuffer query = createPQExpBuffer();
8098 RuleInfo *ruleinfo;
8099 int i_tableoid;
8100 int i_oid;
8101 int i_rulename;
8102 int i_ruletable;
8103 int i_ev_type;
8104 int i_is_instead;
8105 int i_ev_enabled;
8107 appendPQExpBufferStr(query, "SELECT "
8108 "tableoid, oid, rulename, "
8109 "ev_class AS ruletable, ev_type, is_instead, "
8110 "ev_enabled "
8111 "FROM pg_rewrite "
8112 "ORDER BY oid");
8114 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8116 ntups = PQntuples(res);
8118 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8120 i_tableoid = PQfnumber(res, "tableoid");
8121 i_oid = PQfnumber(res, "oid");
8122 i_rulename = PQfnumber(res, "rulename");
8123 i_ruletable = PQfnumber(res, "ruletable");
8124 i_ev_type = PQfnumber(res, "ev_type");
8125 i_is_instead = PQfnumber(res, "is_instead");
8126 i_ev_enabled = PQfnumber(res, "ev_enabled");
8128 for (i = 0; i < ntups; i++)
8130 Oid ruletableoid;
8132 ruleinfo[i].dobj.objType = DO_RULE;
8133 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8134 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8135 AssignDumpId(&ruleinfo[i].dobj);
8136 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8137 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8138 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8139 if (ruleinfo[i].ruletable == NULL)
8140 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8141 ruletableoid, ruleinfo[i].dobj.catId.oid);
8142 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8143 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8144 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8145 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8146 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8147 if (ruleinfo[i].ruletable)
8150 * If the table is a view or materialized view, force its ON
8151 * SELECT rule to be sorted before the view itself --- this
8152 * ensures that any dependencies for the rule affect the table's
8153 * positioning. Other rules are forced to appear after their
8154 * table.
8156 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8157 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8158 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8160 addObjectDependency(&ruleinfo[i].ruletable->dobj,
8161 ruleinfo[i].dobj.dumpId);
8162 /* We'll merge the rule into CREATE VIEW, if possible */
8163 ruleinfo[i].separate = false;
8165 else
8167 addObjectDependency(&ruleinfo[i].dobj,
8168 ruleinfo[i].ruletable->dobj.dumpId);
8169 ruleinfo[i].separate = true;
8172 else
8173 ruleinfo[i].separate = true;
8176 PQclear(res);
8178 destroyPQExpBuffer(query);
8182 * getTriggers
8183 * get information about every trigger on a dumpable table
8185 * Note: trigger data is not returned directly to the caller, but it
8186 * does get entered into the DumpableObject tables.
8188 void
8189 getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8191 PQExpBuffer query = createPQExpBuffer();
8192 PQExpBuffer tbloids = createPQExpBuffer();
8193 PGresult *res;
8194 int ntups;
8195 int curtblindx;
8196 TriggerInfo *tginfo;
8197 int i_tableoid,
8198 i_oid,
8199 i_tgrelid,
8200 i_tgname,
8201 i_tgenabled,
8202 i_tgispartition,
8203 i_tgdef;
8206 * We want to perform just one query against pg_trigger. However, we
8207 * mustn't try to select every row of the catalog and then sort it out on
8208 * the client side, because some of the server-side functions we need
8209 * would be unsafe to apply to tables we don't have lock on. Hence, we
8210 * build an array of the OIDs of tables we care about (and now have lock
8211 * on!), and use a WHERE clause to constrain which rows are selected.
8213 appendPQExpBufferChar(tbloids, '{');
8214 for (int i = 0; i < numTables; i++)
8216 TableInfo *tbinfo = &tblinfo[i];
8218 if (!tbinfo->hastriggers ||
8219 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8220 continue;
8222 /* OK, we need info for this table */
8223 if (tbloids->len > 1) /* do we have more than the '{'? */
8224 appendPQExpBufferChar(tbloids, ',');
8225 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8227 appendPQExpBufferChar(tbloids, '}');
8229 if (fout->remoteVersion >= 150000)
8232 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8233 * result in non-forward-compatible dumps of WHEN clauses due to
8234 * under-parenthesization.
8236 * NB: We need to see partition triggers in case the tgenabled flag
8237 * has been changed from the parent.
8239 appendPQExpBuffer(query,
8240 "SELECT t.tgrelid, t.tgname, "
8241 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8242 "t.tgenabled, t.tableoid, t.oid, "
8243 "t.tgparentid <> 0 AS tgispartition\n"
8244 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8245 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8246 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8247 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8248 "OR t.tgenabled != u.tgenabled) "
8249 "ORDER BY t.tgrelid, t.tgname",
8250 tbloids->data);
8252 else if (fout->remoteVersion >= 130000)
8255 * NB: think not to use pretty=true in pg_get_triggerdef. It could
8256 * result in non-forward-compatible dumps of WHEN clauses due to
8257 * under-parenthesization.
8259 * NB: We need to see tgisinternal triggers in partitions, in case the
8260 * tgenabled flag has been changed from the parent.
8262 appendPQExpBuffer(query,
8263 "SELECT t.tgrelid, t.tgname, "
8264 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8265 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8266 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8267 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8268 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8269 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8270 "ORDER BY t.tgrelid, t.tgname",
8271 tbloids->data);
8273 else if (fout->remoteVersion >= 110000)
8276 * NB: We need to see tgisinternal triggers in partitions, in case the
8277 * tgenabled flag has been changed from the parent. No tgparentid in
8278 * version 11-12, so we have to match them via pg_depend.
8280 * See above about pretty=true in pg_get_triggerdef.
8282 appendPQExpBuffer(query,
8283 "SELECT t.tgrelid, t.tgname, "
8284 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8285 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8286 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8287 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8288 "LEFT JOIN pg_catalog.pg_depend AS d ON "
8289 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8290 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8291 " d.objid = t.oid "
8292 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8293 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8294 "ORDER BY t.tgrelid, t.tgname",
8295 tbloids->data);
8297 else
8299 /* See above about pretty=true in pg_get_triggerdef */
8300 appendPQExpBuffer(query,
8301 "SELECT t.tgrelid, t.tgname, "
8302 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8303 "t.tgenabled, false as tgispartition, "
8304 "t.tableoid, t.oid "
8305 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8306 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8307 "WHERE NOT tgisinternal "
8308 "ORDER BY t.tgrelid, t.tgname",
8309 tbloids->data);
8312 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8314 ntups = PQntuples(res);
8316 i_tableoid = PQfnumber(res, "tableoid");
8317 i_oid = PQfnumber(res, "oid");
8318 i_tgrelid = PQfnumber(res, "tgrelid");
8319 i_tgname = PQfnumber(res, "tgname");
8320 i_tgenabled = PQfnumber(res, "tgenabled");
8321 i_tgispartition = PQfnumber(res, "tgispartition");
8322 i_tgdef = PQfnumber(res, "tgdef");
8324 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8327 * Outer loop iterates once per table, not once per row. Incrementing of
8328 * j is handled by the inner loop.
8330 curtblindx = -1;
8331 for (int j = 0; j < ntups;)
8333 Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8334 TableInfo *tbinfo = NULL;
8335 int numtrigs;
8337 /* Count rows for this table */
8338 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8339 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8340 break;
8343 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8344 * order.
8346 while (++curtblindx < numTables)
8348 tbinfo = &tblinfo[curtblindx];
8349 if (tbinfo->dobj.catId.oid == tgrelid)
8350 break;
8352 if (curtblindx >= numTables)
8353 pg_fatal("unrecognized table OID %u", tgrelid);
8355 /* Save data for this table */
8356 tbinfo->triggers = tginfo + j;
8357 tbinfo->numTriggers = numtrigs;
8359 for (int c = 0; c < numtrigs; c++, j++)
8361 tginfo[j].dobj.objType = DO_TRIGGER;
8362 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8363 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8364 AssignDumpId(&tginfo[j].dobj);
8365 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8366 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8367 tginfo[j].tgtable = tbinfo;
8368 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8369 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8370 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8374 PQclear(res);
8376 destroyPQExpBuffer(query);
8377 destroyPQExpBuffer(tbloids);
8381 * getEventTriggers
8382 * get information about event triggers
8384 void
8385 getEventTriggers(Archive *fout)
8387 int i;
8388 PQExpBuffer query;
8389 PGresult *res;
8390 EventTriggerInfo *evtinfo;
8391 int i_tableoid,
8392 i_oid,
8393 i_evtname,
8394 i_evtevent,
8395 i_evtowner,
8396 i_evttags,
8397 i_evtfname,
8398 i_evtenabled;
8399 int ntups;
8401 /* Before 9.3, there are no event triggers */
8402 if (fout->remoteVersion < 90300)
8403 return;
8405 query = createPQExpBuffer();
8407 appendPQExpBufferStr(query,
8408 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8409 "evtevent, evtowner, "
8410 "array_to_string(array("
8411 "select quote_literal(x) "
8412 " from unnest(evttags) as t(x)), ', ') as evttags, "
8413 "e.evtfoid::regproc as evtfname "
8414 "FROM pg_event_trigger e "
8415 "ORDER BY e.oid");
8417 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8419 ntups = PQntuples(res);
8421 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8423 i_tableoid = PQfnumber(res, "tableoid");
8424 i_oid = PQfnumber(res, "oid");
8425 i_evtname = PQfnumber(res, "evtname");
8426 i_evtevent = PQfnumber(res, "evtevent");
8427 i_evtowner = PQfnumber(res, "evtowner");
8428 i_evttags = PQfnumber(res, "evttags");
8429 i_evtfname = PQfnumber(res, "evtfname");
8430 i_evtenabled = PQfnumber(res, "evtenabled");
8432 for (i = 0; i < ntups; i++)
8434 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8435 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8436 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8437 AssignDumpId(&evtinfo[i].dobj);
8438 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8439 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8440 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8441 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8442 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8443 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8444 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8446 /* Decide whether we want to dump it */
8447 selectDumpableObject(&(evtinfo[i].dobj), fout);
8450 PQclear(res);
8452 destroyPQExpBuffer(query);
8456 * getProcLangs
8457 * get basic information about every procedural language in the system
8459 * NB: this must run after getFuncs() because we assume we can do
8460 * findFuncByOid().
8462 void
8463 getProcLangs(Archive *fout)
8465 PGresult *res;
8466 int ntups;
8467 int i;
8468 PQExpBuffer query = createPQExpBuffer();
8469 ProcLangInfo *planginfo;
8470 int i_tableoid;
8471 int i_oid;
8472 int i_lanname;
8473 int i_lanpltrusted;
8474 int i_lanplcallfoid;
8475 int i_laninline;
8476 int i_lanvalidator;
8477 int i_lanacl;
8478 int i_acldefault;
8479 int i_lanowner;
8481 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8482 "lanname, lanpltrusted, lanplcallfoid, "
8483 "laninline, lanvalidator, "
8484 "lanacl, "
8485 "acldefault('l', lanowner) AS acldefault, "
8486 "lanowner "
8487 "FROM pg_language "
8488 "WHERE lanispl "
8489 "ORDER BY oid");
8491 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8493 ntups = PQntuples(res);
8495 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8497 i_tableoid = PQfnumber(res, "tableoid");
8498 i_oid = PQfnumber(res, "oid");
8499 i_lanname = PQfnumber(res, "lanname");
8500 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8501 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8502 i_laninline = PQfnumber(res, "laninline");
8503 i_lanvalidator = PQfnumber(res, "lanvalidator");
8504 i_lanacl = PQfnumber(res, "lanacl");
8505 i_acldefault = PQfnumber(res, "acldefault");
8506 i_lanowner = PQfnumber(res, "lanowner");
8508 for (i = 0; i < ntups; i++)
8510 planginfo[i].dobj.objType = DO_PROCLANG;
8511 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8512 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8513 AssignDumpId(&planginfo[i].dobj);
8515 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8516 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8517 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8518 planginfo[i].dacl.privtype = 0;
8519 planginfo[i].dacl.initprivs = NULL;
8520 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8521 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8522 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8523 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8524 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8526 /* Decide whether we want to dump it */
8527 selectDumpableProcLang(&(planginfo[i]), fout);
8529 /* Mark whether language has an ACL */
8530 if (!PQgetisnull(res, i, i_lanacl))
8531 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8534 PQclear(res);
8536 destroyPQExpBuffer(query);
8540 * getCasts
8541 * get basic information about most casts in the system
8543 * Skip casts from a range to its multirange, since we'll create those
8544 * automatically.
8546 void
8547 getCasts(Archive *fout)
8549 PGresult *res;
8550 int ntups;
8551 int i;
8552 PQExpBuffer query = createPQExpBuffer();
8553 CastInfo *castinfo;
8554 int i_tableoid;
8555 int i_oid;
8556 int i_castsource;
8557 int i_casttarget;
8558 int i_castfunc;
8559 int i_castcontext;
8560 int i_castmethod;
8562 if (fout->remoteVersion >= 140000)
8564 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8565 "castsource, casttarget, castfunc, castcontext, "
8566 "castmethod "
8567 "FROM pg_cast c "
8568 "WHERE NOT EXISTS ( "
8569 "SELECT 1 FROM pg_range r "
8570 "WHERE c.castsource = r.rngtypid "
8571 "AND c.casttarget = r.rngmultitypid "
8572 ") "
8573 "ORDER BY 3,4");
8575 else
8577 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8578 "castsource, casttarget, castfunc, castcontext, "
8579 "castmethod "
8580 "FROM pg_cast ORDER BY 3,4");
8583 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8585 ntups = PQntuples(res);
8587 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8589 i_tableoid = PQfnumber(res, "tableoid");
8590 i_oid = PQfnumber(res, "oid");
8591 i_castsource = PQfnumber(res, "castsource");
8592 i_casttarget = PQfnumber(res, "casttarget");
8593 i_castfunc = PQfnumber(res, "castfunc");
8594 i_castcontext = PQfnumber(res, "castcontext");
8595 i_castmethod = PQfnumber(res, "castmethod");
8597 for (i = 0; i < ntups; i++)
8599 PQExpBufferData namebuf;
8600 TypeInfo *sTypeInfo;
8601 TypeInfo *tTypeInfo;
8603 castinfo[i].dobj.objType = DO_CAST;
8604 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8605 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8606 AssignDumpId(&castinfo[i].dobj);
8607 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8608 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8609 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8610 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8611 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8614 * Try to name cast as concatenation of typnames. This is only used
8615 * for purposes of sorting. If we fail to find either type, the name
8616 * will be an empty string.
8618 initPQExpBuffer(&namebuf);
8619 sTypeInfo = findTypeByOid(castinfo[i].castsource);
8620 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8621 if (sTypeInfo && tTypeInfo)
8622 appendPQExpBuffer(&namebuf, "%s %s",
8623 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8624 castinfo[i].dobj.name = namebuf.data;
8626 /* Decide whether we want to dump it */
8627 selectDumpableCast(&(castinfo[i]), fout);
8630 PQclear(res);
8632 destroyPQExpBuffer(query);
8635 static char *
8636 get_language_name(Archive *fout, Oid langid)
8638 PQExpBuffer query;
8639 PGresult *res;
8640 char *lanname;
8642 query = createPQExpBuffer();
8643 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8644 res = ExecuteSqlQueryForSingleRow(fout, query->data);
8645 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8646 destroyPQExpBuffer(query);
8647 PQclear(res);
8649 return lanname;
8653 * getTransforms
8654 * get basic information about every transform in the system
8656 void
8657 getTransforms(Archive *fout)
8659 PGresult *res;
8660 int ntups;
8661 int i;
8662 PQExpBuffer query;
8663 TransformInfo *transforminfo;
8664 int i_tableoid;
8665 int i_oid;
8666 int i_trftype;
8667 int i_trflang;
8668 int i_trffromsql;
8669 int i_trftosql;
8671 /* Transforms didn't exist pre-9.5 */
8672 if (fout->remoteVersion < 90500)
8673 return;
8675 query = createPQExpBuffer();
8677 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8678 "trftype, trflang, trffromsql::oid, trftosql::oid "
8679 "FROM pg_transform "
8680 "ORDER BY 3,4");
8682 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8684 ntups = PQntuples(res);
8686 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8688 i_tableoid = PQfnumber(res, "tableoid");
8689 i_oid = PQfnumber(res, "oid");
8690 i_trftype = PQfnumber(res, "trftype");
8691 i_trflang = PQfnumber(res, "trflang");
8692 i_trffromsql = PQfnumber(res, "trffromsql");
8693 i_trftosql = PQfnumber(res, "trftosql");
8695 for (i = 0; i < ntups; i++)
8697 PQExpBufferData namebuf;
8698 TypeInfo *typeInfo;
8699 char *lanname;
8701 transforminfo[i].dobj.objType = DO_TRANSFORM;
8702 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8703 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8704 AssignDumpId(&transforminfo[i].dobj);
8705 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8706 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8707 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8708 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8711 * Try to name transform as concatenation of type and language name.
8712 * This is only used for purposes of sorting. If we fail to find
8713 * either, the name will be an empty string.
8715 initPQExpBuffer(&namebuf);
8716 typeInfo = findTypeByOid(transforminfo[i].trftype);
8717 lanname = get_language_name(fout, transforminfo[i].trflang);
8718 if (typeInfo && lanname)
8719 appendPQExpBuffer(&namebuf, "%s %s",
8720 typeInfo->dobj.name, lanname);
8721 transforminfo[i].dobj.name = namebuf.data;
8722 free(lanname);
8724 /* Decide whether we want to dump it */
8725 selectDumpableObject(&(transforminfo[i].dobj), fout);
8728 PQclear(res);
8730 destroyPQExpBuffer(query);
8734 * getTableAttrs -
8735 * for each interesting table, read info about its attributes
8736 * (names, types, default values, CHECK constraints, etc)
8738 * modifies tblinfo
8740 void
8741 getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8743 DumpOptions *dopt = fout->dopt;
8744 PQExpBuffer q = createPQExpBuffer();
8745 PQExpBuffer tbloids = createPQExpBuffer();
8746 PQExpBuffer checkoids = createPQExpBuffer();
8747 PGresult *res;
8748 int ntups;
8749 int curtblindx;
8750 int i_attrelid;
8751 int i_attnum;
8752 int i_attname;
8753 int i_atttypname;
8754 int i_attstattarget;
8755 int i_attstorage;
8756 int i_typstorage;
8757 int i_attidentity;
8758 int i_attgenerated;
8759 int i_attisdropped;
8760 int i_attlen;
8761 int i_attalign;
8762 int i_attislocal;
8763 int i_notnull_name;
8764 int i_notnull_noinherit;
8765 int i_notnull_islocal;
8766 int i_attoptions;
8767 int i_attcollation;
8768 int i_attcompression;
8769 int i_attfdwoptions;
8770 int i_attmissingval;
8771 int i_atthasdef;
8774 * We want to perform just one query against pg_attribute, and then just
8775 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
8776 * (for CHECK constraints and for NOT NULL constraints). However, we
8777 * mustn't try to select every row of those catalogs and then sort it out
8778 * on the client side, because some of the server-side functions we need
8779 * would be unsafe to apply to tables we don't have lock on. Hence, we
8780 * build an array of the OIDs of tables we care about (and now have lock
8781 * on!), and use a WHERE clause to constrain which rows are selected.
8783 appendPQExpBufferChar(tbloids, '{');
8784 appendPQExpBufferChar(checkoids, '{');
8785 for (int i = 0; i < numTables; i++)
8787 TableInfo *tbinfo = &tblinfo[i];
8789 /* Don't bother to collect info for sequences */
8790 if (tbinfo->relkind == RELKIND_SEQUENCE)
8791 continue;
8793 /* Don't bother with uninteresting tables, either */
8794 if (!tbinfo->interesting)
8795 continue;
8797 /* OK, we need info for this table */
8798 if (tbloids->len > 1) /* do we have more than the '{'? */
8799 appendPQExpBufferChar(tbloids, ',');
8800 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8802 if (tbinfo->ncheck > 0)
8804 /* Also make a list of the ones with check constraints */
8805 if (checkoids->len > 1) /* do we have more than the '{'? */
8806 appendPQExpBufferChar(checkoids, ',');
8807 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
8810 appendPQExpBufferChar(tbloids, '}');
8811 appendPQExpBufferChar(checkoids, '}');
8814 * Find all the user attributes and their types.
8816 * Since we only want to dump COLLATE clauses for attributes whose
8817 * collation is different from their type's default, we use a CASE here to
8818 * suppress uninteresting attcollations cheaply.
8820 appendPQExpBufferStr(q,
8821 "SELECT\n"
8822 "a.attrelid,\n"
8823 "a.attnum,\n"
8824 "a.attname,\n"
8825 "a.attstattarget,\n"
8826 "a.attstorage,\n"
8827 "t.typstorage,\n"
8828 "a.atthasdef,\n"
8829 "a.attisdropped,\n"
8830 "a.attlen,\n"
8831 "a.attalign,\n"
8832 "a.attislocal,\n"
8833 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
8834 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
8835 "CASE WHEN a.attcollation <> t.typcollation "
8836 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
8837 "pg_catalog.array_to_string(ARRAY("
8838 "SELECT pg_catalog.quote_ident(option_name) || "
8839 "' ' || pg_catalog.quote_literal(option_value) "
8840 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8841 "ORDER BY option_name"
8842 "), E',\n ') AS attfdwoptions,\n");
8845 * Find out any NOT NULL markings for each column. In 18 and up we read
8846 * pg_constraint to obtain the constraint name. notnull_noinherit is set
8847 * according to the NO INHERIT property. For versions prior to 18, we
8848 * store an empty string as the name when a constraint is marked as
8849 * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
8850 * without a name); also, such cases are never NO INHERIT.
8852 * We track in notnull_islocal whether the constraint was defined directly
8853 * in this table or via an ancestor, for binary upgrade. flagInhAttrs
8854 * might modify this later for servers older than 18; it's also in charge
8855 * of determining the correct inhcount.
8857 if (fout->remoteVersion >= 180000)
8858 appendPQExpBufferStr(q,
8859 "co.conname AS notnull_name,\n"
8860 "co.connoinherit AS notnull_noinherit,\n"
8861 "co.conislocal AS notnull_islocal,\n");
8862 else
8863 appendPQExpBufferStr(q,
8864 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
8865 "false AS notnull_noinherit,\n"
8866 "a.attislocal AS notnull_islocal,\n");
8868 if (fout->remoteVersion >= 140000)
8869 appendPQExpBufferStr(q,
8870 "a.attcompression AS attcompression,\n");
8871 else
8872 appendPQExpBufferStr(q,
8873 "'' AS attcompression,\n");
8875 if (fout->remoteVersion >= 100000)
8876 appendPQExpBufferStr(q,
8877 "a.attidentity,\n");
8878 else
8879 appendPQExpBufferStr(q,
8880 "'' AS attidentity,\n");
8882 if (fout->remoteVersion >= 110000)
8883 appendPQExpBufferStr(q,
8884 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8885 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8886 else
8887 appendPQExpBufferStr(q,
8888 "NULL AS attmissingval,\n");
8890 if (fout->remoteVersion >= 120000)
8891 appendPQExpBufferStr(q,
8892 "a.attgenerated\n");
8893 else
8894 appendPQExpBufferStr(q,
8895 "'' AS attgenerated\n");
8897 /* need left join to pg_type to not fail on dropped columns ... */
8898 appendPQExpBuffer(q,
8899 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8900 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
8901 "LEFT JOIN pg_catalog.pg_type t "
8902 "ON (a.atttypid = t.oid)\n",
8903 tbloids->data);
8906 * In versions 18 and up, we need pg_constraint for explicit NOT NULL
8907 * entries. Also, we need to know if the NOT NULL for each column is
8908 * backing a primary key.
8910 if (fout->remoteVersion >= 180000)
8911 appendPQExpBufferStr(q,
8912 " LEFT JOIN pg_catalog.pg_constraint co ON "
8913 "(a.attrelid = co.conrelid\n"
8914 " AND co.contype = 'n' AND "
8915 "co.conkey = array[a.attnum])\n");
8917 appendPQExpBufferStr(q,
8918 "WHERE a.attnum > 0::pg_catalog.int2\n"
8919 "ORDER BY a.attrelid, a.attnum");
8921 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8923 ntups = PQntuples(res);
8925 i_attrelid = PQfnumber(res, "attrelid");
8926 i_attnum = PQfnumber(res, "attnum");
8927 i_attname = PQfnumber(res, "attname");
8928 i_atttypname = PQfnumber(res, "atttypname");
8929 i_attstattarget = PQfnumber(res, "attstattarget");
8930 i_attstorage = PQfnumber(res, "attstorage");
8931 i_typstorage = PQfnumber(res, "typstorage");
8932 i_attidentity = PQfnumber(res, "attidentity");
8933 i_attgenerated = PQfnumber(res, "attgenerated");
8934 i_attisdropped = PQfnumber(res, "attisdropped");
8935 i_attlen = PQfnumber(res, "attlen");
8936 i_attalign = PQfnumber(res, "attalign");
8937 i_attislocal = PQfnumber(res, "attislocal");
8938 i_notnull_name = PQfnumber(res, "notnull_name");
8939 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
8940 i_notnull_islocal = PQfnumber(res, "notnull_islocal");
8941 i_attoptions = PQfnumber(res, "attoptions");
8942 i_attcollation = PQfnumber(res, "attcollation");
8943 i_attcompression = PQfnumber(res, "attcompression");
8944 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
8945 i_attmissingval = PQfnumber(res, "attmissingval");
8946 i_atthasdef = PQfnumber(res, "atthasdef");
8948 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
8949 resetPQExpBuffer(tbloids);
8950 appendPQExpBufferChar(tbloids, '{');
8953 * Outer loop iterates once per table, not once per row. Incrementing of
8954 * r is handled by the inner loop.
8956 curtblindx = -1;
8957 for (int r = 0; r < ntups;)
8959 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
8960 TableInfo *tbinfo = NULL;
8961 int numatts;
8962 bool hasdefaults;
8964 /* Count rows for this table */
8965 for (numatts = 1; numatts < ntups - r; numatts++)
8966 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
8967 break;
8970 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8971 * order.
8973 while (++curtblindx < numTables)
8975 tbinfo = &tblinfo[curtblindx];
8976 if (tbinfo->dobj.catId.oid == attrelid)
8977 break;
8979 if (curtblindx >= numTables)
8980 pg_fatal("unrecognized table OID %u", attrelid);
8981 /* cross-check that we only got requested tables */
8982 if (tbinfo->relkind == RELKIND_SEQUENCE ||
8983 !tbinfo->interesting)
8984 pg_fatal("unexpected column data for table \"%s\"",
8985 tbinfo->dobj.name);
8987 /* Save data for this table */
8988 tbinfo->numatts = numatts;
8989 tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
8990 tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
8991 tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
8992 tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
8993 tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
8994 tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
8995 tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
8996 tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
8997 tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
8998 tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
8999 tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9000 tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9001 tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9002 tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9003 tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9004 tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9005 tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9006 tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9007 tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9008 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9009 hasdefaults = false;
9011 for (int j = 0; j < numatts; j++, r++)
9013 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9014 pg_fatal("invalid column numbering in table \"%s\"",
9015 tbinfo->dobj.name);
9016 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9017 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9018 if (PQgetisnull(res, r, i_attstattarget))
9019 tbinfo->attstattarget[j] = -1;
9020 else
9021 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9022 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9023 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9024 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9025 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9026 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9027 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9028 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9029 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9030 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9032 /* Handle not-null constraint name and flags */
9033 determineNotNullFlags(fout, res, r,
9034 tbinfo, j,
9035 i_notnull_name, i_notnull_noinherit,
9036 i_notnull_islocal);
9038 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9039 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9040 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9041 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9042 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9043 tbinfo->attrdefs[j] = NULL; /* fix below */
9044 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9045 hasdefaults = true;
9048 if (hasdefaults)
9050 /* Collect OIDs of interesting tables that have defaults */
9051 if (tbloids->len > 1) /* do we have more than the '{'? */
9052 appendPQExpBufferChar(tbloids, ',');
9053 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9057 PQclear(res);
9060 * Now get info about column defaults. This is skipped for a data-only
9061 * dump, as it is only needed for table schemas.
9063 if (dopt->dumpSchema && tbloids->len > 1)
9065 AttrDefInfo *attrdefs;
9066 int numDefaults;
9067 TableInfo *tbinfo = NULL;
9069 pg_log_info("finding table default expressions");
9071 appendPQExpBufferChar(tbloids, '}');
9073 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9074 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9075 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9076 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9077 "ORDER BY a.adrelid, a.adnum",
9078 tbloids->data);
9080 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9082 numDefaults = PQntuples(res);
9083 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9085 curtblindx = -1;
9086 for (int j = 0; j < numDefaults; j++)
9088 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9089 Oid adoid = atooid(PQgetvalue(res, j, 1));
9090 Oid adrelid = atooid(PQgetvalue(res, j, 2));
9091 int adnum = atoi(PQgetvalue(res, j, 3));
9092 char *adsrc = PQgetvalue(res, j, 4);
9095 * Locate the associated TableInfo; we rely on tblinfo[] being in
9096 * OID order.
9098 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9100 while (++curtblindx < numTables)
9102 tbinfo = &tblinfo[curtblindx];
9103 if (tbinfo->dobj.catId.oid == adrelid)
9104 break;
9106 if (curtblindx >= numTables)
9107 pg_fatal("unrecognized table OID %u", adrelid);
9110 if (adnum <= 0 || adnum > tbinfo->numatts)
9111 pg_fatal("invalid adnum value %d for table \"%s\"",
9112 adnum, tbinfo->dobj.name);
9115 * dropped columns shouldn't have defaults, but just in case,
9116 * ignore 'em
9118 if (tbinfo->attisdropped[adnum - 1])
9119 continue;
9121 attrdefs[j].dobj.objType = DO_ATTRDEF;
9122 attrdefs[j].dobj.catId.tableoid = adtableoid;
9123 attrdefs[j].dobj.catId.oid = adoid;
9124 AssignDumpId(&attrdefs[j].dobj);
9125 attrdefs[j].adtable = tbinfo;
9126 attrdefs[j].adnum = adnum;
9127 attrdefs[j].adef_expr = pg_strdup(adsrc);
9129 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9130 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9132 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9135 * Figure out whether the default/generation expression should be
9136 * dumped as part of the main CREATE TABLE (or similar) command or
9137 * as a separate ALTER TABLE (or similar) command. The preference
9138 * is to put it into the CREATE command, but in some cases that's
9139 * not possible.
9141 if (tbinfo->attgenerated[adnum - 1])
9144 * Column generation expressions cannot be dumped separately,
9145 * because there is no syntax for it. By setting separate to
9146 * false here we prevent the "default" from being processed as
9147 * its own dumpable object. Later, flagInhAttrs() will mark
9148 * it as not to be dumped at all, if possible (that is, if it
9149 * can be inherited from a parent).
9151 attrdefs[j].separate = false;
9153 else if (tbinfo->relkind == RELKIND_VIEW)
9156 * Defaults on a VIEW must always be dumped as separate ALTER
9157 * TABLE commands.
9159 attrdefs[j].separate = true;
9161 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9163 /* column will be suppressed, print default separately */
9164 attrdefs[j].separate = true;
9166 else
9168 attrdefs[j].separate = false;
9171 if (!attrdefs[j].separate)
9174 * Mark the default as needing to appear before the table, so
9175 * that any dependencies it has must be emitted before the
9176 * CREATE TABLE. If this is not possible, we'll change to
9177 * "separate" mode while sorting dependencies.
9179 addObjectDependency(&tbinfo->dobj,
9180 attrdefs[j].dobj.dumpId);
9183 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9186 PQclear(res);
9190 * Get info about table CHECK constraints. This is skipped for a
9191 * data-only dump, as it is only needed for table schemas.
9193 if (dopt->dumpSchema && checkoids->len > 2)
9195 ConstraintInfo *constrs;
9196 int numConstrs;
9197 int i_tableoid;
9198 int i_oid;
9199 int i_conrelid;
9200 int i_conname;
9201 int i_consrc;
9202 int i_conislocal;
9203 int i_convalidated;
9205 pg_log_info("finding table check constraints");
9207 resetPQExpBuffer(q);
9208 appendPQExpBuffer(q,
9209 "SELECT c.tableoid, c.oid, conrelid, conname, "
9210 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9211 "conislocal, convalidated "
9212 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9213 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9214 "WHERE contype = 'c' "
9215 "ORDER BY c.conrelid, c.conname",
9216 checkoids->data);
9218 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9220 numConstrs = PQntuples(res);
9221 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9223 i_tableoid = PQfnumber(res, "tableoid");
9224 i_oid = PQfnumber(res, "oid");
9225 i_conrelid = PQfnumber(res, "conrelid");
9226 i_conname = PQfnumber(res, "conname");
9227 i_consrc = PQfnumber(res, "consrc");
9228 i_conislocal = PQfnumber(res, "conislocal");
9229 i_convalidated = PQfnumber(res, "convalidated");
9231 /* As above, this loop iterates once per table, not once per row */
9232 curtblindx = -1;
9233 for (int j = 0; j < numConstrs;)
9235 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9236 TableInfo *tbinfo = NULL;
9237 int numcons;
9239 /* Count rows for this table */
9240 for (numcons = 1; numcons < numConstrs - j; numcons++)
9241 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9242 break;
9245 * Locate the associated TableInfo; we rely on tblinfo[] being in
9246 * OID order.
9248 while (++curtblindx < numTables)
9250 tbinfo = &tblinfo[curtblindx];
9251 if (tbinfo->dobj.catId.oid == conrelid)
9252 break;
9254 if (curtblindx >= numTables)
9255 pg_fatal("unrecognized table OID %u", conrelid);
9257 if (numcons != tbinfo->ncheck)
9259 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9260 "expected %d check constraints on table \"%s\" but found %d",
9261 tbinfo->ncheck),
9262 tbinfo->ncheck, tbinfo->dobj.name, numcons);
9263 pg_log_error_hint("The system catalogs might be corrupted.");
9264 exit_nicely(1);
9267 tbinfo->checkexprs = constrs + j;
9269 for (int c = 0; c < numcons; c++, j++)
9271 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9273 constrs[j].dobj.objType = DO_CONSTRAINT;
9274 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9275 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9276 AssignDumpId(&constrs[j].dobj);
9277 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9278 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9279 constrs[j].contable = tbinfo;
9280 constrs[j].condomain = NULL;
9281 constrs[j].contype = 'c';
9282 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9283 constrs[j].confrelid = InvalidOid;
9284 constrs[j].conindex = 0;
9285 constrs[j].condeferrable = false;
9286 constrs[j].condeferred = false;
9287 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9290 * An unvalidated constraint needs to be dumped separately, so
9291 * that potentially-violating existing data is loaded before
9292 * the constraint.
9294 constrs[j].separate = !validated;
9296 constrs[j].dobj.dump = tbinfo->dobj.dump;
9299 * Mark the constraint as needing to appear before the table
9300 * --- this is so that any other dependencies of the
9301 * constraint will be emitted before we try to create the
9302 * table. If the constraint is to be dumped separately, it
9303 * will be dumped after data is loaded anyway, so don't do it.
9304 * (There's an automatic dependency in the opposite direction
9305 * anyway, so don't need to add one manually here.)
9307 if (!constrs[j].separate)
9308 addObjectDependency(&tbinfo->dobj,
9309 constrs[j].dobj.dumpId);
9312 * We will detect later whether the constraint must be split
9313 * out from the table definition.
9318 PQclear(res);
9321 destroyPQExpBuffer(q);
9322 destroyPQExpBuffer(tbloids);
9323 destroyPQExpBuffer(checkoids);
9327 * Based on the getTableAttrs query's row corresponding to one column, set
9328 * the name and flags to handle a not-null constraint for that column in
9329 * the tbinfo struct.
9331 * Result row 'r' is for tbinfo's attribute 'j'.
9333 * There are three possibilities:
9334 * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9335 * (the constraint name) remains NULL.
9336 * 2) The column has a constraint with no name (this is the case when
9337 * constraints come from pre-18 servers). In this case, ->notnull_constrs
9338 * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9339 * 3) The column has a constraint with a known name; in that case
9340 * notnull_constrs carries that name and dumpTableSchema will print
9341 * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9342 * (table_column_not_null), there's no need to print that name in the dump,
9343 * so notnull_constrs is set to the empty string and it behaves as the case
9344 * above.
9346 * In a child table that inherits from a parent already containing NOT NULL
9347 * constraints and the columns in the child don't have their own NOT NULL
9348 * declarations, we suppress printing constraints in the child: the
9349 * constraints are acquired at the point where the child is attached to the
9350 * parent. This is tracked in ->notnull_islocal (which is set in flagInhAttrs
9351 * for servers pre-18).
9353 * Any of these constraints might have the NO INHERIT bit. If so we set
9354 * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9356 * In case 3 above, the name comparison is a bit of a hack; it actually fails
9357 * to do the right thing in all but the trivial case. However, the downside
9358 * of getting it wrong is simply that the name is printed rather than
9359 * suppressed, so it's not a big deal.
9361 static void
9362 determineNotNullFlags(Archive *fout, PGresult *res, int r,
9363 TableInfo *tbinfo, int j,
9364 int i_notnull_name, int i_notnull_noinherit,
9365 int i_notnull_islocal)
9367 DumpOptions *dopt = fout->dopt;
9370 * notnull_noinh is straight from the query result. notnull_islocal also,
9371 * though flagInhAttrs may change that one later in versions < 18.
9373 tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9374 tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9377 * Determine a constraint name to use. If the column is not marked not-
9378 * null, we set NULL which cues ... to do nothing. An empty string says
9379 * to print an unnamed NOT NULL, and anything else is a constraint name to
9380 * use.
9382 if (fout->remoteVersion < 180000)
9385 * < 18 doesn't have not-null names, so an unnamed constraint is
9386 * sufficient.
9388 if (PQgetisnull(res, r, i_notnull_name))
9389 tbinfo->notnull_constrs[j] = NULL;
9390 else
9391 tbinfo->notnull_constrs[j] = "";
9393 else
9395 if (PQgetisnull(res, r, i_notnull_name))
9396 tbinfo->notnull_constrs[j] = NULL;
9397 else
9400 * In binary upgrade of inheritance child tables, must have a
9401 * constraint name that we can UPDATE later.
9403 if (dopt->binary_upgrade &&
9404 !tbinfo->ispartition &&
9405 !tbinfo->notnull_islocal)
9407 tbinfo->notnull_constrs[j] =
9408 pstrdup(PQgetvalue(res, r, i_notnull_name));
9410 else
9412 char *default_name;
9414 /* XXX should match ChooseConstraintName better */
9415 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9416 tbinfo->attnames[j]);
9417 if (strcmp(default_name,
9418 PQgetvalue(res, r, i_notnull_name)) == 0)
9419 tbinfo->notnull_constrs[j] = "";
9420 else
9422 tbinfo->notnull_constrs[j] =
9423 pstrdup(PQgetvalue(res, r, i_notnull_name));
9431 * Test whether a column should be printed as part of table's CREATE TABLE.
9432 * Column number is zero-based.
9434 * Normally this is always true, but it's false for dropped columns, as well
9435 * as those that were inherited without any local definition. (If we print
9436 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9437 * For partitions, it's always true, because we want the partitions to be
9438 * created independently and ATTACH PARTITION used afterwards.
9440 * In binary_upgrade mode, we must print all columns and fix the attislocal/
9441 * attisdropped state later, so as to keep control of the physical column
9442 * order.
9444 * This function exists because there are scattered nonobvious places that
9445 * must be kept in sync with this decision.
9447 bool
9448 shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9450 if (dopt->binary_upgrade)
9451 return true;
9452 if (tbinfo->attisdropped[colno])
9453 return false;
9454 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9459 * getTSParsers:
9460 * get information about all text search parsers in the system catalogs
9462 void
9463 getTSParsers(Archive *fout)
9465 PGresult *res;
9466 int ntups;
9467 int i;
9468 PQExpBuffer query;
9469 TSParserInfo *prsinfo;
9470 int i_tableoid;
9471 int i_oid;
9472 int i_prsname;
9473 int i_prsnamespace;
9474 int i_prsstart;
9475 int i_prstoken;
9476 int i_prsend;
9477 int i_prsheadline;
9478 int i_prslextype;
9480 query = createPQExpBuffer();
9483 * find all text search objects, including builtin ones; we filter out
9484 * system-defined objects at dump-out time.
9487 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9488 "prsstart::oid, prstoken::oid, "
9489 "prsend::oid, prsheadline::oid, prslextype::oid "
9490 "FROM pg_ts_parser");
9492 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9494 ntups = PQntuples(res);
9496 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9498 i_tableoid = PQfnumber(res, "tableoid");
9499 i_oid = PQfnumber(res, "oid");
9500 i_prsname = PQfnumber(res, "prsname");
9501 i_prsnamespace = PQfnumber(res, "prsnamespace");
9502 i_prsstart = PQfnumber(res, "prsstart");
9503 i_prstoken = PQfnumber(res, "prstoken");
9504 i_prsend = PQfnumber(res, "prsend");
9505 i_prsheadline = PQfnumber(res, "prsheadline");
9506 i_prslextype = PQfnumber(res, "prslextype");
9508 for (i = 0; i < ntups; i++)
9510 prsinfo[i].dobj.objType = DO_TSPARSER;
9511 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9512 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9513 AssignDumpId(&prsinfo[i].dobj);
9514 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9515 prsinfo[i].dobj.namespace =
9516 findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9517 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9518 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9519 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9520 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9521 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9523 /* Decide whether we want to dump it */
9524 selectDumpableObject(&(prsinfo[i].dobj), fout);
9527 PQclear(res);
9529 destroyPQExpBuffer(query);
9533 * getTSDictionaries:
9534 * get information about all text search dictionaries in the system catalogs
9536 void
9537 getTSDictionaries(Archive *fout)
9539 PGresult *res;
9540 int ntups;
9541 int i;
9542 PQExpBuffer query;
9543 TSDictInfo *dictinfo;
9544 int i_tableoid;
9545 int i_oid;
9546 int i_dictname;
9547 int i_dictnamespace;
9548 int i_dictowner;
9549 int i_dicttemplate;
9550 int i_dictinitoption;
9552 query = createPQExpBuffer();
9554 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9555 "dictnamespace, dictowner, "
9556 "dicttemplate, dictinitoption "
9557 "FROM pg_ts_dict");
9559 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9561 ntups = PQntuples(res);
9563 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9565 i_tableoid = PQfnumber(res, "tableoid");
9566 i_oid = PQfnumber(res, "oid");
9567 i_dictname = PQfnumber(res, "dictname");
9568 i_dictnamespace = PQfnumber(res, "dictnamespace");
9569 i_dictowner = PQfnumber(res, "dictowner");
9570 i_dictinitoption = PQfnumber(res, "dictinitoption");
9571 i_dicttemplate = PQfnumber(res, "dicttemplate");
9573 for (i = 0; i < ntups; i++)
9575 dictinfo[i].dobj.objType = DO_TSDICT;
9576 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9577 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9578 AssignDumpId(&dictinfo[i].dobj);
9579 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9580 dictinfo[i].dobj.namespace =
9581 findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9582 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9583 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9584 if (PQgetisnull(res, i, i_dictinitoption))
9585 dictinfo[i].dictinitoption = NULL;
9586 else
9587 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9589 /* Decide whether we want to dump it */
9590 selectDumpableObject(&(dictinfo[i].dobj), fout);
9593 PQclear(res);
9595 destroyPQExpBuffer(query);
9599 * getTSTemplates:
9600 * get information about all text search templates in the system catalogs
9602 void
9603 getTSTemplates(Archive *fout)
9605 PGresult *res;
9606 int ntups;
9607 int i;
9608 PQExpBuffer query;
9609 TSTemplateInfo *tmplinfo;
9610 int i_tableoid;
9611 int i_oid;
9612 int i_tmplname;
9613 int i_tmplnamespace;
9614 int i_tmplinit;
9615 int i_tmpllexize;
9617 query = createPQExpBuffer();
9619 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9620 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9621 "FROM pg_ts_template");
9623 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9625 ntups = PQntuples(res);
9627 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9629 i_tableoid = PQfnumber(res, "tableoid");
9630 i_oid = PQfnumber(res, "oid");
9631 i_tmplname = PQfnumber(res, "tmplname");
9632 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9633 i_tmplinit = PQfnumber(res, "tmplinit");
9634 i_tmpllexize = PQfnumber(res, "tmpllexize");
9636 for (i = 0; i < ntups; i++)
9638 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9639 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9640 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9641 AssignDumpId(&tmplinfo[i].dobj);
9642 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9643 tmplinfo[i].dobj.namespace =
9644 findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9645 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9646 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9648 /* Decide whether we want to dump it */
9649 selectDumpableObject(&(tmplinfo[i].dobj), fout);
9652 PQclear(res);
9654 destroyPQExpBuffer(query);
9658 * getTSConfigurations:
9659 * get information about all text search configurations
9661 void
9662 getTSConfigurations(Archive *fout)
9664 PGresult *res;
9665 int ntups;
9666 int i;
9667 PQExpBuffer query;
9668 TSConfigInfo *cfginfo;
9669 int i_tableoid;
9670 int i_oid;
9671 int i_cfgname;
9672 int i_cfgnamespace;
9673 int i_cfgowner;
9674 int i_cfgparser;
9676 query = createPQExpBuffer();
9678 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9679 "cfgnamespace, cfgowner, cfgparser "
9680 "FROM pg_ts_config");
9682 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9684 ntups = PQntuples(res);
9686 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9688 i_tableoid = PQfnumber(res, "tableoid");
9689 i_oid = PQfnumber(res, "oid");
9690 i_cfgname = PQfnumber(res, "cfgname");
9691 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9692 i_cfgowner = PQfnumber(res, "cfgowner");
9693 i_cfgparser = PQfnumber(res, "cfgparser");
9695 for (i = 0; i < ntups; i++)
9697 cfginfo[i].dobj.objType = DO_TSCONFIG;
9698 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9699 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9700 AssignDumpId(&cfginfo[i].dobj);
9701 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9702 cfginfo[i].dobj.namespace =
9703 findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9704 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9705 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9707 /* Decide whether we want to dump it */
9708 selectDumpableObject(&(cfginfo[i].dobj), fout);
9711 PQclear(res);
9713 destroyPQExpBuffer(query);
9717 * getForeignDataWrappers:
9718 * get information about all foreign-data wrappers in the system catalogs
9720 void
9721 getForeignDataWrappers(Archive *fout)
9723 PGresult *res;
9724 int ntups;
9725 int i;
9726 PQExpBuffer query;
9727 FdwInfo *fdwinfo;
9728 int i_tableoid;
9729 int i_oid;
9730 int i_fdwname;
9731 int i_fdwowner;
9732 int i_fdwhandler;
9733 int i_fdwvalidator;
9734 int i_fdwacl;
9735 int i_acldefault;
9736 int i_fdwoptions;
9738 query = createPQExpBuffer();
9740 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9741 "fdwowner, "
9742 "fdwhandler::pg_catalog.regproc, "
9743 "fdwvalidator::pg_catalog.regproc, "
9744 "fdwacl, "
9745 "acldefault('F', fdwowner) AS acldefault, "
9746 "array_to_string(ARRAY("
9747 "SELECT quote_ident(option_name) || ' ' || "
9748 "quote_literal(option_value) "
9749 "FROM pg_options_to_table(fdwoptions) "
9750 "ORDER BY option_name"
9751 "), E',\n ') AS fdwoptions "
9752 "FROM pg_foreign_data_wrapper");
9754 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9756 ntups = PQntuples(res);
9758 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9760 i_tableoid = PQfnumber(res, "tableoid");
9761 i_oid = PQfnumber(res, "oid");
9762 i_fdwname = PQfnumber(res, "fdwname");
9763 i_fdwowner = PQfnumber(res, "fdwowner");
9764 i_fdwhandler = PQfnumber(res, "fdwhandler");
9765 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9766 i_fdwacl = PQfnumber(res, "fdwacl");
9767 i_acldefault = PQfnumber(res, "acldefault");
9768 i_fdwoptions = PQfnumber(res, "fdwoptions");
9770 for (i = 0; i < ntups; i++)
9772 fdwinfo[i].dobj.objType = DO_FDW;
9773 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9774 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9775 AssignDumpId(&fdwinfo[i].dobj);
9776 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9777 fdwinfo[i].dobj.namespace = NULL;
9778 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9779 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9780 fdwinfo[i].dacl.privtype = 0;
9781 fdwinfo[i].dacl.initprivs = NULL;
9782 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
9783 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9784 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9785 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9787 /* Decide whether we want to dump it */
9788 selectDumpableObject(&(fdwinfo[i].dobj), fout);
9790 /* Mark whether FDW has an ACL */
9791 if (!PQgetisnull(res, i, i_fdwacl))
9792 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9795 PQclear(res);
9797 destroyPQExpBuffer(query);
9801 * getForeignServers:
9802 * get information about all foreign servers in the system catalogs
9804 void
9805 getForeignServers(Archive *fout)
9807 PGresult *res;
9808 int ntups;
9809 int i;
9810 PQExpBuffer query;
9811 ForeignServerInfo *srvinfo;
9812 int i_tableoid;
9813 int i_oid;
9814 int i_srvname;
9815 int i_srvowner;
9816 int i_srvfdw;
9817 int i_srvtype;
9818 int i_srvversion;
9819 int i_srvacl;
9820 int i_acldefault;
9821 int i_srvoptions;
9823 query = createPQExpBuffer();
9825 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
9826 "srvowner, "
9827 "srvfdw, srvtype, srvversion, srvacl, "
9828 "acldefault('S', srvowner) AS acldefault, "
9829 "array_to_string(ARRAY("
9830 "SELECT quote_ident(option_name) || ' ' || "
9831 "quote_literal(option_value) "
9832 "FROM pg_options_to_table(srvoptions) "
9833 "ORDER BY option_name"
9834 "), E',\n ') AS srvoptions "
9835 "FROM pg_foreign_server");
9837 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9839 ntups = PQntuples(res);
9841 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9843 i_tableoid = PQfnumber(res, "tableoid");
9844 i_oid = PQfnumber(res, "oid");
9845 i_srvname = PQfnumber(res, "srvname");
9846 i_srvowner = PQfnumber(res, "srvowner");
9847 i_srvfdw = PQfnumber(res, "srvfdw");
9848 i_srvtype = PQfnumber(res, "srvtype");
9849 i_srvversion = PQfnumber(res, "srvversion");
9850 i_srvacl = PQfnumber(res, "srvacl");
9851 i_acldefault = PQfnumber(res, "acldefault");
9852 i_srvoptions = PQfnumber(res, "srvoptions");
9854 for (i = 0; i < ntups; i++)
9856 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9857 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9858 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9859 AssignDumpId(&srvinfo[i].dobj);
9860 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9861 srvinfo[i].dobj.namespace = NULL;
9862 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9863 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9864 srvinfo[i].dacl.privtype = 0;
9865 srvinfo[i].dacl.initprivs = NULL;
9866 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
9867 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9868 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9869 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9870 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9872 /* Decide whether we want to dump it */
9873 selectDumpableObject(&(srvinfo[i].dobj), fout);
9875 /* Servers have user mappings */
9876 srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
9878 /* Mark whether server has an ACL */
9879 if (!PQgetisnull(res, i, i_srvacl))
9880 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9883 PQclear(res);
9885 destroyPQExpBuffer(query);
9889 * getDefaultACLs:
9890 * get information about all default ACL information in the system catalogs
9892 void
9893 getDefaultACLs(Archive *fout)
9895 DumpOptions *dopt = fout->dopt;
9896 DefaultACLInfo *daclinfo;
9897 PQExpBuffer query;
9898 PGresult *res;
9899 int i_oid;
9900 int i_tableoid;
9901 int i_defaclrole;
9902 int i_defaclnamespace;
9903 int i_defaclobjtype;
9904 int i_defaclacl;
9905 int i_acldefault;
9906 int i,
9907 ntups;
9909 query = createPQExpBuffer();
9912 * Global entries (with defaclnamespace=0) replace the hard-wired default
9913 * ACL for their object type. We should dump them as deltas from the
9914 * default ACL, since that will be used as a starting point for
9915 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
9916 * non-global entries can only add privileges not revoke them. We must
9917 * dump those as-is (i.e., as deltas from an empty ACL).
9919 * We can use defaclobjtype as the object type for acldefault(), except
9920 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
9921 * 's'.
9923 appendPQExpBufferStr(query,
9924 "SELECT oid, tableoid, "
9925 "defaclrole, "
9926 "defaclnamespace, "
9927 "defaclobjtype, "
9928 "defaclacl, "
9929 "CASE WHEN defaclnamespace = 0 THEN "
9930 "acldefault(CASE WHEN defaclobjtype = 'S' "
9931 "THEN 's'::\"char\" ELSE defaclobjtype END, "
9932 "defaclrole) ELSE '{}' END AS acldefault "
9933 "FROM pg_default_acl");
9935 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9937 ntups = PQntuples(res);
9939 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9941 i_oid = PQfnumber(res, "oid");
9942 i_tableoid = PQfnumber(res, "tableoid");
9943 i_defaclrole = PQfnumber(res, "defaclrole");
9944 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9945 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9946 i_defaclacl = PQfnumber(res, "defaclacl");
9947 i_acldefault = PQfnumber(res, "acldefault");
9949 for (i = 0; i < ntups; i++)
9951 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9953 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9954 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9955 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9956 AssignDumpId(&daclinfo[i].dobj);
9957 /* cheesy ... is it worth coming up with a better object name? */
9958 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
9960 if (nspid != InvalidOid)
9961 daclinfo[i].dobj.namespace = findNamespace(nspid);
9962 else
9963 daclinfo[i].dobj.namespace = NULL;
9965 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
9966 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9967 daclinfo[i].dacl.privtype = 0;
9968 daclinfo[i].dacl.initprivs = NULL;
9969 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
9970 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
9972 /* Default ACLs are ACLs, of course */
9973 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9975 /* Decide whether we want to dump it */
9976 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
9979 PQclear(res);
9981 destroyPQExpBuffer(query);
9985 * getRoleName -- look up the name of a role, given its OID
9987 * In current usage, we don't expect failures, so error out for a bad OID.
9989 static const char *
9990 getRoleName(const char *roleoid_str)
9992 Oid roleoid = atooid(roleoid_str);
9995 * Do binary search to find the appropriate item.
9997 if (nrolenames > 0)
9999 RoleNameItem *low = &rolenames[0];
10000 RoleNameItem *high = &rolenames[nrolenames - 1];
10002 while (low <= high)
10004 RoleNameItem *middle = low + (high - low) / 2;
10006 if (roleoid < middle->roleoid)
10007 high = middle - 1;
10008 else if (roleoid > middle->roleoid)
10009 low = middle + 1;
10010 else
10011 return middle->rolename; /* found a match */
10015 pg_fatal("role with OID %u does not exist", roleoid);
10016 return NULL; /* keep compiler quiet */
10020 * collectRoleNames --
10022 * Construct a table of all known roles.
10023 * The table is sorted by OID for speed in lookup.
10025 static void
10026 collectRoleNames(Archive *fout)
10028 PGresult *res;
10029 const char *query;
10030 int i;
10032 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10034 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10036 nrolenames = PQntuples(res);
10038 rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10040 for (i = 0; i < nrolenames; i++)
10042 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10043 rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10046 PQclear(res);
10050 * getAdditionalACLs
10052 * We have now created all the DumpableObjects, and collected the ACL data
10053 * that appears in the directly-associated catalog entries. However, there's
10054 * more ACL-related info to collect. If any of a table's columns have ACLs,
10055 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10056 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10057 * Also, in versions having the pg_init_privs catalog, read that and load the
10058 * information into the relevant DumpableObjects.
10060 static void
10061 getAdditionalACLs(Archive *fout)
10063 PQExpBuffer query = createPQExpBuffer();
10064 PGresult *res;
10065 int ntups,
10068 /* Check for per-column ACLs */
10069 appendPQExpBufferStr(query,
10070 "SELECT DISTINCT attrelid FROM pg_attribute "
10071 "WHERE attacl IS NOT NULL");
10073 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10075 ntups = PQntuples(res);
10076 for (i = 0; i < ntups; i++)
10078 Oid relid = atooid(PQgetvalue(res, i, 0));
10079 TableInfo *tblinfo;
10081 tblinfo = findTableByOid(relid);
10082 /* OK to ignore tables we haven't got a DumpableObject for */
10083 if (tblinfo)
10085 tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10086 tblinfo->hascolumnACLs = true;
10089 PQclear(res);
10091 /* Fetch initial-privileges data */
10092 if (fout->remoteVersion >= 90600)
10094 printfPQExpBuffer(query,
10095 "SELECT objoid, classoid, objsubid, privtype, initprivs "
10096 "FROM pg_init_privs");
10098 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10100 ntups = PQntuples(res);
10101 for (i = 0; i < ntups; i++)
10103 Oid objoid = atooid(PQgetvalue(res, i, 0));
10104 Oid classoid = atooid(PQgetvalue(res, i, 1));
10105 int objsubid = atoi(PQgetvalue(res, i, 2));
10106 char privtype = *(PQgetvalue(res, i, 3));
10107 char *initprivs = PQgetvalue(res, i, 4);
10108 CatalogId objId;
10109 DumpableObject *dobj;
10111 objId.tableoid = classoid;
10112 objId.oid = objoid;
10113 dobj = findObjectByCatalogId(objId);
10114 /* OK to ignore entries we haven't got a DumpableObject for */
10115 if (dobj)
10117 /* Cope with sub-object initprivs */
10118 if (objsubid != 0)
10120 if (dobj->objType == DO_TABLE)
10122 /* For a column initprivs, set the table's ACL flags */
10123 dobj->components |= DUMP_COMPONENT_ACL;
10124 ((TableInfo *) dobj)->hascolumnACLs = true;
10126 else
10127 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10128 classoid, objoid, objsubid);
10129 continue;
10133 * We ignore any pg_init_privs.initprivs entry for the public
10134 * schema, as explained in getNamespaces().
10136 if (dobj->objType == DO_NAMESPACE &&
10137 strcmp(dobj->name, "public") == 0)
10138 continue;
10140 /* Else it had better be of a type we think has ACLs */
10141 if (dobj->objType == DO_NAMESPACE ||
10142 dobj->objType == DO_TYPE ||
10143 dobj->objType == DO_FUNC ||
10144 dobj->objType == DO_AGG ||
10145 dobj->objType == DO_TABLE ||
10146 dobj->objType == DO_PROCLANG ||
10147 dobj->objType == DO_FDW ||
10148 dobj->objType == DO_FOREIGN_SERVER)
10150 DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10152 daobj->dacl.privtype = privtype;
10153 daobj->dacl.initprivs = pstrdup(initprivs);
10155 else
10156 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10157 classoid, objoid, objsubid);
10160 PQclear(res);
10163 destroyPQExpBuffer(query);
10167 * dumpCommentExtended --
10169 * This routine is used to dump any comments associated with the
10170 * object handed to this routine. The routine takes the object type
10171 * and object name (ready to print, except for schema decoration), plus
10172 * the namespace and owner of the object (for labeling the ArchiveEntry),
10173 * plus catalog ID and subid which are the lookup key for pg_description,
10174 * plus the dump ID for the object (for setting a dependency).
10175 * If a matching pg_description entry is found, it is dumped.
10177 * Note: in some cases, such as comments for triggers and rules, the "type"
10178 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10179 * but it doesn't seem worth complicating the API for all callers to make
10180 * it cleaner.
10182 * Note: although this routine takes a dumpId for dependency purposes,
10183 * that purpose is just to mark the dependency in the emitted dump file
10184 * for possible future use by pg_restore. We do NOT use it for determining
10185 * ordering of the comment in the dump file, because this routine is called
10186 * after dependency sorting occurs. This routine should be called just after
10187 * calling ArchiveEntry() for the specified object.
10189 static void
10190 dumpCommentExtended(Archive *fout, const char *type,
10191 const char *name, const char *namespace,
10192 const char *owner, CatalogId catalogId,
10193 int subid, DumpId dumpId,
10194 const char *initdb_comment)
10196 DumpOptions *dopt = fout->dopt;
10197 CommentItem *comments;
10198 int ncomments;
10200 /* do nothing, if --no-comments is supplied */
10201 if (dopt->no_comments)
10202 return;
10204 /* Comments are schema not data ... except LO comments are data */
10205 if (strcmp(type, "LARGE OBJECT") != 0)
10207 if (!dopt->dumpSchema)
10208 return;
10210 else
10212 /* We do dump LO comments in binary-upgrade mode */
10213 if (!dopt->dumpData && !dopt->binary_upgrade)
10214 return;
10217 /* Search for comments associated with catalogId, using table */
10218 ncomments = findComments(catalogId.tableoid, catalogId.oid,
10219 &comments);
10221 /* Is there one matching the subid? */
10222 while (ncomments > 0)
10224 if (comments->objsubid == subid)
10225 break;
10226 comments++;
10227 ncomments--;
10230 if (initdb_comment != NULL)
10232 static CommentItem empty_comment = {.descr = ""};
10235 * initdb creates this object with a comment. Skip dumping the
10236 * initdb-provided comment, which would complicate matters for
10237 * non-superuser use of pg_dump. When the DBA has removed initdb's
10238 * comment, replicate that.
10240 if (ncomments == 0)
10242 comments = &empty_comment;
10243 ncomments = 1;
10245 else if (strcmp(comments->descr, initdb_comment) == 0)
10246 ncomments = 0;
10249 /* If a comment exists, build COMMENT ON statement */
10250 if (ncomments > 0)
10252 PQExpBuffer query = createPQExpBuffer();
10253 PQExpBuffer tag = createPQExpBuffer();
10255 appendPQExpBuffer(query, "COMMENT ON %s ", type);
10256 if (namespace && *namespace)
10257 appendPQExpBuffer(query, "%s.", fmtId(namespace));
10258 appendPQExpBuffer(query, "%s IS ", name);
10259 appendStringLiteralAH(query, comments->descr, fout);
10260 appendPQExpBufferStr(query, ";\n");
10262 appendPQExpBuffer(tag, "%s %s", type, name);
10265 * We mark comments as SECTION_NONE because they really belong in the
10266 * same section as their parent, whether that is pre-data or
10267 * post-data.
10269 ArchiveEntry(fout, nilCatalogId, createDumpId(),
10270 ARCHIVE_OPTS(.tag = tag->data,
10271 .namespace = namespace,
10272 .owner = owner,
10273 .description = "COMMENT",
10274 .section = SECTION_NONE,
10275 .createStmt = query->data,
10276 .deps = &dumpId,
10277 .nDeps = 1));
10279 destroyPQExpBuffer(query);
10280 destroyPQExpBuffer(tag);
10285 * dumpComment --
10287 * Typical simplification of the above function.
10289 static inline void
10290 dumpComment(Archive *fout, const char *type,
10291 const char *name, const char *namespace,
10292 const char *owner, CatalogId catalogId,
10293 int subid, DumpId dumpId)
10295 dumpCommentExtended(fout, type, name, namespace, owner,
10296 catalogId, subid, dumpId, NULL);
10300 * dumpTableComment --
10302 * As above, but dump comments for both the specified table (or view)
10303 * and its columns.
10305 static void
10306 dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10307 const char *reltypename)
10309 DumpOptions *dopt = fout->dopt;
10310 CommentItem *comments;
10311 int ncomments;
10312 PQExpBuffer query;
10313 PQExpBuffer tag;
10315 /* do nothing, if --no-comments is supplied */
10316 if (dopt->no_comments)
10317 return;
10319 /* Comments are SCHEMA not data */
10320 if (!dopt->dumpSchema)
10321 return;
10323 /* Search for comments associated with relation, using table */
10324 ncomments = findComments(tbinfo->dobj.catId.tableoid,
10325 tbinfo->dobj.catId.oid,
10326 &comments);
10328 /* If comments exist, build COMMENT ON statements */
10329 if (ncomments <= 0)
10330 return;
10332 query = createPQExpBuffer();
10333 tag = createPQExpBuffer();
10335 while (ncomments > 0)
10337 const char *descr = comments->descr;
10338 int objsubid = comments->objsubid;
10340 if (objsubid == 0)
10342 resetPQExpBuffer(tag);
10343 appendPQExpBuffer(tag, "%s %s", reltypename,
10344 fmtId(tbinfo->dobj.name));
10346 resetPQExpBuffer(query);
10347 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10348 fmtQualifiedDumpable(tbinfo));
10349 appendStringLiteralAH(query, descr, fout);
10350 appendPQExpBufferStr(query, ";\n");
10352 ArchiveEntry(fout, nilCatalogId, createDumpId(),
10353 ARCHIVE_OPTS(.tag = tag->data,
10354 .namespace = tbinfo->dobj.namespace->dobj.name,
10355 .owner = tbinfo->rolname,
10356 .description = "COMMENT",
10357 .section = SECTION_NONE,
10358 .createStmt = query->data,
10359 .deps = &(tbinfo->dobj.dumpId),
10360 .nDeps = 1));
10362 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10364 resetPQExpBuffer(tag);
10365 appendPQExpBuffer(tag, "COLUMN %s.",
10366 fmtId(tbinfo->dobj.name));
10367 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10369 resetPQExpBuffer(query);
10370 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10371 fmtQualifiedDumpable(tbinfo));
10372 appendPQExpBuffer(query, "%s IS ",
10373 fmtId(tbinfo->attnames[objsubid - 1]));
10374 appendStringLiteralAH(query, descr, fout);
10375 appendPQExpBufferStr(query, ";\n");
10377 ArchiveEntry(fout, nilCatalogId, createDumpId(),
10378 ARCHIVE_OPTS(.tag = tag->data,
10379 .namespace = tbinfo->dobj.namespace->dobj.name,
10380 .owner = tbinfo->rolname,
10381 .description = "COMMENT",
10382 .section = SECTION_NONE,
10383 .createStmt = query->data,
10384 .deps = &(tbinfo->dobj.dumpId),
10385 .nDeps = 1));
10388 comments++;
10389 ncomments--;
10392 destroyPQExpBuffer(query);
10393 destroyPQExpBuffer(tag);
10397 * findComments --
10399 * Find the comment(s), if any, associated with the given object. All the
10400 * objsubid values associated with the given classoid/objoid are found with
10401 * one search.
10403 static int
10404 findComments(Oid classoid, Oid objoid, CommentItem **items)
10406 CommentItem *middle = NULL;
10407 CommentItem *low;
10408 CommentItem *high;
10409 int nmatch;
10412 * Do binary search to find some item matching the object.
10414 low = &comments[0];
10415 high = &comments[ncomments - 1];
10416 while (low <= high)
10418 middle = low + (high - low) / 2;
10420 if (classoid < middle->classoid)
10421 high = middle - 1;
10422 else if (classoid > middle->classoid)
10423 low = middle + 1;
10424 else if (objoid < middle->objoid)
10425 high = middle - 1;
10426 else if (objoid > middle->objoid)
10427 low = middle + 1;
10428 else
10429 break; /* found a match */
10432 if (low > high) /* no matches */
10434 *items = NULL;
10435 return 0;
10439 * Now determine how many items match the object. The search loop
10440 * invariant still holds: only items between low and high inclusive could
10441 * match.
10443 nmatch = 1;
10444 while (middle > low)
10446 if (classoid != middle[-1].classoid ||
10447 objoid != middle[-1].objoid)
10448 break;
10449 middle--;
10450 nmatch++;
10453 *items = middle;
10455 middle += nmatch;
10456 while (middle <= high)
10458 if (classoid != middle->classoid ||
10459 objoid != middle->objoid)
10460 break;
10461 middle++;
10462 nmatch++;
10465 return nmatch;
10469 * collectComments --
10471 * Construct a table of all comments available for database objects;
10472 * also set the has-comment component flag for each relevant object.
10474 * We used to do per-object queries for the comments, but it's much faster
10475 * to pull them all over at once, and on most databases the memory cost
10476 * isn't high.
10478 * The table is sorted by classoid/objid/objsubid for speed in lookup.
10480 static void
10481 collectComments(Archive *fout)
10483 PGresult *res;
10484 PQExpBuffer query;
10485 int i_description;
10486 int i_classoid;
10487 int i_objoid;
10488 int i_objsubid;
10489 int ntups;
10490 int i;
10491 DumpableObject *dobj;
10493 query = createPQExpBuffer();
10495 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10496 "FROM pg_catalog.pg_description "
10497 "ORDER BY classoid, objoid, objsubid");
10499 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10501 /* Construct lookup table containing OIDs in numeric form */
10503 i_description = PQfnumber(res, "description");
10504 i_classoid = PQfnumber(res, "classoid");
10505 i_objoid = PQfnumber(res, "objoid");
10506 i_objsubid = PQfnumber(res, "objsubid");
10508 ntups = PQntuples(res);
10510 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10511 ncomments = 0;
10512 dobj = NULL;
10514 for (i = 0; i < ntups; i++)
10516 CatalogId objId;
10517 int subid;
10519 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
10520 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
10521 subid = atoi(PQgetvalue(res, i, i_objsubid));
10523 /* We needn't remember comments that don't match any dumpable object */
10524 if (dobj == NULL ||
10525 dobj->catId.tableoid != objId.tableoid ||
10526 dobj->catId.oid != objId.oid)
10527 dobj = findObjectByCatalogId(objId);
10528 if (dobj == NULL)
10529 continue;
10532 * Comments on columns of composite types are linked to the type's
10533 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
10534 * in the type's own DumpableObject.
10536 if (subid != 0 && dobj->objType == DO_TABLE &&
10537 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
10539 TypeInfo *cTypeInfo;
10541 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
10542 if (cTypeInfo)
10543 cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
10545 else
10546 dobj->components |= DUMP_COMPONENT_COMMENT;
10548 comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
10549 comments[ncomments].classoid = objId.tableoid;
10550 comments[ncomments].objoid = objId.oid;
10551 comments[ncomments].objsubid = subid;
10552 ncomments++;
10555 PQclear(res);
10556 destroyPQExpBuffer(query);
10560 * dumpDumpableObject
10562 * This routine and its subsidiaries are responsible for creating
10563 * ArchiveEntries (TOC objects) for each object to be dumped.
10565 static void
10566 dumpDumpableObject(Archive *fout, DumpableObject *dobj)
10569 * Clear any dump-request bits for components that don't exist for this
10570 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
10571 * request for every kind of object.)
10573 dobj->dump &= dobj->components;
10575 /* Now, short-circuit if there's nothing to be done here. */
10576 if (dobj->dump == 0)
10577 return;
10579 switch (dobj->objType)
10581 case DO_NAMESPACE:
10582 dumpNamespace(fout, (const NamespaceInfo *) dobj);
10583 break;
10584 case DO_EXTENSION:
10585 dumpExtension(fout, (const ExtensionInfo *) dobj);
10586 break;
10587 case DO_TYPE:
10588 dumpType(fout, (const TypeInfo *) dobj);
10589 break;
10590 case DO_SHELL_TYPE:
10591 dumpShellType(fout, (const ShellTypeInfo *) dobj);
10592 break;
10593 case DO_FUNC:
10594 dumpFunc(fout, (const FuncInfo *) dobj);
10595 break;
10596 case DO_AGG:
10597 dumpAgg(fout, (const AggInfo *) dobj);
10598 break;
10599 case DO_OPERATOR:
10600 dumpOpr(fout, (const OprInfo *) dobj);
10601 break;
10602 case DO_ACCESS_METHOD:
10603 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
10604 break;
10605 case DO_OPCLASS:
10606 dumpOpclass(fout, (const OpclassInfo *) dobj);
10607 break;
10608 case DO_OPFAMILY:
10609 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
10610 break;
10611 case DO_COLLATION:
10612 dumpCollation(fout, (const CollInfo *) dobj);
10613 break;
10614 case DO_CONVERSION:
10615 dumpConversion(fout, (const ConvInfo *) dobj);
10616 break;
10617 case DO_TABLE:
10618 dumpTable(fout, (const TableInfo *) dobj);
10619 break;
10620 case DO_TABLE_ATTACH:
10621 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
10622 break;
10623 case DO_ATTRDEF:
10624 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
10625 break;
10626 case DO_INDEX:
10627 dumpIndex(fout, (const IndxInfo *) dobj);
10628 break;
10629 case DO_INDEX_ATTACH:
10630 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
10631 break;
10632 case DO_STATSEXT:
10633 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
10634 break;
10635 case DO_REFRESH_MATVIEW:
10636 refreshMatViewData(fout, (const TableDataInfo *) dobj);
10637 break;
10638 case DO_RULE:
10639 dumpRule(fout, (const RuleInfo *) dobj);
10640 break;
10641 case DO_TRIGGER:
10642 dumpTrigger(fout, (const TriggerInfo *) dobj);
10643 break;
10644 case DO_EVENT_TRIGGER:
10645 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
10646 break;
10647 case DO_CONSTRAINT:
10648 dumpConstraint(fout, (const ConstraintInfo *) dobj);
10649 break;
10650 case DO_FK_CONSTRAINT:
10651 dumpConstraint(fout, (const ConstraintInfo *) dobj);
10652 break;
10653 case DO_PROCLANG:
10654 dumpProcLang(fout, (const ProcLangInfo *) dobj);
10655 break;
10656 case DO_CAST:
10657 dumpCast(fout, (const CastInfo *) dobj);
10658 break;
10659 case DO_TRANSFORM:
10660 dumpTransform(fout, (const TransformInfo *) dobj);
10661 break;
10662 case DO_SEQUENCE_SET:
10663 dumpSequenceData(fout, (const TableDataInfo *) dobj);
10664 break;
10665 case DO_TABLE_DATA:
10666 dumpTableData(fout, (const TableDataInfo *) dobj);
10667 break;
10668 case DO_DUMMY_TYPE:
10669 /* table rowtypes and array types are never dumped separately */
10670 break;
10671 case DO_TSPARSER:
10672 dumpTSParser(fout, (const TSParserInfo *) dobj);
10673 break;
10674 case DO_TSDICT:
10675 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
10676 break;
10677 case DO_TSTEMPLATE:
10678 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
10679 break;
10680 case DO_TSCONFIG:
10681 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
10682 break;
10683 case DO_FDW:
10684 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
10685 break;
10686 case DO_FOREIGN_SERVER:
10687 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
10688 break;
10689 case DO_DEFAULT_ACL:
10690 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
10691 break;
10692 case DO_LARGE_OBJECT:
10693 dumpLO(fout, (const LoInfo *) dobj);
10694 break;
10695 case DO_LARGE_OBJECT_DATA:
10696 if (dobj->dump & DUMP_COMPONENT_DATA)
10698 LoInfo *loinfo;
10699 TocEntry *te;
10701 loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
10702 if (loinfo == NULL)
10703 pg_fatal("missing metadata for large objects \"%s\"",
10704 dobj->name);
10706 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
10707 ARCHIVE_OPTS(.tag = dobj->name,
10708 .owner = loinfo->rolname,
10709 .description = "BLOBS",
10710 .section = SECTION_DATA,
10711 .deps = dobj->dependencies,
10712 .nDeps = dobj->nDeps,
10713 .dumpFn = dumpLOs,
10714 .dumpArg = loinfo));
10717 * Set the TocEntry's dataLength in case we are doing a
10718 * parallel dump and want to order dump jobs by table size.
10719 * (We need some size estimate for every TocEntry with a
10720 * DataDumper function.) We don't currently have any cheap
10721 * way to estimate the size of LOs, but fortunately it doesn't
10722 * matter too much as long as we get large batches of LOs
10723 * processed reasonably early. Assume 8K per blob.
10725 te->dataLength = loinfo->numlos * (pgoff_t) 8192;
10727 break;
10728 case DO_POLICY:
10729 dumpPolicy(fout, (const PolicyInfo *) dobj);
10730 break;
10731 case DO_PUBLICATION:
10732 dumpPublication(fout, (const PublicationInfo *) dobj);
10733 break;
10734 case DO_PUBLICATION_REL:
10735 dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
10736 break;
10737 case DO_PUBLICATION_TABLE_IN_SCHEMA:
10738 dumpPublicationNamespace(fout,
10739 (const PublicationSchemaInfo *) dobj);
10740 break;
10741 case DO_SUBSCRIPTION:
10742 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
10743 break;
10744 case DO_SUBSCRIPTION_REL:
10745 dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
10746 break;
10747 case DO_PRE_DATA_BOUNDARY:
10748 case DO_POST_DATA_BOUNDARY:
10749 /* never dumped, nothing to do */
10750 break;
10755 * dumpNamespace
10756 * writes out to fout the queries to recreate a user-defined namespace
10758 static void
10759 dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
10761 DumpOptions *dopt = fout->dopt;
10762 PQExpBuffer q;
10763 PQExpBuffer delq;
10764 char *qnspname;
10766 /* Do nothing if not dumping schema */
10767 if (!dopt->dumpSchema)
10768 return;
10770 q = createPQExpBuffer();
10771 delq = createPQExpBuffer();
10773 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
10775 if (nspinfo->create)
10777 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
10778 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
10780 else
10782 /* see selectDumpableNamespace() */
10783 appendPQExpBufferStr(delq,
10784 "-- *not* dropping schema, since initdb creates it\n");
10785 appendPQExpBufferStr(q,
10786 "-- *not* creating schema, since initdb creates it\n");
10789 if (dopt->binary_upgrade)
10790 binary_upgrade_extension_member(q, &nspinfo->dobj,
10791 "SCHEMA", qnspname, NULL);
10793 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10794 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
10795 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
10796 .owner = nspinfo->rolname,
10797 .description = "SCHEMA",
10798 .section = SECTION_PRE_DATA,
10799 .createStmt = q->data,
10800 .dropStmt = delq->data));
10802 /* Dump Schema Comments and Security Labels */
10803 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10805 const char *initdb_comment = NULL;
10807 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
10808 initdb_comment = "standard public schema";
10809 dumpCommentExtended(fout, "SCHEMA", qnspname,
10810 NULL, nspinfo->rolname,
10811 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
10812 initdb_comment);
10815 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10816 dumpSecLabel(fout, "SCHEMA", qnspname,
10817 NULL, nspinfo->rolname,
10818 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
10820 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
10821 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
10822 qnspname, NULL, NULL,
10823 NULL, nspinfo->rolname, &nspinfo->dacl);
10825 free(qnspname);
10827 destroyPQExpBuffer(q);
10828 destroyPQExpBuffer(delq);
10832 * dumpExtension
10833 * writes out to fout the queries to recreate an extension
10835 static void
10836 dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
10838 DumpOptions *dopt = fout->dopt;
10839 PQExpBuffer q;
10840 PQExpBuffer delq;
10841 char *qextname;
10843 /* Do nothing if not dumping schema */
10844 if (!dopt->dumpSchema)
10845 return;
10847 q = createPQExpBuffer();
10848 delq = createPQExpBuffer();
10850 qextname = pg_strdup(fmtId(extinfo->dobj.name));
10852 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
10854 if (!dopt->binary_upgrade)
10857 * In a regular dump, we simply create the extension, intentionally
10858 * not specifying a version, so that the destination installation's
10859 * default version is used.
10861 * Use of IF NOT EXISTS here is unlike our behavior for other object
10862 * types; but there are various scenarios in which it's convenient to
10863 * manually create the desired extension before restoring, so we
10864 * prefer to allow it to exist already.
10866 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
10867 qextname, fmtId(extinfo->namespace));
10869 else
10872 * In binary-upgrade mode, it's critical to reproduce the state of the
10873 * database exactly, so our procedure is to create an empty extension,
10874 * restore all the contained objects normally, and add them to the
10875 * extension one by one. This function performs just the first of
10876 * those steps. binary_upgrade_extension_member() takes care of
10877 * adding member objects as they're created.
10879 int i;
10880 int n;
10882 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
10885 * We unconditionally create the extension, so we must drop it if it
10886 * exists. This could happen if the user deleted 'plpgsql' and then
10887 * readded it, causing its oid to be greater than g_last_builtin_oid.
10889 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
10891 appendPQExpBufferStr(q,
10892 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
10893 appendStringLiteralAH(q, extinfo->dobj.name, fout);
10894 appendPQExpBufferStr(q, ", ");
10895 appendStringLiteralAH(q, extinfo->namespace, fout);
10896 appendPQExpBufferStr(q, ", ");
10897 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
10898 appendStringLiteralAH(q, extinfo->extversion, fout);
10899 appendPQExpBufferStr(q, ", ");
10902 * Note that we're pushing extconfig (an OID array) back into
10903 * pg_extension exactly as-is. This is OK because pg_class OIDs are
10904 * preserved in binary upgrade.
10906 if (strlen(extinfo->extconfig) > 2)
10907 appendStringLiteralAH(q, extinfo->extconfig, fout);
10908 else
10909 appendPQExpBufferStr(q, "NULL");
10910 appendPQExpBufferStr(q, ", ");
10911 if (strlen(extinfo->extcondition) > 2)
10912 appendStringLiteralAH(q, extinfo->extcondition, fout);
10913 else
10914 appendPQExpBufferStr(q, "NULL");
10915 appendPQExpBufferStr(q, ", ");
10916 appendPQExpBufferStr(q, "ARRAY[");
10917 n = 0;
10918 for (i = 0; i < extinfo->dobj.nDeps; i++)
10920 DumpableObject *extobj;
10922 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
10923 if (extobj && extobj->objType == DO_EXTENSION)
10925 if (n++ > 0)
10926 appendPQExpBufferChar(q, ',');
10927 appendStringLiteralAH(q, extobj->name, fout);
10930 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
10931 appendPQExpBufferStr(q, ");\n");
10934 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10935 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
10936 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
10937 .description = "EXTENSION",
10938 .section = SECTION_PRE_DATA,
10939 .createStmt = q->data,
10940 .dropStmt = delq->data));
10942 /* Dump Extension Comments and Security Labels */
10943 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10944 dumpComment(fout, "EXTENSION", qextname,
10945 NULL, "",
10946 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10948 if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10949 dumpSecLabel(fout, "EXTENSION", qextname,
10950 NULL, "",
10951 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10953 free(qextname);
10955 destroyPQExpBuffer(q);
10956 destroyPQExpBuffer(delq);
10960 * dumpType
10961 * writes out to fout the queries to recreate a user-defined type
10963 static void
10964 dumpType(Archive *fout, const TypeInfo *tyinfo)
10966 DumpOptions *dopt = fout->dopt;
10968 /* Do nothing if not dumping schema */
10969 if (!dopt->dumpSchema)
10970 return;
10972 /* Dump out in proper style */
10973 if (tyinfo->typtype == TYPTYPE_BASE)
10974 dumpBaseType(fout, tyinfo);
10975 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
10976 dumpDomain(fout, tyinfo);
10977 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
10978 dumpCompositeType(fout, tyinfo);
10979 else if (tyinfo->typtype == TYPTYPE_ENUM)
10980 dumpEnumType(fout, tyinfo);
10981 else if (tyinfo->typtype == TYPTYPE_RANGE)
10982 dumpRangeType(fout, tyinfo);
10983 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
10984 dumpUndefinedType(fout, tyinfo);
10985 else
10986 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
10987 tyinfo->dobj.name);
10991 * dumpEnumType
10992 * writes out to fout the queries to recreate a user-defined enum type
10994 static void
10995 dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
10997 DumpOptions *dopt = fout->dopt;
10998 PQExpBuffer q = createPQExpBuffer();
10999 PQExpBuffer delq = createPQExpBuffer();
11000 PQExpBuffer query = createPQExpBuffer();
11001 PGresult *res;
11002 int num,
11004 Oid enum_oid;
11005 char *qtypname;
11006 char *qualtypname;
11007 char *label;
11008 int i_enumlabel;
11009 int i_oid;
11011 if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11013 /* Set up query for enum-specific details */
11014 appendPQExpBufferStr(query,
11015 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11016 "SELECT oid, enumlabel "
11017 "FROM pg_catalog.pg_enum "
11018 "WHERE enumtypid = $1 "
11019 "ORDER BY enumsortorder");
11021 ExecuteSqlStatement(fout, query->data);
11023 fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11026 printfPQExpBuffer(query,
11027 "EXECUTE dumpEnumType('%u')",
11028 tyinfo->dobj.catId.oid);
11030 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11032 num = PQntuples(res);
11034 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11035 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11038 * CASCADE shouldn't be required here as for normal types since the I/O
11039 * functions are generic and do not get dropped.
11041 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11043 if (dopt->binary_upgrade)
11044 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11045 tyinfo->dobj.catId.oid,
11046 false, false);
11048 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11049 qualtypname);
11051 if (!dopt->binary_upgrade)
11053 i_enumlabel = PQfnumber(res, "enumlabel");
11055 /* Labels with server-assigned oids */
11056 for (i = 0; i < num; i++)
11058 label = PQgetvalue(res, i, i_enumlabel);
11059 if (i > 0)
11060 appendPQExpBufferChar(q, ',');
11061 appendPQExpBufferStr(q, "\n ");
11062 appendStringLiteralAH(q, label, fout);
11066 appendPQExpBufferStr(q, "\n);\n");
11068 if (dopt->binary_upgrade)
11070 i_oid = PQfnumber(res, "oid");
11071 i_enumlabel = PQfnumber(res, "enumlabel");
11073 /* Labels with dump-assigned (preserved) oids */
11074 for (i = 0; i < num; i++)
11076 enum_oid = atooid(PQgetvalue(res, i, i_oid));
11077 label = PQgetvalue(res, i, i_enumlabel);
11079 if (i == 0)
11080 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11081 appendPQExpBuffer(q,
11082 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11083 enum_oid);
11084 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11085 appendStringLiteralAH(q, label, fout);
11086 appendPQExpBufferStr(q, ";\n\n");
11090 if (dopt->binary_upgrade)
11091 binary_upgrade_extension_member(q, &tyinfo->dobj,
11092 "TYPE", qtypname,
11093 tyinfo->dobj.namespace->dobj.name);
11095 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11096 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11097 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11098 .namespace = tyinfo->dobj.namespace->dobj.name,
11099 .owner = tyinfo->rolname,
11100 .description = "TYPE",
11101 .section = SECTION_PRE_DATA,
11102 .createStmt = q->data,
11103 .dropStmt = delq->data));
11105 /* Dump Type Comments and Security Labels */
11106 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11107 dumpComment(fout, "TYPE", qtypname,
11108 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11109 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11111 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11112 dumpSecLabel(fout, "TYPE", qtypname,
11113 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11114 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11116 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11117 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11118 qtypname, NULL,
11119 tyinfo->dobj.namespace->dobj.name,
11120 NULL, tyinfo->rolname, &tyinfo->dacl);
11122 PQclear(res);
11123 destroyPQExpBuffer(q);
11124 destroyPQExpBuffer(delq);
11125 destroyPQExpBuffer(query);
11126 free(qtypname);
11127 free(qualtypname);
11131 * dumpRangeType
11132 * writes out to fout the queries to recreate a user-defined range type
11134 static void
11135 dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11137 DumpOptions *dopt = fout->dopt;
11138 PQExpBuffer q = createPQExpBuffer();
11139 PQExpBuffer delq = createPQExpBuffer();
11140 PQExpBuffer query = createPQExpBuffer();
11141 PGresult *res;
11142 Oid collationOid;
11143 char *qtypname;
11144 char *qualtypname;
11145 char *procname;
11147 if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11149 /* Set up query for range-specific details */
11150 appendPQExpBufferStr(query,
11151 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11153 appendPQExpBufferStr(query,
11154 "SELECT ");
11156 if (fout->remoteVersion >= 140000)
11157 appendPQExpBufferStr(query,
11158 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11159 else
11160 appendPQExpBufferStr(query,
11161 "NULL AS rngmultitype, ");
11163 appendPQExpBufferStr(query,
11164 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11165 "opc.opcname AS opcname, "
11166 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11167 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11168 "opc.opcdefault, "
11169 "CASE WHEN rngcollation = st.typcollation THEN 0 "
11170 " ELSE rngcollation END AS collation, "
11171 "rngcanonical, rngsubdiff "
11172 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11173 " pg_catalog.pg_opclass opc "
11174 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11175 "rngtypid = $1");
11177 ExecuteSqlStatement(fout, query->data);
11179 fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11182 printfPQExpBuffer(query,
11183 "EXECUTE dumpRangeType('%u')",
11184 tyinfo->dobj.catId.oid);
11186 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11188 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11189 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11192 * CASCADE shouldn't be required here as for normal types since the I/O
11193 * functions are generic and do not get dropped.
11195 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11197 if (dopt->binary_upgrade)
11198 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11199 tyinfo->dobj.catId.oid,
11200 false, true);
11202 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
11203 qualtypname);
11205 appendPQExpBuffer(q, "\n subtype = %s",
11206 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
11208 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
11209 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
11210 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
11212 /* print subtype_opclass only if not default for subtype */
11213 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
11215 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
11216 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
11218 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
11219 fmtId(nspname));
11220 appendPQExpBufferStr(q, fmtId(opcname));
11223 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
11224 if (OidIsValid(collationOid))
11226 CollInfo *coll = findCollationByOid(collationOid);
11228 if (coll)
11229 appendPQExpBuffer(q, ",\n collation = %s",
11230 fmtQualifiedDumpable(coll));
11233 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
11234 if (strcmp(procname, "-") != 0)
11235 appendPQExpBuffer(q, ",\n canonical = %s", procname);
11237 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
11238 if (strcmp(procname, "-") != 0)
11239 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
11241 appendPQExpBufferStr(q, "\n);\n");
11243 if (dopt->binary_upgrade)
11244 binary_upgrade_extension_member(q, &tyinfo->dobj,
11245 "TYPE", qtypname,
11246 tyinfo->dobj.namespace->dobj.name);
11248 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11249 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11250 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11251 .namespace = tyinfo->dobj.namespace->dobj.name,
11252 .owner = tyinfo->rolname,
11253 .description = "TYPE",
11254 .section = SECTION_PRE_DATA,
11255 .createStmt = q->data,
11256 .dropStmt = delq->data));
11258 /* Dump Type Comments and Security Labels */
11259 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11260 dumpComment(fout, "TYPE", qtypname,
11261 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11262 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11264 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11265 dumpSecLabel(fout, "TYPE", qtypname,
11266 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11267 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11269 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11270 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11271 qtypname, NULL,
11272 tyinfo->dobj.namespace->dobj.name,
11273 NULL, tyinfo->rolname, &tyinfo->dacl);
11275 PQclear(res);
11276 destroyPQExpBuffer(q);
11277 destroyPQExpBuffer(delq);
11278 destroyPQExpBuffer(query);
11279 free(qtypname);
11280 free(qualtypname);
11284 * dumpUndefinedType
11285 * writes out to fout the queries to recreate a !typisdefined type
11287 * This is a shell type, but we use different terminology to distinguish
11288 * this case from where we have to emit a shell type definition to break
11289 * circular dependencies. An undefined type shouldn't ever have anything
11290 * depending on it.
11292 static void
11293 dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
11295 DumpOptions *dopt = fout->dopt;
11296 PQExpBuffer q = createPQExpBuffer();
11297 PQExpBuffer delq = createPQExpBuffer();
11298 char *qtypname;
11299 char *qualtypname;
11301 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11302 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11304 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11306 if (dopt->binary_upgrade)
11307 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11308 tyinfo->dobj.catId.oid,
11309 false, false);
11311 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11312 qualtypname);
11314 if (dopt->binary_upgrade)
11315 binary_upgrade_extension_member(q, &tyinfo->dobj,
11316 "TYPE", qtypname,
11317 tyinfo->dobj.namespace->dobj.name);
11319 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11320 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11321 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11322 .namespace = tyinfo->dobj.namespace->dobj.name,
11323 .owner = tyinfo->rolname,
11324 .description = "TYPE",
11325 .section = SECTION_PRE_DATA,
11326 .createStmt = q->data,
11327 .dropStmt = delq->data));
11329 /* Dump Type Comments and Security Labels */
11330 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11331 dumpComment(fout, "TYPE", qtypname,
11332 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11333 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11335 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11336 dumpSecLabel(fout, "TYPE", qtypname,
11337 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11338 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11340 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11341 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11342 qtypname, NULL,
11343 tyinfo->dobj.namespace->dobj.name,
11344 NULL, tyinfo->rolname, &tyinfo->dacl);
11346 destroyPQExpBuffer(q);
11347 destroyPQExpBuffer(delq);
11348 free(qtypname);
11349 free(qualtypname);
11353 * dumpBaseType
11354 * writes out to fout the queries to recreate a user-defined base type
11356 static void
11357 dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11359 DumpOptions *dopt = fout->dopt;
11360 PQExpBuffer q = createPQExpBuffer();
11361 PQExpBuffer delq = createPQExpBuffer();
11362 PQExpBuffer query = createPQExpBuffer();
11363 PGresult *res;
11364 char *qtypname;
11365 char *qualtypname;
11366 char *typlen;
11367 char *typinput;
11368 char *typoutput;
11369 char *typreceive;
11370 char *typsend;
11371 char *typmodin;
11372 char *typmodout;
11373 char *typanalyze;
11374 char *typsubscript;
11375 Oid typreceiveoid;
11376 Oid typsendoid;
11377 Oid typmodinoid;
11378 Oid typmodoutoid;
11379 Oid typanalyzeoid;
11380 Oid typsubscriptoid;
11381 char *typcategory;
11382 char *typispreferred;
11383 char *typdelim;
11384 char *typbyval;
11385 char *typalign;
11386 char *typstorage;
11387 char *typcollatable;
11388 char *typdefault;
11389 bool typdefault_is_literal = false;
11391 if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11393 /* Set up query for type-specific details */
11394 appendPQExpBufferStr(query,
11395 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11396 "SELECT typlen, "
11397 "typinput, typoutput, typreceive, typsend, "
11398 "typreceive::pg_catalog.oid AS typreceiveoid, "
11399 "typsend::pg_catalog.oid AS typsendoid, "
11400 "typanalyze, "
11401 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11402 "typdelim, typbyval, typalign, typstorage, "
11403 "typmodin, typmodout, "
11404 "typmodin::pg_catalog.oid AS typmodinoid, "
11405 "typmodout::pg_catalog.oid AS typmodoutoid, "
11406 "typcategory, typispreferred, "
11407 "(typcollation <> 0) AS typcollatable, "
11408 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11410 if (fout->remoteVersion >= 140000)
11411 appendPQExpBufferStr(query,
11412 "typsubscript, "
11413 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11414 else
11415 appendPQExpBufferStr(query,
11416 "'-' AS typsubscript, 0 AS typsubscriptoid ");
11418 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11419 "WHERE oid = $1");
11421 ExecuteSqlStatement(fout, query->data);
11423 fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11426 printfPQExpBuffer(query,
11427 "EXECUTE dumpBaseType('%u')",
11428 tyinfo->dobj.catId.oid);
11430 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11432 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11433 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11434 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11435 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11436 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11437 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11438 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11439 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11440 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11441 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11442 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11443 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11444 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11445 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11446 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11447 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11448 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11449 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11450 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11451 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11452 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11453 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11454 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11455 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11456 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11458 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11459 typdefault_is_literal = true; /* it needs quotes */
11461 else
11462 typdefault = NULL;
11464 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11465 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11468 * The reason we include CASCADE is that the circular dependency between
11469 * the type and its I/O functions makes it impossible to drop the type any
11470 * other way.
11472 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11475 * We might already have a shell type, but setting pg_type_oid is
11476 * harmless, and in any case we'd better set the array type OID.
11478 if (dopt->binary_upgrade)
11479 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11480 tyinfo->dobj.catId.oid,
11481 false, false);
11483 appendPQExpBuffer(q,
11484 "CREATE TYPE %s (\n"
11485 " INTERNALLENGTH = %s",
11486 qualtypname,
11487 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11489 /* regproc result is sufficiently quoted already */
11490 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11491 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11492 if (OidIsValid(typreceiveoid))
11493 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11494 if (OidIsValid(typsendoid))
11495 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11496 if (OidIsValid(typmodinoid))
11497 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11498 if (OidIsValid(typmodoutoid))
11499 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11500 if (OidIsValid(typanalyzeoid))
11501 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11503 if (strcmp(typcollatable, "t") == 0)
11504 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11506 if (typdefault != NULL)
11508 appendPQExpBufferStr(q, ",\n DEFAULT = ");
11509 if (typdefault_is_literal)
11510 appendStringLiteralAH(q, typdefault, fout);
11511 else
11512 appendPQExpBufferStr(q, typdefault);
11515 if (OidIsValid(typsubscriptoid))
11516 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
11518 if (OidIsValid(tyinfo->typelem))
11519 appendPQExpBuffer(q, ",\n ELEMENT = %s",
11520 getFormattedTypeName(fout, tyinfo->typelem,
11521 zeroIsError));
11523 if (strcmp(typcategory, "U") != 0)
11525 appendPQExpBufferStr(q, ",\n CATEGORY = ");
11526 appendStringLiteralAH(q, typcategory, fout);
11529 if (strcmp(typispreferred, "t") == 0)
11530 appendPQExpBufferStr(q, ",\n PREFERRED = true");
11532 if (typdelim && strcmp(typdelim, ",") != 0)
11534 appendPQExpBufferStr(q, ",\n DELIMITER = ");
11535 appendStringLiteralAH(q, typdelim, fout);
11538 if (*typalign == TYPALIGN_CHAR)
11539 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
11540 else if (*typalign == TYPALIGN_SHORT)
11541 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
11542 else if (*typalign == TYPALIGN_INT)
11543 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
11544 else if (*typalign == TYPALIGN_DOUBLE)
11545 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
11547 if (*typstorage == TYPSTORAGE_PLAIN)
11548 appendPQExpBufferStr(q, ",\n STORAGE = plain");
11549 else if (*typstorage == TYPSTORAGE_EXTERNAL)
11550 appendPQExpBufferStr(q, ",\n STORAGE = external");
11551 else if (*typstorage == TYPSTORAGE_EXTENDED)
11552 appendPQExpBufferStr(q, ",\n STORAGE = extended");
11553 else if (*typstorage == TYPSTORAGE_MAIN)
11554 appendPQExpBufferStr(q, ",\n STORAGE = main");
11556 if (strcmp(typbyval, "t") == 0)
11557 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
11559 appendPQExpBufferStr(q, "\n);\n");
11561 if (dopt->binary_upgrade)
11562 binary_upgrade_extension_member(q, &tyinfo->dobj,
11563 "TYPE", qtypname,
11564 tyinfo->dobj.namespace->dobj.name);
11566 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11567 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11568 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11569 .namespace = tyinfo->dobj.namespace->dobj.name,
11570 .owner = tyinfo->rolname,
11571 .description = "TYPE",
11572 .section = SECTION_PRE_DATA,
11573 .createStmt = q->data,
11574 .dropStmt = delq->data));
11576 /* Dump Type Comments and Security Labels */
11577 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11578 dumpComment(fout, "TYPE", qtypname,
11579 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11580 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11582 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11583 dumpSecLabel(fout, "TYPE", qtypname,
11584 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11585 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11587 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11588 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11589 qtypname, NULL,
11590 tyinfo->dobj.namespace->dobj.name,
11591 NULL, tyinfo->rolname, &tyinfo->dacl);
11593 PQclear(res);
11594 destroyPQExpBuffer(q);
11595 destroyPQExpBuffer(delq);
11596 destroyPQExpBuffer(query);
11597 free(qtypname);
11598 free(qualtypname);
11602 * dumpDomain
11603 * writes out to fout the queries to recreate a user-defined domain
11605 static void
11606 dumpDomain(Archive *fout, const TypeInfo *tyinfo)
11608 DumpOptions *dopt = fout->dopt;
11609 PQExpBuffer q = createPQExpBuffer();
11610 PQExpBuffer delq = createPQExpBuffer();
11611 PQExpBuffer query = createPQExpBuffer();
11612 PGresult *res;
11613 int i;
11614 char *qtypname;
11615 char *qualtypname;
11616 char *typnotnull;
11617 char *typdefn;
11618 char *typdefault;
11619 Oid typcollation;
11620 bool typdefault_is_literal = false;
11622 if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
11624 /* Set up query for domain-specific details */
11625 appendPQExpBufferStr(query,
11626 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
11628 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
11629 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
11630 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
11631 "t.typdefault, "
11632 "CASE WHEN t.typcollation <> u.typcollation "
11633 "THEN t.typcollation ELSE 0 END AS typcollation "
11634 "FROM pg_catalog.pg_type t "
11635 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
11636 "WHERE t.oid = $1");
11638 ExecuteSqlStatement(fout, query->data);
11640 fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
11643 printfPQExpBuffer(query,
11644 "EXECUTE dumpDomain('%u')",
11645 tyinfo->dobj.catId.oid);
11647 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11649 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
11650 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
11651 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11652 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11653 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11655 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11656 typdefault_is_literal = true; /* it needs quotes */
11658 else
11659 typdefault = NULL;
11660 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
11662 if (dopt->binary_upgrade)
11663 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11664 tyinfo->dobj.catId.oid,
11665 true, /* force array type */
11666 false); /* force multirange type */
11668 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11669 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11671 appendPQExpBuffer(q,
11672 "CREATE DOMAIN %s AS %s",
11673 qualtypname,
11674 typdefn);
11676 /* Print collation only if different from base type's collation */
11677 if (OidIsValid(typcollation))
11679 CollInfo *coll;
11681 coll = findCollationByOid(typcollation);
11682 if (coll)
11683 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
11686 if (typnotnull[0] == 't')
11687 appendPQExpBufferStr(q, " NOT NULL");
11689 if (typdefault != NULL)
11691 appendPQExpBufferStr(q, " DEFAULT ");
11692 if (typdefault_is_literal)
11693 appendStringLiteralAH(q, typdefault, fout);
11694 else
11695 appendPQExpBufferStr(q, typdefault);
11698 PQclear(res);
11701 * Add any CHECK constraints for the domain
11703 for (i = 0; i < tyinfo->nDomChecks; i++)
11705 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11707 if (!domcheck->separate)
11708 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
11709 fmtId(domcheck->dobj.name), domcheck->condef);
11712 appendPQExpBufferStr(q, ";\n");
11714 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
11716 if (dopt->binary_upgrade)
11717 binary_upgrade_extension_member(q, &tyinfo->dobj,
11718 "DOMAIN", qtypname,
11719 tyinfo->dobj.namespace->dobj.name);
11721 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11722 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11723 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11724 .namespace = tyinfo->dobj.namespace->dobj.name,
11725 .owner = tyinfo->rolname,
11726 .description = "DOMAIN",
11727 .section = SECTION_PRE_DATA,
11728 .createStmt = q->data,
11729 .dropStmt = delq->data));
11731 /* Dump Domain Comments and Security Labels */
11732 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11733 dumpComment(fout, "DOMAIN", qtypname,
11734 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11735 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11737 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11738 dumpSecLabel(fout, "DOMAIN", qtypname,
11739 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11740 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11742 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11743 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11744 qtypname, NULL,
11745 tyinfo->dobj.namespace->dobj.name,
11746 NULL, tyinfo->rolname, &tyinfo->dacl);
11748 /* Dump any per-constraint comments */
11749 for (i = 0; i < tyinfo->nDomChecks; i++)
11751 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11752 PQExpBuffer conprefix = createPQExpBuffer();
11754 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
11755 fmtId(domcheck->dobj.name));
11757 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
11758 dumpComment(fout, conprefix->data, qtypname,
11759 tyinfo->dobj.namespace->dobj.name,
11760 tyinfo->rolname,
11761 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
11763 destroyPQExpBuffer(conprefix);
11766 destroyPQExpBuffer(q);
11767 destroyPQExpBuffer(delq);
11768 destroyPQExpBuffer(query);
11769 free(qtypname);
11770 free(qualtypname);
11774 * dumpCompositeType
11775 * writes out to fout the queries to recreate a user-defined stand-alone
11776 * composite type
11778 static void
11779 dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
11781 DumpOptions *dopt = fout->dopt;
11782 PQExpBuffer q = createPQExpBuffer();
11783 PQExpBuffer dropped = createPQExpBuffer();
11784 PQExpBuffer delq = createPQExpBuffer();
11785 PQExpBuffer query = createPQExpBuffer();
11786 PGresult *res;
11787 char *qtypname;
11788 char *qualtypname;
11789 int ntups;
11790 int i_attname;
11791 int i_atttypdefn;
11792 int i_attlen;
11793 int i_attalign;
11794 int i_attisdropped;
11795 int i_attcollation;
11796 int i;
11797 int actual_atts;
11799 if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
11802 * Set up query for type-specific details.
11804 * Since we only want to dump COLLATE clauses for attributes whose
11805 * collation is different from their type's default, we use a CASE
11806 * here to suppress uninteresting attcollations cheaply. atttypid
11807 * will be 0 for dropped columns; collation does not matter for those.
11809 appendPQExpBufferStr(query,
11810 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
11811 "SELECT a.attname, a.attnum, "
11812 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
11813 "a.attlen, a.attalign, a.attisdropped, "
11814 "CASE WHEN a.attcollation <> at.typcollation "
11815 "THEN a.attcollation ELSE 0 END AS attcollation "
11816 "FROM pg_catalog.pg_type ct "
11817 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
11818 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
11819 "WHERE ct.oid = $1 "
11820 "ORDER BY a.attnum");
11822 ExecuteSqlStatement(fout, query->data);
11824 fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
11827 printfPQExpBuffer(query,
11828 "EXECUTE dumpCompositeType('%u')",
11829 tyinfo->dobj.catId.oid);
11831 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11833 ntups = PQntuples(res);
11835 i_attname = PQfnumber(res, "attname");
11836 i_atttypdefn = PQfnumber(res, "atttypdefn");
11837 i_attlen = PQfnumber(res, "attlen");
11838 i_attalign = PQfnumber(res, "attalign");
11839 i_attisdropped = PQfnumber(res, "attisdropped");
11840 i_attcollation = PQfnumber(res, "attcollation");
11842 if (dopt->binary_upgrade)
11844 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11845 tyinfo->dobj.catId.oid,
11846 false, false);
11847 binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
11850 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11851 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11853 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
11854 qualtypname);
11856 actual_atts = 0;
11857 for (i = 0; i < ntups; i++)
11859 char *attname;
11860 char *atttypdefn;
11861 char *attlen;
11862 char *attalign;
11863 bool attisdropped;
11864 Oid attcollation;
11866 attname = PQgetvalue(res, i, i_attname);
11867 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
11868 attlen = PQgetvalue(res, i, i_attlen);
11869 attalign = PQgetvalue(res, i, i_attalign);
11870 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
11871 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
11873 if (attisdropped && !dopt->binary_upgrade)
11874 continue;
11876 /* Format properly if not first attr */
11877 if (actual_atts++ > 0)
11878 appendPQExpBufferChar(q, ',');
11879 appendPQExpBufferStr(q, "\n\t");
11881 if (!attisdropped)
11883 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
11885 /* Add collation if not default for the column type */
11886 if (OidIsValid(attcollation))
11888 CollInfo *coll;
11890 coll = findCollationByOid(attcollation);
11891 if (coll)
11892 appendPQExpBuffer(q, " COLLATE %s",
11893 fmtQualifiedDumpable(coll));
11896 else
11899 * This is a dropped attribute and we're in binary_upgrade mode.
11900 * Insert a placeholder for it in the CREATE TYPE command, and set
11901 * length and alignment with direct UPDATE to the catalogs
11902 * afterwards. See similar code in dumpTableSchema().
11904 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
11906 /* stash separately for insertion after the CREATE TYPE */
11907 appendPQExpBufferStr(dropped,
11908 "\n-- For binary upgrade, recreate dropped column.\n");
11909 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
11910 "SET attlen = %s, "
11911 "attalign = '%s', attbyval = false\n"
11912 "WHERE attname = ", attlen, attalign);
11913 appendStringLiteralAH(dropped, attname, fout);
11914 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
11915 appendStringLiteralAH(dropped, qualtypname, fout);
11916 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
11918 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
11919 qualtypname);
11920 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
11921 fmtId(attname));
11924 appendPQExpBufferStr(q, "\n);\n");
11925 appendPQExpBufferStr(q, dropped->data);
11927 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11929 if (dopt->binary_upgrade)
11930 binary_upgrade_extension_member(q, &tyinfo->dobj,
11931 "TYPE", qtypname,
11932 tyinfo->dobj.namespace->dobj.name);
11934 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11935 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11936 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11937 .namespace = tyinfo->dobj.namespace->dobj.name,
11938 .owner = tyinfo->rolname,
11939 .description = "TYPE",
11940 .section = SECTION_PRE_DATA,
11941 .createStmt = q->data,
11942 .dropStmt = delq->data));
11945 /* Dump Type Comments and Security Labels */
11946 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11947 dumpComment(fout, "TYPE", qtypname,
11948 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11949 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11951 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11952 dumpSecLabel(fout, "TYPE", qtypname,
11953 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11954 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11956 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11957 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11958 qtypname, NULL,
11959 tyinfo->dobj.namespace->dobj.name,
11960 NULL, tyinfo->rolname, &tyinfo->dacl);
11962 /* Dump any per-column comments */
11963 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11964 dumpCompositeTypeColComments(fout, tyinfo, res);
11966 PQclear(res);
11967 destroyPQExpBuffer(q);
11968 destroyPQExpBuffer(dropped);
11969 destroyPQExpBuffer(delq);
11970 destroyPQExpBuffer(query);
11971 free(qtypname);
11972 free(qualtypname);
11976 * dumpCompositeTypeColComments
11977 * writes out to fout the queries to recreate comments on the columns of
11978 * a user-defined stand-alone composite type.
11980 * The caller has already made a query to collect the names and attnums
11981 * of the type's columns, so we just pass that result into here rather
11982 * than reading them again.
11984 static void
11985 dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
11986 PGresult *res)
11988 CommentItem *comments;
11989 int ncomments;
11990 PQExpBuffer query;
11991 PQExpBuffer target;
11992 int i;
11993 int ntups;
11994 int i_attname;
11995 int i_attnum;
11996 int i_attisdropped;
11998 /* do nothing, if --no-comments is supplied */
11999 if (fout->dopt->no_comments)
12000 return;
12002 /* Search for comments associated with type's pg_class OID */
12003 ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12004 &comments);
12006 /* If no comments exist, we're done */
12007 if (ncomments <= 0)
12008 return;
12010 /* Build COMMENT ON statements */
12011 query = createPQExpBuffer();
12012 target = createPQExpBuffer();
12014 ntups = PQntuples(res);
12015 i_attnum = PQfnumber(res, "attnum");
12016 i_attname = PQfnumber(res, "attname");
12017 i_attisdropped = PQfnumber(res, "attisdropped");
12018 while (ncomments > 0)
12020 const char *attname;
12022 attname = NULL;
12023 for (i = 0; i < ntups; i++)
12025 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12026 PQgetvalue(res, i, i_attisdropped)[0] != 't')
12028 attname = PQgetvalue(res, i, i_attname);
12029 break;
12032 if (attname) /* just in case we don't find it */
12034 const char *descr = comments->descr;
12036 resetPQExpBuffer(target);
12037 appendPQExpBuffer(target, "COLUMN %s.",
12038 fmtId(tyinfo->dobj.name));
12039 appendPQExpBufferStr(target, fmtId(attname));
12041 resetPQExpBuffer(query);
12042 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12043 fmtQualifiedDumpable(tyinfo));
12044 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12045 appendStringLiteralAH(query, descr, fout);
12046 appendPQExpBufferStr(query, ";\n");
12048 ArchiveEntry(fout, nilCatalogId, createDumpId(),
12049 ARCHIVE_OPTS(.tag = target->data,
12050 .namespace = tyinfo->dobj.namespace->dobj.name,
12051 .owner = tyinfo->rolname,
12052 .description = "COMMENT",
12053 .section = SECTION_NONE,
12054 .createStmt = query->data,
12055 .deps = &(tyinfo->dobj.dumpId),
12056 .nDeps = 1));
12059 comments++;
12060 ncomments--;
12063 destroyPQExpBuffer(query);
12064 destroyPQExpBuffer(target);
12068 * dumpShellType
12069 * writes out to fout the queries to create a shell type
12071 * We dump a shell definition in advance of the I/O functions for the type.
12073 static void
12074 dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12076 DumpOptions *dopt = fout->dopt;
12077 PQExpBuffer q;
12079 /* Do nothing if not dumping schema */
12080 if (!dopt->dumpSchema)
12081 return;
12083 q = createPQExpBuffer();
12086 * Note the lack of a DROP command for the shell type; any required DROP
12087 * is driven off the base type entry, instead. This interacts with
12088 * _printTocEntry()'s use of the presence of a DROP command to decide
12089 * whether an entry needs an ALTER OWNER command. We don't want to alter
12090 * the shell type's owner immediately on creation; that should happen only
12091 * after it's filled in, otherwise the backend complains.
12094 if (dopt->binary_upgrade)
12095 binary_upgrade_set_type_oids_by_type_oid(fout, q,
12096 stinfo->baseType->dobj.catId.oid,
12097 false, false);
12099 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12100 fmtQualifiedDumpable(stinfo));
12102 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12103 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12104 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12105 .namespace = stinfo->dobj.namespace->dobj.name,
12106 .owner = stinfo->baseType->rolname,
12107 .description = "SHELL TYPE",
12108 .section = SECTION_PRE_DATA,
12109 .createStmt = q->data));
12111 destroyPQExpBuffer(q);
12115 * dumpProcLang
12116 * writes out to fout the queries to recreate a user-defined
12117 * procedural language
12119 static void
12120 dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12122 DumpOptions *dopt = fout->dopt;
12123 PQExpBuffer defqry;
12124 PQExpBuffer delqry;
12125 bool useParams;
12126 char *qlanname;
12127 FuncInfo *funcInfo;
12128 FuncInfo *inlineInfo = NULL;
12129 FuncInfo *validatorInfo = NULL;
12131 /* Do nothing if not dumping schema */
12132 if (!dopt->dumpSchema)
12133 return;
12136 * Try to find the support function(s). It is not an error if we don't
12137 * find them --- if the functions are in the pg_catalog schema, as is
12138 * standard in 8.1 and up, then we won't have loaded them. (In this case
12139 * we will emit a parameterless CREATE LANGUAGE command, which will
12140 * require PL template knowledge in the backend to reload.)
12143 funcInfo = findFuncByOid(plang->lanplcallfoid);
12144 if (funcInfo != NULL && !funcInfo->dobj.dump)
12145 funcInfo = NULL; /* treat not-dumped same as not-found */
12147 if (OidIsValid(plang->laninline))
12149 inlineInfo = findFuncByOid(plang->laninline);
12150 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12151 inlineInfo = NULL;
12154 if (OidIsValid(plang->lanvalidator))
12156 validatorInfo = findFuncByOid(plang->lanvalidator);
12157 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12158 validatorInfo = NULL;
12162 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12163 * parameters. Otherwise, we'll write a parameterless command, which will
12164 * be interpreted as CREATE EXTENSION.
12166 useParams = (funcInfo != NULL &&
12167 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12168 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12170 defqry = createPQExpBuffer();
12171 delqry = createPQExpBuffer();
12173 qlanname = pg_strdup(fmtId(plang->dobj.name));
12175 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12176 qlanname);
12178 if (useParams)
12180 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12181 plang->lanpltrusted ? "TRUSTED " : "",
12182 qlanname);
12183 appendPQExpBuffer(defqry, " HANDLER %s",
12184 fmtQualifiedDumpable(funcInfo));
12185 if (OidIsValid(plang->laninline))
12186 appendPQExpBuffer(defqry, " INLINE %s",
12187 fmtQualifiedDumpable(inlineInfo));
12188 if (OidIsValid(plang->lanvalidator))
12189 appendPQExpBuffer(defqry, " VALIDATOR %s",
12190 fmtQualifiedDumpable(validatorInfo));
12192 else
12195 * If not dumping parameters, then use CREATE OR REPLACE so that the
12196 * command will not fail if the language is preinstalled in the target
12197 * database.
12199 * Modern servers will interpret this as CREATE EXTENSION IF NOT
12200 * EXISTS; perhaps we should emit that instead? But it might just add
12201 * confusion.
12203 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
12204 qlanname);
12206 appendPQExpBufferStr(defqry, ";\n");
12208 if (dopt->binary_upgrade)
12209 binary_upgrade_extension_member(defqry, &plang->dobj,
12210 "LANGUAGE", qlanname, NULL);
12212 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
12213 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
12214 ARCHIVE_OPTS(.tag = plang->dobj.name,
12215 .owner = plang->lanowner,
12216 .description = "PROCEDURAL LANGUAGE",
12217 .section = SECTION_PRE_DATA,
12218 .createStmt = defqry->data,
12219 .dropStmt = delqry->data,
12222 /* Dump Proc Lang Comments and Security Labels */
12223 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
12224 dumpComment(fout, "LANGUAGE", qlanname,
12225 NULL, plang->lanowner,
12226 plang->dobj.catId, 0, plang->dobj.dumpId);
12228 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
12229 dumpSecLabel(fout, "LANGUAGE", qlanname,
12230 NULL, plang->lanowner,
12231 plang->dobj.catId, 0, plang->dobj.dumpId);
12233 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
12234 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
12235 qlanname, NULL, NULL,
12236 NULL, plang->lanowner, &plang->dacl);
12238 free(qlanname);
12240 destroyPQExpBuffer(defqry);
12241 destroyPQExpBuffer(delqry);
12245 * format_function_arguments: generate function name and argument list
12247 * This is used when we can rely on pg_get_function_arguments to format
12248 * the argument list. Note, however, that pg_get_function_arguments
12249 * does not special-case zero-argument aggregates.
12251 static char *
12252 format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
12254 PQExpBufferData fn;
12256 initPQExpBuffer(&fn);
12257 appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
12258 if (is_agg && finfo->nargs == 0)
12259 appendPQExpBufferStr(&fn, "(*)");
12260 else
12261 appendPQExpBuffer(&fn, "(%s)", funcargs);
12262 return fn.data;
12266 * format_function_signature: generate function name and argument list
12268 * Only a minimal list of input argument types is generated; this is
12269 * sufficient to reference the function, but not to define it.
12271 * If honor_quotes is false then the function name is never quoted.
12272 * This is appropriate for use in TOC tags, but not in SQL commands.
12274 static char *
12275 format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
12277 PQExpBufferData fn;
12278 int j;
12280 initPQExpBuffer(&fn);
12281 if (honor_quotes)
12282 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
12283 else
12284 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
12285 for (j = 0; j < finfo->nargs; j++)
12287 if (j > 0)
12288 appendPQExpBufferStr(&fn, ", ");
12290 appendPQExpBufferStr(&fn,
12291 getFormattedTypeName(fout, finfo->argtypes[j],
12292 zeroIsError));
12294 appendPQExpBufferChar(&fn, ')');
12295 return fn.data;
12300 * dumpFunc:
12301 * dump out one function
12303 static void
12304 dumpFunc(Archive *fout, const FuncInfo *finfo)
12306 DumpOptions *dopt = fout->dopt;
12307 PQExpBuffer query;
12308 PQExpBuffer q;
12309 PQExpBuffer delqry;
12310 PQExpBuffer asPart;
12311 PGresult *res;
12312 char *funcsig; /* identity signature */
12313 char *funcfullsig = NULL; /* full signature */
12314 char *funcsig_tag;
12315 char *qual_funcsig;
12316 char *proretset;
12317 char *prosrc;
12318 char *probin;
12319 char *prosqlbody;
12320 char *funcargs;
12321 char *funciargs;
12322 char *funcresult;
12323 char *protrftypes;
12324 char *prokind;
12325 char *provolatile;
12326 char *proisstrict;
12327 char *prosecdef;
12328 char *proleakproof;
12329 char *proconfig;
12330 char *procost;
12331 char *prorows;
12332 char *prosupport;
12333 char *proparallel;
12334 char *lanname;
12335 char **configitems = NULL;
12336 int nconfigitems = 0;
12337 const char *keyword;
12339 /* Do nothing if not dumping schema */
12340 if (!dopt->dumpSchema)
12341 return;
12343 query = createPQExpBuffer();
12344 q = createPQExpBuffer();
12345 delqry = createPQExpBuffer();
12346 asPart = createPQExpBuffer();
12348 if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12350 /* Set up query for function-specific details */
12351 appendPQExpBufferStr(query,
12352 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12354 appendPQExpBufferStr(query,
12355 "SELECT\n"
12356 "proretset,\n"
12357 "prosrc,\n"
12358 "probin,\n"
12359 "provolatile,\n"
12360 "proisstrict,\n"
12361 "prosecdef,\n"
12362 "lanname,\n"
12363 "proconfig,\n"
12364 "procost,\n"
12365 "prorows,\n"
12366 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12367 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12368 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12369 "proleakproof,\n");
12371 if (fout->remoteVersion >= 90500)
12372 appendPQExpBufferStr(query,
12373 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12374 else
12375 appendPQExpBufferStr(query,
12376 "NULL AS protrftypes,\n");
12378 if (fout->remoteVersion >= 90600)
12379 appendPQExpBufferStr(query,
12380 "proparallel,\n");
12381 else
12382 appendPQExpBufferStr(query,
12383 "'u' AS proparallel,\n");
12385 if (fout->remoteVersion >= 110000)
12386 appendPQExpBufferStr(query,
12387 "prokind,\n");
12388 else
12389 appendPQExpBufferStr(query,
12390 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12392 if (fout->remoteVersion >= 120000)
12393 appendPQExpBufferStr(query,
12394 "prosupport,\n");
12395 else
12396 appendPQExpBufferStr(query,
12397 "'-' AS prosupport,\n");
12399 if (fout->remoteVersion >= 140000)
12400 appendPQExpBufferStr(query,
12401 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12402 else
12403 appendPQExpBufferStr(query,
12404 "NULL AS prosqlbody\n");
12406 appendPQExpBufferStr(query,
12407 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12408 "WHERE p.oid = $1 "
12409 "AND l.oid = p.prolang");
12411 ExecuteSqlStatement(fout, query->data);
12413 fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12416 printfPQExpBuffer(query,
12417 "EXECUTE dumpFunc('%u')",
12418 finfo->dobj.catId.oid);
12420 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12422 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12423 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12425 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12426 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12427 prosqlbody = NULL;
12429 else
12431 prosrc = NULL;
12432 probin = NULL;
12433 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12435 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12436 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12437 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12438 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12439 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12440 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12441 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12442 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12443 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12444 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12445 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12446 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12447 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12448 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12449 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12452 * See backend/commands/functioncmds.c for details of how the 'AS' clause
12453 * is used.
12455 if (prosqlbody)
12457 appendPQExpBufferStr(asPart, prosqlbody);
12459 else if (probin[0] != '\0')
12461 appendPQExpBufferStr(asPart, "AS ");
12462 appendStringLiteralAH(asPart, probin, fout);
12463 if (prosrc[0] != '\0')
12465 appendPQExpBufferStr(asPart, ", ");
12468 * where we have bin, use dollar quoting if allowed and src
12469 * contains quote or backslash; else use regular quoting.
12471 if (dopt->disable_dollar_quoting ||
12472 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12473 appendStringLiteralAH(asPart, prosrc, fout);
12474 else
12475 appendStringLiteralDQ(asPart, prosrc, NULL);
12478 else
12480 appendPQExpBufferStr(asPart, "AS ");
12481 /* with no bin, dollar quote src unconditionally if allowed */
12482 if (dopt->disable_dollar_quoting)
12483 appendStringLiteralAH(asPart, prosrc, fout);
12484 else
12485 appendStringLiteralDQ(asPart, prosrc, NULL);
12488 if (*proconfig)
12490 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12491 pg_fatal("could not parse %s array", "proconfig");
12493 else
12495 configitems = NULL;
12496 nconfigitems = 0;
12499 funcfullsig = format_function_arguments(finfo, funcargs, false);
12500 funcsig = format_function_arguments(finfo, funciargs, false);
12502 funcsig_tag = format_function_signature(fout, finfo, false);
12504 qual_funcsig = psprintf("%s.%s",
12505 fmtId(finfo->dobj.namespace->dobj.name),
12506 funcsig);
12508 if (prokind[0] == PROKIND_PROCEDURE)
12509 keyword = "PROCEDURE";
12510 else
12511 keyword = "FUNCTION"; /* works for window functions too */
12513 appendPQExpBuffer(delqry, "DROP %s %s;\n",
12514 keyword, qual_funcsig);
12516 appendPQExpBuffer(q, "CREATE %s %s.%s",
12517 keyword,
12518 fmtId(finfo->dobj.namespace->dobj.name),
12519 funcfullsig ? funcfullsig :
12520 funcsig);
12522 if (prokind[0] == PROKIND_PROCEDURE)
12523 /* no result type to output */ ;
12524 else if (funcresult)
12525 appendPQExpBuffer(q, " RETURNS %s", funcresult);
12526 else
12527 appendPQExpBuffer(q, " RETURNS %s%s",
12528 (proretset[0] == 't') ? "SETOF " : "",
12529 getFormattedTypeName(fout, finfo->prorettype,
12530 zeroIsError));
12532 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
12534 if (*protrftypes)
12536 Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
12537 int i;
12539 appendPQExpBufferStr(q, " TRANSFORM ");
12540 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
12541 for (i = 0; typeids[i]; i++)
12543 if (i != 0)
12544 appendPQExpBufferStr(q, ", ");
12545 appendPQExpBuffer(q, "FOR TYPE %s",
12546 getFormattedTypeName(fout, typeids[i], zeroAsNone));
12549 free(typeids);
12552 if (prokind[0] == PROKIND_WINDOW)
12553 appendPQExpBufferStr(q, " WINDOW");
12555 if (provolatile[0] != PROVOLATILE_VOLATILE)
12557 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
12558 appendPQExpBufferStr(q, " IMMUTABLE");
12559 else if (provolatile[0] == PROVOLATILE_STABLE)
12560 appendPQExpBufferStr(q, " STABLE");
12561 else if (provolatile[0] != PROVOLATILE_VOLATILE)
12562 pg_fatal("unrecognized provolatile value for function \"%s\"",
12563 finfo->dobj.name);
12566 if (proisstrict[0] == 't')
12567 appendPQExpBufferStr(q, " STRICT");
12569 if (prosecdef[0] == 't')
12570 appendPQExpBufferStr(q, " SECURITY DEFINER");
12572 if (proleakproof[0] == 't')
12573 appendPQExpBufferStr(q, " LEAKPROOF");
12576 * COST and ROWS are emitted only if present and not default, so as not to
12577 * break backwards-compatibility of the dump without need. Keep this code
12578 * in sync with the defaults in functioncmds.c.
12580 if (strcmp(procost, "0") != 0)
12582 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12584 /* default cost is 1 */
12585 if (strcmp(procost, "1") != 0)
12586 appendPQExpBuffer(q, " COST %s", procost);
12588 else
12590 /* default cost is 100 */
12591 if (strcmp(procost, "100") != 0)
12592 appendPQExpBuffer(q, " COST %s", procost);
12595 if (proretset[0] == 't' &&
12596 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12597 appendPQExpBuffer(q, " ROWS %s", prorows);
12599 if (strcmp(prosupport, "-") != 0)
12601 /* We rely on regprocout to provide quoting and qualification */
12602 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12605 if (proparallel[0] != PROPARALLEL_UNSAFE)
12607 if (proparallel[0] == PROPARALLEL_SAFE)
12608 appendPQExpBufferStr(q, " PARALLEL SAFE");
12609 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12610 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12611 else if (proparallel[0] != PROPARALLEL_UNSAFE)
12612 pg_fatal("unrecognized proparallel value for function \"%s\"",
12613 finfo->dobj.name);
12616 for (int i = 0; i < nconfigitems; i++)
12618 /* we feel free to scribble on configitems[] here */
12619 char *configitem = configitems[i];
12620 char *pos;
12622 pos = strchr(configitem, '=');
12623 if (pos == NULL)
12624 continue;
12625 *pos++ = '\0';
12626 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
12629 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
12630 * by flatten_set_variable_args() before they were put into the
12631 * proconfig array. However, because the quoting rules used there
12632 * aren't exactly like SQL's, we have to break the list value apart
12633 * and then quote the elements as string literals. (The elements may
12634 * be double-quoted as-is, but we can't just feed them to the SQL
12635 * parser; it would do the wrong thing with elements that are
12636 * zero-length or longer than NAMEDATALEN.)
12638 * Variables that are not so marked should just be emitted as simple
12639 * string literals. If the variable is not known to
12640 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
12641 * to use GUC_LIST_QUOTE for extension variables.
12643 if (variable_is_guc_list_quote(configitem))
12645 char **namelist;
12646 char **nameptr;
12648 /* Parse string into list of identifiers */
12649 /* this shouldn't fail really */
12650 if (SplitGUCList(pos, ',', &namelist))
12652 for (nameptr = namelist; *nameptr; nameptr++)
12654 if (nameptr != namelist)
12655 appendPQExpBufferStr(q, ", ");
12656 appendStringLiteralAH(q, *nameptr, fout);
12659 pg_free(namelist);
12661 else
12662 appendStringLiteralAH(q, pos, fout);
12665 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
12667 append_depends_on_extension(fout, q, &finfo->dobj,
12668 "pg_catalog.pg_proc", keyword,
12669 qual_funcsig);
12671 if (dopt->binary_upgrade)
12672 binary_upgrade_extension_member(q, &finfo->dobj,
12673 keyword, funcsig,
12674 finfo->dobj.namespace->dobj.name);
12676 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12677 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
12678 ARCHIVE_OPTS(.tag = funcsig_tag,
12679 .namespace = finfo->dobj.namespace->dobj.name,
12680 .owner = finfo->rolname,
12681 .description = keyword,
12682 .section = finfo->postponed_def ?
12683 SECTION_POST_DATA : SECTION_PRE_DATA,
12684 .createStmt = q->data,
12685 .dropStmt = delqry->data));
12687 /* Dump Function Comments and Security Labels */
12688 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12689 dumpComment(fout, keyword, funcsig,
12690 finfo->dobj.namespace->dobj.name, finfo->rolname,
12691 finfo->dobj.catId, 0, finfo->dobj.dumpId);
12693 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12694 dumpSecLabel(fout, keyword, funcsig,
12695 finfo->dobj.namespace->dobj.name, finfo->rolname,
12696 finfo->dobj.catId, 0, finfo->dobj.dumpId);
12698 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12699 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
12700 funcsig, NULL,
12701 finfo->dobj.namespace->dobj.name,
12702 NULL, finfo->rolname, &finfo->dacl);
12704 PQclear(res);
12706 destroyPQExpBuffer(query);
12707 destroyPQExpBuffer(q);
12708 destroyPQExpBuffer(delqry);
12709 destroyPQExpBuffer(asPart);
12710 free(funcsig);
12711 free(funcfullsig);
12712 free(funcsig_tag);
12713 free(qual_funcsig);
12714 free(configitems);
12719 * Dump a user-defined cast
12721 static void
12722 dumpCast(Archive *fout, const CastInfo *cast)
12724 DumpOptions *dopt = fout->dopt;
12725 PQExpBuffer defqry;
12726 PQExpBuffer delqry;
12727 PQExpBuffer labelq;
12728 PQExpBuffer castargs;
12729 FuncInfo *funcInfo = NULL;
12730 const char *sourceType;
12731 const char *targetType;
12733 /* Do nothing if not dumping schema */
12734 if (!dopt->dumpSchema)
12735 return;
12737 /* Cannot dump if we don't have the cast function's info */
12738 if (OidIsValid(cast->castfunc))
12740 funcInfo = findFuncByOid(cast->castfunc);
12741 if (funcInfo == NULL)
12742 pg_fatal("could not find function definition for function with OID %u",
12743 cast->castfunc);
12746 defqry = createPQExpBuffer();
12747 delqry = createPQExpBuffer();
12748 labelq = createPQExpBuffer();
12749 castargs = createPQExpBuffer();
12751 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
12752 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
12753 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
12754 sourceType, targetType);
12756 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
12757 sourceType, targetType);
12759 switch (cast->castmethod)
12761 case COERCION_METHOD_BINARY:
12762 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
12763 break;
12764 case COERCION_METHOD_INOUT:
12765 appendPQExpBufferStr(defqry, "WITH INOUT");
12766 break;
12767 case COERCION_METHOD_FUNCTION:
12768 if (funcInfo)
12770 char *fsig = format_function_signature(fout, funcInfo, true);
12773 * Always qualify the function name (format_function_signature
12774 * won't qualify it).
12776 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
12777 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
12778 free(fsig);
12780 else
12781 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
12782 break;
12783 default:
12784 pg_log_warning("bogus value in pg_cast.castmethod field");
12787 if (cast->castcontext == 'a')
12788 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
12789 else if (cast->castcontext == 'i')
12790 appendPQExpBufferStr(defqry, " AS IMPLICIT");
12791 appendPQExpBufferStr(defqry, ";\n");
12793 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
12794 sourceType, targetType);
12796 appendPQExpBuffer(castargs, "(%s AS %s)",
12797 sourceType, targetType);
12799 if (dopt->binary_upgrade)
12800 binary_upgrade_extension_member(defqry, &cast->dobj,
12801 "CAST", castargs->data, NULL);
12803 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
12804 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
12805 ARCHIVE_OPTS(.tag = labelq->data,
12806 .description = "CAST",
12807 .section = SECTION_PRE_DATA,
12808 .createStmt = defqry->data,
12809 .dropStmt = delqry->data));
12811 /* Dump Cast Comments */
12812 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
12813 dumpComment(fout, "CAST", castargs->data,
12814 NULL, "",
12815 cast->dobj.catId, 0, cast->dobj.dumpId);
12817 destroyPQExpBuffer(defqry);
12818 destroyPQExpBuffer(delqry);
12819 destroyPQExpBuffer(labelq);
12820 destroyPQExpBuffer(castargs);
12824 * Dump a transform
12826 static void
12827 dumpTransform(Archive *fout, const TransformInfo *transform)
12829 DumpOptions *dopt = fout->dopt;
12830 PQExpBuffer defqry;
12831 PQExpBuffer delqry;
12832 PQExpBuffer labelq;
12833 PQExpBuffer transformargs;
12834 FuncInfo *fromsqlFuncInfo = NULL;
12835 FuncInfo *tosqlFuncInfo = NULL;
12836 char *lanname;
12837 const char *transformType;
12839 /* Do nothing if not dumping schema */
12840 if (!dopt->dumpSchema)
12841 return;
12843 /* Cannot dump if we don't have the transform functions' info */
12844 if (OidIsValid(transform->trffromsql))
12846 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
12847 if (fromsqlFuncInfo == NULL)
12848 pg_fatal("could not find function definition for function with OID %u",
12849 transform->trffromsql);
12851 if (OidIsValid(transform->trftosql))
12853 tosqlFuncInfo = findFuncByOid(transform->trftosql);
12854 if (tosqlFuncInfo == NULL)
12855 pg_fatal("could not find function definition for function with OID %u",
12856 transform->trftosql);
12859 defqry = createPQExpBuffer();
12860 delqry = createPQExpBuffer();
12861 labelq = createPQExpBuffer();
12862 transformargs = createPQExpBuffer();
12864 lanname = get_language_name(fout, transform->trflang);
12865 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
12867 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
12868 transformType, lanname);
12870 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
12871 transformType, lanname);
12873 if (!transform->trffromsql && !transform->trftosql)
12874 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
12876 if (transform->trffromsql)
12878 if (fromsqlFuncInfo)
12880 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
12883 * Always qualify the function name (format_function_signature
12884 * won't qualify it).
12886 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
12887 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
12888 free(fsig);
12890 else
12891 pg_log_warning("bogus value in pg_transform.trffromsql field");
12894 if (transform->trftosql)
12896 if (transform->trffromsql)
12897 appendPQExpBufferStr(defqry, ", ");
12899 if (tosqlFuncInfo)
12901 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
12904 * Always qualify the function name (format_function_signature
12905 * won't qualify it).
12907 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
12908 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
12909 free(fsig);
12911 else
12912 pg_log_warning("bogus value in pg_transform.trftosql field");
12915 appendPQExpBufferStr(defqry, ");\n");
12917 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
12918 transformType, lanname);
12920 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
12921 transformType, lanname);
12923 if (dopt->binary_upgrade)
12924 binary_upgrade_extension_member(defqry, &transform->dobj,
12925 "TRANSFORM", transformargs->data, NULL);
12927 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
12928 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
12929 ARCHIVE_OPTS(.tag = labelq->data,
12930 .description = "TRANSFORM",
12931 .section = SECTION_PRE_DATA,
12932 .createStmt = defqry->data,
12933 .dropStmt = delqry->data,
12934 .deps = transform->dobj.dependencies,
12935 .nDeps = transform->dobj.nDeps));
12937 /* Dump Transform Comments */
12938 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
12939 dumpComment(fout, "TRANSFORM", transformargs->data,
12940 NULL, "",
12941 transform->dobj.catId, 0, transform->dobj.dumpId);
12943 free(lanname);
12944 destroyPQExpBuffer(defqry);
12945 destroyPQExpBuffer(delqry);
12946 destroyPQExpBuffer(labelq);
12947 destroyPQExpBuffer(transformargs);
12952 * dumpOpr
12953 * write out a single operator definition
12955 static void
12956 dumpOpr(Archive *fout, const OprInfo *oprinfo)
12958 DumpOptions *dopt = fout->dopt;
12959 PQExpBuffer query;
12960 PQExpBuffer q;
12961 PQExpBuffer delq;
12962 PQExpBuffer oprid;
12963 PQExpBuffer details;
12964 PGresult *res;
12965 int i_oprkind;
12966 int i_oprcode;
12967 int i_oprleft;
12968 int i_oprright;
12969 int i_oprcom;
12970 int i_oprnegate;
12971 int i_oprrest;
12972 int i_oprjoin;
12973 int i_oprcanmerge;
12974 int i_oprcanhash;
12975 char *oprkind;
12976 char *oprcode;
12977 char *oprleft;
12978 char *oprright;
12979 char *oprcom;
12980 char *oprnegate;
12981 char *oprrest;
12982 char *oprjoin;
12983 char *oprcanmerge;
12984 char *oprcanhash;
12985 char *oprregproc;
12986 char *oprref;
12988 /* Do nothing if not dumping schema */
12989 if (!dopt->dumpSchema)
12990 return;
12993 * some operators are invalid because they were the result of user
12994 * defining operators before commutators exist
12996 if (!OidIsValid(oprinfo->oprcode))
12997 return;
12999 query = createPQExpBuffer();
13000 q = createPQExpBuffer();
13001 delq = createPQExpBuffer();
13002 oprid = createPQExpBuffer();
13003 details = createPQExpBuffer();
13005 if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13007 /* Set up query for operator-specific details */
13008 appendPQExpBufferStr(query,
13009 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13010 "SELECT oprkind, "
13011 "oprcode::pg_catalog.regprocedure, "
13012 "oprleft::pg_catalog.regtype, "
13013 "oprright::pg_catalog.regtype, "
13014 "oprcom, "
13015 "oprnegate, "
13016 "oprrest::pg_catalog.regprocedure, "
13017 "oprjoin::pg_catalog.regprocedure, "
13018 "oprcanmerge, oprcanhash "
13019 "FROM pg_catalog.pg_operator "
13020 "WHERE oid = $1");
13022 ExecuteSqlStatement(fout, query->data);
13024 fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13027 printfPQExpBuffer(query,
13028 "EXECUTE dumpOpr('%u')",
13029 oprinfo->dobj.catId.oid);
13031 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13033 i_oprkind = PQfnumber(res, "oprkind");
13034 i_oprcode = PQfnumber(res, "oprcode");
13035 i_oprleft = PQfnumber(res, "oprleft");
13036 i_oprright = PQfnumber(res, "oprright");
13037 i_oprcom = PQfnumber(res, "oprcom");
13038 i_oprnegate = PQfnumber(res, "oprnegate");
13039 i_oprrest = PQfnumber(res, "oprrest");
13040 i_oprjoin = PQfnumber(res, "oprjoin");
13041 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13042 i_oprcanhash = PQfnumber(res, "oprcanhash");
13044 oprkind = PQgetvalue(res, 0, i_oprkind);
13045 oprcode = PQgetvalue(res, 0, i_oprcode);
13046 oprleft = PQgetvalue(res, 0, i_oprleft);
13047 oprright = PQgetvalue(res, 0, i_oprright);
13048 oprcom = PQgetvalue(res, 0, i_oprcom);
13049 oprnegate = PQgetvalue(res, 0, i_oprnegate);
13050 oprrest = PQgetvalue(res, 0, i_oprrest);
13051 oprjoin = PQgetvalue(res, 0, i_oprjoin);
13052 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13053 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13055 /* In PG14 upwards postfix operator support does not exist anymore. */
13056 if (strcmp(oprkind, "r") == 0)
13057 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13058 oprcode);
13060 oprregproc = convertRegProcReference(oprcode);
13061 if (oprregproc)
13063 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13064 free(oprregproc);
13067 appendPQExpBuffer(oprid, "%s (",
13068 oprinfo->dobj.name);
13071 * right unary means there's a left arg and left unary means there's a
13072 * right arg. (Although the "r" case is dead code for PG14 and later,
13073 * continue to support it in case we're dumping from an old server.)
13075 if (strcmp(oprkind, "r") == 0 ||
13076 strcmp(oprkind, "b") == 0)
13078 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13079 appendPQExpBufferStr(oprid, oprleft);
13081 else
13082 appendPQExpBufferStr(oprid, "NONE");
13084 if (strcmp(oprkind, "l") == 0 ||
13085 strcmp(oprkind, "b") == 0)
13087 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13088 appendPQExpBuffer(oprid, ", %s)", oprright);
13090 else
13091 appendPQExpBufferStr(oprid, ", NONE)");
13093 oprref = getFormattedOperatorName(oprcom);
13094 if (oprref)
13096 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13097 free(oprref);
13100 oprref = getFormattedOperatorName(oprnegate);
13101 if (oprref)
13103 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13104 free(oprref);
13107 if (strcmp(oprcanmerge, "t") == 0)
13108 appendPQExpBufferStr(details, ",\n MERGES");
13110 if (strcmp(oprcanhash, "t") == 0)
13111 appendPQExpBufferStr(details, ",\n HASHES");
13113 oprregproc = convertRegProcReference(oprrest);
13114 if (oprregproc)
13116 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13117 free(oprregproc);
13120 oprregproc = convertRegProcReference(oprjoin);
13121 if (oprregproc)
13123 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13124 free(oprregproc);
13127 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13128 fmtId(oprinfo->dobj.namespace->dobj.name),
13129 oprid->data);
13131 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13132 fmtId(oprinfo->dobj.namespace->dobj.name),
13133 oprinfo->dobj.name, details->data);
13135 if (dopt->binary_upgrade)
13136 binary_upgrade_extension_member(q, &oprinfo->dobj,
13137 "OPERATOR", oprid->data,
13138 oprinfo->dobj.namespace->dobj.name);
13140 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13141 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13142 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13143 .namespace = oprinfo->dobj.namespace->dobj.name,
13144 .owner = oprinfo->rolname,
13145 .description = "OPERATOR",
13146 .section = SECTION_PRE_DATA,
13147 .createStmt = q->data,
13148 .dropStmt = delq->data));
13150 /* Dump Operator Comments */
13151 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13152 dumpComment(fout, "OPERATOR", oprid->data,
13153 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13154 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13156 PQclear(res);
13158 destroyPQExpBuffer(query);
13159 destroyPQExpBuffer(q);
13160 destroyPQExpBuffer(delq);
13161 destroyPQExpBuffer(oprid);
13162 destroyPQExpBuffer(details);
13166 * Convert a function reference obtained from pg_operator
13168 * Returns allocated string of what to print, or NULL if function references
13169 * is InvalidOid. Returned string is expected to be free'd by the caller.
13171 * The input is a REGPROCEDURE display; we have to strip the argument-types
13172 * part.
13174 static char *
13175 convertRegProcReference(const char *proc)
13177 char *name;
13178 char *paren;
13179 bool inquote;
13181 /* In all cases "-" means a null reference */
13182 if (strcmp(proc, "-") == 0)
13183 return NULL;
13185 name = pg_strdup(proc);
13186 /* find non-double-quoted left paren */
13187 inquote = false;
13188 for (paren = name; *paren; paren++)
13190 if (*paren == '(' && !inquote)
13192 *paren = '\0';
13193 break;
13195 if (*paren == '"')
13196 inquote = !inquote;
13198 return name;
13202 * getFormattedOperatorName - retrieve the operator name for the
13203 * given operator OID (presented in string form).
13205 * Returns an allocated string, or NULL if the given OID is invalid.
13206 * Caller is responsible for free'ing result string.
13208 * What we produce has the format "OPERATOR(schema.oprname)". This is only
13209 * useful in commands where the operator's argument types can be inferred from
13210 * context. We always schema-qualify the name, though. The predecessor to
13211 * this code tried to skip the schema qualification if possible, but that led
13212 * to wrong results in corner cases, such as if an operator and its negator
13213 * are in different schemas.
13215 static char *
13216 getFormattedOperatorName(const char *oproid)
13218 OprInfo *oprInfo;
13220 /* In all cases "0" means a null reference */
13221 if (strcmp(oproid, "0") == 0)
13222 return NULL;
13224 oprInfo = findOprByOid(atooid(oproid));
13225 if (oprInfo == NULL)
13227 pg_log_warning("could not find operator with OID %s",
13228 oproid);
13229 return NULL;
13232 return psprintf("OPERATOR(%s.%s)",
13233 fmtId(oprInfo->dobj.namespace->dobj.name),
13234 oprInfo->dobj.name);
13238 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
13240 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
13241 * argument lists of these functions are predetermined. Note that the
13242 * caller should ensure we are in the proper schema, because the results
13243 * are search path dependent!
13245 static char *
13246 convertTSFunction(Archive *fout, Oid funcOid)
13248 char *result;
13249 char query[128];
13250 PGresult *res;
13252 snprintf(query, sizeof(query),
13253 "SELECT '%u'::pg_catalog.regproc", funcOid);
13254 res = ExecuteSqlQueryForSingleRow(fout, query);
13256 result = pg_strdup(PQgetvalue(res, 0, 0));
13258 PQclear(res);
13260 return result;
13264 * dumpAccessMethod
13265 * write out a single access method definition
13267 static void
13268 dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
13270 DumpOptions *dopt = fout->dopt;
13271 PQExpBuffer q;
13272 PQExpBuffer delq;
13273 char *qamname;
13275 /* Do nothing if not dumping schema */
13276 if (!dopt->dumpSchema)
13277 return;
13279 q = createPQExpBuffer();
13280 delq = createPQExpBuffer();
13282 qamname = pg_strdup(fmtId(aminfo->dobj.name));
13284 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
13286 switch (aminfo->amtype)
13288 case AMTYPE_INDEX:
13289 appendPQExpBufferStr(q, "TYPE INDEX ");
13290 break;
13291 case AMTYPE_TABLE:
13292 appendPQExpBufferStr(q, "TYPE TABLE ");
13293 break;
13294 default:
13295 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
13296 aminfo->amtype, qamname);
13297 destroyPQExpBuffer(q);
13298 destroyPQExpBuffer(delq);
13299 free(qamname);
13300 return;
13303 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13305 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13306 qamname);
13308 if (dopt->binary_upgrade)
13309 binary_upgrade_extension_member(q, &aminfo->dobj,
13310 "ACCESS METHOD", qamname, NULL);
13312 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13313 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13314 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13315 .description = "ACCESS METHOD",
13316 .section = SECTION_PRE_DATA,
13317 .createStmt = q->data,
13318 .dropStmt = delq->data));
13320 /* Dump Access Method Comments */
13321 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13322 dumpComment(fout, "ACCESS METHOD", qamname,
13323 NULL, "",
13324 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13326 destroyPQExpBuffer(q);
13327 destroyPQExpBuffer(delq);
13328 free(qamname);
13332 * dumpOpclass
13333 * write out a single operator class definition
13335 static void
13336 dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13338 DumpOptions *dopt = fout->dopt;
13339 PQExpBuffer query;
13340 PQExpBuffer q;
13341 PQExpBuffer delq;
13342 PQExpBuffer nameusing;
13343 PGresult *res;
13344 int ntups;
13345 int i_opcintype;
13346 int i_opckeytype;
13347 int i_opcdefault;
13348 int i_opcfamily;
13349 int i_opcfamilyname;
13350 int i_opcfamilynsp;
13351 int i_amname;
13352 int i_amopstrategy;
13353 int i_amopopr;
13354 int i_sortfamily;
13355 int i_sortfamilynsp;
13356 int i_amprocnum;
13357 int i_amproc;
13358 int i_amproclefttype;
13359 int i_amprocrighttype;
13360 char *opcintype;
13361 char *opckeytype;
13362 char *opcdefault;
13363 char *opcfamily;
13364 char *opcfamilyname;
13365 char *opcfamilynsp;
13366 char *amname;
13367 char *amopstrategy;
13368 char *amopopr;
13369 char *sortfamily;
13370 char *sortfamilynsp;
13371 char *amprocnum;
13372 char *amproc;
13373 char *amproclefttype;
13374 char *amprocrighttype;
13375 bool needComma;
13376 int i;
13378 /* Do nothing if not dumping schema */
13379 if (!dopt->dumpSchema)
13380 return;
13382 query = createPQExpBuffer();
13383 q = createPQExpBuffer();
13384 delq = createPQExpBuffer();
13385 nameusing = createPQExpBuffer();
13387 /* Get additional fields from the pg_opclass row */
13388 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13389 "opckeytype::pg_catalog.regtype, "
13390 "opcdefault, opcfamily, "
13391 "opfname AS opcfamilyname, "
13392 "nspname AS opcfamilynsp, "
13393 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13394 "FROM pg_catalog.pg_opclass c "
13395 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13396 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13397 "WHERE c.oid = '%u'::pg_catalog.oid",
13398 opcinfo->dobj.catId.oid);
13400 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13402 i_opcintype = PQfnumber(res, "opcintype");
13403 i_opckeytype = PQfnumber(res, "opckeytype");
13404 i_opcdefault = PQfnumber(res, "opcdefault");
13405 i_opcfamily = PQfnumber(res, "opcfamily");
13406 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13407 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13408 i_amname = PQfnumber(res, "amname");
13410 /* opcintype may still be needed after we PQclear res */
13411 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13412 opckeytype = PQgetvalue(res, 0, i_opckeytype);
13413 opcdefault = PQgetvalue(res, 0, i_opcdefault);
13414 /* opcfamily will still be needed after we PQclear res */
13415 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13416 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13417 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13418 /* amname will still be needed after we PQclear res */
13419 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13421 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13422 fmtQualifiedDumpable(opcinfo));
13423 appendPQExpBuffer(delq, " USING %s;\n",
13424 fmtId(amname));
13426 /* Build the fixed portion of the CREATE command */
13427 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13428 fmtQualifiedDumpable(opcinfo));
13429 if (strcmp(opcdefault, "t") == 0)
13430 appendPQExpBufferStr(q, "DEFAULT ");
13431 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13432 opcintype,
13433 fmtId(amname));
13434 if (strlen(opcfamilyname) > 0)
13436 appendPQExpBufferStr(q, " FAMILY ");
13437 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13438 appendPQExpBufferStr(q, fmtId(opcfamilyname));
13440 appendPQExpBufferStr(q, " AS\n ");
13442 needComma = false;
13444 if (strcmp(opckeytype, "-") != 0)
13446 appendPQExpBuffer(q, "STORAGE %s",
13447 opckeytype);
13448 needComma = true;
13451 PQclear(res);
13454 * Now fetch and print the OPERATOR entries (pg_amop rows).
13456 * Print only those opfamily members that are tied to the opclass by
13457 * pg_depend entries.
13459 resetPQExpBuffer(query);
13460 appendPQExpBuffer(query, "SELECT amopstrategy, "
13461 "amopopr::pg_catalog.regoperator, "
13462 "opfname AS sortfamily, "
13463 "nspname AS sortfamilynsp "
13464 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13465 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13466 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13467 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13468 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13469 "AND refobjid = '%u'::pg_catalog.oid "
13470 "AND amopfamily = '%s'::pg_catalog.oid "
13471 "ORDER BY amopstrategy",
13472 opcinfo->dobj.catId.oid,
13473 opcfamily);
13475 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13477 ntups = PQntuples(res);
13479 i_amopstrategy = PQfnumber(res, "amopstrategy");
13480 i_amopopr = PQfnumber(res, "amopopr");
13481 i_sortfamily = PQfnumber(res, "sortfamily");
13482 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13484 for (i = 0; i < ntups; i++)
13486 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13487 amopopr = PQgetvalue(res, i, i_amopopr);
13488 sortfamily = PQgetvalue(res, i, i_sortfamily);
13489 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13491 if (needComma)
13492 appendPQExpBufferStr(q, " ,\n ");
13494 appendPQExpBuffer(q, "OPERATOR %s %s",
13495 amopstrategy, amopopr);
13497 if (strlen(sortfamily) > 0)
13499 appendPQExpBufferStr(q, " FOR ORDER BY ");
13500 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13501 appendPQExpBufferStr(q, fmtId(sortfamily));
13504 needComma = true;
13507 PQclear(res);
13510 * Now fetch and print the FUNCTION entries (pg_amproc rows).
13512 * Print only those opfamily members that are tied to the opclass by
13513 * pg_depend entries.
13515 * We print the amproclefttype/amprocrighttype even though in most cases
13516 * the backend could deduce the right values, because of the corner case
13517 * of a btree sort support function for a cross-type comparison.
13519 resetPQExpBuffer(query);
13521 appendPQExpBuffer(query, "SELECT amprocnum, "
13522 "amproc::pg_catalog.regprocedure, "
13523 "amproclefttype::pg_catalog.regtype, "
13524 "amprocrighttype::pg_catalog.regtype "
13525 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13526 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13527 "AND refobjid = '%u'::pg_catalog.oid "
13528 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13529 "AND objid = ap.oid "
13530 "ORDER BY amprocnum",
13531 opcinfo->dobj.catId.oid);
13533 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13535 ntups = PQntuples(res);
13537 i_amprocnum = PQfnumber(res, "amprocnum");
13538 i_amproc = PQfnumber(res, "amproc");
13539 i_amproclefttype = PQfnumber(res, "amproclefttype");
13540 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13542 for (i = 0; i < ntups; i++)
13544 amprocnum = PQgetvalue(res, i, i_amprocnum);
13545 amproc = PQgetvalue(res, i, i_amproc);
13546 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13547 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13549 if (needComma)
13550 appendPQExpBufferStr(q, " ,\n ");
13552 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13554 if (*amproclefttype && *amprocrighttype)
13555 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13557 appendPQExpBuffer(q, " %s", amproc);
13559 needComma = true;
13562 PQclear(res);
13565 * If needComma is still false it means we haven't added anything after
13566 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13567 * clause with the same datatype. This isn't sanctioned by the
13568 * documentation, but actually DefineOpClass will treat it as a no-op.
13570 if (!needComma)
13571 appendPQExpBuffer(q, "STORAGE %s", opcintype);
13573 appendPQExpBufferStr(q, ";\n");
13575 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13576 appendPQExpBuffer(nameusing, " USING %s",
13577 fmtId(amname));
13579 if (dopt->binary_upgrade)
13580 binary_upgrade_extension_member(q, &opcinfo->dobj,
13581 "OPERATOR CLASS", nameusing->data,
13582 opcinfo->dobj.namespace->dobj.name);
13584 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13585 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13586 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13587 .namespace = opcinfo->dobj.namespace->dobj.name,
13588 .owner = opcinfo->rolname,
13589 .description = "OPERATOR CLASS",
13590 .section = SECTION_PRE_DATA,
13591 .createStmt = q->data,
13592 .dropStmt = delq->data));
13594 /* Dump Operator Class Comments */
13595 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13596 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13597 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13598 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13600 free(opcintype);
13601 free(opcfamily);
13602 free(amname);
13603 destroyPQExpBuffer(query);
13604 destroyPQExpBuffer(q);
13605 destroyPQExpBuffer(delq);
13606 destroyPQExpBuffer(nameusing);
13610 * dumpOpfamily
13611 * write out a single operator family definition
13613 * Note: this also dumps any "loose" operator members that aren't bound to a
13614 * specific opclass within the opfamily.
13616 static void
13617 dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
13619 DumpOptions *dopt = fout->dopt;
13620 PQExpBuffer query;
13621 PQExpBuffer q;
13622 PQExpBuffer delq;
13623 PQExpBuffer nameusing;
13624 PGresult *res;
13625 PGresult *res_ops;
13626 PGresult *res_procs;
13627 int ntups;
13628 int i_amname;
13629 int i_amopstrategy;
13630 int i_amopopr;
13631 int i_sortfamily;
13632 int i_sortfamilynsp;
13633 int i_amprocnum;
13634 int i_amproc;
13635 int i_amproclefttype;
13636 int i_amprocrighttype;
13637 char *amname;
13638 char *amopstrategy;
13639 char *amopopr;
13640 char *sortfamily;
13641 char *sortfamilynsp;
13642 char *amprocnum;
13643 char *amproc;
13644 char *amproclefttype;
13645 char *amprocrighttype;
13646 bool needComma;
13647 int i;
13649 /* Do nothing if not dumping schema */
13650 if (!dopt->dumpSchema)
13651 return;
13653 query = createPQExpBuffer();
13654 q = createPQExpBuffer();
13655 delq = createPQExpBuffer();
13656 nameusing = createPQExpBuffer();
13659 * Fetch only those opfamily members that are tied directly to the
13660 * opfamily by pg_depend entries.
13662 appendPQExpBuffer(query, "SELECT amopstrategy, "
13663 "amopopr::pg_catalog.regoperator, "
13664 "opfname AS sortfamily, "
13665 "nspname AS sortfamilynsp "
13666 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13667 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13668 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13669 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13670 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13671 "AND refobjid = '%u'::pg_catalog.oid "
13672 "AND amopfamily = '%u'::pg_catalog.oid "
13673 "ORDER BY amopstrategy",
13674 opfinfo->dobj.catId.oid,
13675 opfinfo->dobj.catId.oid);
13677 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13679 resetPQExpBuffer(query);
13681 appendPQExpBuffer(query, "SELECT amprocnum, "
13682 "amproc::pg_catalog.regprocedure, "
13683 "amproclefttype::pg_catalog.regtype, "
13684 "amprocrighttype::pg_catalog.regtype "
13685 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13686 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13687 "AND refobjid = '%u'::pg_catalog.oid "
13688 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13689 "AND objid = ap.oid "
13690 "ORDER BY amprocnum",
13691 opfinfo->dobj.catId.oid);
13693 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13695 /* Get additional fields from the pg_opfamily row */
13696 resetPQExpBuffer(query);
13698 appendPQExpBuffer(query, "SELECT "
13699 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
13700 "FROM pg_catalog.pg_opfamily "
13701 "WHERE oid = '%u'::pg_catalog.oid",
13702 opfinfo->dobj.catId.oid);
13704 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13706 i_amname = PQfnumber(res, "amname");
13708 /* amname will still be needed after we PQclear res */
13709 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13711 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
13712 fmtQualifiedDumpable(opfinfo));
13713 appendPQExpBuffer(delq, " USING %s;\n",
13714 fmtId(amname));
13716 /* Build the fixed portion of the CREATE command */
13717 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
13718 fmtQualifiedDumpable(opfinfo));
13719 appendPQExpBuffer(q, " USING %s;\n",
13720 fmtId(amname));
13722 PQclear(res);
13724 /* Do we need an ALTER to add loose members? */
13725 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
13727 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
13728 fmtQualifiedDumpable(opfinfo));
13729 appendPQExpBuffer(q, " USING %s ADD\n ",
13730 fmtId(amname));
13732 needComma = false;
13735 * Now fetch and print the OPERATOR entries (pg_amop rows).
13737 ntups = PQntuples(res_ops);
13739 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
13740 i_amopopr = PQfnumber(res_ops, "amopopr");
13741 i_sortfamily = PQfnumber(res_ops, "sortfamily");
13742 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
13744 for (i = 0; i < ntups; i++)
13746 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
13747 amopopr = PQgetvalue(res_ops, i, i_amopopr);
13748 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
13749 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
13751 if (needComma)
13752 appendPQExpBufferStr(q, " ,\n ");
13754 appendPQExpBuffer(q, "OPERATOR %s %s",
13755 amopstrategy, amopopr);
13757 if (strlen(sortfamily) > 0)
13759 appendPQExpBufferStr(q, " FOR ORDER BY ");
13760 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13761 appendPQExpBufferStr(q, fmtId(sortfamily));
13764 needComma = true;
13768 * Now fetch and print the FUNCTION entries (pg_amproc rows).
13770 ntups = PQntuples(res_procs);
13772 i_amprocnum = PQfnumber(res_procs, "amprocnum");
13773 i_amproc = PQfnumber(res_procs, "amproc");
13774 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
13775 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
13777 for (i = 0; i < ntups; i++)
13779 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
13780 amproc = PQgetvalue(res_procs, i, i_amproc);
13781 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
13782 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
13784 if (needComma)
13785 appendPQExpBufferStr(q, " ,\n ");
13787 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
13788 amprocnum, amproclefttype, amprocrighttype,
13789 amproc);
13791 needComma = true;
13794 appendPQExpBufferStr(q, ";\n");
13797 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
13798 appendPQExpBuffer(nameusing, " USING %s",
13799 fmtId(amname));
13801 if (dopt->binary_upgrade)
13802 binary_upgrade_extension_member(q, &opfinfo->dobj,
13803 "OPERATOR FAMILY", nameusing->data,
13804 opfinfo->dobj.namespace->dobj.name);
13806 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13807 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
13808 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
13809 .namespace = opfinfo->dobj.namespace->dobj.name,
13810 .owner = opfinfo->rolname,
13811 .description = "OPERATOR FAMILY",
13812 .section = SECTION_PRE_DATA,
13813 .createStmt = q->data,
13814 .dropStmt = delq->data));
13816 /* Dump Operator Family Comments */
13817 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13818 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
13819 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
13820 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
13822 free(amname);
13823 PQclear(res_ops);
13824 PQclear(res_procs);
13825 destroyPQExpBuffer(query);
13826 destroyPQExpBuffer(q);
13827 destroyPQExpBuffer(delq);
13828 destroyPQExpBuffer(nameusing);
13832 * dumpCollation
13833 * write out a single collation definition
13835 static void
13836 dumpCollation(Archive *fout, const CollInfo *collinfo)
13838 DumpOptions *dopt = fout->dopt;
13839 PQExpBuffer query;
13840 PQExpBuffer q;
13841 PQExpBuffer delq;
13842 char *qcollname;
13843 PGresult *res;
13844 int i_collprovider;
13845 int i_collisdeterministic;
13846 int i_collcollate;
13847 int i_collctype;
13848 int i_colllocale;
13849 int i_collicurules;
13850 const char *collprovider;
13851 const char *collcollate;
13852 const char *collctype;
13853 const char *colllocale;
13854 const char *collicurules;
13856 /* Do nothing if not dumping schema */
13857 if (!dopt->dumpSchema)
13858 return;
13860 query = createPQExpBuffer();
13861 q = createPQExpBuffer();
13862 delq = createPQExpBuffer();
13864 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
13866 /* Get collation-specific details */
13867 appendPQExpBufferStr(query, "SELECT ");
13869 if (fout->remoteVersion >= 100000)
13870 appendPQExpBufferStr(query,
13871 "collprovider, "
13872 "collversion, ");
13873 else
13874 appendPQExpBufferStr(query,
13875 "'c' AS collprovider, "
13876 "NULL AS collversion, ");
13878 if (fout->remoteVersion >= 120000)
13879 appendPQExpBufferStr(query,
13880 "collisdeterministic, ");
13881 else
13882 appendPQExpBufferStr(query,
13883 "true AS collisdeterministic, ");
13885 if (fout->remoteVersion >= 170000)
13886 appendPQExpBufferStr(query,
13887 "colllocale, ");
13888 else if (fout->remoteVersion >= 150000)
13889 appendPQExpBufferStr(query,
13890 "colliculocale AS colllocale, ");
13891 else
13892 appendPQExpBufferStr(query,
13893 "NULL AS colllocale, ");
13895 if (fout->remoteVersion >= 160000)
13896 appendPQExpBufferStr(query,
13897 "collicurules, ");
13898 else
13899 appendPQExpBufferStr(query,
13900 "NULL AS collicurules, ");
13902 appendPQExpBuffer(query,
13903 "collcollate, "
13904 "collctype "
13905 "FROM pg_catalog.pg_collation c "
13906 "WHERE c.oid = '%u'::pg_catalog.oid",
13907 collinfo->dobj.catId.oid);
13909 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13911 i_collprovider = PQfnumber(res, "collprovider");
13912 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
13913 i_collcollate = PQfnumber(res, "collcollate");
13914 i_collctype = PQfnumber(res, "collctype");
13915 i_colllocale = PQfnumber(res, "colllocale");
13916 i_collicurules = PQfnumber(res, "collicurules");
13918 collprovider = PQgetvalue(res, 0, i_collprovider);
13920 if (!PQgetisnull(res, 0, i_collcollate))
13921 collcollate = PQgetvalue(res, 0, i_collcollate);
13922 else
13923 collcollate = NULL;
13925 if (!PQgetisnull(res, 0, i_collctype))
13926 collctype = PQgetvalue(res, 0, i_collctype);
13927 else
13928 collctype = NULL;
13931 * Before version 15, collcollate and collctype were of type NAME and
13932 * non-nullable. Treat empty strings as NULL for consistency.
13934 if (fout->remoteVersion < 150000)
13936 if (collcollate[0] == '\0')
13937 collcollate = NULL;
13938 if (collctype[0] == '\0')
13939 collctype = NULL;
13942 if (!PQgetisnull(res, 0, i_colllocale))
13943 colllocale = PQgetvalue(res, 0, i_colllocale);
13944 else
13945 colllocale = NULL;
13947 if (!PQgetisnull(res, 0, i_collicurules))
13948 collicurules = PQgetvalue(res, 0, i_collicurules);
13949 else
13950 collicurules = NULL;
13952 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
13953 fmtQualifiedDumpable(collinfo));
13955 appendPQExpBuffer(q, "CREATE COLLATION %s (",
13956 fmtQualifiedDumpable(collinfo));
13958 appendPQExpBufferStr(q, "provider = ");
13959 if (collprovider[0] == 'b')
13960 appendPQExpBufferStr(q, "builtin");
13961 else if (collprovider[0] == 'c')
13962 appendPQExpBufferStr(q, "libc");
13963 else if (collprovider[0] == 'i')
13964 appendPQExpBufferStr(q, "icu");
13965 else if (collprovider[0] == 'd')
13966 /* to allow dumping pg_catalog; not accepted on input */
13967 appendPQExpBufferStr(q, "default");
13968 else
13969 pg_fatal("unrecognized collation provider: %s",
13970 collprovider);
13972 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
13973 appendPQExpBufferStr(q, ", deterministic = false");
13975 if (collprovider[0] == 'd')
13977 if (collcollate || collctype || colllocale || collicurules)
13978 pg_log_warning("invalid collation \"%s\"", qcollname);
13980 /* no locale -- the default collation cannot be reloaded anyway */
13982 else if (collprovider[0] == 'b')
13984 if (collcollate || collctype || !colllocale || collicurules)
13985 pg_log_warning("invalid collation \"%s\"", qcollname);
13987 appendPQExpBufferStr(q, ", locale = ");
13988 appendStringLiteralAH(q, colllocale ? colllocale : "",
13989 fout);
13991 else if (collprovider[0] == 'i')
13993 if (fout->remoteVersion >= 150000)
13995 if (collcollate || collctype || !colllocale)
13996 pg_log_warning("invalid collation \"%s\"", qcollname);
13998 appendPQExpBufferStr(q, ", locale = ");
13999 appendStringLiteralAH(q, colllocale ? colllocale : "",
14000 fout);
14002 else
14004 if (!collcollate || !collctype || colllocale ||
14005 strcmp(collcollate, collctype) != 0)
14006 pg_log_warning("invalid collation \"%s\"", qcollname);
14008 appendPQExpBufferStr(q, ", locale = ");
14009 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14012 if (collicurules)
14014 appendPQExpBufferStr(q, ", rules = ");
14015 appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14018 else if (collprovider[0] == 'c')
14020 if (colllocale || collicurules || !collcollate || !collctype)
14021 pg_log_warning("invalid collation \"%s\"", qcollname);
14023 if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14025 appendPQExpBufferStr(q, ", locale = ");
14026 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14028 else
14030 appendPQExpBufferStr(q, ", lc_collate = ");
14031 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14032 appendPQExpBufferStr(q, ", lc_ctype = ");
14033 appendStringLiteralAH(q, collctype ? collctype : "", fout);
14036 else
14037 pg_fatal("unrecognized collation provider: %s", collprovider);
14040 * For binary upgrade, carry over the collation version. For normal
14041 * dump/restore, omit the version, so that it is computed upon restore.
14043 if (dopt->binary_upgrade)
14045 int i_collversion;
14047 i_collversion = PQfnumber(res, "collversion");
14048 if (!PQgetisnull(res, 0, i_collversion))
14050 appendPQExpBufferStr(q, ", version = ");
14051 appendStringLiteralAH(q,
14052 PQgetvalue(res, 0, i_collversion),
14053 fout);
14057 appendPQExpBufferStr(q, ");\n");
14059 if (dopt->binary_upgrade)
14060 binary_upgrade_extension_member(q, &collinfo->dobj,
14061 "COLLATION", qcollname,
14062 collinfo->dobj.namespace->dobj.name);
14064 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14065 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14066 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14067 .namespace = collinfo->dobj.namespace->dobj.name,
14068 .owner = collinfo->rolname,
14069 .description = "COLLATION",
14070 .section = SECTION_PRE_DATA,
14071 .createStmt = q->data,
14072 .dropStmt = delq->data));
14074 /* Dump Collation Comments */
14075 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14076 dumpComment(fout, "COLLATION", qcollname,
14077 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14078 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14080 PQclear(res);
14082 destroyPQExpBuffer(query);
14083 destroyPQExpBuffer(q);
14084 destroyPQExpBuffer(delq);
14085 free(qcollname);
14089 * dumpConversion
14090 * write out a single conversion definition
14092 static void
14093 dumpConversion(Archive *fout, const ConvInfo *convinfo)
14095 DumpOptions *dopt = fout->dopt;
14096 PQExpBuffer query;
14097 PQExpBuffer q;
14098 PQExpBuffer delq;
14099 char *qconvname;
14100 PGresult *res;
14101 int i_conforencoding;
14102 int i_contoencoding;
14103 int i_conproc;
14104 int i_condefault;
14105 const char *conforencoding;
14106 const char *contoencoding;
14107 const char *conproc;
14108 bool condefault;
14110 /* Do nothing if not dumping schema */
14111 if (!dopt->dumpSchema)
14112 return;
14114 query = createPQExpBuffer();
14115 q = createPQExpBuffer();
14116 delq = createPQExpBuffer();
14118 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14120 /* Get conversion-specific details */
14121 appendPQExpBuffer(query, "SELECT "
14122 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14123 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14124 "conproc, condefault "
14125 "FROM pg_catalog.pg_conversion c "
14126 "WHERE c.oid = '%u'::pg_catalog.oid",
14127 convinfo->dobj.catId.oid);
14129 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14131 i_conforencoding = PQfnumber(res, "conforencoding");
14132 i_contoencoding = PQfnumber(res, "contoencoding");
14133 i_conproc = PQfnumber(res, "conproc");
14134 i_condefault = PQfnumber(res, "condefault");
14136 conforencoding = PQgetvalue(res, 0, i_conforencoding);
14137 contoencoding = PQgetvalue(res, 0, i_contoencoding);
14138 conproc = PQgetvalue(res, 0, i_conproc);
14139 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14141 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14142 fmtQualifiedDumpable(convinfo));
14144 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14145 (condefault) ? "DEFAULT " : "",
14146 fmtQualifiedDumpable(convinfo));
14147 appendStringLiteralAH(q, conforencoding, fout);
14148 appendPQExpBufferStr(q, " TO ");
14149 appendStringLiteralAH(q, contoencoding, fout);
14150 /* regproc output is already sufficiently quoted */
14151 appendPQExpBuffer(q, " FROM %s;\n", conproc);
14153 if (dopt->binary_upgrade)
14154 binary_upgrade_extension_member(q, &convinfo->dobj,
14155 "CONVERSION", qconvname,
14156 convinfo->dobj.namespace->dobj.name);
14158 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14159 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14160 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14161 .namespace = convinfo->dobj.namespace->dobj.name,
14162 .owner = convinfo->rolname,
14163 .description = "CONVERSION",
14164 .section = SECTION_PRE_DATA,
14165 .createStmt = q->data,
14166 .dropStmt = delq->data));
14168 /* Dump Conversion Comments */
14169 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14170 dumpComment(fout, "CONVERSION", qconvname,
14171 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14172 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14174 PQclear(res);
14176 destroyPQExpBuffer(query);
14177 destroyPQExpBuffer(q);
14178 destroyPQExpBuffer(delq);
14179 free(qconvname);
14183 * format_aggregate_signature: generate aggregate name and argument list
14185 * The argument type names are qualified if needed. The aggregate name
14186 * is never qualified.
14188 static char *
14189 format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
14191 PQExpBufferData buf;
14192 int j;
14194 initPQExpBuffer(&buf);
14195 if (honor_quotes)
14196 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
14197 else
14198 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
14200 if (agginfo->aggfn.nargs == 0)
14201 appendPQExpBufferStr(&buf, "(*)");
14202 else
14204 appendPQExpBufferChar(&buf, '(');
14205 for (j = 0; j < agginfo->aggfn.nargs; j++)
14206 appendPQExpBuffer(&buf, "%s%s",
14207 (j > 0) ? ", " : "",
14208 getFormattedTypeName(fout,
14209 agginfo->aggfn.argtypes[j],
14210 zeroIsError));
14211 appendPQExpBufferChar(&buf, ')');
14213 return buf.data;
14217 * dumpAgg
14218 * write out a single aggregate definition
14220 static void
14221 dumpAgg(Archive *fout, const AggInfo *agginfo)
14223 DumpOptions *dopt = fout->dopt;
14224 PQExpBuffer query;
14225 PQExpBuffer q;
14226 PQExpBuffer delq;
14227 PQExpBuffer details;
14228 char *aggsig; /* identity signature */
14229 char *aggfullsig = NULL; /* full signature */
14230 char *aggsig_tag;
14231 PGresult *res;
14232 int i_agginitval;
14233 int i_aggminitval;
14234 const char *aggtransfn;
14235 const char *aggfinalfn;
14236 const char *aggcombinefn;
14237 const char *aggserialfn;
14238 const char *aggdeserialfn;
14239 const char *aggmtransfn;
14240 const char *aggminvtransfn;
14241 const char *aggmfinalfn;
14242 bool aggfinalextra;
14243 bool aggmfinalextra;
14244 char aggfinalmodify;
14245 char aggmfinalmodify;
14246 const char *aggsortop;
14247 char *aggsortconvop;
14248 char aggkind;
14249 const char *aggtranstype;
14250 const char *aggtransspace;
14251 const char *aggmtranstype;
14252 const char *aggmtransspace;
14253 const char *agginitval;
14254 const char *aggminitval;
14255 const char *proparallel;
14256 char defaultfinalmodify;
14258 /* Do nothing if not dumping schema */
14259 if (!dopt->dumpSchema)
14260 return;
14262 query = createPQExpBuffer();
14263 q = createPQExpBuffer();
14264 delq = createPQExpBuffer();
14265 details = createPQExpBuffer();
14267 if (!fout->is_prepared[PREPQUERY_DUMPAGG])
14269 /* Set up query for aggregate-specific details */
14270 appendPQExpBufferStr(query,
14271 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
14273 appendPQExpBufferStr(query,
14274 "SELECT "
14275 "aggtransfn,\n"
14276 "aggfinalfn,\n"
14277 "aggtranstype::pg_catalog.regtype,\n"
14278 "agginitval,\n"
14279 "aggsortop,\n"
14280 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
14281 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
14283 if (fout->remoteVersion >= 90400)
14284 appendPQExpBufferStr(query,
14285 "aggkind,\n"
14286 "aggmtransfn,\n"
14287 "aggminvtransfn,\n"
14288 "aggmfinalfn,\n"
14289 "aggmtranstype::pg_catalog.regtype,\n"
14290 "aggfinalextra,\n"
14291 "aggmfinalextra,\n"
14292 "aggtransspace,\n"
14293 "aggmtransspace,\n"
14294 "aggminitval,\n");
14295 else
14296 appendPQExpBufferStr(query,
14297 "'n' AS aggkind,\n"
14298 "'-' AS aggmtransfn,\n"
14299 "'-' AS aggminvtransfn,\n"
14300 "'-' AS aggmfinalfn,\n"
14301 "0 AS aggmtranstype,\n"
14302 "false AS aggfinalextra,\n"
14303 "false AS aggmfinalextra,\n"
14304 "0 AS aggtransspace,\n"
14305 "0 AS aggmtransspace,\n"
14306 "NULL AS aggminitval,\n");
14308 if (fout->remoteVersion >= 90600)
14309 appendPQExpBufferStr(query,
14310 "aggcombinefn,\n"
14311 "aggserialfn,\n"
14312 "aggdeserialfn,\n"
14313 "proparallel,\n");
14314 else
14315 appendPQExpBufferStr(query,
14316 "'-' AS aggcombinefn,\n"
14317 "'-' AS aggserialfn,\n"
14318 "'-' AS aggdeserialfn,\n"
14319 "'u' AS proparallel,\n");
14321 if (fout->remoteVersion >= 110000)
14322 appendPQExpBufferStr(query,
14323 "aggfinalmodify,\n"
14324 "aggmfinalmodify\n");
14325 else
14326 appendPQExpBufferStr(query,
14327 "'0' AS aggfinalmodify,\n"
14328 "'0' AS aggmfinalmodify\n");
14330 appendPQExpBufferStr(query,
14331 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14332 "WHERE a.aggfnoid = p.oid "
14333 "AND p.oid = $1");
14335 ExecuteSqlStatement(fout, query->data);
14337 fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14340 printfPQExpBuffer(query,
14341 "EXECUTE dumpAgg('%u')",
14342 agginfo->aggfn.dobj.catId.oid);
14344 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14346 i_agginitval = PQfnumber(res, "agginitval");
14347 i_aggminitval = PQfnumber(res, "aggminitval");
14349 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14350 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14351 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14352 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14353 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14354 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14355 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14356 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14357 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14358 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14359 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14360 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14361 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14362 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14363 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14364 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14365 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14366 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14367 agginitval = PQgetvalue(res, 0, i_agginitval);
14368 aggminitval = PQgetvalue(res, 0, i_aggminitval);
14369 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14372 char *funcargs;
14373 char *funciargs;
14375 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14376 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14377 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14378 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14381 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14383 /* identify default modify flag for aggkind (must match DefineAggregate) */
14384 defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14385 /* replace omitted flags for old versions */
14386 if (aggfinalmodify == '0')
14387 aggfinalmodify = defaultfinalmodify;
14388 if (aggmfinalmodify == '0')
14389 aggmfinalmodify = defaultfinalmodify;
14391 /* regproc and regtype output is already sufficiently quoted */
14392 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14393 aggtransfn, aggtranstype);
14395 if (strcmp(aggtransspace, "0") != 0)
14397 appendPQExpBuffer(details, ",\n SSPACE = %s",
14398 aggtransspace);
14401 if (!PQgetisnull(res, 0, i_agginitval))
14403 appendPQExpBufferStr(details, ",\n INITCOND = ");
14404 appendStringLiteralAH(details, agginitval, fout);
14407 if (strcmp(aggfinalfn, "-") != 0)
14409 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14410 aggfinalfn);
14411 if (aggfinalextra)
14412 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14413 if (aggfinalmodify != defaultfinalmodify)
14415 switch (aggfinalmodify)
14417 case AGGMODIFY_READ_ONLY:
14418 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14419 break;
14420 case AGGMODIFY_SHAREABLE:
14421 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14422 break;
14423 case AGGMODIFY_READ_WRITE:
14424 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14425 break;
14426 default:
14427 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14428 agginfo->aggfn.dobj.name);
14429 break;
14434 if (strcmp(aggcombinefn, "-") != 0)
14435 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14437 if (strcmp(aggserialfn, "-") != 0)
14438 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14440 if (strcmp(aggdeserialfn, "-") != 0)
14441 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14443 if (strcmp(aggmtransfn, "-") != 0)
14445 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14446 aggmtransfn,
14447 aggminvtransfn,
14448 aggmtranstype);
14451 if (strcmp(aggmtransspace, "0") != 0)
14453 appendPQExpBuffer(details, ",\n MSSPACE = %s",
14454 aggmtransspace);
14457 if (!PQgetisnull(res, 0, i_aggminitval))
14459 appendPQExpBufferStr(details, ",\n MINITCOND = ");
14460 appendStringLiteralAH(details, aggminitval, fout);
14463 if (strcmp(aggmfinalfn, "-") != 0)
14465 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14466 aggmfinalfn);
14467 if (aggmfinalextra)
14468 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14469 if (aggmfinalmodify != defaultfinalmodify)
14471 switch (aggmfinalmodify)
14473 case AGGMODIFY_READ_ONLY:
14474 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14475 break;
14476 case AGGMODIFY_SHAREABLE:
14477 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14478 break;
14479 case AGGMODIFY_READ_WRITE:
14480 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14481 break;
14482 default:
14483 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14484 agginfo->aggfn.dobj.name);
14485 break;
14490 aggsortconvop = getFormattedOperatorName(aggsortop);
14491 if (aggsortconvop)
14493 appendPQExpBuffer(details, ",\n SORTOP = %s",
14494 aggsortconvop);
14495 free(aggsortconvop);
14498 if (aggkind == AGGKIND_HYPOTHETICAL)
14499 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14501 if (proparallel[0] != PROPARALLEL_UNSAFE)
14503 if (proparallel[0] == PROPARALLEL_SAFE)
14504 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14505 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14506 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14507 else if (proparallel[0] != PROPARALLEL_UNSAFE)
14508 pg_fatal("unrecognized proparallel value for function \"%s\"",
14509 agginfo->aggfn.dobj.name);
14512 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14513 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14514 aggsig);
14516 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14517 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14518 aggfullsig ? aggfullsig : aggsig, details->data);
14520 if (dopt->binary_upgrade)
14521 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14522 "AGGREGATE", aggsig,
14523 agginfo->aggfn.dobj.namespace->dobj.name);
14525 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14526 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14527 agginfo->aggfn.dobj.dumpId,
14528 ARCHIVE_OPTS(.tag = aggsig_tag,
14529 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14530 .owner = agginfo->aggfn.rolname,
14531 .description = "AGGREGATE",
14532 .section = SECTION_PRE_DATA,
14533 .createStmt = q->data,
14534 .dropStmt = delq->data));
14536 /* Dump Aggregate Comments */
14537 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14538 dumpComment(fout, "AGGREGATE", aggsig,
14539 agginfo->aggfn.dobj.namespace->dobj.name,
14540 agginfo->aggfn.rolname,
14541 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14543 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14544 dumpSecLabel(fout, "AGGREGATE", aggsig,
14545 agginfo->aggfn.dobj.namespace->dobj.name,
14546 agginfo->aggfn.rolname,
14547 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14550 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14551 * command look like a function's GRANT; in particular this affects the
14552 * syntax for zero-argument aggregates and ordered-set aggregates.
14554 free(aggsig);
14556 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14558 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14559 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
14560 "FUNCTION", aggsig, NULL,
14561 agginfo->aggfn.dobj.namespace->dobj.name,
14562 NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
14564 free(aggsig);
14565 free(aggfullsig);
14566 free(aggsig_tag);
14568 PQclear(res);
14570 destroyPQExpBuffer(query);
14571 destroyPQExpBuffer(q);
14572 destroyPQExpBuffer(delq);
14573 destroyPQExpBuffer(details);
14577 * dumpTSParser
14578 * write out a single text search parser
14580 static void
14581 dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
14583 DumpOptions *dopt = fout->dopt;
14584 PQExpBuffer q;
14585 PQExpBuffer delq;
14586 char *qprsname;
14588 /* Do nothing if not dumping schema */
14589 if (!dopt->dumpSchema)
14590 return;
14592 q = createPQExpBuffer();
14593 delq = createPQExpBuffer();
14595 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14597 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14598 fmtQualifiedDumpable(prsinfo));
14600 appendPQExpBuffer(q, " START = %s,\n",
14601 convertTSFunction(fout, prsinfo->prsstart));
14602 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14603 convertTSFunction(fout, prsinfo->prstoken));
14604 appendPQExpBuffer(q, " END = %s,\n",
14605 convertTSFunction(fout, prsinfo->prsend));
14606 if (prsinfo->prsheadline != InvalidOid)
14607 appendPQExpBuffer(q, " HEADLINE = %s,\n",
14608 convertTSFunction(fout, prsinfo->prsheadline));
14609 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14610 convertTSFunction(fout, prsinfo->prslextype));
14612 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14613 fmtQualifiedDumpable(prsinfo));
14615 if (dopt->binary_upgrade)
14616 binary_upgrade_extension_member(q, &prsinfo->dobj,
14617 "TEXT SEARCH PARSER", qprsname,
14618 prsinfo->dobj.namespace->dobj.name);
14620 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14621 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
14622 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
14623 .namespace = prsinfo->dobj.namespace->dobj.name,
14624 .description = "TEXT SEARCH PARSER",
14625 .section = SECTION_PRE_DATA,
14626 .createStmt = q->data,
14627 .dropStmt = delq->data));
14629 /* Dump Parser Comments */
14630 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14631 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
14632 prsinfo->dobj.namespace->dobj.name, "",
14633 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
14635 destroyPQExpBuffer(q);
14636 destroyPQExpBuffer(delq);
14637 free(qprsname);
14641 * dumpTSDictionary
14642 * write out a single text search dictionary
14644 static void
14645 dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
14647 DumpOptions *dopt = fout->dopt;
14648 PQExpBuffer q;
14649 PQExpBuffer delq;
14650 PQExpBuffer query;
14651 char *qdictname;
14652 PGresult *res;
14653 char *nspname;
14654 char *tmplname;
14656 /* Do nothing if not dumping schema */
14657 if (!dopt->dumpSchema)
14658 return;
14660 q = createPQExpBuffer();
14661 delq = createPQExpBuffer();
14662 query = createPQExpBuffer();
14664 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
14666 /* Fetch name and namespace of the dictionary's template */
14667 appendPQExpBuffer(query, "SELECT nspname, tmplname "
14668 "FROM pg_ts_template p, pg_namespace n "
14669 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
14670 dictinfo->dicttemplate);
14671 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14672 nspname = PQgetvalue(res, 0, 0);
14673 tmplname = PQgetvalue(res, 0, 1);
14675 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
14676 fmtQualifiedDumpable(dictinfo));
14678 appendPQExpBufferStr(q, " TEMPLATE = ");
14679 appendPQExpBuffer(q, "%s.", fmtId(nspname));
14680 appendPQExpBufferStr(q, fmtId(tmplname));
14682 PQclear(res);
14684 /* the dictinitoption can be dumped straight into the command */
14685 if (dictinfo->dictinitoption)
14686 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
14688 appendPQExpBufferStr(q, " );\n");
14690 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
14691 fmtQualifiedDumpable(dictinfo));
14693 if (dopt->binary_upgrade)
14694 binary_upgrade_extension_member(q, &dictinfo->dobj,
14695 "TEXT SEARCH DICTIONARY", qdictname,
14696 dictinfo->dobj.namespace->dobj.name);
14698 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14699 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
14700 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
14701 .namespace = dictinfo->dobj.namespace->dobj.name,
14702 .owner = dictinfo->rolname,
14703 .description = "TEXT SEARCH DICTIONARY",
14704 .section = SECTION_PRE_DATA,
14705 .createStmt = q->data,
14706 .dropStmt = delq->data));
14708 /* Dump Dictionary Comments */
14709 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14710 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
14711 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
14712 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
14714 destroyPQExpBuffer(q);
14715 destroyPQExpBuffer(delq);
14716 destroyPQExpBuffer(query);
14717 free(qdictname);
14721 * dumpTSTemplate
14722 * write out a single text search template
14724 static void
14725 dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
14727 DumpOptions *dopt = fout->dopt;
14728 PQExpBuffer q;
14729 PQExpBuffer delq;
14730 char *qtmplname;
14732 /* Do nothing if not dumping schema */
14733 if (!dopt->dumpSchema)
14734 return;
14736 q = createPQExpBuffer();
14737 delq = createPQExpBuffer();
14739 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
14741 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
14742 fmtQualifiedDumpable(tmplinfo));
14744 if (tmplinfo->tmplinit != InvalidOid)
14745 appendPQExpBuffer(q, " INIT = %s,\n",
14746 convertTSFunction(fout, tmplinfo->tmplinit));
14747 appendPQExpBuffer(q, " LEXIZE = %s );\n",
14748 convertTSFunction(fout, tmplinfo->tmpllexize));
14750 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
14751 fmtQualifiedDumpable(tmplinfo));
14753 if (dopt->binary_upgrade)
14754 binary_upgrade_extension_member(q, &tmplinfo->dobj,
14755 "TEXT SEARCH TEMPLATE", qtmplname,
14756 tmplinfo->dobj.namespace->dobj.name);
14758 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14759 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
14760 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
14761 .namespace = tmplinfo->dobj.namespace->dobj.name,
14762 .description = "TEXT SEARCH TEMPLATE",
14763 .section = SECTION_PRE_DATA,
14764 .createStmt = q->data,
14765 .dropStmt = delq->data));
14767 /* Dump Template Comments */
14768 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14769 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
14770 tmplinfo->dobj.namespace->dobj.name, "",
14771 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
14773 destroyPQExpBuffer(q);
14774 destroyPQExpBuffer(delq);
14775 free(qtmplname);
14779 * dumpTSConfig
14780 * write out a single text search configuration
14782 static void
14783 dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
14785 DumpOptions *dopt = fout->dopt;
14786 PQExpBuffer q;
14787 PQExpBuffer delq;
14788 PQExpBuffer query;
14789 char *qcfgname;
14790 PGresult *res;
14791 char *nspname;
14792 char *prsname;
14793 int ntups,
14795 int i_tokenname;
14796 int i_dictname;
14798 /* Do nothing if not dumping schema */
14799 if (!dopt->dumpSchema)
14800 return;
14802 q = createPQExpBuffer();
14803 delq = createPQExpBuffer();
14804 query = createPQExpBuffer();
14806 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
14808 /* Fetch name and namespace of the config's parser */
14809 appendPQExpBuffer(query, "SELECT nspname, prsname "
14810 "FROM pg_ts_parser p, pg_namespace n "
14811 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
14812 cfginfo->cfgparser);
14813 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14814 nspname = PQgetvalue(res, 0, 0);
14815 prsname = PQgetvalue(res, 0, 1);
14817 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
14818 fmtQualifiedDumpable(cfginfo));
14820 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
14821 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
14823 PQclear(res);
14825 resetPQExpBuffer(query);
14826 appendPQExpBuffer(query,
14827 "SELECT\n"
14828 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
14829 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
14830 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
14831 "FROM pg_catalog.pg_ts_config_map AS m\n"
14832 "WHERE m.mapcfg = '%u'\n"
14833 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
14834 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
14836 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14837 ntups = PQntuples(res);
14839 i_tokenname = PQfnumber(res, "tokenname");
14840 i_dictname = PQfnumber(res, "dictname");
14842 for (i = 0; i < ntups; i++)
14844 char *tokenname = PQgetvalue(res, i, i_tokenname);
14845 char *dictname = PQgetvalue(res, i, i_dictname);
14847 if (i == 0 ||
14848 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
14850 /* starting a new token type, so start a new command */
14851 if (i > 0)
14852 appendPQExpBufferStr(q, ";\n");
14853 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
14854 fmtQualifiedDumpable(cfginfo));
14855 /* tokenname needs quoting, dictname does NOT */
14856 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
14857 fmtId(tokenname), dictname);
14859 else
14860 appendPQExpBuffer(q, ", %s", dictname);
14863 if (ntups > 0)
14864 appendPQExpBufferStr(q, ";\n");
14866 PQclear(res);
14868 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
14869 fmtQualifiedDumpable(cfginfo));
14871 if (dopt->binary_upgrade)
14872 binary_upgrade_extension_member(q, &cfginfo->dobj,
14873 "TEXT SEARCH CONFIGURATION", qcfgname,
14874 cfginfo->dobj.namespace->dobj.name);
14876 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14877 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
14878 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
14879 .namespace = cfginfo->dobj.namespace->dobj.name,
14880 .owner = cfginfo->rolname,
14881 .description = "TEXT SEARCH CONFIGURATION",
14882 .section = SECTION_PRE_DATA,
14883 .createStmt = q->data,
14884 .dropStmt = delq->data));
14886 /* Dump Configuration Comments */
14887 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14888 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
14889 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
14890 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
14892 destroyPQExpBuffer(q);
14893 destroyPQExpBuffer(delq);
14894 destroyPQExpBuffer(query);
14895 free(qcfgname);
14899 * dumpForeignDataWrapper
14900 * write out a single foreign-data wrapper definition
14902 static void
14903 dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
14905 DumpOptions *dopt = fout->dopt;
14906 PQExpBuffer q;
14907 PQExpBuffer delq;
14908 char *qfdwname;
14910 /* Do nothing if not dumping schema */
14911 if (!dopt->dumpSchema)
14912 return;
14914 q = createPQExpBuffer();
14915 delq = createPQExpBuffer();
14917 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
14919 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
14920 qfdwname);
14922 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
14923 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
14925 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
14926 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
14928 if (strlen(fdwinfo->fdwoptions) > 0)
14929 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
14931 appendPQExpBufferStr(q, ";\n");
14933 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
14934 qfdwname);
14936 if (dopt->binary_upgrade)
14937 binary_upgrade_extension_member(q, &fdwinfo->dobj,
14938 "FOREIGN DATA WRAPPER", qfdwname,
14939 NULL);
14941 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14942 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14943 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
14944 .owner = fdwinfo->rolname,
14945 .description = "FOREIGN DATA WRAPPER",
14946 .section = SECTION_PRE_DATA,
14947 .createStmt = q->data,
14948 .dropStmt = delq->data));
14950 /* Dump Foreign Data Wrapper Comments */
14951 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14952 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
14953 NULL, fdwinfo->rolname,
14954 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14956 /* Handle the ACL */
14957 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14958 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
14959 "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
14960 NULL, fdwinfo->rolname, &fdwinfo->dacl);
14962 free(qfdwname);
14964 destroyPQExpBuffer(q);
14965 destroyPQExpBuffer(delq);
14969 * dumpForeignServer
14970 * write out a foreign server definition
14972 static void
14973 dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
14975 DumpOptions *dopt = fout->dopt;
14976 PQExpBuffer q;
14977 PQExpBuffer delq;
14978 PQExpBuffer query;
14979 PGresult *res;
14980 char *qsrvname;
14981 char *fdwname;
14983 /* Do nothing if not dumping schema */
14984 if (!dopt->dumpSchema)
14985 return;
14987 q = createPQExpBuffer();
14988 delq = createPQExpBuffer();
14989 query = createPQExpBuffer();
14991 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
14993 /* look up the foreign-data wrapper */
14994 appendPQExpBuffer(query, "SELECT fdwname "
14995 "FROM pg_foreign_data_wrapper w "
14996 "WHERE w.oid = '%u'",
14997 srvinfo->srvfdw);
14998 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14999 fdwname = PQgetvalue(res, 0, 0);
15001 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15002 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15004 appendPQExpBufferStr(q, " TYPE ");
15005 appendStringLiteralAH(q, srvinfo->srvtype, fout);
15007 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15009 appendPQExpBufferStr(q, " VERSION ");
15010 appendStringLiteralAH(q, srvinfo->srvversion, fout);
15013 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15014 appendPQExpBufferStr(q, fmtId(fdwname));
15016 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15017 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15019 appendPQExpBufferStr(q, ";\n");
15021 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15022 qsrvname);
15024 if (dopt->binary_upgrade)
15025 binary_upgrade_extension_member(q, &srvinfo->dobj,
15026 "SERVER", qsrvname, NULL);
15028 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15029 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15030 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15031 .owner = srvinfo->rolname,
15032 .description = "SERVER",
15033 .section = SECTION_PRE_DATA,
15034 .createStmt = q->data,
15035 .dropStmt = delq->data));
15037 /* Dump Foreign Server Comments */
15038 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15039 dumpComment(fout, "SERVER", qsrvname,
15040 NULL, srvinfo->rolname,
15041 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15043 /* Handle the ACL */
15044 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15045 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15046 "FOREIGN SERVER", qsrvname, NULL, NULL,
15047 NULL, srvinfo->rolname, &srvinfo->dacl);
15049 /* Dump user mappings */
15050 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15051 dumpUserMappings(fout,
15052 srvinfo->dobj.name, NULL,
15053 srvinfo->rolname,
15054 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15056 PQclear(res);
15058 free(qsrvname);
15060 destroyPQExpBuffer(q);
15061 destroyPQExpBuffer(delq);
15062 destroyPQExpBuffer(query);
15066 * dumpUserMappings
15068 * This routine is used to dump any user mappings associated with the
15069 * server handed to this routine. Should be called after ArchiveEntry()
15070 * for the server.
15072 static void
15073 dumpUserMappings(Archive *fout,
15074 const char *servername, const char *namespace,
15075 const char *owner,
15076 CatalogId catalogId, DumpId dumpId)
15078 PQExpBuffer q;
15079 PQExpBuffer delq;
15080 PQExpBuffer query;
15081 PQExpBuffer tag;
15082 PGresult *res;
15083 int ntups;
15084 int i_usename;
15085 int i_umoptions;
15086 int i;
15088 q = createPQExpBuffer();
15089 tag = createPQExpBuffer();
15090 delq = createPQExpBuffer();
15091 query = createPQExpBuffer();
15094 * We read from the publicly accessible view pg_user_mappings, so as not
15095 * to fail if run by a non-superuser. Note that the view will show
15096 * umoptions as null if the user hasn't got privileges for the associated
15097 * server; this means that pg_dump will dump such a mapping, but with no
15098 * OPTIONS clause. A possible alternative is to skip such mappings
15099 * altogether, but it's not clear that that's an improvement.
15101 appendPQExpBuffer(query,
15102 "SELECT usename, "
15103 "array_to_string(ARRAY("
15104 "SELECT quote_ident(option_name) || ' ' || "
15105 "quote_literal(option_value) "
15106 "FROM pg_options_to_table(umoptions) "
15107 "ORDER BY option_name"
15108 "), E',\n ') AS umoptions "
15109 "FROM pg_user_mappings "
15110 "WHERE srvid = '%u' "
15111 "ORDER BY usename",
15112 catalogId.oid);
15114 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15116 ntups = PQntuples(res);
15117 i_usename = PQfnumber(res, "usename");
15118 i_umoptions = PQfnumber(res, "umoptions");
15120 for (i = 0; i < ntups; i++)
15122 char *usename;
15123 char *umoptions;
15125 usename = PQgetvalue(res, i, i_usename);
15126 umoptions = PQgetvalue(res, i, i_umoptions);
15128 resetPQExpBuffer(q);
15129 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15130 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15132 if (umoptions && strlen(umoptions) > 0)
15133 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15135 appendPQExpBufferStr(q, ";\n");
15137 resetPQExpBuffer(delq);
15138 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15139 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15141 resetPQExpBuffer(tag);
15142 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15143 usename, servername);
15145 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15146 ARCHIVE_OPTS(.tag = tag->data,
15147 .namespace = namespace,
15148 .owner = owner,
15149 .description = "USER MAPPING",
15150 .section = SECTION_PRE_DATA,
15151 .createStmt = q->data,
15152 .dropStmt = delq->data));
15155 PQclear(res);
15157 destroyPQExpBuffer(query);
15158 destroyPQExpBuffer(delq);
15159 destroyPQExpBuffer(tag);
15160 destroyPQExpBuffer(q);
15164 * Write out default privileges information
15166 static void
15167 dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15169 DumpOptions *dopt = fout->dopt;
15170 PQExpBuffer q;
15171 PQExpBuffer tag;
15172 const char *type;
15174 /* Do nothing if not dumping schema, or if we're skipping ACLs */
15175 if (!dopt->dumpSchema || dopt->aclsSkip)
15176 return;
15178 q = createPQExpBuffer();
15179 tag = createPQExpBuffer();
15181 switch (daclinfo->defaclobjtype)
15183 case DEFACLOBJ_RELATION:
15184 type = "TABLES";
15185 break;
15186 case DEFACLOBJ_SEQUENCE:
15187 type = "SEQUENCES";
15188 break;
15189 case DEFACLOBJ_FUNCTION:
15190 type = "FUNCTIONS";
15191 break;
15192 case DEFACLOBJ_TYPE:
15193 type = "TYPES";
15194 break;
15195 case DEFACLOBJ_NAMESPACE:
15196 type = "SCHEMAS";
15197 break;
15198 default:
15199 /* shouldn't get here */
15200 pg_fatal("unrecognized object type in default privileges: %d",
15201 (int) daclinfo->defaclobjtype);
15202 type = ""; /* keep compiler quiet */
15205 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
15207 /* build the actual command(s) for this tuple */
15208 if (!buildDefaultACLCommands(type,
15209 daclinfo->dobj.namespace != NULL ?
15210 daclinfo->dobj.namespace->dobj.name : NULL,
15211 daclinfo->dacl.acl,
15212 daclinfo->dacl.acldefault,
15213 daclinfo->defaclrole,
15214 fout->remoteVersion,
15216 pg_fatal("could not parse default ACL list (%s)",
15217 daclinfo->dacl.acl);
15219 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
15220 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
15221 ARCHIVE_OPTS(.tag = tag->data,
15222 .namespace = daclinfo->dobj.namespace ?
15223 daclinfo->dobj.namespace->dobj.name : NULL,
15224 .owner = daclinfo->defaclrole,
15225 .description = "DEFAULT ACL",
15226 .section = SECTION_POST_DATA,
15227 .createStmt = q->data));
15229 destroyPQExpBuffer(tag);
15230 destroyPQExpBuffer(q);
15233 /*----------
15234 * Write out grant/revoke information
15236 * 'objDumpId' is the dump ID of the underlying object.
15237 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
15238 * or InvalidDumpId if there is no need for a second dependency.
15239 * 'type' must be one of
15240 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
15241 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
15242 * 'name' is the formatted name of the object. Must be quoted etc. already.
15243 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
15244 * (Currently we assume that subname is only provided for table columns.)
15245 * 'nspname' is the namespace the object is in (NULL if none).
15246 * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
15247 * to use the default for the object type.
15248 * 'owner' is the owner, NULL if there is no owner (for languages).
15249 * 'dacl' is the DumpableAcl struct for the object.
15251 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15252 * no ACL entry was created.
15253 *----------
15255 static DumpId
15256 dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
15257 const char *type, const char *name, const char *subname,
15258 const char *nspname, const char *tag, const char *owner,
15259 const DumpableAcl *dacl)
15261 DumpId aclDumpId = InvalidDumpId;
15262 DumpOptions *dopt = fout->dopt;
15263 const char *acls = dacl->acl;
15264 const char *acldefault = dacl->acldefault;
15265 char privtype = dacl->privtype;
15266 const char *initprivs = dacl->initprivs;
15267 const char *baseacls;
15268 PQExpBuffer sql;
15270 /* Do nothing if ACL dump is not enabled */
15271 if (dopt->aclsSkip)
15272 return InvalidDumpId;
15274 /* --data-only skips ACLs *except* large object ACLs */
15275 if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
15276 return InvalidDumpId;
15278 sql = createPQExpBuffer();
15281 * In binary upgrade mode, we don't run an extension's script but instead
15282 * dump out the objects independently and then recreate them. To preserve
15283 * any initial privileges which were set on extension objects, we need to
15284 * compute the set of GRANT and REVOKE commands necessary to get from the
15285 * default privileges of an object to its initial privileges as recorded
15286 * in pg_init_privs.
15288 * At restore time, we apply these commands after having called
15289 * binary_upgrade_set_record_init_privs(true). That tells the backend to
15290 * copy the results into pg_init_privs. This is how we preserve the
15291 * contents of that catalog across binary upgrades.
15293 if (dopt->binary_upgrade && privtype == 'e' &&
15294 initprivs && *initprivs != '\0')
15296 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
15297 if (!buildACLCommands(name, subname, nspname, type,
15298 initprivs, acldefault, owner,
15299 "", fout->remoteVersion, sql))
15300 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
15301 initprivs, acldefault, name, type);
15302 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
15306 * Now figure the GRANT and REVOKE commands needed to get to the object's
15307 * actual current ACL, starting from the initprivs if given, else from the
15308 * object-type-specific default. Also, while buildACLCommands will assume
15309 * that a NULL/empty acls string means it needn't do anything, what that
15310 * actually represents is the object-type-specific default; so we need to
15311 * substitute the acldefault string to get the right results in that case.
15313 if (initprivs && *initprivs != '\0')
15315 baseacls = initprivs;
15316 if (acls == NULL || *acls == '\0')
15317 acls = acldefault;
15319 else
15320 baseacls = acldefault;
15322 if (!buildACLCommands(name, subname, nspname, type,
15323 acls, baseacls, owner,
15324 "", fout->remoteVersion, sql))
15325 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15326 acls, baseacls, name, type);
15328 if (sql->len > 0)
15330 PQExpBuffer tagbuf = createPQExpBuffer();
15331 DumpId aclDeps[2];
15332 int nDeps = 0;
15334 if (tag)
15335 appendPQExpBufferStr(tagbuf, tag);
15336 else if (subname)
15337 appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
15338 else
15339 appendPQExpBuffer(tagbuf, "%s %s", type, name);
15341 aclDeps[nDeps++] = objDumpId;
15342 if (altDumpId != InvalidDumpId)
15343 aclDeps[nDeps++] = altDumpId;
15345 aclDumpId = createDumpId();
15347 ArchiveEntry(fout, nilCatalogId, aclDumpId,
15348 ARCHIVE_OPTS(.tag = tagbuf->data,
15349 .namespace = nspname,
15350 .owner = owner,
15351 .description = "ACL",
15352 .section = SECTION_NONE,
15353 .createStmt = sql->data,
15354 .deps = aclDeps,
15355 .nDeps = nDeps));
15357 destroyPQExpBuffer(tagbuf);
15360 destroyPQExpBuffer(sql);
15362 return aclDumpId;
15366 * dumpSecLabel
15368 * This routine is used to dump any security labels associated with the
15369 * object handed to this routine. The routine takes the object type
15370 * and object name (ready to print, except for schema decoration), plus
15371 * the namespace and owner of the object (for labeling the ArchiveEntry),
15372 * plus catalog ID and subid which are the lookup key for pg_seclabel,
15373 * plus the dump ID for the object (for setting a dependency).
15374 * If a matching pg_seclabel entry is found, it is dumped.
15376 * Note: although this routine takes a dumpId for dependency purposes,
15377 * that purpose is just to mark the dependency in the emitted dump file
15378 * for possible future use by pg_restore. We do NOT use it for determining
15379 * ordering of the label in the dump file, because this routine is called
15380 * after dependency sorting occurs. This routine should be called just after
15381 * calling ArchiveEntry() for the specified object.
15383 static void
15384 dumpSecLabel(Archive *fout, const char *type, const char *name,
15385 const char *namespace, const char *owner,
15386 CatalogId catalogId, int subid, DumpId dumpId)
15388 DumpOptions *dopt = fout->dopt;
15389 SecLabelItem *labels;
15390 int nlabels;
15391 int i;
15392 PQExpBuffer query;
15394 /* do nothing, if --no-security-labels is supplied */
15395 if (dopt->no_security_labels)
15396 return;
15399 * Security labels are schema not data ... except large object labels are
15400 * data
15402 if (strcmp(type, "LARGE OBJECT") != 0)
15404 if (!dopt->dumpSchema)
15405 return;
15407 else
15409 /* We do dump large object security labels in binary-upgrade mode */
15410 if (!dopt->dumpData && !dopt->binary_upgrade)
15411 return;
15414 /* Search for security labels associated with catalogId, using table */
15415 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15417 query = createPQExpBuffer();
15419 for (i = 0; i < nlabels; i++)
15422 * Ignore label entries for which the subid doesn't match.
15424 if (labels[i].objsubid != subid)
15425 continue;
15427 appendPQExpBuffer(query,
15428 "SECURITY LABEL FOR %s ON %s ",
15429 fmtId(labels[i].provider), type);
15430 if (namespace && *namespace)
15431 appendPQExpBuffer(query, "%s.", fmtId(namespace));
15432 appendPQExpBuffer(query, "%s IS ", name);
15433 appendStringLiteralAH(query, labels[i].label, fout);
15434 appendPQExpBufferStr(query, ";\n");
15437 if (query->len > 0)
15439 PQExpBuffer tag = createPQExpBuffer();
15441 appendPQExpBuffer(tag, "%s %s", type, name);
15442 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15443 ARCHIVE_OPTS(.tag = tag->data,
15444 .namespace = namespace,
15445 .owner = owner,
15446 .description = "SECURITY LABEL",
15447 .section = SECTION_NONE,
15448 .createStmt = query->data,
15449 .deps = &dumpId,
15450 .nDeps = 1));
15451 destroyPQExpBuffer(tag);
15454 destroyPQExpBuffer(query);
15458 * dumpTableSecLabel
15460 * As above, but dump security label for both the specified table (or view)
15461 * and its columns.
15463 static void
15464 dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15466 DumpOptions *dopt = fout->dopt;
15467 SecLabelItem *labels;
15468 int nlabels;
15469 int i;
15470 PQExpBuffer query;
15471 PQExpBuffer target;
15473 /* do nothing, if --no-security-labels is supplied */
15474 if (dopt->no_security_labels)
15475 return;
15477 /* SecLabel are SCHEMA not data */
15478 if (!dopt->dumpSchema)
15479 return;
15481 /* Search for comments associated with relation, using table */
15482 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15483 tbinfo->dobj.catId.oid,
15484 &labels);
15486 /* If security labels exist, build SECURITY LABEL statements */
15487 if (nlabels <= 0)
15488 return;
15490 query = createPQExpBuffer();
15491 target = createPQExpBuffer();
15493 for (i = 0; i < nlabels; i++)
15495 const char *colname;
15496 const char *provider = labels[i].provider;
15497 const char *label = labels[i].label;
15498 int objsubid = labels[i].objsubid;
15500 resetPQExpBuffer(target);
15501 if (objsubid == 0)
15503 appendPQExpBuffer(target, "%s %s", reltypename,
15504 fmtQualifiedDumpable(tbinfo));
15506 else
15508 colname = getAttrName(objsubid, tbinfo);
15509 /* first fmtXXX result must be consumed before calling again */
15510 appendPQExpBuffer(target, "COLUMN %s",
15511 fmtQualifiedDumpable(tbinfo));
15512 appendPQExpBuffer(target, ".%s", fmtId(colname));
15514 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15515 fmtId(provider), target->data);
15516 appendStringLiteralAH(query, label, fout);
15517 appendPQExpBufferStr(query, ";\n");
15519 if (query->len > 0)
15521 resetPQExpBuffer(target);
15522 appendPQExpBuffer(target, "%s %s", reltypename,
15523 fmtId(tbinfo->dobj.name));
15524 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15525 ARCHIVE_OPTS(.tag = target->data,
15526 .namespace = tbinfo->dobj.namespace->dobj.name,
15527 .owner = tbinfo->rolname,
15528 .description = "SECURITY LABEL",
15529 .section = SECTION_NONE,
15530 .createStmt = query->data,
15531 .deps = &(tbinfo->dobj.dumpId),
15532 .nDeps = 1));
15534 destroyPQExpBuffer(query);
15535 destroyPQExpBuffer(target);
15539 * findSecLabels
15541 * Find the security label(s), if any, associated with the given object.
15542 * All the objsubid values associated with the given classoid/objoid are
15543 * found with one search.
15545 static int
15546 findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
15548 SecLabelItem *middle = NULL;
15549 SecLabelItem *low;
15550 SecLabelItem *high;
15551 int nmatch;
15553 if (nseclabels <= 0) /* no labels, so no match is possible */
15555 *items = NULL;
15556 return 0;
15560 * Do binary search to find some item matching the object.
15562 low = &seclabels[0];
15563 high = &seclabels[nseclabels - 1];
15564 while (low <= high)
15566 middle = low + (high - low) / 2;
15568 if (classoid < middle->classoid)
15569 high = middle - 1;
15570 else if (classoid > middle->classoid)
15571 low = middle + 1;
15572 else if (objoid < middle->objoid)
15573 high = middle - 1;
15574 else if (objoid > middle->objoid)
15575 low = middle + 1;
15576 else
15577 break; /* found a match */
15580 if (low > high) /* no matches */
15582 *items = NULL;
15583 return 0;
15587 * Now determine how many items match the object. The search loop
15588 * invariant still holds: only items between low and high inclusive could
15589 * match.
15591 nmatch = 1;
15592 while (middle > low)
15594 if (classoid != middle[-1].classoid ||
15595 objoid != middle[-1].objoid)
15596 break;
15597 middle--;
15598 nmatch++;
15601 *items = middle;
15603 middle += nmatch;
15604 while (middle <= high)
15606 if (classoid != middle->classoid ||
15607 objoid != middle->objoid)
15608 break;
15609 middle++;
15610 nmatch++;
15613 return nmatch;
15617 * collectSecLabels
15619 * Construct a table of all security labels available for database objects;
15620 * also set the has-seclabel component flag for each relevant object.
15622 * The table is sorted by classoid/objid/objsubid for speed in lookup.
15624 static void
15625 collectSecLabels(Archive *fout)
15627 PGresult *res;
15628 PQExpBuffer query;
15629 int i_label;
15630 int i_provider;
15631 int i_classoid;
15632 int i_objoid;
15633 int i_objsubid;
15634 int ntups;
15635 int i;
15636 DumpableObject *dobj;
15638 query = createPQExpBuffer();
15640 appendPQExpBufferStr(query,
15641 "SELECT label, provider, classoid, objoid, objsubid "
15642 "FROM pg_catalog.pg_seclabel "
15643 "ORDER BY classoid, objoid, objsubid");
15645 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15647 /* Construct lookup table containing OIDs in numeric form */
15648 i_label = PQfnumber(res, "label");
15649 i_provider = PQfnumber(res, "provider");
15650 i_classoid = PQfnumber(res, "classoid");
15651 i_objoid = PQfnumber(res, "objoid");
15652 i_objsubid = PQfnumber(res, "objsubid");
15654 ntups = PQntuples(res);
15656 seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
15657 nseclabels = 0;
15658 dobj = NULL;
15660 for (i = 0; i < ntups; i++)
15662 CatalogId objId;
15663 int subid;
15665 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
15666 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
15667 subid = atoi(PQgetvalue(res, i, i_objsubid));
15669 /* We needn't remember labels that don't match any dumpable object */
15670 if (dobj == NULL ||
15671 dobj->catId.tableoid != objId.tableoid ||
15672 dobj->catId.oid != objId.oid)
15673 dobj = findObjectByCatalogId(objId);
15674 if (dobj == NULL)
15675 continue;
15678 * Labels on columns of composite types are linked to the type's
15679 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
15680 * in the type's own DumpableObject.
15682 if (subid != 0 && dobj->objType == DO_TABLE &&
15683 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
15685 TypeInfo *cTypeInfo;
15687 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
15688 if (cTypeInfo)
15689 cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
15691 else
15692 dobj->components |= DUMP_COMPONENT_SECLABEL;
15694 seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
15695 seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
15696 seclabels[nseclabels].classoid = objId.tableoid;
15697 seclabels[nseclabels].objoid = objId.oid;
15698 seclabels[nseclabels].objsubid = subid;
15699 nseclabels++;
15702 PQclear(res);
15703 destroyPQExpBuffer(query);
15707 * dumpTable
15708 * write out to fout the declarations (not data) of a user-defined table
15710 static void
15711 dumpTable(Archive *fout, const TableInfo *tbinfo)
15713 DumpOptions *dopt = fout->dopt;
15714 DumpId tableAclDumpId = InvalidDumpId;
15715 char *namecopy;
15717 /* Do nothing if not dumping schema */
15718 if (!dopt->dumpSchema)
15719 return;
15721 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15723 if (tbinfo->relkind == RELKIND_SEQUENCE)
15724 dumpSequence(fout, tbinfo);
15725 else
15726 dumpTableSchema(fout, tbinfo);
15729 /* Handle the ACL here */
15730 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
15731 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15733 const char *objtype =
15734 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
15736 tableAclDumpId =
15737 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
15738 objtype, namecopy, NULL,
15739 tbinfo->dobj.namespace->dobj.name,
15740 NULL, tbinfo->rolname, &tbinfo->dacl);
15744 * Handle column ACLs, if any. Note: we pull these with a separate query
15745 * rather than trying to fetch them during getTableAttrs, so that we won't
15746 * miss ACLs on system columns. Doing it this way also allows us to dump
15747 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
15749 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
15751 PQExpBuffer query = createPQExpBuffer();
15752 PGresult *res;
15753 int i;
15755 if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
15757 /* Set up query for column ACLs */
15758 appendPQExpBufferStr(query,
15759 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
15761 if (fout->remoteVersion >= 90600)
15764 * In principle we should call acldefault('c', relowner) to
15765 * get the default ACL for a column. However, we don't
15766 * currently store the numeric OID of the relowner in
15767 * TableInfo. We could convert the owner name using regrole,
15768 * but that creates a risk of failure due to concurrent role
15769 * renames. Given that the default ACL for columns is empty
15770 * and is likely to stay that way, it's not worth extra cycles
15771 * and risk to avoid hard-wiring that knowledge here.
15773 appendPQExpBufferStr(query,
15774 "SELECT at.attname, "
15775 "at.attacl, "
15776 "'{}' AS acldefault, "
15777 "pip.privtype, pip.initprivs "
15778 "FROM pg_catalog.pg_attribute at "
15779 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
15780 "(at.attrelid = pip.objoid "
15781 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
15782 "AND at.attnum = pip.objsubid) "
15783 "WHERE at.attrelid = $1 AND "
15784 "NOT at.attisdropped "
15785 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
15786 "ORDER BY at.attnum");
15788 else
15790 appendPQExpBufferStr(query,
15791 "SELECT attname, attacl, '{}' AS acldefault, "
15792 "NULL AS privtype, NULL AS initprivs "
15793 "FROM pg_catalog.pg_attribute "
15794 "WHERE attrelid = $1 AND NOT attisdropped "
15795 "AND attacl IS NOT NULL "
15796 "ORDER BY attnum");
15799 ExecuteSqlStatement(fout, query->data);
15801 fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
15804 printfPQExpBuffer(query,
15805 "EXECUTE getColumnACLs('%u')",
15806 tbinfo->dobj.catId.oid);
15808 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15810 for (i = 0; i < PQntuples(res); i++)
15812 char *attname = PQgetvalue(res, i, 0);
15813 char *attacl = PQgetvalue(res, i, 1);
15814 char *acldefault = PQgetvalue(res, i, 2);
15815 char privtype = *(PQgetvalue(res, i, 3));
15816 char *initprivs = PQgetvalue(res, i, 4);
15817 DumpableAcl coldacl;
15818 char *attnamecopy;
15820 coldacl.acl = attacl;
15821 coldacl.acldefault = acldefault;
15822 coldacl.privtype = privtype;
15823 coldacl.initprivs = initprivs;
15824 attnamecopy = pg_strdup(fmtId(attname));
15827 * Column's GRANT type is always TABLE. Each column ACL depends
15828 * on the table-level ACL, since we can restore column ACLs in
15829 * parallel but the table-level ACL has to be done first.
15831 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
15832 "TABLE", namecopy, attnamecopy,
15833 tbinfo->dobj.namespace->dobj.name,
15834 NULL, tbinfo->rolname, &coldacl);
15835 free(attnamecopy);
15837 PQclear(res);
15838 destroyPQExpBuffer(query);
15841 free(namecopy);
15845 * Create the AS clause for a view or materialized view. The semicolon is
15846 * stripped because a materialized view must add a WITH NO DATA clause.
15848 * This returns a new buffer which must be freed by the caller.
15850 static PQExpBuffer
15851 createViewAsClause(Archive *fout, const TableInfo *tbinfo)
15853 PQExpBuffer query = createPQExpBuffer();
15854 PQExpBuffer result = createPQExpBuffer();
15855 PGresult *res;
15856 int len;
15858 /* Fetch the view definition */
15859 appendPQExpBuffer(query,
15860 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
15861 tbinfo->dobj.catId.oid);
15863 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15865 if (PQntuples(res) != 1)
15867 if (PQntuples(res) < 1)
15868 pg_fatal("query to obtain definition of view \"%s\" returned no data",
15869 tbinfo->dobj.name);
15870 else
15871 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
15872 tbinfo->dobj.name);
15875 len = PQgetlength(res, 0, 0);
15877 if (len == 0)
15878 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
15879 tbinfo->dobj.name);
15881 /* Strip off the trailing semicolon so that other things may follow. */
15882 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
15883 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
15885 PQclear(res);
15886 destroyPQExpBuffer(query);
15888 return result;
15892 * Create a dummy AS clause for a view. This is used when the real view
15893 * definition has to be postponed because of circular dependencies.
15894 * We must duplicate the view's external properties -- column names and types
15895 * (including collation) -- so that it works for subsequent references.
15897 * This returns a new buffer which must be freed by the caller.
15899 static PQExpBuffer
15900 createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
15902 PQExpBuffer result = createPQExpBuffer();
15903 int j;
15905 appendPQExpBufferStr(result, "SELECT");
15907 for (j = 0; j < tbinfo->numatts; j++)
15909 if (j > 0)
15910 appendPQExpBufferChar(result, ',');
15911 appendPQExpBufferStr(result, "\n ");
15913 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
15916 * Must add collation if not default for the type, because CREATE OR
15917 * REPLACE VIEW won't change it
15919 if (OidIsValid(tbinfo->attcollation[j]))
15921 CollInfo *coll;
15923 coll = findCollationByOid(tbinfo->attcollation[j]);
15924 if (coll)
15925 appendPQExpBuffer(result, " COLLATE %s",
15926 fmtQualifiedDumpable(coll));
15929 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
15932 return result;
15936 * dumpTableSchema
15937 * write the declaration (not data) of one user-defined table or view
15939 static void
15940 dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
15942 DumpOptions *dopt = fout->dopt;
15943 PQExpBuffer q = createPQExpBuffer();
15944 PQExpBuffer delq = createPQExpBuffer();
15945 PQExpBuffer extra = createPQExpBuffer();
15946 char *qrelname;
15947 char *qualrelname;
15948 int numParents;
15949 TableInfo **parents;
15950 int actual_atts; /* number of attrs in this CREATE statement */
15951 const char *reltypename;
15952 char *storage;
15953 int j,
15956 /* We had better have loaded per-column details about this table */
15957 Assert(tbinfo->interesting);
15959 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
15960 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
15962 if (tbinfo->hasoids)
15963 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
15964 qrelname);
15966 if (dopt->binary_upgrade)
15967 binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
15969 /* Is it a table or a view? */
15970 if (tbinfo->relkind == RELKIND_VIEW)
15972 PQExpBuffer result;
15975 * Note: keep this code in sync with the is_view case in dumpRule()
15978 reltypename = "VIEW";
15980 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
15982 if (dopt->binary_upgrade)
15983 binary_upgrade_set_pg_class_oids(fout, q,
15984 tbinfo->dobj.catId.oid);
15986 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
15988 if (tbinfo->dummy_view)
15989 result = createDummyViewAsClause(fout, tbinfo);
15990 else
15992 if (nonemptyReloptions(tbinfo->reloptions))
15994 appendPQExpBufferStr(q, " WITH (");
15995 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15996 appendPQExpBufferChar(q, ')');
15998 result = createViewAsClause(fout, tbinfo);
16000 appendPQExpBuffer(q, " AS\n%s", result->data);
16001 destroyPQExpBuffer(result);
16003 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16004 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16005 appendPQExpBufferStr(q, ";\n");
16007 else
16009 char *partkeydef = NULL;
16010 char *ftoptions = NULL;
16011 char *srvname = NULL;
16012 const char *foreign = "";
16015 * Set reltypename, and collect any relkind-specific data that we
16016 * didn't fetch during getTables().
16018 switch (tbinfo->relkind)
16020 case RELKIND_PARTITIONED_TABLE:
16022 PQExpBuffer query = createPQExpBuffer();
16023 PGresult *res;
16025 reltypename = "TABLE";
16027 /* retrieve partition key definition */
16028 appendPQExpBuffer(query,
16029 "SELECT pg_get_partkeydef('%u')",
16030 tbinfo->dobj.catId.oid);
16031 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16032 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16033 PQclear(res);
16034 destroyPQExpBuffer(query);
16035 break;
16037 case RELKIND_FOREIGN_TABLE:
16039 PQExpBuffer query = createPQExpBuffer();
16040 PGresult *res;
16041 int i_srvname;
16042 int i_ftoptions;
16044 reltypename = "FOREIGN TABLE";
16046 /* retrieve name of foreign server and generic options */
16047 appendPQExpBuffer(query,
16048 "SELECT fs.srvname, "
16049 "pg_catalog.array_to_string(ARRAY("
16050 "SELECT pg_catalog.quote_ident(option_name) || "
16051 "' ' || pg_catalog.quote_literal(option_value) "
16052 "FROM pg_catalog.pg_options_to_table(ftoptions) "
16053 "ORDER BY option_name"
16054 "), E',\n ') AS ftoptions "
16055 "FROM pg_catalog.pg_foreign_table ft "
16056 "JOIN pg_catalog.pg_foreign_server fs "
16057 "ON (fs.oid = ft.ftserver) "
16058 "WHERE ft.ftrelid = '%u'",
16059 tbinfo->dobj.catId.oid);
16060 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16061 i_srvname = PQfnumber(res, "srvname");
16062 i_ftoptions = PQfnumber(res, "ftoptions");
16063 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16064 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16065 PQclear(res);
16066 destroyPQExpBuffer(query);
16068 foreign = "FOREIGN ";
16069 break;
16071 case RELKIND_MATVIEW:
16072 reltypename = "MATERIALIZED VIEW";
16073 break;
16074 default:
16075 reltypename = "TABLE";
16076 break;
16079 numParents = tbinfo->numParents;
16080 parents = tbinfo->parents;
16082 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16084 if (dopt->binary_upgrade)
16085 binary_upgrade_set_pg_class_oids(fout, q,
16086 tbinfo->dobj.catId.oid);
16089 * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
16090 * ignore it when dumping if it was set in this case.
16092 appendPQExpBuffer(q, "CREATE %s%s %s",
16093 (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
16094 tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
16095 "UNLOGGED " : "",
16096 reltypename,
16097 qualrelname);
16100 * Attach to type, if reloftype; except in case of a binary upgrade,
16101 * we dump the table normally and attach it to the type afterward.
16103 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16104 appendPQExpBuffer(q, " OF %s",
16105 getFormattedTypeName(fout, tbinfo->reloftype,
16106 zeroIsError));
16108 if (tbinfo->relkind != RELKIND_MATVIEW)
16110 /* Dump the attributes */
16111 actual_atts = 0;
16112 for (j = 0; j < tbinfo->numatts; j++)
16115 * Normally, dump if it's locally defined in this table, and
16116 * not dropped. But for binary upgrade, we'll dump all the
16117 * columns, and then fix up the dropped and nonlocal cases
16118 * below.
16120 if (shouldPrintColumn(dopt, tbinfo, j))
16122 bool print_default;
16123 bool print_notnull;
16126 * Default value --- suppress if to be printed separately
16127 * or not at all.
16129 print_default = (tbinfo->attrdefs[j] != NULL &&
16130 tbinfo->attrdefs[j]->dobj.dump &&
16131 !tbinfo->attrdefs[j]->separate);
16134 * Not Null constraint --- print it if it is locally
16135 * defined, or if binary upgrade. (In the latter case, we
16136 * reset conislocal below.)
16138 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16139 (tbinfo->notnull_islocal[j] ||
16140 dopt->binary_upgrade ||
16141 tbinfo->ispartition));
16144 * Skip column if fully defined by reloftype, except in
16145 * binary upgrade
16147 if (OidIsValid(tbinfo->reloftype) &&
16148 !print_default && !print_notnull &&
16149 !dopt->binary_upgrade)
16150 continue;
16152 /* Format properly if not first attr */
16153 if (actual_atts == 0)
16154 appendPQExpBufferStr(q, " (");
16155 else
16156 appendPQExpBufferChar(q, ',');
16157 appendPQExpBufferStr(q, "\n ");
16158 actual_atts++;
16160 /* Attribute name */
16161 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16163 if (tbinfo->attisdropped[j])
16166 * ALTER TABLE DROP COLUMN clears
16167 * pg_attribute.atttypid, so we will not have gotten a
16168 * valid type name; insert INTEGER as a stopgap. We'll
16169 * clean things up later.
16171 appendPQExpBufferStr(q, " INTEGER /* dummy */");
16172 /* and skip to the next column */
16173 continue;
16177 * Attribute type; print it except when creating a typed
16178 * table ('OF type_name'), but in binary-upgrade mode,
16179 * print it in that case too.
16181 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16183 appendPQExpBuffer(q, " %s",
16184 tbinfo->atttypnames[j]);
16187 if (print_default)
16189 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
16190 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
16191 tbinfo->attrdefs[j]->adef_expr);
16192 else
16193 appendPQExpBuffer(q, " DEFAULT %s",
16194 tbinfo->attrdefs[j]->adef_expr);
16197 print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
16198 (tbinfo->notnull_islocal[j] ||
16199 dopt->binary_upgrade ||
16200 tbinfo->ispartition));
16202 if (print_notnull)
16204 if (tbinfo->notnull_constrs[j][0] == '\0')
16205 appendPQExpBufferStr(q, " NOT NULL");
16206 else
16207 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
16208 fmtId(tbinfo->notnull_constrs[j]));
16210 if (tbinfo->notnull_noinh[j])
16211 appendPQExpBufferStr(q, " NO INHERIT");
16214 /* Add collation if not default for the type */
16215 if (OidIsValid(tbinfo->attcollation[j]))
16217 CollInfo *coll;
16219 coll = findCollationByOid(tbinfo->attcollation[j]);
16220 if (coll)
16221 appendPQExpBuffer(q, " COLLATE %s",
16222 fmtQualifiedDumpable(coll));
16227 * On the other hand, if we choose not to print a column
16228 * (likely because it is created by inheritance), but the
16229 * column has a locally-defined not-null constraint, we need
16230 * to dump the constraint as a standalone object.
16232 * This syntax isn't SQL-conforming, but if you wanted
16233 * standard output you wouldn't be creating non-standard
16234 * objects to begin with.
16236 if (!shouldPrintColumn(dopt, tbinfo, j) &&
16237 !tbinfo->attisdropped[j] &&
16238 tbinfo->notnull_constrs[j] != NULL &&
16239 tbinfo->notnull_islocal[j])
16241 /* Format properly if not first attr */
16242 if (actual_atts == 0)
16243 appendPQExpBufferStr(q, " (");
16244 else
16245 appendPQExpBufferChar(q, ',');
16246 appendPQExpBufferStr(q, "\n ");
16247 actual_atts++;
16249 if (tbinfo->notnull_constrs[j][0] == '\0')
16250 appendPQExpBuffer(q, "NOT NULL %s",
16251 fmtId(tbinfo->attnames[j]));
16252 else
16253 appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
16254 tbinfo->notnull_constrs[j],
16255 fmtId(tbinfo->attnames[j]));
16260 * Add non-inherited CHECK constraints, if any.
16262 * For partitions, we need to include check constraints even if
16263 * they're not defined locally, because the ALTER TABLE ATTACH
16264 * PARTITION that we'll emit later expects the constraint to be
16265 * there. (No need to fix conislocal: ATTACH PARTITION does that)
16267 for (j = 0; j < tbinfo->ncheck; j++)
16269 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16271 if (constr->separate ||
16272 (!constr->conislocal && !tbinfo->ispartition))
16273 continue;
16275 if (actual_atts == 0)
16276 appendPQExpBufferStr(q, " (\n ");
16277 else
16278 appendPQExpBufferStr(q, ",\n ");
16280 appendPQExpBuffer(q, "CONSTRAINT %s ",
16281 fmtId(constr->dobj.name));
16282 appendPQExpBufferStr(q, constr->condef);
16284 actual_atts++;
16287 if (actual_atts)
16288 appendPQExpBufferStr(q, "\n)");
16289 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
16292 * No attributes? we must have a parenthesized attribute list,
16293 * even though empty, when not using the OF TYPE syntax.
16295 appendPQExpBufferStr(q, " (\n)");
16299 * Emit the INHERITS clause (not for partitions), except in
16300 * binary-upgrade mode.
16302 if (numParents > 0 && !tbinfo->ispartition &&
16303 !dopt->binary_upgrade)
16305 appendPQExpBufferStr(q, "\nINHERITS (");
16306 for (k = 0; k < numParents; k++)
16308 TableInfo *parentRel = parents[k];
16310 if (k > 0)
16311 appendPQExpBufferStr(q, ", ");
16312 appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
16314 appendPQExpBufferChar(q, ')');
16317 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16318 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
16320 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
16321 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
16324 if (nonemptyReloptions(tbinfo->reloptions) ||
16325 nonemptyReloptions(tbinfo->toast_reloptions))
16327 bool addcomma = false;
16329 appendPQExpBufferStr(q, "\nWITH (");
16330 if (nonemptyReloptions(tbinfo->reloptions))
16332 addcomma = true;
16333 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16335 if (nonemptyReloptions(tbinfo->toast_reloptions))
16337 if (addcomma)
16338 appendPQExpBufferStr(q, ", ");
16339 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
16340 fout);
16342 appendPQExpBufferChar(q, ')');
16345 /* Dump generic options if any */
16346 if (ftoptions && ftoptions[0])
16347 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
16350 * For materialized views, create the AS clause just like a view. At
16351 * this point, we always mark the view as not populated.
16353 if (tbinfo->relkind == RELKIND_MATVIEW)
16355 PQExpBuffer result;
16357 result = createViewAsClause(fout, tbinfo);
16358 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
16359 result->data);
16360 destroyPQExpBuffer(result);
16362 else
16363 appendPQExpBufferStr(q, ";\n");
16365 /* Materialized views can depend on extensions */
16366 if (tbinfo->relkind == RELKIND_MATVIEW)
16367 append_depends_on_extension(fout, q, &tbinfo->dobj,
16368 "pg_catalog.pg_class",
16369 "MATERIALIZED VIEW",
16370 qualrelname);
16373 * in binary upgrade mode, update the catalog with any missing values
16374 * that might be present.
16376 if (dopt->binary_upgrade)
16378 for (j = 0; j < tbinfo->numatts; j++)
16380 if (tbinfo->attmissingval[j][0] != '\0')
16382 appendPQExpBufferStr(q, "\n-- set missing value.\n");
16383 appendPQExpBufferStr(q,
16384 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16385 appendStringLiteralAH(q, qualrelname, fout);
16386 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16387 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16388 appendPQExpBufferChar(q, ',');
16389 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16390 appendPQExpBufferStr(q, ");\n\n");
16396 * To create binary-compatible heap files, we have to ensure the same
16397 * physical column order, including dropped columns, as in the
16398 * original. Therefore, we create dropped columns above and drop them
16399 * here, also updating their attlen/attalign values so that the
16400 * dropped column can be skipped properly. (We do not bother with
16401 * restoring the original attbyval setting.) Also, inheritance
16402 * relationships are set up by doing ALTER TABLE INHERIT rather than
16403 * using an INHERITS clause --- the latter would possibly mess up the
16404 * column order. That also means we have to take care about setting
16405 * attislocal correctly, plus fix up any inherited CHECK constraints.
16406 * Analogously, we set up typed tables using ALTER TABLE / OF here.
16408 * We process foreign and partitioned tables here, even though they
16409 * lack heap storage, because they can participate in inheritance
16410 * relationships and we want this stuff to be consistent across the
16411 * inheritance tree. We can exclude indexes, toast tables, sequences
16412 * and matviews, even though they have storage, because we don't
16413 * support altering or dropping columns in them, nor can they be part
16414 * of inheritance trees.
16416 if (dopt->binary_upgrade &&
16417 (tbinfo->relkind == RELKIND_RELATION ||
16418 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16419 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16421 bool firstitem;
16422 bool firstitem_extra;
16425 * Drop any dropped columns. Merge the pg_attribute manipulations
16426 * into a single SQL command, so that we don't cause repeated
16427 * relcache flushes on the target table. Otherwise we risk O(N^2)
16428 * relcache bloat while dropping N columns.
16430 resetPQExpBuffer(extra);
16431 firstitem = true;
16432 for (j = 0; j < tbinfo->numatts; j++)
16434 if (tbinfo->attisdropped[j])
16436 if (firstitem)
16438 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
16439 "UPDATE pg_catalog.pg_attribute\n"
16440 "SET attlen = v.dlen, "
16441 "attalign = v.dalign, "
16442 "attbyval = false\n"
16443 "FROM (VALUES ");
16444 firstitem = false;
16446 else
16447 appendPQExpBufferStr(q, ",\n ");
16448 appendPQExpBufferChar(q, '(');
16449 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16450 appendPQExpBuffer(q, ", %d, '%c')",
16451 tbinfo->attlen[j],
16452 tbinfo->attalign[j]);
16453 /* The ALTER ... DROP COLUMN commands must come after */
16454 appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
16455 foreign, qualrelname);
16456 appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
16457 fmtId(tbinfo->attnames[j]));
16460 if (!firstitem)
16462 appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
16463 "WHERE attrelid = ");
16464 appendStringLiteralAH(q, qualrelname, fout);
16465 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16466 " AND attname = v.dname;\n");
16467 /* Now we can issue the actual DROP COLUMN commands */
16468 appendBinaryPQExpBuffer(q, extra->data, extra->len);
16472 * Fix up inherited columns. As above, do the pg_attribute
16473 * manipulations in a single SQL command.
16475 firstitem = true;
16476 for (j = 0; j < tbinfo->numatts; j++)
16478 if (!tbinfo->attisdropped[j] &&
16479 !tbinfo->attislocal[j])
16481 if (firstitem)
16483 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
16484 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16485 "SET attislocal = false\n"
16486 "WHERE attrelid = ");
16487 appendStringLiteralAH(q, qualrelname, fout);
16488 appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16489 " AND attname IN (");
16490 firstitem = false;
16492 else
16493 appendPQExpBufferStr(q, ", ");
16494 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16497 if (!firstitem)
16498 appendPQExpBufferStr(q, ");\n");
16501 * Fix up not-null constraints that come from inheritance. As
16502 * above, do the pg_constraint manipulations in a single SQL
16503 * command. (Actually, two in special cases, if we're doing an
16504 * upgrade from < 18).
16506 firstitem = true;
16507 firstitem_extra = true;
16508 resetPQExpBuffer(extra);
16509 for (j = 0; j < tbinfo->numatts; j++)
16512 * If a not-null constraint comes from inheritance, reset
16513 * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
16514 * below. Special hack: in versions < 18, columns with no
16515 * local definition need their constraint to be matched by
16516 * column number in conkeys instead of by constraint name,
16517 * because the latter is not available. (We distinguish the
16518 * case because the constraint name is the empty string.)
16520 if (tbinfo->notnull_constrs[j] != NULL &&
16521 !tbinfo->notnull_islocal[j])
16523 if (tbinfo->notnull_constrs[j][0] != '\0')
16525 if (firstitem)
16527 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16528 "SET conislocal = false\n"
16529 "WHERE contype = 'n' AND conrelid = ");
16530 appendStringLiteralAH(q, qualrelname, fout);
16531 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
16532 "conname IN (");
16533 firstitem = false;
16535 else
16536 appendPQExpBufferStr(q, ", ");
16537 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
16539 else
16541 if (firstitem_extra)
16543 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16544 "SET conislocal = false\n"
16545 "WHERE contype = 'n' AND conrelid = ");
16546 appendStringLiteralAH(extra, qualrelname, fout);
16547 appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
16548 "conkey IN (");
16549 firstitem_extra = false;
16551 else
16552 appendPQExpBufferStr(extra, ", ");
16553 appendPQExpBuffer(extra, "'{%d}'", j + 1);
16557 if (!firstitem)
16558 appendPQExpBufferStr(q, ");\n");
16559 if (!firstitem_extra)
16560 appendPQExpBufferStr(extra, ");\n");
16562 if (extra->len > 0)
16563 appendBinaryPQExpBuffer(q, extra->data, extra->len);
16566 * Add inherited CHECK constraints, if any.
16568 * For partitions, they were already dumped, and conislocal
16569 * doesn't need fixing.
16571 * As above, issue only one direct manipulation of pg_constraint.
16572 * Although it is tempting to merge the ALTER ADD CONSTRAINT
16573 * commands into one as well, refrain for now due to concern about
16574 * possible backend memory bloat if there are many such
16575 * constraints.
16577 resetPQExpBuffer(extra);
16578 firstitem = true;
16579 for (k = 0; k < tbinfo->ncheck; k++)
16581 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
16583 if (constr->separate || constr->conislocal || tbinfo->ispartition)
16584 continue;
16586 if (firstitem)
16587 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
16588 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
16589 foreign, qualrelname,
16590 fmtId(constr->dobj.name),
16591 constr->condef);
16592 /* Update pg_constraint after all the ALTER TABLEs */
16593 if (firstitem)
16595 appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16596 "SET conislocal = false\n"
16597 "WHERE contype = 'c' AND conrelid = ");
16598 appendStringLiteralAH(extra, qualrelname, fout);
16599 appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
16600 appendPQExpBufferStr(extra, " AND conname IN (");
16601 firstitem = false;
16603 else
16604 appendPQExpBufferStr(extra, ", ");
16605 appendStringLiteralAH(extra, constr->dobj.name, fout);
16607 if (!firstitem)
16609 appendPQExpBufferStr(extra, ");\n");
16610 appendBinaryPQExpBuffer(q, extra->data, extra->len);
16613 if (numParents > 0 && !tbinfo->ispartition)
16615 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
16616 for (k = 0; k < numParents; k++)
16618 TableInfo *parentRel = parents[k];
16620 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
16621 qualrelname,
16622 fmtQualifiedDumpable(parentRel));
16626 if (OidIsValid(tbinfo->reloftype))
16628 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
16629 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
16630 qualrelname,
16631 getFormattedTypeName(fout, tbinfo->reloftype,
16632 zeroIsError));
16637 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
16638 * relminmxid of all vacuumable relations. (While vacuum.c processes
16639 * TOAST tables semi-independently, here we see them only as children
16640 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
16641 * child toast table is handled below.)
16643 if (dopt->binary_upgrade &&
16644 (tbinfo->relkind == RELKIND_RELATION ||
16645 tbinfo->relkind == RELKIND_MATVIEW))
16647 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
16648 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16649 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16650 "WHERE oid = ",
16651 tbinfo->frozenxid, tbinfo->minmxid);
16652 appendStringLiteralAH(q, qualrelname, fout);
16653 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16655 if (tbinfo->toast_oid)
16658 * The toast table will have the same OID at restore, so we
16659 * can safely target it by OID.
16661 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
16662 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16663 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16664 "WHERE oid = '%u';\n",
16665 tbinfo->toast_frozenxid,
16666 tbinfo->toast_minmxid, tbinfo->toast_oid);
16671 * In binary_upgrade mode, restore matviews' populated status by
16672 * poking pg_class directly. This is pretty ugly, but we can't use
16673 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
16674 * matview is not populated even though this matview is; in any case,
16675 * we want to transfer the matview's heap storage, not run REFRESH.
16677 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
16678 tbinfo->relispopulated)
16680 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
16681 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
16682 "SET relispopulated = 't'\n"
16683 "WHERE oid = ");
16684 appendStringLiteralAH(q, qualrelname, fout);
16685 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16689 * Dump additional per-column properties that we can't handle in the
16690 * main CREATE TABLE command.
16692 for (j = 0; j < tbinfo->numatts; j++)
16694 /* None of this applies to dropped columns */
16695 if (tbinfo->attisdropped[j])
16696 continue;
16699 * Dump per-column statistics information. We only issue an ALTER
16700 * TABLE statement if the attstattarget entry for this column is
16701 * not the default value.
16703 if (tbinfo->attstattarget[j] >= 0)
16704 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
16705 foreign, qualrelname,
16706 fmtId(tbinfo->attnames[j]),
16707 tbinfo->attstattarget[j]);
16710 * Dump per-column storage information. The statement is only
16711 * dumped if the storage has been changed from the type's default.
16713 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
16715 switch (tbinfo->attstorage[j])
16717 case TYPSTORAGE_PLAIN:
16718 storage = "PLAIN";
16719 break;
16720 case TYPSTORAGE_EXTERNAL:
16721 storage = "EXTERNAL";
16722 break;
16723 case TYPSTORAGE_EXTENDED:
16724 storage = "EXTENDED";
16725 break;
16726 case TYPSTORAGE_MAIN:
16727 storage = "MAIN";
16728 break;
16729 default:
16730 storage = NULL;
16734 * Only dump the statement if it's a storage type we recognize
16736 if (storage != NULL)
16737 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
16738 foreign, qualrelname,
16739 fmtId(tbinfo->attnames[j]),
16740 storage);
16744 * Dump per-column compression, if it's been set.
16746 if (!dopt->no_toast_compression)
16748 const char *cmname;
16750 switch (tbinfo->attcompression[j])
16752 case 'p':
16753 cmname = "pglz";
16754 break;
16755 case 'l':
16756 cmname = "lz4";
16757 break;
16758 default:
16759 cmname = NULL;
16760 break;
16763 if (cmname != NULL)
16764 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
16765 foreign, qualrelname,
16766 fmtId(tbinfo->attnames[j]),
16767 cmname);
16771 * Dump per-column attributes.
16773 if (tbinfo->attoptions[j][0] != '\0')
16774 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
16775 foreign, qualrelname,
16776 fmtId(tbinfo->attnames[j]),
16777 tbinfo->attoptions[j]);
16780 * Dump per-column fdw options.
16782 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
16783 tbinfo->attfdwoptions[j][0] != '\0')
16784 appendPQExpBuffer(q,
16785 "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
16786 " %s\n"
16787 ");\n",
16788 qualrelname,
16789 fmtId(tbinfo->attnames[j]),
16790 tbinfo->attfdwoptions[j]);
16791 } /* end loop over columns */
16793 free(partkeydef);
16794 free(ftoptions);
16795 free(srvname);
16799 * dump properties we only have ALTER TABLE syntax for
16801 if ((tbinfo->relkind == RELKIND_RELATION ||
16802 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
16803 tbinfo->relkind == RELKIND_MATVIEW) &&
16804 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
16806 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
16808 /* nothing to do, will be set when the index is dumped */
16810 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
16812 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
16813 qualrelname);
16815 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
16817 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
16818 qualrelname);
16822 if (tbinfo->forcerowsec)
16823 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
16824 qualrelname);
16826 if (dopt->binary_upgrade)
16827 binary_upgrade_extension_member(q, &tbinfo->dobj,
16828 reltypename, qrelname,
16829 tbinfo->dobj.namespace->dobj.name);
16831 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16833 char *tablespace = NULL;
16834 char *tableam = NULL;
16837 * _selectTablespace() relies on tablespace-enabled objects in the
16838 * default tablespace to have a tablespace of "" (empty string) versus
16839 * non-tablespace-enabled objects to have a tablespace of NULL.
16840 * getTables() sets tbinfo->reltablespace to "" for the default
16841 * tablespace (not NULL).
16843 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
16844 tablespace = tbinfo->reltablespace;
16846 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
16847 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16848 tableam = tbinfo->amname;
16850 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16851 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
16852 .namespace = tbinfo->dobj.namespace->dobj.name,
16853 .tablespace = tablespace,
16854 .tableam = tableam,
16855 .relkind = tbinfo->relkind,
16856 .owner = tbinfo->rolname,
16857 .description = reltypename,
16858 .section = tbinfo->postponed_def ?
16859 SECTION_POST_DATA : SECTION_PRE_DATA,
16860 .createStmt = q->data,
16861 .dropStmt = delq->data));
16864 /* Dump Table Comments */
16865 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16866 dumpTableComment(fout, tbinfo, reltypename);
16868 /* Dump Table Security Labels */
16869 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16870 dumpTableSecLabel(fout, tbinfo, reltypename);
16872 /* Dump comments on inlined table constraints */
16873 for (j = 0; j < tbinfo->ncheck; j++)
16875 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16877 if (constr->separate || !constr->conislocal)
16878 continue;
16880 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
16881 dumpTableConstraintComment(fout, constr);
16884 destroyPQExpBuffer(q);
16885 destroyPQExpBuffer(delq);
16886 destroyPQExpBuffer(extra);
16887 free(qrelname);
16888 free(qualrelname);
16892 * dumpTableAttach
16893 * write to fout the commands to attach a child partition
16895 * Child partitions are always made by creating them separately
16896 * and then using ATTACH PARTITION, rather than using
16897 * CREATE TABLE ... PARTITION OF. This is important for preserving
16898 * any possible discrepancy in column layout, to allow assigning the
16899 * correct tablespace if different, and so that it's possible to restore
16900 * a partition without restoring its parent. (You'll get an error from
16901 * the ATTACH PARTITION command, but that can be ignored, or skipped
16902 * using "pg_restore -L" if you prefer.) The last point motivates
16903 * treating ATTACH PARTITION as a completely separate ArchiveEntry
16904 * rather than emitting it within the child partition's ArchiveEntry.
16906 static void
16907 dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
16909 DumpOptions *dopt = fout->dopt;
16910 PQExpBuffer q;
16911 PGresult *res;
16912 char *partbound;
16914 /* Do nothing if not dumping schema */
16915 if (!dopt->dumpSchema)
16916 return;
16918 q = createPQExpBuffer();
16920 if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
16922 /* Set up query for partbound details */
16923 appendPQExpBufferStr(q,
16924 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
16926 appendPQExpBufferStr(q,
16927 "SELECT pg_get_expr(c.relpartbound, c.oid) "
16928 "FROM pg_class c "
16929 "WHERE c.oid = $1");
16931 ExecuteSqlStatement(fout, q->data);
16933 fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
16936 printfPQExpBuffer(q,
16937 "EXECUTE dumpTableAttach('%u')",
16938 attachinfo->partitionTbl->dobj.catId.oid);
16940 res = ExecuteSqlQueryForSingleRow(fout, q->data);
16941 partbound = PQgetvalue(res, 0, 0);
16943 /* Perform ALTER TABLE on the parent */
16944 printfPQExpBuffer(q,
16945 "ALTER TABLE ONLY %s ",
16946 fmtQualifiedDumpable(attachinfo->parentTbl));
16947 appendPQExpBuffer(q,
16948 "ATTACH PARTITION %s %s;\n",
16949 fmtQualifiedDumpable(attachinfo->partitionTbl),
16950 partbound);
16953 * There is no point in creating a drop query as the drop is done by table
16954 * drop. (If you think to change this, see also _printTocEntry().)
16955 * Although this object doesn't really have ownership as such, set the
16956 * owner field anyway to ensure that the command is run by the correct
16957 * role at restore time.
16959 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16960 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16961 .namespace = attachinfo->dobj.namespace->dobj.name,
16962 .owner = attachinfo->partitionTbl->rolname,
16963 .description = "TABLE ATTACH",
16964 .section = SECTION_PRE_DATA,
16965 .createStmt = q->data));
16967 PQclear(res);
16968 destroyPQExpBuffer(q);
16972 * dumpAttrDef --- dump an attribute's default-value declaration
16974 static void
16975 dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
16977 DumpOptions *dopt = fout->dopt;
16978 TableInfo *tbinfo = adinfo->adtable;
16979 int adnum = adinfo->adnum;
16980 PQExpBuffer q;
16981 PQExpBuffer delq;
16982 char *qualrelname;
16983 char *tag;
16984 char *foreign;
16986 /* Do nothing if not dumping schema */
16987 if (!dopt->dumpSchema)
16988 return;
16990 /* Skip if not "separate"; it was dumped in the table's definition */
16991 if (!adinfo->separate)
16992 return;
16994 q = createPQExpBuffer();
16995 delq = createPQExpBuffer();
16997 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16999 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17001 appendPQExpBuffer(q,
17002 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
17003 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
17004 adinfo->adef_expr);
17006 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
17007 foreign, qualrelname,
17008 fmtId(tbinfo->attnames[adnum - 1]));
17010 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
17012 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17013 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
17014 ARCHIVE_OPTS(.tag = tag,
17015 .namespace = tbinfo->dobj.namespace->dobj.name,
17016 .owner = tbinfo->rolname,
17017 .description = "DEFAULT",
17018 .section = SECTION_PRE_DATA,
17019 .createStmt = q->data,
17020 .dropStmt = delq->data));
17022 free(tag);
17023 destroyPQExpBuffer(q);
17024 destroyPQExpBuffer(delq);
17025 free(qualrelname);
17029 * getAttrName: extract the correct name for an attribute
17031 * The array tblInfo->attnames[] only provides names of user attributes;
17032 * if a system attribute number is supplied, we have to fake it.
17033 * We also do a little bit of bounds checking for safety's sake.
17035 static const char *
17036 getAttrName(int attrnum, const TableInfo *tblInfo)
17038 if (attrnum > 0 && attrnum <= tblInfo->numatts)
17039 return tblInfo->attnames[attrnum - 1];
17040 switch (attrnum)
17042 case SelfItemPointerAttributeNumber:
17043 return "ctid";
17044 case MinTransactionIdAttributeNumber:
17045 return "xmin";
17046 case MinCommandIdAttributeNumber:
17047 return "cmin";
17048 case MaxTransactionIdAttributeNumber:
17049 return "xmax";
17050 case MaxCommandIdAttributeNumber:
17051 return "cmax";
17052 case TableOidAttributeNumber:
17053 return "tableoid";
17055 pg_fatal("invalid column number %d for table \"%s\"",
17056 attrnum, tblInfo->dobj.name);
17057 return NULL; /* keep compiler quiet */
17061 * dumpIndex
17062 * write out to fout a user-defined index
17064 static void
17065 dumpIndex(Archive *fout, const IndxInfo *indxinfo)
17067 DumpOptions *dopt = fout->dopt;
17068 TableInfo *tbinfo = indxinfo->indextable;
17069 bool is_constraint = (indxinfo->indexconstraint != 0);
17070 PQExpBuffer q;
17071 PQExpBuffer delq;
17072 char *qindxname;
17073 char *qqindxname;
17075 /* Do nothing if not dumping schema */
17076 if (!dopt->dumpSchema)
17077 return;
17079 q = createPQExpBuffer();
17080 delq = createPQExpBuffer();
17082 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
17083 qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
17086 * If there's an associated constraint, don't dump the index per se, but
17087 * do dump any comment for it. (This is safe because dependency ordering
17088 * will have ensured the constraint is emitted first.) Note that the
17089 * emitted comment has to be shown as depending on the constraint, not the
17090 * index, in such cases.
17092 if (!is_constraint)
17094 char *indstatcols = indxinfo->indstatcols;
17095 char *indstatvals = indxinfo->indstatvals;
17096 char **indstatcolsarray = NULL;
17097 char **indstatvalsarray = NULL;
17098 int nstatcols = 0;
17099 int nstatvals = 0;
17101 if (dopt->binary_upgrade)
17102 binary_upgrade_set_pg_class_oids(fout, q,
17103 indxinfo->dobj.catId.oid);
17105 /* Plain secondary index */
17106 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17109 * Append ALTER TABLE commands as needed to set properties that we
17110 * only have ALTER TABLE syntax for. Keep this in sync with the
17111 * similar code in dumpConstraint!
17114 /* If the index is clustered, we need to record that. */
17115 if (indxinfo->indisclustered)
17117 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17118 fmtQualifiedDumpable(tbinfo));
17119 /* index name is not qualified in this syntax */
17120 appendPQExpBuffer(q, " ON %s;\n",
17121 qindxname);
17125 * If the index has any statistics on some of its columns, generate
17126 * the associated ALTER INDEX queries.
17128 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
17130 int j;
17132 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
17133 pg_fatal("could not parse index statistic columns");
17134 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
17135 pg_fatal("could not parse index statistic values");
17136 if (nstatcols != nstatvals)
17137 pg_fatal("mismatched number of columns and values for index statistics");
17139 for (j = 0; j < nstatcols; j++)
17141 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
17144 * Note that this is a column number, so no quotes should be
17145 * used.
17147 appendPQExpBuffer(q, "ALTER COLUMN %s ",
17148 indstatcolsarray[j]);
17149 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
17150 indstatvalsarray[j]);
17154 /* Indexes can depend on extensions */
17155 append_depends_on_extension(fout, q, &indxinfo->dobj,
17156 "pg_catalog.pg_class",
17157 "INDEX", qqindxname);
17159 /* If the index defines identity, we need to record that. */
17160 if (indxinfo->indisreplident)
17162 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17163 fmtQualifiedDumpable(tbinfo));
17164 /* index name is not qualified in this syntax */
17165 appendPQExpBuffer(q, " INDEX %s;\n",
17166 qindxname);
17169 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
17171 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17172 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
17173 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
17174 .namespace = tbinfo->dobj.namespace->dobj.name,
17175 .tablespace = indxinfo->tablespace,
17176 .owner = tbinfo->rolname,
17177 .description = "INDEX",
17178 .section = SECTION_POST_DATA,
17179 .createStmt = q->data,
17180 .dropStmt = delq->data));
17182 free(indstatcolsarray);
17183 free(indstatvalsarray);
17186 /* Dump Index Comments */
17187 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17188 dumpComment(fout, "INDEX", qindxname,
17189 tbinfo->dobj.namespace->dobj.name,
17190 tbinfo->rolname,
17191 indxinfo->dobj.catId, 0,
17192 is_constraint ? indxinfo->indexconstraint :
17193 indxinfo->dobj.dumpId);
17195 destroyPQExpBuffer(q);
17196 destroyPQExpBuffer(delq);
17197 free(qindxname);
17198 free(qqindxname);
17202 * dumpIndexAttach
17203 * write out to fout a partitioned-index attachment clause
17205 static void
17206 dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
17208 /* Do nothing if not dumping schema */
17209 if (!fout->dopt->dumpSchema)
17210 return;
17212 if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
17214 PQExpBuffer q = createPQExpBuffer();
17216 appendPQExpBuffer(q, "ALTER INDEX %s ",
17217 fmtQualifiedDumpable(attachinfo->parentIdx));
17218 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
17219 fmtQualifiedDumpable(attachinfo->partitionIdx));
17222 * There is no point in creating a drop query as the drop is done by
17223 * index drop. (If you think to change this, see also
17224 * _printTocEntry().) Although this object doesn't really have
17225 * ownership as such, set the owner field anyway to ensure that the
17226 * command is run by the correct role at restore time.
17228 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17229 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17230 .namespace = attachinfo->dobj.namespace->dobj.name,
17231 .owner = attachinfo->parentIdx->indextable->rolname,
17232 .description = "INDEX ATTACH",
17233 .section = SECTION_POST_DATA,
17234 .createStmt = q->data));
17236 destroyPQExpBuffer(q);
17241 * dumpStatisticsExt
17242 * write out to fout an extended statistics object
17244 static void
17245 dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
17247 DumpOptions *dopt = fout->dopt;
17248 PQExpBuffer q;
17249 PQExpBuffer delq;
17250 PQExpBuffer query;
17251 char *qstatsextname;
17252 PGresult *res;
17253 char *stxdef;
17255 /* Do nothing if not dumping schema */
17256 if (!dopt->dumpSchema)
17257 return;
17259 q = createPQExpBuffer();
17260 delq = createPQExpBuffer();
17261 query = createPQExpBuffer();
17263 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
17265 appendPQExpBuffer(query, "SELECT "
17266 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
17267 statsextinfo->dobj.catId.oid);
17269 res = ExecuteSqlQueryForSingleRow(fout, query->data);
17271 stxdef = PQgetvalue(res, 0, 0);
17273 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
17274 appendPQExpBuffer(q, "%s;\n", stxdef);
17277 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
17278 * for this statistics object is not the default value.
17280 if (statsextinfo->stattarget >= 0)
17282 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
17283 fmtQualifiedDumpable(statsextinfo));
17284 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
17285 statsextinfo->stattarget);
17288 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
17289 fmtQualifiedDumpable(statsextinfo));
17291 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17292 ArchiveEntry(fout, statsextinfo->dobj.catId,
17293 statsextinfo->dobj.dumpId,
17294 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
17295 .namespace = statsextinfo->dobj.namespace->dobj.name,
17296 .owner = statsextinfo->rolname,
17297 .description = "STATISTICS",
17298 .section = SECTION_POST_DATA,
17299 .createStmt = q->data,
17300 .dropStmt = delq->data));
17302 /* Dump Statistics Comments */
17303 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17304 dumpComment(fout, "STATISTICS", qstatsextname,
17305 statsextinfo->dobj.namespace->dobj.name,
17306 statsextinfo->rolname,
17307 statsextinfo->dobj.catId, 0,
17308 statsextinfo->dobj.dumpId);
17310 PQclear(res);
17311 destroyPQExpBuffer(q);
17312 destroyPQExpBuffer(delq);
17313 destroyPQExpBuffer(query);
17314 free(qstatsextname);
17318 * dumpConstraint
17319 * write out to fout a user-defined constraint
17321 static void
17322 dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
17324 DumpOptions *dopt = fout->dopt;
17325 TableInfo *tbinfo = coninfo->contable;
17326 PQExpBuffer q;
17327 PQExpBuffer delq;
17328 char *tag = NULL;
17329 char *foreign;
17331 /* Do nothing if not dumping schema */
17332 if (!dopt->dumpSchema)
17333 return;
17335 q = createPQExpBuffer();
17336 delq = createPQExpBuffer();
17338 foreign = tbinfo &&
17339 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17341 if (coninfo->contype == 'p' ||
17342 coninfo->contype == 'u' ||
17343 coninfo->contype == 'x')
17345 /* Index-related constraint */
17346 IndxInfo *indxinfo;
17347 int k;
17349 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
17351 if (indxinfo == NULL)
17352 pg_fatal("missing index for constraint \"%s\"",
17353 coninfo->dobj.name);
17355 if (dopt->binary_upgrade)
17356 binary_upgrade_set_pg_class_oids(fout, q,
17357 indxinfo->dobj.catId.oid);
17359 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
17360 fmtQualifiedDumpable(tbinfo));
17361 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
17362 fmtId(coninfo->dobj.name));
17364 if (coninfo->condef)
17366 /* pg_get_constraintdef should have provided everything */
17367 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
17369 else
17371 appendPQExpBufferStr(q,
17372 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
17375 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
17376 * indexes. Being able to create this was fixed, but we need to
17377 * make the index distinct in order to be able to restore the
17378 * dump.
17380 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
17381 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
17382 appendPQExpBufferStr(q, " (");
17383 for (k = 0; k < indxinfo->indnkeyattrs; k++)
17385 int indkey = (int) indxinfo->indkeys[k];
17386 const char *attname;
17388 if (indkey == InvalidAttrNumber)
17389 break;
17390 attname = getAttrName(indkey, tbinfo);
17392 appendPQExpBuffer(q, "%s%s",
17393 (k == 0) ? "" : ", ",
17394 fmtId(attname));
17396 if (coninfo->conperiod)
17397 appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
17399 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
17400 appendPQExpBufferStr(q, ") INCLUDE (");
17402 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
17404 int indkey = (int) indxinfo->indkeys[k];
17405 const char *attname;
17407 if (indkey == InvalidAttrNumber)
17408 break;
17409 attname = getAttrName(indkey, tbinfo);
17411 appendPQExpBuffer(q, "%s%s",
17412 (k == indxinfo->indnkeyattrs) ? "" : ", ",
17413 fmtId(attname));
17416 appendPQExpBufferChar(q, ')');
17418 if (nonemptyReloptions(indxinfo->indreloptions))
17420 appendPQExpBufferStr(q, " WITH (");
17421 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
17422 appendPQExpBufferChar(q, ')');
17425 if (coninfo->condeferrable)
17427 appendPQExpBufferStr(q, " DEFERRABLE");
17428 if (coninfo->condeferred)
17429 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
17432 appendPQExpBufferStr(q, ";\n");
17436 * Append ALTER TABLE commands as needed to set properties that we
17437 * only have ALTER TABLE syntax for. Keep this in sync with the
17438 * similar code in dumpIndex!
17441 /* If the index is clustered, we need to record that. */
17442 if (indxinfo->indisclustered)
17444 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17445 fmtQualifiedDumpable(tbinfo));
17446 /* index name is not qualified in this syntax */
17447 appendPQExpBuffer(q, " ON %s;\n",
17448 fmtId(indxinfo->dobj.name));
17451 /* If the index defines identity, we need to record that. */
17452 if (indxinfo->indisreplident)
17454 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17455 fmtQualifiedDumpable(tbinfo));
17456 /* index name is not qualified in this syntax */
17457 appendPQExpBuffer(q, " INDEX %s;\n",
17458 fmtId(indxinfo->dobj.name));
17461 /* Indexes can depend on extensions */
17462 append_depends_on_extension(fout, q, &indxinfo->dobj,
17463 "pg_catalog.pg_class", "INDEX",
17464 fmtQualifiedDumpable(indxinfo));
17466 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17467 fmtQualifiedDumpable(tbinfo));
17468 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17469 fmtId(coninfo->dobj.name));
17471 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17473 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17474 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17475 ARCHIVE_OPTS(.tag = tag,
17476 .namespace = tbinfo->dobj.namespace->dobj.name,
17477 .tablespace = indxinfo->tablespace,
17478 .owner = tbinfo->rolname,
17479 .description = "CONSTRAINT",
17480 .section = SECTION_POST_DATA,
17481 .createStmt = q->data,
17482 .dropStmt = delq->data));
17484 else if (coninfo->contype == 'f')
17486 char *only;
17489 * Foreign keys on partitioned tables are always declared as
17490 * inheriting to partitions; for all other cases, emit them as
17491 * applying ONLY directly to the named table, because that's how they
17492 * work for regular inherited tables.
17494 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17497 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17498 * current table data is not processed
17500 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17501 only, fmtQualifiedDumpable(tbinfo));
17502 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17503 fmtId(coninfo->dobj.name),
17504 coninfo->condef);
17506 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
17507 only, fmtQualifiedDumpable(tbinfo));
17508 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17509 fmtId(coninfo->dobj.name));
17511 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17513 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17514 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17515 ARCHIVE_OPTS(.tag = tag,
17516 .namespace = tbinfo->dobj.namespace->dobj.name,
17517 .owner = tbinfo->rolname,
17518 .description = "FK CONSTRAINT",
17519 .section = SECTION_POST_DATA,
17520 .createStmt = q->data,
17521 .dropStmt = delq->data));
17523 else if (coninfo->contype == 'c' && tbinfo)
17525 /* CHECK constraint on a table */
17527 /* Ignore if not to be dumped separately, or if it was inherited */
17528 if (coninfo->separate && coninfo->conislocal)
17530 /* not ONLY since we want it to propagate to children */
17531 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
17532 fmtQualifiedDumpable(tbinfo));
17533 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17534 fmtId(coninfo->dobj.name),
17535 coninfo->condef);
17537 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
17538 fmtQualifiedDumpable(tbinfo));
17539 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17540 fmtId(coninfo->dobj.name));
17542 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17544 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17545 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17546 ARCHIVE_OPTS(.tag = tag,
17547 .namespace = tbinfo->dobj.namespace->dobj.name,
17548 .owner = tbinfo->rolname,
17549 .description = "CHECK CONSTRAINT",
17550 .section = SECTION_POST_DATA,
17551 .createStmt = q->data,
17552 .dropStmt = delq->data));
17555 else if (coninfo->contype == 'c' && tbinfo == NULL)
17557 /* CHECK constraint on a domain */
17558 TypeInfo *tyinfo = coninfo->condomain;
17560 /* Ignore if not to be dumped separately */
17561 if (coninfo->separate)
17563 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
17564 fmtQualifiedDumpable(tyinfo));
17565 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17566 fmtId(coninfo->dobj.name),
17567 coninfo->condef);
17569 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
17570 fmtQualifiedDumpable(tyinfo));
17571 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17572 fmtId(coninfo->dobj.name));
17574 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
17576 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17577 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17578 ARCHIVE_OPTS(.tag = tag,
17579 .namespace = tyinfo->dobj.namespace->dobj.name,
17580 .owner = tyinfo->rolname,
17581 .description = "CHECK CONSTRAINT",
17582 .section = SECTION_POST_DATA,
17583 .createStmt = q->data,
17584 .dropStmt = delq->data));
17587 else
17589 pg_fatal("unrecognized constraint type: %c",
17590 coninfo->contype);
17593 /* Dump Constraint Comments --- only works for table constraints */
17594 if (tbinfo && coninfo->separate &&
17595 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17596 dumpTableConstraintComment(fout, coninfo);
17598 free(tag);
17599 destroyPQExpBuffer(q);
17600 destroyPQExpBuffer(delq);
17604 * dumpTableConstraintComment --- dump a constraint's comment if any
17606 * This is split out because we need the function in two different places
17607 * depending on whether the constraint is dumped as part of CREATE TABLE
17608 * or as a separate ALTER command.
17610 static void
17611 dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
17613 TableInfo *tbinfo = coninfo->contable;
17614 PQExpBuffer conprefix = createPQExpBuffer();
17615 char *qtabname;
17617 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17619 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
17620 fmtId(coninfo->dobj.name));
17622 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17623 dumpComment(fout, conprefix->data, qtabname,
17624 tbinfo->dobj.namespace->dobj.name,
17625 tbinfo->rolname,
17626 coninfo->dobj.catId, 0,
17627 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
17629 destroyPQExpBuffer(conprefix);
17630 free(qtabname);
17633 static inline SeqType
17634 parse_sequence_type(const char *name)
17636 for (int i = 0; i < lengthof(SeqTypeNames); i++)
17638 if (strcmp(SeqTypeNames[i], name) == 0)
17639 return (SeqType) i;
17642 pg_fatal("unrecognized sequence type: %s", name);
17643 return (SeqType) 0; /* keep compiler quiet */
17647 * bsearch() comparator for SequenceItem
17649 static int
17650 SequenceItemCmp(const void *p1, const void *p2)
17652 SequenceItem v1 = *((const SequenceItem *) p1);
17653 SequenceItem v2 = *((const SequenceItem *) p2);
17655 return pg_cmp_u32(v1.oid, v2.oid);
17659 * collectSequences
17661 * Construct a table of sequence information. This table is sorted by OID for
17662 * speed in lookup.
17664 static void
17665 collectSequences(Archive *fout)
17667 PGresult *res;
17668 const char *query;
17671 * Before Postgres 10, sequence metadata is in the sequence itself. With
17672 * some extra effort, we might be able to use the sorted table for those
17673 * versions, but for now it seems unlikely to be worth it.
17675 * Since version 18, we can gather the sequence data in this query with
17676 * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
17678 if (fout->remoteVersion < 100000)
17679 return;
17680 else if (fout->remoteVersion < 180000 ||
17681 (!fout->dopt->dumpData && !fout->dopt->sequence_data))
17682 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
17683 "seqstart, seqincrement, "
17684 "seqmax, seqmin, "
17685 "seqcache, seqcycle, "
17686 "NULL, 'f' "
17687 "FROM pg_catalog.pg_sequence "
17688 "ORDER BY seqrelid";
17689 else
17690 query = "SELECT seqrelid, format_type(seqtypid, NULL), "
17691 "seqstart, seqincrement, "
17692 "seqmax, seqmin, "
17693 "seqcache, seqcycle, "
17694 "last_value, is_called "
17695 "FROM pg_catalog.pg_sequence, "
17696 "pg_get_sequence_data(seqrelid) "
17697 "ORDER BY seqrelid;";
17699 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
17701 nsequences = PQntuples(res);
17702 sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
17704 for (int i = 0; i < nsequences; i++)
17706 sequences[i].oid = atooid(PQgetvalue(res, i, 0));
17707 sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
17708 sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
17709 sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
17710 sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
17711 sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
17712 sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
17713 sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
17714 sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
17715 sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
17718 PQclear(res);
17722 * dumpSequence
17723 * write the declaration (not data) of one user-defined sequence
17725 static void
17726 dumpSequence(Archive *fout, const TableInfo *tbinfo)
17728 DumpOptions *dopt = fout->dopt;
17729 SequenceItem *seq;
17730 bool is_ascending;
17731 int64 default_minv,
17732 default_maxv;
17733 PQExpBuffer query = createPQExpBuffer();
17734 PQExpBuffer delqry = createPQExpBuffer();
17735 char *qseqname;
17736 TableInfo *owning_tab = NULL;
17738 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
17741 * For versions >= 10, the sequence information is gathered in a sorted
17742 * table before any calls to dumpSequence(). See collectSequences() for
17743 * more information.
17745 if (fout->remoteVersion >= 100000)
17747 SequenceItem key = {0};
17749 Assert(sequences);
17751 key.oid = tbinfo->dobj.catId.oid;
17752 seq = bsearch(&key, sequences, nsequences,
17753 sizeof(SequenceItem), SequenceItemCmp);
17755 else
17757 PGresult *res;
17760 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
17762 * Note: it might seem that 'bigint' potentially needs to be
17763 * schema-qualified, but actually that's a keyword.
17765 appendPQExpBuffer(query,
17766 "SELECT 'bigint' AS sequence_type, "
17767 "start_value, increment_by, max_value, min_value, "
17768 "cache_value, is_cycled FROM %s",
17769 fmtQualifiedDumpable(tbinfo));
17771 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17773 if (PQntuples(res) != 1)
17774 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17775 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17776 PQntuples(res)),
17777 tbinfo->dobj.name, PQntuples(res));
17779 seq = pg_malloc0(sizeof(SequenceItem));
17780 seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
17781 seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
17782 seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
17783 seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
17784 seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
17785 seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
17786 seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
17788 PQclear(res);
17791 /* Calculate default limits for a sequence of this type */
17792 is_ascending = (seq->incby >= 0);
17793 if (seq->seqtype == SEQTYPE_SMALLINT)
17795 default_minv = is_ascending ? 1 : PG_INT16_MIN;
17796 default_maxv = is_ascending ? PG_INT16_MAX : -1;
17798 else if (seq->seqtype == SEQTYPE_INTEGER)
17800 default_minv = is_ascending ? 1 : PG_INT32_MIN;
17801 default_maxv = is_ascending ? PG_INT32_MAX : -1;
17803 else if (seq->seqtype == SEQTYPE_BIGINT)
17805 default_minv = is_ascending ? 1 : PG_INT64_MIN;
17806 default_maxv = is_ascending ? PG_INT64_MAX : -1;
17808 else
17810 pg_fatal("unrecognized sequence type: %d", seq->seqtype);
17811 default_minv = default_maxv = 0; /* keep compiler quiet */
17815 * Identity sequences are not to be dropped separately.
17817 if (!tbinfo->is_identity_sequence)
17819 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
17820 fmtQualifiedDumpable(tbinfo));
17823 resetPQExpBuffer(query);
17825 if (dopt->binary_upgrade)
17827 binary_upgrade_set_pg_class_oids(fout, query,
17828 tbinfo->dobj.catId.oid);
17831 * In older PG versions a sequence will have a pg_type entry, but v14
17832 * and up don't use that, so don't attempt to preserve the type OID.
17836 if (tbinfo->is_identity_sequence)
17838 owning_tab = findTableByOid(tbinfo->owning_tab);
17840 appendPQExpBuffer(query,
17841 "ALTER TABLE %s ",
17842 fmtQualifiedDumpable(owning_tab));
17843 appendPQExpBuffer(query,
17844 "ALTER COLUMN %s ADD GENERATED ",
17845 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17846 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
17847 appendPQExpBufferStr(query, "ALWAYS");
17848 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
17849 appendPQExpBufferStr(query, "BY DEFAULT");
17850 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
17851 fmtQualifiedDumpable(tbinfo));
17854 * Emit persistence option only if it's different from the owning
17855 * table's. This avoids using this new syntax unnecessarily.
17857 if (tbinfo->relpersistence != owning_tab->relpersistence)
17858 appendPQExpBuffer(query, " %s\n",
17859 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17860 "UNLOGGED" : "LOGGED");
17862 else
17864 appendPQExpBuffer(query,
17865 "CREATE %sSEQUENCE %s\n",
17866 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17867 "UNLOGGED " : "",
17868 fmtQualifiedDumpable(tbinfo));
17870 if (seq->seqtype != SEQTYPE_BIGINT)
17871 appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
17874 appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
17876 appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
17878 if (seq->minv != default_minv)
17879 appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
17880 else
17881 appendPQExpBufferStr(query, " NO MINVALUE\n");
17883 if (seq->maxv != default_maxv)
17884 appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
17885 else
17886 appendPQExpBufferStr(query, " NO MAXVALUE\n");
17888 appendPQExpBuffer(query,
17889 " CACHE " INT64_FORMAT "%s",
17890 seq->cache, (seq->cycled ? "\n CYCLE" : ""));
17892 if (tbinfo->is_identity_sequence)
17893 appendPQExpBufferStr(query, "\n);\n");
17894 else
17895 appendPQExpBufferStr(query, ";\n");
17897 /* binary_upgrade: no need to clear TOAST table oid */
17899 if (dopt->binary_upgrade)
17900 binary_upgrade_extension_member(query, &tbinfo->dobj,
17901 "SEQUENCE", qseqname,
17902 tbinfo->dobj.namespace->dobj.name);
17904 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17905 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17906 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17907 .namespace = tbinfo->dobj.namespace->dobj.name,
17908 .owner = tbinfo->rolname,
17909 .description = "SEQUENCE",
17910 .section = SECTION_PRE_DATA,
17911 .createStmt = query->data,
17912 .dropStmt = delqry->data));
17915 * If the sequence is owned by a table column, emit the ALTER for it as a
17916 * separate TOC entry immediately following the sequence's own entry. It's
17917 * OK to do this rather than using full sorting logic, because the
17918 * dependency that tells us it's owned will have forced the table to be
17919 * created first. We can't just include the ALTER in the TOC entry
17920 * because it will fail if we haven't reassigned the sequence owner to
17921 * match the table's owner.
17923 * We need not schema-qualify the table reference because both sequence
17924 * and table must be in the same schema.
17926 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
17928 owning_tab = findTableByOid(tbinfo->owning_tab);
17930 if (owning_tab == NULL)
17931 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
17932 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
17934 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
17936 resetPQExpBuffer(query);
17937 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
17938 fmtQualifiedDumpable(tbinfo));
17939 appendPQExpBuffer(query, " OWNED BY %s",
17940 fmtQualifiedDumpable(owning_tab));
17941 appendPQExpBuffer(query, ".%s;\n",
17942 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17944 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17945 ArchiveEntry(fout, nilCatalogId, createDumpId(),
17946 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17947 .namespace = tbinfo->dobj.namespace->dobj.name,
17948 .owner = tbinfo->rolname,
17949 .description = "SEQUENCE OWNED BY",
17950 .section = SECTION_PRE_DATA,
17951 .createStmt = query->data,
17952 .deps = &(tbinfo->dobj.dumpId),
17953 .nDeps = 1));
17957 /* Dump Sequence Comments and Security Labels */
17958 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17959 dumpComment(fout, "SEQUENCE", qseqname,
17960 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17961 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17963 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17964 dumpSecLabel(fout, "SEQUENCE", qseqname,
17965 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17966 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17968 if (fout->remoteVersion < 100000)
17969 pg_free(seq);
17970 destroyPQExpBuffer(query);
17971 destroyPQExpBuffer(delqry);
17972 free(qseqname);
17976 * dumpSequenceData
17977 * write the data of one user-defined sequence
17979 static void
17980 dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
17982 TableInfo *tbinfo = tdinfo->tdtable;
17983 int64 last;
17984 bool called;
17985 PQExpBuffer query = createPQExpBuffer();
17988 * For versions >= 18, the sequence information is gathered in the sorted
17989 * array before any calls to dumpSequenceData(). See collectSequences()
17990 * for more information.
17992 * For older versions, we have to query the sequence relations
17993 * individually.
17995 if (fout->remoteVersion < 180000)
17997 PGresult *res;
17999 appendPQExpBuffer(query,
18000 "SELECT last_value, is_called FROM %s",
18001 fmtQualifiedDumpable(tbinfo));
18003 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18005 if (PQntuples(res) != 1)
18006 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18007 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18008 PQntuples(res)),
18009 tbinfo->dobj.name, PQntuples(res));
18011 last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
18012 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
18014 PQclear(res);
18016 else
18018 SequenceItem key = {0};
18019 SequenceItem *entry;
18021 Assert(sequences);
18022 Assert(tbinfo->dobj.catId.oid);
18024 key.oid = tbinfo->dobj.catId.oid;
18025 entry = bsearch(&key, sequences, nsequences,
18026 sizeof(SequenceItem), SequenceItemCmp);
18028 last = entry->last_value;
18029 called = entry->is_called;
18032 resetPQExpBuffer(query);
18033 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
18034 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
18035 appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
18036 last, (called ? "true" : "false"));
18038 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
18039 ArchiveEntry(fout, nilCatalogId, createDumpId(),
18040 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18041 .namespace = tbinfo->dobj.namespace->dobj.name,
18042 .owner = tbinfo->rolname,
18043 .description = "SEQUENCE SET",
18044 .section = SECTION_DATA,
18045 .createStmt = query->data,
18046 .deps = &(tbinfo->dobj.dumpId),
18047 .nDeps = 1));
18049 destroyPQExpBuffer(query);
18053 * dumpTrigger
18054 * write the declaration of one user-defined table trigger
18056 static void
18057 dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
18059 DumpOptions *dopt = fout->dopt;
18060 TableInfo *tbinfo = tginfo->tgtable;
18061 PQExpBuffer query;
18062 PQExpBuffer delqry;
18063 PQExpBuffer trigprefix;
18064 PQExpBuffer trigidentity;
18065 char *qtabname;
18066 char *tag;
18068 /* Do nothing if not dumping schema */
18069 if (!dopt->dumpSchema)
18070 return;
18072 query = createPQExpBuffer();
18073 delqry = createPQExpBuffer();
18074 trigprefix = createPQExpBuffer();
18075 trigidentity = createPQExpBuffer();
18077 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18079 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
18080 appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
18082 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
18083 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
18085 /* Triggers can depend on extensions */
18086 append_depends_on_extension(fout, query, &tginfo->dobj,
18087 "pg_catalog.pg_trigger", "TRIGGER",
18088 trigidentity->data);
18090 if (tginfo->tgispartition)
18092 Assert(tbinfo->ispartition);
18095 * Partition triggers only appear here because their 'tgenabled' flag
18096 * differs from its parent's. The trigger is created already, so
18097 * remove the CREATE and replace it with an ALTER. (Clear out the
18098 * DROP query too, so that pg_dump --create does not cause errors.)
18100 resetPQExpBuffer(query);
18101 resetPQExpBuffer(delqry);
18102 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18103 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18104 fmtQualifiedDumpable(tbinfo));
18105 switch (tginfo->tgenabled)
18107 case 'f':
18108 case 'D':
18109 appendPQExpBufferStr(query, "DISABLE");
18110 break;
18111 case 't':
18112 case 'O':
18113 appendPQExpBufferStr(query, "ENABLE");
18114 break;
18115 case 'R':
18116 appendPQExpBufferStr(query, "ENABLE REPLICA");
18117 break;
18118 case 'A':
18119 appendPQExpBufferStr(query, "ENABLE ALWAYS");
18120 break;
18122 appendPQExpBuffer(query, " TRIGGER %s;\n",
18123 fmtId(tginfo->dobj.name));
18125 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
18127 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
18128 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
18129 fmtQualifiedDumpable(tbinfo));
18130 switch (tginfo->tgenabled)
18132 case 'D':
18133 case 'f':
18134 appendPQExpBufferStr(query, "DISABLE");
18135 break;
18136 case 'A':
18137 appendPQExpBufferStr(query, "ENABLE ALWAYS");
18138 break;
18139 case 'R':
18140 appendPQExpBufferStr(query, "ENABLE REPLICA");
18141 break;
18142 default:
18143 appendPQExpBufferStr(query, "ENABLE");
18144 break;
18146 appendPQExpBuffer(query, " TRIGGER %s;\n",
18147 fmtId(tginfo->dobj.name));
18150 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
18151 fmtId(tginfo->dobj.name));
18153 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
18155 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18156 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
18157 ARCHIVE_OPTS(.tag = tag,
18158 .namespace = tbinfo->dobj.namespace->dobj.name,
18159 .owner = tbinfo->rolname,
18160 .description = "TRIGGER",
18161 .section = SECTION_POST_DATA,
18162 .createStmt = query->data,
18163 .dropStmt = delqry->data));
18165 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18166 dumpComment(fout, trigprefix->data, qtabname,
18167 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
18168 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
18170 free(tag);
18171 destroyPQExpBuffer(query);
18172 destroyPQExpBuffer(delqry);
18173 destroyPQExpBuffer(trigprefix);
18174 destroyPQExpBuffer(trigidentity);
18175 free(qtabname);
18179 * dumpEventTrigger
18180 * write the declaration of one user-defined event trigger
18182 static void
18183 dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
18185 DumpOptions *dopt = fout->dopt;
18186 PQExpBuffer query;
18187 PQExpBuffer delqry;
18188 char *qevtname;
18190 /* Do nothing if not dumping schema */
18191 if (!dopt->dumpSchema)
18192 return;
18194 query = createPQExpBuffer();
18195 delqry = createPQExpBuffer();
18197 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
18199 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
18200 appendPQExpBufferStr(query, qevtname);
18201 appendPQExpBufferStr(query, " ON ");
18202 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
18204 if (strcmp("", evtinfo->evttags) != 0)
18206 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
18207 appendPQExpBufferStr(query, evtinfo->evttags);
18208 appendPQExpBufferChar(query, ')');
18211 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
18212 appendPQExpBufferStr(query, evtinfo->evtfname);
18213 appendPQExpBufferStr(query, "();\n");
18215 if (evtinfo->evtenabled != 'O')
18217 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
18218 qevtname);
18219 switch (evtinfo->evtenabled)
18221 case 'D':
18222 appendPQExpBufferStr(query, "DISABLE");
18223 break;
18224 case 'A':
18225 appendPQExpBufferStr(query, "ENABLE ALWAYS");
18226 break;
18227 case 'R':
18228 appendPQExpBufferStr(query, "ENABLE REPLICA");
18229 break;
18230 default:
18231 appendPQExpBufferStr(query, "ENABLE");
18232 break;
18234 appendPQExpBufferStr(query, ";\n");
18237 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
18238 qevtname);
18240 if (dopt->binary_upgrade)
18241 binary_upgrade_extension_member(query, &evtinfo->dobj,
18242 "EVENT TRIGGER", qevtname, NULL);
18244 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18245 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
18246 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
18247 .owner = evtinfo->evtowner,
18248 .description = "EVENT TRIGGER",
18249 .section = SECTION_POST_DATA,
18250 .createStmt = query->data,
18251 .dropStmt = delqry->data));
18253 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18254 dumpComment(fout, "EVENT TRIGGER", qevtname,
18255 NULL, evtinfo->evtowner,
18256 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
18258 destroyPQExpBuffer(query);
18259 destroyPQExpBuffer(delqry);
18260 free(qevtname);
18264 * dumpRule
18265 * Dump a rule
18267 static void
18268 dumpRule(Archive *fout, const RuleInfo *rinfo)
18270 DumpOptions *dopt = fout->dopt;
18271 TableInfo *tbinfo = rinfo->ruletable;
18272 bool is_view;
18273 PQExpBuffer query;
18274 PQExpBuffer cmd;
18275 PQExpBuffer delcmd;
18276 PQExpBuffer ruleprefix;
18277 char *qtabname;
18278 PGresult *res;
18279 char *tag;
18281 /* Do nothing if not dumping schema */
18282 if (!dopt->dumpSchema)
18283 return;
18286 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
18287 * we do not want to dump it as a separate object.
18289 if (!rinfo->separate)
18290 return;
18293 * If it's an ON SELECT rule, we want to print it as a view definition,
18294 * instead of a rule.
18296 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
18298 query = createPQExpBuffer();
18299 cmd = createPQExpBuffer();
18300 delcmd = createPQExpBuffer();
18301 ruleprefix = createPQExpBuffer();
18303 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18305 if (is_view)
18307 PQExpBuffer result;
18310 * We need OR REPLACE here because we'll be replacing a dummy view.
18311 * Otherwise this should look largely like the regular view dump code.
18313 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
18314 fmtQualifiedDumpable(tbinfo));
18315 if (nonemptyReloptions(tbinfo->reloptions))
18317 appendPQExpBufferStr(cmd, " WITH (");
18318 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
18319 appendPQExpBufferChar(cmd, ')');
18321 result = createViewAsClause(fout, tbinfo);
18322 appendPQExpBuffer(cmd, " AS\n%s", result->data);
18323 destroyPQExpBuffer(result);
18324 if (tbinfo->checkoption != NULL)
18325 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
18326 tbinfo->checkoption);
18327 appendPQExpBufferStr(cmd, ";\n");
18329 else
18331 /* In the rule case, just print pg_get_ruledef's result verbatim */
18332 appendPQExpBuffer(query,
18333 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
18334 rinfo->dobj.catId.oid);
18336 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18338 if (PQntuples(res) != 1)
18339 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
18340 rinfo->dobj.name, tbinfo->dobj.name);
18342 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
18344 PQclear(res);
18348 * Add the command to alter the rules replication firing semantics if it
18349 * differs from the default.
18351 if (rinfo->ev_enabled != 'O')
18353 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
18354 switch (rinfo->ev_enabled)
18356 case 'A':
18357 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
18358 fmtId(rinfo->dobj.name));
18359 break;
18360 case 'R':
18361 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
18362 fmtId(rinfo->dobj.name));
18363 break;
18364 case 'D':
18365 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
18366 fmtId(rinfo->dobj.name));
18367 break;
18371 if (is_view)
18374 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
18375 * REPLACE VIEW to replace the rule with something with minimal
18376 * dependencies.
18378 PQExpBuffer result;
18380 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
18381 fmtQualifiedDumpable(tbinfo));
18382 result = createDummyViewAsClause(fout, tbinfo);
18383 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
18384 destroyPQExpBuffer(result);
18386 else
18388 appendPQExpBuffer(delcmd, "DROP RULE %s ",
18389 fmtId(rinfo->dobj.name));
18390 appendPQExpBuffer(delcmd, "ON %s;\n",
18391 fmtQualifiedDumpable(tbinfo));
18394 appendPQExpBuffer(ruleprefix, "RULE %s ON",
18395 fmtId(rinfo->dobj.name));
18397 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
18399 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18400 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
18401 ARCHIVE_OPTS(.tag = tag,
18402 .namespace = tbinfo->dobj.namespace->dobj.name,
18403 .owner = tbinfo->rolname,
18404 .description = "RULE",
18405 .section = SECTION_POST_DATA,
18406 .createStmt = cmd->data,
18407 .dropStmt = delcmd->data));
18409 /* Dump rule comments */
18410 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18411 dumpComment(fout, ruleprefix->data, qtabname,
18412 tbinfo->dobj.namespace->dobj.name,
18413 tbinfo->rolname,
18414 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
18416 free(tag);
18417 destroyPQExpBuffer(query);
18418 destroyPQExpBuffer(cmd);
18419 destroyPQExpBuffer(delcmd);
18420 destroyPQExpBuffer(ruleprefix);
18421 free(qtabname);
18425 * getExtensionMembership --- obtain extension membership data
18427 * We need to identify objects that are extension members as soon as they're
18428 * loaded, so that we can correctly determine whether they need to be dumped.
18429 * Generally speaking, extension member objects will get marked as *not* to
18430 * be dumped, as they will be recreated by the single CREATE EXTENSION
18431 * command. However, in binary upgrade mode we still need to dump the members
18432 * individually.
18434 void
18435 getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18436 int numExtensions)
18438 PQExpBuffer query;
18439 PGresult *res;
18440 int ntups,
18442 int i_classid,
18443 i_objid,
18444 i_refobjid;
18445 ExtensionInfo *ext;
18447 /* Nothing to do if no extensions */
18448 if (numExtensions == 0)
18449 return;
18451 query = createPQExpBuffer();
18453 /* refclassid constraint is redundant but may speed the search */
18454 appendPQExpBufferStr(query, "SELECT "
18455 "classid, objid, refobjid "
18456 "FROM pg_depend "
18457 "WHERE refclassid = 'pg_extension'::regclass "
18458 "AND deptype = 'e' "
18459 "ORDER BY 3");
18461 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18463 ntups = PQntuples(res);
18465 i_classid = PQfnumber(res, "classid");
18466 i_objid = PQfnumber(res, "objid");
18467 i_refobjid = PQfnumber(res, "refobjid");
18470 * Since we ordered the SELECT by referenced ID, we can expect that
18471 * multiple entries for the same extension will appear together; this
18472 * saves on searches.
18474 ext = NULL;
18476 for (i = 0; i < ntups; i++)
18478 CatalogId objId;
18479 Oid extId;
18481 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18482 objId.oid = atooid(PQgetvalue(res, i, i_objid));
18483 extId = atooid(PQgetvalue(res, i, i_refobjid));
18485 if (ext == NULL ||
18486 ext->dobj.catId.oid != extId)
18487 ext = findExtensionByOid(extId);
18489 if (ext == NULL)
18491 /* shouldn't happen */
18492 pg_log_warning("could not find referenced extension %u", extId);
18493 continue;
18496 recordExtensionMembership(objId, ext);
18499 PQclear(res);
18501 destroyPQExpBuffer(query);
18505 * processExtensionTables --- deal with extension configuration tables
18507 * There are two parts to this process:
18509 * 1. Identify and create dump records for extension configuration tables.
18511 * Extensions can mark tables as "configuration", which means that the user
18512 * is able and expected to modify those tables after the extension has been
18513 * loaded. For these tables, we dump out only the data- the structure is
18514 * expected to be handled at CREATE EXTENSION time, including any indexes or
18515 * foreign keys, which brings us to-
18517 * 2. Record FK dependencies between configuration tables.
18519 * Due to the FKs being created at CREATE EXTENSION time and therefore before
18520 * the data is loaded, we have to work out what the best order for reloading
18521 * the data is, to avoid FK violations when the tables are restored. This is
18522 * not perfect- we can't handle circular dependencies and if any exist they
18523 * will cause an invalid dump to be produced (though at least all of the data
18524 * is included for a user to manually restore). This is currently documented
18525 * but perhaps we can provide a better solution in the future.
18527 void
18528 processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
18529 int numExtensions)
18531 DumpOptions *dopt = fout->dopt;
18532 PQExpBuffer query;
18533 PGresult *res;
18534 int ntups,
18536 int i_conrelid,
18537 i_confrelid;
18539 /* Nothing to do if no extensions */
18540 if (numExtensions == 0)
18541 return;
18544 * Identify extension configuration tables and create TableDataInfo
18545 * objects for them, ensuring their data will be dumped even though the
18546 * tables themselves won't be.
18548 * Note that we create TableDataInfo objects even in schema-only mode, ie,
18549 * user data in a configuration table is treated like schema data. This
18550 * seems appropriate since system data in a config table would get
18551 * reloaded by CREATE EXTENSION. If the extension is not listed in the
18552 * list of extensions to be included, none of its data is dumped.
18554 for (i = 0; i < numExtensions; i++)
18556 ExtensionInfo *curext = &(extinfo[i]);
18557 char *extconfig = curext->extconfig;
18558 char *extcondition = curext->extcondition;
18559 char **extconfigarray = NULL;
18560 char **extconditionarray = NULL;
18561 int nconfigitems = 0;
18562 int nconditionitems = 0;
18565 * Check if this extension is listed as to include in the dump. If
18566 * not, any table data associated with it is discarded.
18568 if (extension_include_oids.head != NULL &&
18569 !simple_oid_list_member(&extension_include_oids,
18570 curext->dobj.catId.oid))
18571 continue;
18574 * Check if this extension is listed as to exclude in the dump. If
18575 * yes, any table data associated with it is discarded.
18577 if (extension_exclude_oids.head != NULL &&
18578 simple_oid_list_member(&extension_exclude_oids,
18579 curext->dobj.catId.oid))
18580 continue;
18582 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
18584 int j;
18586 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
18587 pg_fatal("could not parse %s array", "extconfig");
18588 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
18589 pg_fatal("could not parse %s array", "extcondition");
18590 if (nconfigitems != nconditionitems)
18591 pg_fatal("mismatched number of configurations and conditions for extension");
18593 for (j = 0; j < nconfigitems; j++)
18595 TableInfo *configtbl;
18596 Oid configtbloid = atooid(extconfigarray[j]);
18597 bool dumpobj =
18598 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
18600 configtbl = findTableByOid(configtbloid);
18601 if (configtbl == NULL)
18602 continue;
18605 * Tables of not-to-be-dumped extensions shouldn't be dumped
18606 * unless the table or its schema is explicitly included
18608 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
18610 /* check table explicitly requested */
18611 if (table_include_oids.head != NULL &&
18612 simple_oid_list_member(&table_include_oids,
18613 configtbloid))
18614 dumpobj = true;
18616 /* check table's schema explicitly requested */
18617 if (configtbl->dobj.namespace->dobj.dump &
18618 DUMP_COMPONENT_DATA)
18619 dumpobj = true;
18622 /* check table excluded by an exclusion switch */
18623 if (table_exclude_oids.head != NULL &&
18624 simple_oid_list_member(&table_exclude_oids,
18625 configtbloid))
18626 dumpobj = false;
18628 /* check schema excluded by an exclusion switch */
18629 if (simple_oid_list_member(&schema_exclude_oids,
18630 configtbl->dobj.namespace->dobj.catId.oid))
18631 dumpobj = false;
18633 if (dumpobj)
18635 makeTableDataInfo(dopt, configtbl);
18636 if (configtbl->dataObj != NULL)
18638 if (strlen(extconditionarray[j]) > 0)
18639 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
18644 if (extconfigarray)
18645 free(extconfigarray);
18646 if (extconditionarray)
18647 free(extconditionarray);
18651 * Now that all the TableDataInfo objects have been created for all the
18652 * extensions, check their FK dependencies and register them to try and
18653 * dump the data out in an order that they can be restored in.
18655 * Note that this is not a problem for user tables as their FKs are
18656 * recreated after the data has been loaded.
18659 query = createPQExpBuffer();
18661 printfPQExpBuffer(query,
18662 "SELECT conrelid, confrelid "
18663 "FROM pg_constraint "
18664 "JOIN pg_depend ON (objid = confrelid) "
18665 "WHERE contype = 'f' "
18666 "AND refclassid = 'pg_extension'::regclass "
18667 "AND classid = 'pg_class'::regclass;");
18669 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18670 ntups = PQntuples(res);
18672 i_conrelid = PQfnumber(res, "conrelid");
18673 i_confrelid = PQfnumber(res, "confrelid");
18675 /* Now get the dependencies and register them */
18676 for (i = 0; i < ntups; i++)
18678 Oid conrelid,
18679 confrelid;
18680 TableInfo *reftable,
18681 *contable;
18683 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
18684 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
18685 contable = findTableByOid(conrelid);
18686 reftable = findTableByOid(confrelid);
18688 if (reftable == NULL ||
18689 reftable->dataObj == NULL ||
18690 contable == NULL ||
18691 contable->dataObj == NULL)
18692 continue;
18695 * Make referencing TABLE_DATA object depend on the referenced table's
18696 * TABLE_DATA object.
18698 addObjectDependency(&contable->dataObj->dobj,
18699 reftable->dataObj->dobj.dumpId);
18701 PQclear(res);
18702 destroyPQExpBuffer(query);
18706 * getDependencies --- obtain available dependency data
18708 static void
18709 getDependencies(Archive *fout)
18711 PQExpBuffer query;
18712 PGresult *res;
18713 int ntups,
18715 int i_classid,
18716 i_objid,
18717 i_refclassid,
18718 i_refobjid,
18719 i_deptype;
18720 DumpableObject *dobj,
18721 *refdobj;
18723 pg_log_info("reading dependency data");
18725 query = createPQExpBuffer();
18728 * Messy query to collect the dependency data we need. Note that we
18729 * ignore the sub-object column, so that dependencies of or on a column
18730 * look the same as dependencies of or on a whole table.
18732 * PIN dependencies aren't interesting, and EXTENSION dependencies were
18733 * already processed by getExtensionMembership.
18735 appendPQExpBufferStr(query, "SELECT "
18736 "classid, objid, refclassid, refobjid, deptype "
18737 "FROM pg_depend "
18738 "WHERE deptype != 'p' AND deptype != 'e'\n");
18741 * Since we don't treat pg_amop entries as separate DumpableObjects, we
18742 * have to translate their dependencies into dependencies of their parent
18743 * opfamily. Ignore internal dependencies though, as those will point to
18744 * their parent opclass, which we needn't consider here (and if we did,
18745 * it'd just result in circular dependencies). Also, "loose" opfamily
18746 * entries will have dependencies on their parent opfamily, which we
18747 * should drop since they'd likewise become useless self-dependencies.
18748 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
18750 appendPQExpBufferStr(query, "UNION ALL\n"
18751 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
18752 "FROM pg_depend d, pg_amop o "
18753 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18754 "classid = 'pg_amop'::regclass AND objid = o.oid "
18755 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
18757 /* Likewise for pg_amproc entries */
18758 appendPQExpBufferStr(query, "UNION ALL\n"
18759 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
18760 "FROM pg_depend d, pg_amproc p "
18761 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18762 "classid = 'pg_amproc'::regclass AND objid = p.oid "
18763 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
18765 /* Sort the output for efficiency below */
18766 appendPQExpBufferStr(query, "ORDER BY 1,2");
18768 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18770 ntups = PQntuples(res);
18772 i_classid = PQfnumber(res, "classid");
18773 i_objid = PQfnumber(res, "objid");
18774 i_refclassid = PQfnumber(res, "refclassid");
18775 i_refobjid = PQfnumber(res, "refobjid");
18776 i_deptype = PQfnumber(res, "deptype");
18779 * Since we ordered the SELECT by referencing ID, we can expect that
18780 * multiple entries for the same object will appear together; this saves
18781 * on searches.
18783 dobj = NULL;
18785 for (i = 0; i < ntups; i++)
18787 CatalogId objId;
18788 CatalogId refobjId;
18789 char deptype;
18791 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18792 objId.oid = atooid(PQgetvalue(res, i, i_objid));
18793 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
18794 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
18795 deptype = *(PQgetvalue(res, i, i_deptype));
18797 if (dobj == NULL ||
18798 dobj->catId.tableoid != objId.tableoid ||
18799 dobj->catId.oid != objId.oid)
18800 dobj = findObjectByCatalogId(objId);
18803 * Failure to find objects mentioned in pg_depend is not unexpected,
18804 * since for example we don't collect info about TOAST tables.
18806 if (dobj == NULL)
18808 #ifdef NOT_USED
18809 pg_log_warning("no referencing object %u %u",
18810 objId.tableoid, objId.oid);
18811 #endif
18812 continue;
18815 refdobj = findObjectByCatalogId(refobjId);
18817 if (refdobj == NULL)
18819 #ifdef NOT_USED
18820 pg_log_warning("no referenced object %u %u",
18821 refobjId.tableoid, refobjId.oid);
18822 #endif
18823 continue;
18827 * For 'x' dependencies, mark the object for later; we still add the
18828 * normal dependency, for possible ordering purposes. Currently
18829 * pg_dump_sort.c knows to put extensions ahead of all object types
18830 * that could possibly depend on them, but this is safer.
18832 if (deptype == 'x')
18833 dobj->depends_on_ext = true;
18836 * Ordinarily, table rowtypes have implicit dependencies on their
18837 * tables. However, for a composite type the implicit dependency goes
18838 * the other way in pg_depend; which is the right thing for DROP but
18839 * it doesn't produce the dependency ordering we need. So in that one
18840 * case, we reverse the direction of the dependency.
18842 if (deptype == 'i' &&
18843 dobj->objType == DO_TABLE &&
18844 refdobj->objType == DO_TYPE)
18845 addObjectDependency(refdobj, dobj->dumpId);
18846 else
18847 /* normal case */
18848 addObjectDependency(dobj, refdobj->dumpId);
18851 PQclear(res);
18853 destroyPQExpBuffer(query);
18858 * createBoundaryObjects - create dummy DumpableObjects to represent
18859 * dump section boundaries.
18861 static DumpableObject *
18862 createBoundaryObjects(void)
18864 DumpableObject *dobjs;
18866 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
18868 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
18869 dobjs[0].catId = nilCatalogId;
18870 AssignDumpId(dobjs + 0);
18871 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
18873 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
18874 dobjs[1].catId = nilCatalogId;
18875 AssignDumpId(dobjs + 1);
18876 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
18878 return dobjs;
18882 * addBoundaryDependencies - add dependencies as needed to enforce the dump
18883 * section boundaries.
18885 static void
18886 addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
18887 DumpableObject *boundaryObjs)
18889 DumpableObject *preDataBound = boundaryObjs + 0;
18890 DumpableObject *postDataBound = boundaryObjs + 1;
18891 int i;
18893 for (i = 0; i < numObjs; i++)
18895 DumpableObject *dobj = dobjs[i];
18898 * The classification of object types here must match the SECTION_xxx
18899 * values assigned during subsequent ArchiveEntry calls!
18901 switch (dobj->objType)
18903 case DO_NAMESPACE:
18904 case DO_EXTENSION:
18905 case DO_TYPE:
18906 case DO_SHELL_TYPE:
18907 case DO_FUNC:
18908 case DO_AGG:
18909 case DO_OPERATOR:
18910 case DO_ACCESS_METHOD:
18911 case DO_OPCLASS:
18912 case DO_OPFAMILY:
18913 case DO_COLLATION:
18914 case DO_CONVERSION:
18915 case DO_TABLE:
18916 case DO_TABLE_ATTACH:
18917 case DO_ATTRDEF:
18918 case DO_PROCLANG:
18919 case DO_CAST:
18920 case DO_DUMMY_TYPE:
18921 case DO_TSPARSER:
18922 case DO_TSDICT:
18923 case DO_TSTEMPLATE:
18924 case DO_TSCONFIG:
18925 case DO_FDW:
18926 case DO_FOREIGN_SERVER:
18927 case DO_TRANSFORM:
18928 /* Pre-data objects: must come before the pre-data boundary */
18929 addObjectDependency(preDataBound, dobj->dumpId);
18930 break;
18931 case DO_TABLE_DATA:
18932 case DO_SEQUENCE_SET:
18933 case DO_LARGE_OBJECT:
18934 case DO_LARGE_OBJECT_DATA:
18935 /* Data objects: must come between the boundaries */
18936 addObjectDependency(dobj, preDataBound->dumpId);
18937 addObjectDependency(postDataBound, dobj->dumpId);
18938 break;
18939 case DO_INDEX:
18940 case DO_INDEX_ATTACH:
18941 case DO_STATSEXT:
18942 case DO_REFRESH_MATVIEW:
18943 case DO_TRIGGER:
18944 case DO_EVENT_TRIGGER:
18945 case DO_DEFAULT_ACL:
18946 case DO_POLICY:
18947 case DO_PUBLICATION:
18948 case DO_PUBLICATION_REL:
18949 case DO_PUBLICATION_TABLE_IN_SCHEMA:
18950 case DO_SUBSCRIPTION:
18951 case DO_SUBSCRIPTION_REL:
18952 /* Post-data objects: must come after the post-data boundary */
18953 addObjectDependency(dobj, postDataBound->dumpId);
18954 break;
18955 case DO_RULE:
18956 /* Rules are post-data, but only if dumped separately */
18957 if (((RuleInfo *) dobj)->separate)
18958 addObjectDependency(dobj, postDataBound->dumpId);
18959 break;
18960 case DO_CONSTRAINT:
18961 case DO_FK_CONSTRAINT:
18962 /* Constraints are post-data, but only if dumped separately */
18963 if (((ConstraintInfo *) dobj)->separate)
18964 addObjectDependency(dobj, postDataBound->dumpId);
18965 break;
18966 case DO_PRE_DATA_BOUNDARY:
18967 /* nothing to do */
18968 break;
18969 case DO_POST_DATA_BOUNDARY:
18970 /* must come after the pre-data boundary */
18971 addObjectDependency(dobj, preDataBound->dumpId);
18972 break;
18979 * BuildArchiveDependencies - create dependency data for archive TOC entries
18981 * The raw dependency data obtained by getDependencies() is not terribly
18982 * useful in an archive dump, because in many cases there are dependency
18983 * chains linking through objects that don't appear explicitly in the dump.
18984 * For example, a view will depend on its _RETURN rule while the _RETURN rule
18985 * will depend on other objects --- but the rule will not appear as a separate
18986 * object in the dump. We need to adjust the view's dependencies to include
18987 * whatever the rule depends on that is included in the dump.
18989 * Just to make things more complicated, there are also "special" dependencies
18990 * such as the dependency of a TABLE DATA item on its TABLE, which we must
18991 * not rearrange because pg_restore knows that TABLE DATA only depends on
18992 * its table. In these cases we must leave the dependencies strictly as-is
18993 * even if they refer to not-to-be-dumped objects.
18995 * To handle this, the convention is that "special" dependencies are created
18996 * during ArchiveEntry calls, and an archive TOC item that has any such
18997 * entries will not be touched here. Otherwise, we recursively search the
18998 * DumpableObject data structures to build the correct dependencies for each
18999 * archive TOC item.
19001 static void
19002 BuildArchiveDependencies(Archive *fout)
19004 ArchiveHandle *AH = (ArchiveHandle *) fout;
19005 TocEntry *te;
19007 /* Scan all TOC entries in the archive */
19008 for (te = AH->toc->next; te != AH->toc; te = te->next)
19010 DumpableObject *dobj;
19011 DumpId *dependencies;
19012 int nDeps;
19013 int allocDeps;
19015 /* No need to process entries that will not be dumped */
19016 if (te->reqs == 0)
19017 continue;
19018 /* Ignore entries that already have "special" dependencies */
19019 if (te->nDeps > 0)
19020 continue;
19021 /* Otherwise, look up the item's original DumpableObject, if any */
19022 dobj = findObjectByDumpId(te->dumpId);
19023 if (dobj == NULL)
19024 continue;
19025 /* No work if it has no dependencies */
19026 if (dobj->nDeps <= 0)
19027 continue;
19028 /* Set up work array */
19029 allocDeps = 64;
19030 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
19031 nDeps = 0;
19032 /* Recursively find all dumpable dependencies */
19033 findDumpableDependencies(AH, dobj,
19034 &dependencies, &nDeps, &allocDeps);
19035 /* And save 'em ... */
19036 if (nDeps > 0)
19038 dependencies = (DumpId *) pg_realloc(dependencies,
19039 nDeps * sizeof(DumpId));
19040 te->dependencies = dependencies;
19041 te->nDeps = nDeps;
19043 else
19044 free(dependencies);
19048 /* Recursive search subroutine for BuildArchiveDependencies */
19049 static void
19050 findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
19051 DumpId **dependencies, int *nDeps, int *allocDeps)
19053 int i;
19056 * Ignore section boundary objects: if we search through them, we'll
19057 * report lots of bogus dependencies.
19059 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
19060 dobj->objType == DO_POST_DATA_BOUNDARY)
19061 return;
19063 for (i = 0; i < dobj->nDeps; i++)
19065 DumpId depid = dobj->dependencies[i];
19067 if (TocIDRequired(AH, depid) != 0)
19069 /* Object will be dumped, so just reference it as a dependency */
19070 if (*nDeps >= *allocDeps)
19072 *allocDeps *= 2;
19073 *dependencies = (DumpId *) pg_realloc(*dependencies,
19074 *allocDeps * sizeof(DumpId));
19076 (*dependencies)[*nDeps] = depid;
19077 (*nDeps)++;
19079 else
19082 * Object will not be dumped, so recursively consider its deps. We
19083 * rely on the assumption that sortDumpableObjects already broke
19084 * any dependency loops, else we might recurse infinitely.
19086 DumpableObject *otherdobj = findObjectByDumpId(depid);
19088 if (otherdobj)
19089 findDumpableDependencies(AH, otherdobj,
19090 dependencies, nDeps, allocDeps);
19097 * getFormattedTypeName - retrieve a nicely-formatted type name for the
19098 * given type OID.
19100 * This does not guarantee to schema-qualify the output, so it should not
19101 * be used to create the target object name for CREATE or ALTER commands.
19103 * Note that the result is cached and must not be freed by the caller.
19105 static const char *
19106 getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
19108 TypeInfo *typeInfo;
19109 char *result;
19110 PQExpBuffer query;
19111 PGresult *res;
19113 if (oid == 0)
19115 if ((opts & zeroAsStar) != 0)
19116 return "*";
19117 else if ((opts & zeroAsNone) != 0)
19118 return "NONE";
19121 /* see if we have the result cached in the type's TypeInfo record */
19122 typeInfo = findTypeByOid(oid);
19123 if (typeInfo && typeInfo->ftypname)
19124 return typeInfo->ftypname;
19126 query = createPQExpBuffer();
19127 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
19128 oid);
19130 res = ExecuteSqlQueryForSingleRow(fout, query->data);
19132 /* result of format_type is already quoted */
19133 result = pg_strdup(PQgetvalue(res, 0, 0));
19135 PQclear(res);
19136 destroyPQExpBuffer(query);
19139 * Cache the result for re-use in later requests, if possible. If we
19140 * don't have a TypeInfo for the type, the string will be leaked once the
19141 * caller is done with it ... but that case really should not happen, so
19142 * leaking if it does seems acceptable.
19144 if (typeInfo)
19145 typeInfo->ftypname = result;
19147 return result;
19151 * Return a column list clause for the given relation.
19153 * Special case: if there are no undropped columns in the relation, return
19154 * "", not an invalid "()" column list.
19156 static const char *
19157 fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
19159 int numatts = ti->numatts;
19160 char **attnames = ti->attnames;
19161 bool *attisdropped = ti->attisdropped;
19162 char *attgenerated = ti->attgenerated;
19163 bool needComma;
19164 int i;
19166 appendPQExpBufferChar(buffer, '(');
19167 needComma = false;
19168 for (i = 0; i < numatts; i++)
19170 if (attisdropped[i])
19171 continue;
19172 if (attgenerated[i])
19173 continue;
19174 if (needComma)
19175 appendPQExpBufferStr(buffer, ", ");
19176 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
19177 needComma = true;
19180 if (!needComma)
19181 return ""; /* no undropped columns */
19183 appendPQExpBufferChar(buffer, ')');
19184 return buffer->data;
19188 * Check if a reloptions array is nonempty.
19190 static bool
19191 nonemptyReloptions(const char *reloptions)
19193 /* Don't want to print it if it's just "{}" */
19194 return (reloptions != NULL && strlen(reloptions) > 2);
19198 * Format a reloptions array and append it to the given buffer.
19200 * "prefix" is prepended to the option names; typically it's "" or "toast.".
19202 static void
19203 appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
19204 const char *prefix, Archive *fout)
19206 bool res;
19208 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
19209 fout->std_strings);
19210 if (!res)
19211 pg_log_warning("could not parse %s array", "reloptions");
19215 * read_dump_filters - retrieve object identifier patterns from file
19217 * Parse the specified filter file for include and exclude patterns, and add
19218 * them to the relevant lists. If the filename is "-" then filters will be
19219 * read from STDIN rather than a file.
19221 static void
19222 read_dump_filters(const char *filename, DumpOptions *dopt)
19224 FilterStateData fstate;
19225 char *objname;
19226 FilterCommandType comtype;
19227 FilterObjectType objtype;
19229 filter_init(&fstate, filename, exit_nicely);
19231 while (filter_read_item(&fstate, &objname, &comtype, &objtype))
19233 if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
19235 switch (objtype)
19237 case FILTER_OBJECT_TYPE_NONE:
19238 break;
19239 case FILTER_OBJECT_TYPE_DATABASE:
19240 case FILTER_OBJECT_TYPE_FUNCTION:
19241 case FILTER_OBJECT_TYPE_INDEX:
19242 case FILTER_OBJECT_TYPE_TABLE_DATA:
19243 case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19244 case FILTER_OBJECT_TYPE_TRIGGER:
19245 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19246 "include",
19247 filter_object_type_name(objtype));
19248 exit_nicely(1);
19249 break; /* unreachable */
19251 case FILTER_OBJECT_TYPE_EXTENSION:
19252 simple_string_list_append(&extension_include_patterns, objname);
19253 break;
19254 case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19255 simple_string_list_append(&foreign_servers_include_patterns, objname);
19256 break;
19257 case FILTER_OBJECT_TYPE_SCHEMA:
19258 simple_string_list_append(&schema_include_patterns, objname);
19259 dopt->include_everything = false;
19260 break;
19261 case FILTER_OBJECT_TYPE_TABLE:
19262 simple_string_list_append(&table_include_patterns, objname);
19263 dopt->include_everything = false;
19264 break;
19265 case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19266 simple_string_list_append(&table_include_patterns_and_children,
19267 objname);
19268 dopt->include_everything = false;
19269 break;
19272 else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
19274 switch (objtype)
19276 case FILTER_OBJECT_TYPE_NONE:
19277 break;
19278 case FILTER_OBJECT_TYPE_DATABASE:
19279 case FILTER_OBJECT_TYPE_FUNCTION:
19280 case FILTER_OBJECT_TYPE_INDEX:
19281 case FILTER_OBJECT_TYPE_TRIGGER:
19282 case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19283 pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19284 "exclude",
19285 filter_object_type_name(objtype));
19286 exit_nicely(1);
19287 break;
19289 case FILTER_OBJECT_TYPE_EXTENSION:
19290 simple_string_list_append(&extension_exclude_patterns, objname);
19291 break;
19292 case FILTER_OBJECT_TYPE_TABLE_DATA:
19293 simple_string_list_append(&tabledata_exclude_patterns,
19294 objname);
19295 break;
19296 case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19297 simple_string_list_append(&tabledata_exclude_patterns_and_children,
19298 objname);
19299 break;
19300 case FILTER_OBJECT_TYPE_SCHEMA:
19301 simple_string_list_append(&schema_exclude_patterns, objname);
19302 break;
19303 case FILTER_OBJECT_TYPE_TABLE:
19304 simple_string_list_append(&table_exclude_patterns, objname);
19305 break;
19306 case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19307 simple_string_list_append(&table_exclude_patterns_and_children,
19308 objname);
19309 break;
19312 else
19314 Assert(comtype == FILTER_COMMAND_TYPE_NONE);
19315 Assert(objtype == FILTER_OBJECT_TYPE_NONE);
19318 if (objname)
19319 free(objname);
19322 filter_free(&fstate);