doc: clarify that pg_global can _only_ be used for system tabs.
[pgsql.git] / src / bin / pg_dump / pg_dump.c
blobe863913849240f3fff3d72acfdea570c43aba979
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-2023, 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_largeobject_metadata_d.h"
53 #include "catalog/pg_proc_d.h"
54 #include "catalog/pg_subscription.h"
55 #include "catalog/pg_trigger_d.h"
56 #include "catalog/pg_type_d.h"
57 #include "common/connect.h"
58 #include "common/relpath.h"
59 #include "compress_io.h"
60 #include "dumputils.h"
61 #include "fe_utils/option_utils.h"
62 #include "fe_utils/string_utils.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 enum OidOptions
96 zeroIsError = 1,
97 zeroAsStar = 2,
98 zeroAsNone = 4,
99 } OidOptions;
101 /* global decls */
102 static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
104 static Oid g_last_builtin_oid; /* value of the last builtin oid */
106 /* The specified names/patterns should to match at least one entity */
107 static int strict_names = 0;
109 static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
112 * Object inclusion/exclusion lists
114 * The string lists record the patterns given by command-line switches,
115 * which we then convert to lists of OIDs of matching objects.
117 static SimpleStringList schema_include_patterns = {NULL, NULL};
118 static SimpleOidList schema_include_oids = {NULL, NULL};
119 static SimpleStringList schema_exclude_patterns = {NULL, NULL};
120 static SimpleOidList schema_exclude_oids = {NULL, NULL};
122 static SimpleStringList table_include_patterns = {NULL, NULL};
123 static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
124 static SimpleOidList table_include_oids = {NULL, NULL};
125 static SimpleStringList table_exclude_patterns = {NULL, NULL};
126 static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
127 static SimpleOidList table_exclude_oids = {NULL, NULL};
128 static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
129 static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
130 static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
132 static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
133 static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
135 static SimpleStringList extension_include_patterns = {NULL, NULL};
136 static SimpleOidList extension_include_oids = {NULL, NULL};
138 static const CatalogId nilCatalogId = {0, 0};
140 /* override for standard extra_float_digits setting */
141 static bool have_extra_float_digits = false;
142 static int extra_float_digits;
144 /* sorted table of role names */
145 static RoleNameItem *rolenames = NULL;
146 static int nrolenames = 0;
148 /* sorted table of comments */
149 static CommentItem *comments = NULL;
150 static int ncomments = 0;
152 /* sorted table of security labels */
153 static SecLabelItem *seclabels = NULL;
154 static int nseclabels = 0;
157 * The default number of rows per INSERT when
158 * --inserts is specified without --rows-per-insert
160 #define DUMP_DEFAULT_ROWS_PER_INSERT 1
163 * Macro for producing quoted, schema-qualified name of a dumpable object.
165 #define fmtQualifiedDumpable(obj) \
166 fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
167 (obj)->dobj.name)
169 static void help(const char *progname);
170 static void setup_connection(Archive *AH,
171 const char *dumpencoding, const char *dumpsnapshot,
172 char *use_role);
173 static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
174 static void expand_schema_name_patterns(Archive *fout,
175 SimpleStringList *patterns,
176 SimpleOidList *oids,
177 bool strict_names);
178 static void expand_extension_name_patterns(Archive *fout,
179 SimpleStringList *patterns,
180 SimpleOidList *oids,
181 bool strict_names);
182 static void expand_foreign_server_name_patterns(Archive *fout,
183 SimpleStringList *patterns,
184 SimpleOidList *oids);
185 static void expand_table_name_patterns(Archive *fout,
186 SimpleStringList *patterns,
187 SimpleOidList *oids,
188 bool strict_names,
189 bool with_child_tables);
190 static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
191 const char *pattern);
193 static NamespaceInfo *findNamespace(Oid nsoid);
194 static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
195 static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
196 static const char *getRoleName(const char *roleoid_str);
197 static void collectRoleNames(Archive *fout);
198 static void getAdditionalACLs(Archive *fout);
199 static void dumpCommentExtended(Archive *fout, const char *type,
200 const char *name, const char *namespace,
201 const char *owner, CatalogId catalogId,
202 int subid, DumpId dumpId,
203 const char *initdb_comment);
204 static inline void dumpComment(Archive *fout, const char *type,
205 const char *name, const char *namespace,
206 const char *owner, CatalogId catalogId,
207 int subid, DumpId dumpId);
208 static int findComments(Oid classoid, Oid objoid, CommentItem **items);
209 static void collectComments(Archive *fout);
210 static void dumpSecLabel(Archive *fout, const char *type, const char *name,
211 const char *namespace, const char *owner,
212 CatalogId catalogId, int subid, DumpId dumpId);
213 static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
214 static void collectSecLabels(Archive *fout);
215 static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
216 static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
217 static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
218 static void dumpType(Archive *fout, const TypeInfo *tyinfo);
219 static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
220 static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
221 static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
222 static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
223 static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
224 static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
225 static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
226 PGresult *res);
227 static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
228 static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
229 static void dumpFunc(Archive *fout, const FuncInfo *finfo);
230 static void dumpCast(Archive *fout, const CastInfo *cast);
231 static void dumpTransform(Archive *fout, const TransformInfo *transform);
232 static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
233 static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
234 static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
235 static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
236 static void dumpCollation(Archive *fout, const CollInfo *collinfo);
237 static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
238 static void dumpRule(Archive *fout, const RuleInfo *rinfo);
239 static void dumpAgg(Archive *fout, const AggInfo *agginfo);
240 static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
241 static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
242 static void dumpTable(Archive *fout, const TableInfo *tbinfo);
243 static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
244 static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
245 static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
246 static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
247 static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
248 static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
249 static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
250 static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
251 static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
252 static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
253 static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
254 static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
255 static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
256 static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
257 static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
258 static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
259 static void dumpUserMappings(Archive *fout,
260 const char *servername, const char *namespace,
261 const char *owner, CatalogId catalogId, DumpId dumpId);
262 static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
264 static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
265 const char *type, const char *name, const char *subname,
266 const char *nspname, const char *owner,
267 const DumpableAcl *dacl);
269 static void getDependencies(Archive *fout);
270 static void BuildArchiveDependencies(Archive *fout);
271 static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
272 DumpId **dependencies, int *nDeps, int *allocDeps);
274 static DumpableObject *createBoundaryObjects(void);
275 static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
276 DumpableObject *boundaryObjs);
278 static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
279 static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
280 static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
281 static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
282 static void buildMatViewRefreshDependencies(Archive *fout);
283 static void getTableDataFKConstraints(void);
284 static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
285 bool is_agg);
286 static char *format_function_signature(Archive *fout,
287 const FuncInfo *finfo, bool honor_quotes);
288 static char *convertRegProcReference(const char *proc);
289 static char *getFormattedOperatorName(const char *oproid);
290 static char *convertTSFunction(Archive *fout, Oid funcOid);
291 static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
292 static void getLOs(Archive *fout);
293 static void dumpLO(Archive *fout, const LoInfo *loinfo);
294 static int dumpLOs(Archive *fout, const void *arg);
295 static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
296 static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
297 static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
298 static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
299 static void dumpDatabase(Archive *fout);
300 static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
301 const char *dbname, Oid dboid);
302 static void dumpEncoding(Archive *AH);
303 static void dumpStdStrings(Archive *AH);
304 static void dumpSearchPath(Archive *AH);
305 static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
306 PQExpBuffer upgrade_buffer,
307 Oid pg_type_oid,
308 bool force_array_type,
309 bool include_multirange_type);
310 static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
311 PQExpBuffer upgrade_buffer,
312 const TableInfo *tbinfo);
313 static void binary_upgrade_set_pg_class_oids(Archive *fout,
314 PQExpBuffer upgrade_buffer,
315 Oid pg_class_oid, bool is_index);
316 static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
317 const DumpableObject *dobj,
318 const char *objtype,
319 const char *objname,
320 const char *objnamespace);
321 static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
322 static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
323 static bool nonemptyReloptions(const char *reloptions);
324 static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
325 const char *prefix, Archive *fout);
326 static char *get_synchronized_snapshot(Archive *fout);
327 static void setupDumpWorker(Archive *AH);
328 static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
329 static bool forcePartitionRootLoad(const TableInfo *tbinfo);
333 main(int argc, char **argv)
335 int c;
336 const char *filename = NULL;
337 const char *format = "p";
338 TableInfo *tblinfo;
339 int numTables;
340 DumpableObject **dobjs;
341 int numObjs;
342 DumpableObject *boundaryObjs;
343 int i;
344 int optindex;
345 RestoreOptions *ropt;
346 Archive *fout; /* the script file */
347 bool g_verbose = false;
348 const char *dumpencoding = NULL;
349 const char *dumpsnapshot = NULL;
350 char *use_role = NULL;
351 int numWorkers = 1;
352 int plainText = 0;
353 ArchiveFormat archiveFormat = archUnknown;
354 ArchiveMode archiveMode;
355 pg_compress_specification compression_spec = {0};
356 char *compression_detail = NULL;
357 char *compression_algorithm_str = "none";
358 char *error_detail = NULL;
359 bool user_compression_defined = false;
360 DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
362 static DumpOptions dopt;
364 static struct option long_options[] = {
365 {"data-only", no_argument, NULL, 'a'},
366 {"blobs", no_argument, NULL, 'b'},
367 {"large-objects", no_argument, NULL, 'b'},
368 {"no-blobs", no_argument, NULL, 'B'},
369 {"no-large-objects", no_argument, NULL, 'B'},
370 {"clean", no_argument, NULL, 'c'},
371 {"create", no_argument, NULL, 'C'},
372 {"dbname", required_argument, NULL, 'd'},
373 {"extension", required_argument, NULL, 'e'},
374 {"file", required_argument, NULL, 'f'},
375 {"format", required_argument, NULL, 'F'},
376 {"host", required_argument, NULL, 'h'},
377 {"jobs", 1, NULL, 'j'},
378 {"no-reconnect", no_argument, NULL, 'R'},
379 {"no-owner", no_argument, NULL, 'O'},
380 {"port", required_argument, NULL, 'p'},
381 {"schema", required_argument, NULL, 'n'},
382 {"exclude-schema", required_argument, NULL, 'N'},
383 {"schema-only", no_argument, NULL, 's'},
384 {"superuser", required_argument, NULL, 'S'},
385 {"table", required_argument, NULL, 't'},
386 {"exclude-table", required_argument, NULL, 'T'},
387 {"no-password", no_argument, NULL, 'w'},
388 {"password", no_argument, NULL, 'W'},
389 {"username", required_argument, NULL, 'U'},
390 {"verbose", no_argument, NULL, 'v'},
391 {"no-privileges", no_argument, NULL, 'x'},
392 {"no-acl", no_argument, NULL, 'x'},
393 {"compress", required_argument, NULL, 'Z'},
394 {"encoding", required_argument, NULL, 'E'},
395 {"help", no_argument, NULL, '?'},
396 {"version", no_argument, NULL, 'V'},
399 * the following options don't have an equivalent short option letter
401 {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
402 {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
403 {"column-inserts", no_argument, &dopt.column_inserts, 1},
404 {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
405 {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
406 {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
407 {"exclude-table-data", required_argument, NULL, 4},
408 {"extra-float-digits", required_argument, NULL, 8},
409 {"if-exists", no_argument, &dopt.if_exists, 1},
410 {"inserts", no_argument, NULL, 9},
411 {"lock-wait-timeout", required_argument, NULL, 2},
412 {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
413 {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
414 {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
415 {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
416 {"role", required_argument, NULL, 3},
417 {"section", required_argument, NULL, 5},
418 {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
419 {"snapshot", required_argument, NULL, 6},
420 {"strict-names", no_argument, &strict_names, 1},
421 {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
422 {"no-comments", no_argument, &dopt.no_comments, 1},
423 {"no-publications", no_argument, &dopt.no_publications, 1},
424 {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
425 {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
426 {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
427 {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
428 {"no-sync", no_argument, NULL, 7},
429 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
430 {"rows-per-insert", required_argument, NULL, 10},
431 {"include-foreign-data", required_argument, NULL, 11},
432 {"table-and-children", required_argument, NULL, 12},
433 {"exclude-table-and-children", required_argument, NULL, 13},
434 {"exclude-table-data-and-children", required_argument, NULL, 14},
435 {"sync-method", required_argument, NULL, 15},
437 {NULL, 0, NULL, 0}
440 pg_logging_init(argv[0]);
441 pg_logging_set_level(PG_LOG_WARNING);
442 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
445 * Initialize what we need for parallel execution, especially for thread
446 * support on Windows.
448 init_parallel_dump_utils();
450 progname = get_progname(argv[0]);
452 if (argc > 1)
454 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
456 help(progname);
457 exit_nicely(0);
459 if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
461 puts("pg_dump (PostgreSQL) " PG_VERSION);
462 exit_nicely(0);
466 InitDumpOptions(&dopt);
468 while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
469 long_options, &optindex)) != -1)
471 switch (c)
473 case 'a': /* Dump data only */
474 dopt.dataOnly = true;
475 break;
477 case 'b': /* Dump LOs */
478 dopt.outputLOs = true;
479 break;
481 case 'B': /* Don't dump LOs */
482 dopt.dontOutputLOs = true;
483 break;
485 case 'c': /* clean (i.e., drop) schema prior to create */
486 dopt.outputClean = 1;
487 break;
489 case 'C': /* Create DB */
490 dopt.outputCreateDB = 1;
491 break;
493 case 'd': /* database name */
494 dopt.cparams.dbname = pg_strdup(optarg);
495 break;
497 case 'e': /* include extension(s) */
498 simple_string_list_append(&extension_include_patterns, optarg);
499 dopt.include_everything = false;
500 break;
502 case 'E': /* Dump encoding */
503 dumpencoding = pg_strdup(optarg);
504 break;
506 case 'f':
507 filename = pg_strdup(optarg);
508 break;
510 case 'F':
511 format = pg_strdup(optarg);
512 break;
514 case 'h': /* server host */
515 dopt.cparams.pghost = pg_strdup(optarg);
516 break;
518 case 'j': /* number of dump jobs */
519 if (!option_parse_int(optarg, "-j/--jobs", 1,
520 PG_MAX_JOBS,
521 &numWorkers))
522 exit_nicely(1);
523 break;
525 case 'n': /* include schema(s) */
526 simple_string_list_append(&schema_include_patterns, optarg);
527 dopt.include_everything = false;
528 break;
530 case 'N': /* exclude schema(s) */
531 simple_string_list_append(&schema_exclude_patterns, optarg);
532 break;
534 case 'O': /* Don't reconnect to match owner */
535 dopt.outputNoOwner = 1;
536 break;
538 case 'p': /* server port */
539 dopt.cparams.pgport = pg_strdup(optarg);
540 break;
542 case 'R':
543 /* no-op, still accepted for backwards compatibility */
544 break;
546 case 's': /* dump schema only */
547 dopt.schemaOnly = true;
548 break;
550 case 'S': /* Username for superuser in plain text output */
551 dopt.outputSuperuser = pg_strdup(optarg);
552 break;
554 case 't': /* include table(s) */
555 simple_string_list_append(&table_include_patterns, optarg);
556 dopt.include_everything = false;
557 break;
559 case 'T': /* exclude table(s) */
560 simple_string_list_append(&table_exclude_patterns, optarg);
561 break;
563 case 'U':
564 dopt.cparams.username = pg_strdup(optarg);
565 break;
567 case 'v': /* verbose */
568 g_verbose = true;
569 pg_logging_increase_verbosity();
570 break;
572 case 'w':
573 dopt.cparams.promptPassword = TRI_NO;
574 break;
576 case 'W':
577 dopt.cparams.promptPassword = TRI_YES;
578 break;
580 case 'x': /* skip ACL dump */
581 dopt.aclsSkip = true;
582 break;
584 case 'Z': /* Compression */
585 parse_compress_options(optarg, &compression_algorithm_str,
586 &compression_detail);
587 user_compression_defined = true;
588 break;
590 case 0:
591 /* This covers the long options. */
592 break;
594 case 2: /* lock-wait-timeout */
595 dopt.lockWaitTimeout = pg_strdup(optarg);
596 break;
598 case 3: /* SET ROLE */
599 use_role = pg_strdup(optarg);
600 break;
602 case 4: /* exclude table(s) data */
603 simple_string_list_append(&tabledata_exclude_patterns, optarg);
604 break;
606 case 5: /* section */
607 set_dump_section(optarg, &dopt.dumpSections);
608 break;
610 case 6: /* snapshot */
611 dumpsnapshot = pg_strdup(optarg);
612 break;
614 case 7: /* no-sync */
615 dosync = false;
616 break;
618 case 8:
619 have_extra_float_digits = true;
620 if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
621 &extra_float_digits))
622 exit_nicely(1);
623 break;
625 case 9: /* inserts */
628 * dump_inserts also stores --rows-per-insert, careful not to
629 * overwrite that.
631 if (dopt.dump_inserts == 0)
632 dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
633 break;
635 case 10: /* rows per insert */
636 if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
637 &dopt.dump_inserts))
638 exit_nicely(1);
639 break;
641 case 11: /* include foreign data */
642 simple_string_list_append(&foreign_servers_include_patterns,
643 optarg);
644 break;
646 case 12: /* include table(s) and their children */
647 simple_string_list_append(&table_include_patterns_and_children,
648 optarg);
649 dopt.include_everything = false;
650 break;
652 case 13: /* exclude table(s) and their children */
653 simple_string_list_append(&table_exclude_patterns_and_children,
654 optarg);
655 break;
657 case 14: /* exclude data of table(s) and children */
658 simple_string_list_append(&tabledata_exclude_patterns_and_children,
659 optarg);
660 break;
662 case 15:
663 if (!parse_sync_method(optarg, &sync_method))
664 exit_nicely(1);
665 break;
667 default:
668 /* getopt_long already emitted a complaint */
669 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
670 exit_nicely(1);
675 * Non-option argument specifies database name as long as it wasn't
676 * already specified with -d / --dbname
678 if (optind < argc && dopt.cparams.dbname == NULL)
679 dopt.cparams.dbname = argv[optind++];
681 /* Complain if any arguments remain */
682 if (optind < argc)
684 pg_log_error("too many command-line arguments (first is \"%s\")",
685 argv[optind]);
686 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
687 exit_nicely(1);
690 /* --column-inserts implies --inserts */
691 if (dopt.column_inserts && dopt.dump_inserts == 0)
692 dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
695 * Binary upgrade mode implies dumping sequence data even in schema-only
696 * mode. This is not exposed as a separate option, but kept separate
697 * internally for clarity.
699 if (dopt.binary_upgrade)
700 dopt.sequence_data = 1;
702 if (dopt.dataOnly && dopt.schemaOnly)
703 pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
705 if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
706 pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
708 if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
709 pg_fatal("option --include-foreign-data is not supported with parallel backup");
711 if (dopt.dataOnly && dopt.outputClean)
712 pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
714 if (dopt.if_exists && !dopt.outputClean)
715 pg_fatal("option --if-exists requires option -c/--clean");
718 * --inserts are already implied above if --column-inserts or
719 * --rows-per-insert were specified.
721 if (dopt.do_nothing && dopt.dump_inserts == 0)
722 pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
724 /* Identify archive format to emit */
725 archiveFormat = parseArchiveFormat(format, &archiveMode);
727 /* archiveFormat specific setup */
728 if (archiveFormat == archNull)
729 plainText = 1;
732 * Custom and directory formats are compressed by default with gzip when
733 * available, not the others. If gzip is not available, no compression is
734 * done by default.
736 if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
737 !user_compression_defined)
739 #ifdef HAVE_LIBZ
740 compression_algorithm_str = "gzip";
741 #else
742 compression_algorithm_str = "none";
743 #endif
747 * Compression options
749 if (!parse_compress_algorithm(compression_algorithm_str,
750 &compression_algorithm))
751 pg_fatal("unrecognized compression algorithm: \"%s\"",
752 compression_algorithm_str);
754 parse_compress_specification(compression_algorithm, compression_detail,
755 &compression_spec);
756 error_detail = validate_compress_specification(&compression_spec);
757 if (error_detail != NULL)
758 pg_fatal("invalid compression specification: %s",
759 error_detail);
761 error_detail = supports_compression(compression_spec);
762 if (error_detail != NULL)
763 pg_fatal("%s", error_detail);
766 * Disable support for zstd workers for now - these are based on
767 * threading, and it's unclear how it interacts with parallel dumps on
768 * platforms where that relies on threads too (e.g. Windows).
770 if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
771 pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
772 "workers");
775 * If emitting an archive format, we always want to emit a DATABASE item,
776 * in case --create is specified at pg_restore time.
778 if (!plainText)
779 dopt.outputCreateDB = 1;
781 /* Parallel backup only in the directory archive format so far */
782 if (archiveFormat != archDirectory && numWorkers > 1)
783 pg_fatal("parallel backup only supported by the directory format");
785 /* Open the output file */
786 fout = CreateArchive(filename, archiveFormat, compression_spec,
787 dosync, archiveMode, setupDumpWorker, sync_method);
789 /* Make dump options accessible right away */
790 SetArchiveOptions(fout, &dopt, NULL);
792 /* Register the cleanup hook */
793 on_exit_close_archive(fout);
795 /* Let the archiver know how noisy to be */
796 fout->verbose = g_verbose;
800 * We allow the server to be back to 9.2, and up to any minor release of
801 * our own major version. (See also version check in pg_dumpall.c.)
803 fout->minRemoteVersion = 90200;
804 fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
806 fout->numWorkers = numWorkers;
809 * Open the database using the Archiver, so it knows about it. Errors mean
810 * death.
812 ConnectDatabase(fout, &dopt.cparams, false);
813 setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
816 * On hot standbys, never try to dump unlogged table data, since it will
817 * just throw an error.
819 if (fout->isStandby)
820 dopt.no_unlogged_table_data = true;
823 * Find the last built-in OID, if needed (prior to 8.1)
825 * With 8.1 and above, we can just use FirstNormalObjectId - 1.
827 g_last_builtin_oid = FirstNormalObjectId - 1;
829 pg_log_info("last built-in OID is %u", g_last_builtin_oid);
831 /* Expand schema selection patterns into OID lists */
832 if (schema_include_patterns.head != NULL)
834 expand_schema_name_patterns(fout, &schema_include_patterns,
835 &schema_include_oids,
836 strict_names);
837 if (schema_include_oids.head == NULL)
838 pg_fatal("no matching schemas were found");
840 expand_schema_name_patterns(fout, &schema_exclude_patterns,
841 &schema_exclude_oids,
842 false);
843 /* non-matching exclusion patterns aren't an error */
845 /* Expand table selection patterns into OID lists */
846 expand_table_name_patterns(fout, &table_include_patterns,
847 &table_include_oids,
848 strict_names, false);
849 expand_table_name_patterns(fout, &table_include_patterns_and_children,
850 &table_include_oids,
851 strict_names, true);
852 if ((table_include_patterns.head != NULL ||
853 table_include_patterns_and_children.head != NULL) &&
854 table_include_oids.head == NULL)
855 pg_fatal("no matching tables were found");
857 expand_table_name_patterns(fout, &table_exclude_patterns,
858 &table_exclude_oids,
859 false, false);
860 expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
861 &table_exclude_oids,
862 false, true);
864 expand_table_name_patterns(fout, &tabledata_exclude_patterns,
865 &tabledata_exclude_oids,
866 false, false);
867 expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
868 &tabledata_exclude_oids,
869 false, true);
871 expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
872 &foreign_servers_include_oids);
874 /* non-matching exclusion patterns aren't an error */
876 /* Expand extension selection patterns into OID lists */
877 if (extension_include_patterns.head != NULL)
879 expand_extension_name_patterns(fout, &extension_include_patterns,
880 &extension_include_oids,
881 strict_names);
882 if (extension_include_oids.head == NULL)
883 pg_fatal("no matching extensions were found");
887 * Dumping LOs is the default for dumps where an inclusion switch is not
888 * used (an "include everything" dump). -B can be used to exclude LOs
889 * from those dumps. -b can be used to include LOs even when an inclusion
890 * switch is used.
892 * -s means "schema only" and LOs are data, not schema, so we never
893 * include LOs when -s is used.
895 if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputLOs)
896 dopt.outputLOs = true;
899 * Collect role names so we can map object owner OIDs to names.
901 collectRoleNames(fout);
904 * Now scan the database and create DumpableObject structs for all the
905 * objects we intend to dump.
907 tblinfo = getSchemaData(fout, &numTables);
909 if (!dopt.schemaOnly)
911 getTableData(&dopt, tblinfo, numTables, 0);
912 buildMatViewRefreshDependencies(fout);
913 if (dopt.dataOnly)
914 getTableDataFKConstraints();
917 if (dopt.schemaOnly && dopt.sequence_data)
918 getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
921 * In binary-upgrade mode, we do not have to worry about the actual LO
922 * data or the associated metadata that resides in the pg_largeobject and
923 * pg_largeobject_metadata tables, respectively.
925 * However, we do need to collect LO information as there may be comments
926 * or other information on LOs that we do need to dump out.
928 if (dopt.outputLOs || dopt.binary_upgrade)
929 getLOs(fout);
932 * Collect dependency data to assist in ordering the objects.
934 getDependencies(fout);
937 * Collect ACLs, comments, and security labels, if wanted.
939 if (!dopt.aclsSkip)
940 getAdditionalACLs(fout);
941 if (!dopt.no_comments)
942 collectComments(fout);
943 if (!dopt.no_security_labels)
944 collectSecLabels(fout);
946 /* Lastly, create dummy objects to represent the section boundaries */
947 boundaryObjs = createBoundaryObjects();
949 /* Get pointers to all the known DumpableObjects */
950 getDumpableObjects(&dobjs, &numObjs);
953 * Add dummy dependencies to enforce the dump section ordering.
955 addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
958 * Sort the objects into a safe dump order (no forward references).
960 * We rely on dependency information to help us determine a safe order, so
961 * the initial sort is mostly for cosmetic purposes: we sort by name to
962 * ensure that logically identical schemas will dump identically.
964 sortDumpableObjectsByTypeName(dobjs, numObjs);
966 sortDumpableObjects(dobjs, numObjs,
967 boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
970 * Create archive TOC entries for all the objects to be dumped, in a safe
971 * order.
975 * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
977 dumpEncoding(fout);
978 dumpStdStrings(fout);
979 dumpSearchPath(fout);
981 /* The database items are always next, unless we don't want them at all */
982 if (dopt.outputCreateDB)
983 dumpDatabase(fout);
985 /* Now the rearrangeable objects. */
986 for (i = 0; i < numObjs; i++)
987 dumpDumpableObject(fout, dobjs[i]);
990 * Set up options info to ensure we dump what we want.
992 ropt = NewRestoreOptions();
993 ropt->filename = filename;
995 /* if you change this list, see dumpOptionsFromRestoreOptions */
996 ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
997 ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
998 ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
999 ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1000 ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1001 ropt->dropSchema = dopt.outputClean;
1002 ropt->dataOnly = dopt.dataOnly;
1003 ropt->schemaOnly = dopt.schemaOnly;
1004 ropt->if_exists = dopt.if_exists;
1005 ropt->column_inserts = dopt.column_inserts;
1006 ropt->dumpSections = dopt.dumpSections;
1007 ropt->aclsSkip = dopt.aclsSkip;
1008 ropt->superuser = dopt.outputSuperuser;
1009 ropt->createDB = dopt.outputCreateDB;
1010 ropt->noOwner = dopt.outputNoOwner;
1011 ropt->noTableAm = dopt.outputNoTableAm;
1012 ropt->noTablespace = dopt.outputNoTablespaces;
1013 ropt->disable_triggers = dopt.disable_triggers;
1014 ropt->use_setsessauth = dopt.use_setsessauth;
1015 ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1016 ropt->dump_inserts = dopt.dump_inserts;
1017 ropt->no_comments = dopt.no_comments;
1018 ropt->no_publications = dopt.no_publications;
1019 ropt->no_security_labels = dopt.no_security_labels;
1020 ropt->no_subscriptions = dopt.no_subscriptions;
1021 ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1022 ropt->include_everything = dopt.include_everything;
1023 ropt->enable_row_security = dopt.enable_row_security;
1024 ropt->sequence_data = dopt.sequence_data;
1025 ropt->binary_upgrade = dopt.binary_upgrade;
1027 ropt->compression_spec = compression_spec;
1029 ropt->suppressDumpWarnings = true; /* We've already shown them */
1031 SetArchiveOptions(fout, &dopt, ropt);
1033 /* Mark which entries should be output */
1034 ProcessArchiveRestoreOptions(fout);
1037 * The archive's TOC entries are now marked as to which ones will actually
1038 * be output, so we can set up their dependency lists properly. This isn't
1039 * necessary for plain-text output, though.
1041 if (!plainText)
1042 BuildArchiveDependencies(fout);
1045 * And finally we can do the actual output.
1047 * Note: for non-plain-text output formats, the output file is written
1048 * inside CloseArchive(). This is, um, bizarre; but not worth changing
1049 * right now.
1051 if (plainText)
1052 RestoreArchive(fout);
1054 CloseArchive(fout);
1056 exit_nicely(0);
1060 static void
1061 help(const char *progname)
1063 printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1064 printf(_("Usage:\n"));
1065 printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1067 printf(_("\nGeneral options:\n"));
1068 printf(_(" -f, --file=FILENAME output file or directory name\n"));
1069 printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1070 " plain text (default))\n"));
1071 printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1072 printf(_(" -v, --verbose verbose mode\n"));
1073 printf(_(" -V, --version output version information, then exit\n"));
1074 printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1075 " compress as specified\n"));
1076 printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1077 printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1078 printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1079 printf(_(" -?, --help show this help, then exit\n"));
1081 printf(_("\nOptions controlling the output content:\n"));
1082 printf(_(" -a, --data-only dump only the data, not the schema\n"));
1083 printf(_(" -b, --large-objects include large objects in dump\n"));
1084 printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1085 printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1086 printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1087 printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1088 printf(_(" -C, --create include commands to create database in dump\n"));
1089 printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1090 printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1091 printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1092 printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1093 printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1094 " plain-text format\n"));
1095 printf(_(" -s, --schema-only dump only the schema, no data\n"));
1096 printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1097 printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1098 printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1099 printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1100 printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1101 printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1102 printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1103 printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1104 printf(_(" --enable-row-security enable row security (dump only content user has\n"
1105 " access to)\n"));
1106 printf(_(" --exclude-table-and-children=PATTERN\n"
1107 " do NOT dump the specified table(s), including\n"
1108 " child and partition tables\n"));
1109 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1110 printf(_(" --exclude-table-data-and-children=PATTERN\n"
1111 " do NOT dump data for the specified table(s),\n"
1112 " including child and partition tables\n"));
1113 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1114 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1115 printf(_(" --include-foreign-data=PATTERN\n"
1116 " include data of foreign tables on foreign\n"
1117 " servers matching PATTERN\n"));
1118 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1119 printf(_(" --load-via-partition-root load partitions via the root table\n"));
1120 printf(_(" --no-comments do not dump comments\n"));
1121 printf(_(" --no-publications do not dump publications\n"));
1122 printf(_(" --no-security-labels do not dump security label assignments\n"));
1123 printf(_(" --no-subscriptions do not dump subscriptions\n"));
1124 printf(_(" --no-table-access-method do not dump table access methods\n"));
1125 printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1126 printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1127 printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1128 printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1129 printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1130 printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1131 printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1132 printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1133 printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1134 printf(_(" --strict-names require table and/or schema include patterns to\n"
1135 " match at least one entity each\n"));
1136 printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1137 " child and partition tables\n"));
1138 printf(_(" --use-set-session-authorization\n"
1139 " use SET SESSION AUTHORIZATION commands instead of\n"
1140 " ALTER OWNER commands to set ownership\n"));
1142 printf(_("\nConnection options:\n"));
1143 printf(_(" -d, --dbname=DBNAME database to dump\n"));
1144 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1145 printf(_(" -p, --port=PORT database server port number\n"));
1146 printf(_(" -U, --username=NAME connect as specified database user\n"));
1147 printf(_(" -w, --no-password never prompt for password\n"));
1148 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1149 printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1151 printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1152 "variable value is used.\n\n"));
1153 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1154 printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1157 static void
1158 setup_connection(Archive *AH, const char *dumpencoding,
1159 const char *dumpsnapshot, char *use_role)
1161 DumpOptions *dopt = AH->dopt;
1162 PGconn *conn = GetConnection(AH);
1163 const char *std_strings;
1165 PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1168 * Set the client encoding if requested.
1170 if (dumpencoding)
1172 if (PQsetClientEncoding(conn, dumpencoding) < 0)
1173 pg_fatal("invalid client encoding \"%s\" specified",
1174 dumpencoding);
1178 * Get the active encoding and the standard_conforming_strings setting, so
1179 * we know how to escape strings.
1181 AH->encoding = PQclientEncoding(conn);
1183 std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1184 AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1187 * Set the role if requested. In a parallel dump worker, we'll be passed
1188 * use_role == NULL, but AH->use_role is already set (if user specified it
1189 * originally) and we should use that.
1191 if (!use_role && AH->use_role)
1192 use_role = AH->use_role;
1194 /* Set the role if requested */
1195 if (use_role)
1197 PQExpBuffer query = createPQExpBuffer();
1199 appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1200 ExecuteSqlStatement(AH, query->data);
1201 destroyPQExpBuffer(query);
1203 /* save it for possible later use by parallel workers */
1204 if (!AH->use_role)
1205 AH->use_role = pg_strdup(use_role);
1208 /* Set the datestyle to ISO to ensure the dump's portability */
1209 ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1211 /* Likewise, avoid using sql_standard intervalstyle */
1212 ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1215 * Use an explicitly specified extra_float_digits if it has been provided.
1216 * Otherwise, set extra_float_digits so that we can dump float data
1217 * exactly (given correctly implemented float I/O code, anyway).
1219 if (have_extra_float_digits)
1221 PQExpBuffer q = createPQExpBuffer();
1223 appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1224 extra_float_digits);
1225 ExecuteSqlStatement(AH, q->data);
1226 destroyPQExpBuffer(q);
1228 else
1229 ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1232 * Disable synchronized scanning, to prevent unpredictable changes in row
1233 * ordering across a dump and reload.
1235 ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1238 * Disable timeouts if supported.
1240 ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1241 if (AH->remoteVersion >= 90300)
1242 ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1243 if (AH->remoteVersion >= 90600)
1244 ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1247 * Quote all identifiers, if requested.
1249 if (quote_all_identifiers)
1250 ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1253 * Adjust row-security mode, if supported.
1255 if (AH->remoteVersion >= 90500)
1257 if (dopt->enable_row_security)
1258 ExecuteSqlStatement(AH, "SET row_security = on");
1259 else
1260 ExecuteSqlStatement(AH, "SET row_security = off");
1264 * Initialize prepared-query state to "nothing prepared". We do this here
1265 * so that a parallel dump worker will have its own state.
1267 AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1270 * Start transaction-snapshot mode transaction to dump consistent data.
1272 ExecuteSqlStatement(AH, "BEGIN");
1275 * To support the combination of serializable_deferrable with the jobs
1276 * option we use REPEATABLE READ for the worker connections that are
1277 * passed a snapshot. As long as the snapshot is acquired in a
1278 * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1279 * REPEATABLE READ transaction provides the appropriate integrity
1280 * guarantees. This is a kluge, but safe for back-patching.
1282 if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1283 ExecuteSqlStatement(AH,
1284 "SET TRANSACTION ISOLATION LEVEL "
1285 "SERIALIZABLE, READ ONLY, DEFERRABLE");
1286 else
1287 ExecuteSqlStatement(AH,
1288 "SET TRANSACTION ISOLATION LEVEL "
1289 "REPEATABLE READ, READ ONLY");
1292 * If user specified a snapshot to use, select that. In a parallel dump
1293 * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1294 * is already set (if the server can handle it) and we should use that.
1296 if (dumpsnapshot)
1297 AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1299 if (AH->sync_snapshot_id)
1301 PQExpBuffer query = createPQExpBuffer();
1303 appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1304 appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1305 ExecuteSqlStatement(AH, query->data);
1306 destroyPQExpBuffer(query);
1308 else if (AH->numWorkers > 1)
1310 if (AH->isStandby && AH->remoteVersion < 100000)
1311 pg_fatal("parallel dumps from standby servers are not supported by this server version");
1312 AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1316 /* Set up connection for a parallel worker process */
1317 static void
1318 setupDumpWorker(Archive *AH)
1321 * We want to re-select all the same values the leader connection is
1322 * using. We'll have inherited directly-usable values in
1323 * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1324 * inherited encoding value back to a string to pass to setup_connection.
1326 setup_connection(AH,
1327 pg_encoding_to_char(AH->encoding),
1328 NULL,
1329 NULL);
1332 static char *
1333 get_synchronized_snapshot(Archive *fout)
1335 char *query = "SELECT pg_catalog.pg_export_snapshot()";
1336 char *result;
1337 PGresult *res;
1339 res = ExecuteSqlQueryForSingleRow(fout, query);
1340 result = pg_strdup(PQgetvalue(res, 0, 0));
1341 PQclear(res);
1343 return result;
1346 static ArchiveFormat
1347 parseArchiveFormat(const char *format, ArchiveMode *mode)
1349 ArchiveFormat archiveFormat;
1351 *mode = archModeWrite;
1353 if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1355 /* This is used by pg_dumpall, and is not documented */
1356 archiveFormat = archNull;
1357 *mode = archModeAppend;
1359 else if (pg_strcasecmp(format, "c") == 0)
1360 archiveFormat = archCustom;
1361 else if (pg_strcasecmp(format, "custom") == 0)
1362 archiveFormat = archCustom;
1363 else if (pg_strcasecmp(format, "d") == 0)
1364 archiveFormat = archDirectory;
1365 else if (pg_strcasecmp(format, "directory") == 0)
1366 archiveFormat = archDirectory;
1367 else if (pg_strcasecmp(format, "p") == 0)
1368 archiveFormat = archNull;
1369 else if (pg_strcasecmp(format, "plain") == 0)
1370 archiveFormat = archNull;
1371 else if (pg_strcasecmp(format, "t") == 0)
1372 archiveFormat = archTar;
1373 else if (pg_strcasecmp(format, "tar") == 0)
1374 archiveFormat = archTar;
1375 else
1376 pg_fatal("invalid output format \"%s\" specified", format);
1377 return archiveFormat;
1381 * Find the OIDs of all schemas matching the given list of patterns,
1382 * and append them to the given OID list.
1384 static void
1385 expand_schema_name_patterns(Archive *fout,
1386 SimpleStringList *patterns,
1387 SimpleOidList *oids,
1388 bool strict_names)
1390 PQExpBuffer query;
1391 PGresult *res;
1392 SimpleStringListCell *cell;
1393 int i;
1395 if (patterns->head == NULL)
1396 return; /* nothing to do */
1398 query = createPQExpBuffer();
1401 * The loop below runs multiple SELECTs might sometimes result in
1402 * duplicate entries in the OID list, but we don't care.
1405 for (cell = patterns->head; cell; cell = cell->next)
1407 PQExpBufferData dbbuf;
1408 int dotcnt;
1410 appendPQExpBufferStr(query,
1411 "SELECT oid FROM pg_catalog.pg_namespace n\n");
1412 initPQExpBuffer(&dbbuf);
1413 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1414 false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1415 &dotcnt);
1416 if (dotcnt > 1)
1417 pg_fatal("improper qualified name (too many dotted names): %s",
1418 cell->val);
1419 else if (dotcnt == 1)
1420 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1421 termPQExpBuffer(&dbbuf);
1423 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1424 if (strict_names && PQntuples(res) == 0)
1425 pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1427 for (i = 0; i < PQntuples(res); i++)
1429 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1432 PQclear(res);
1433 resetPQExpBuffer(query);
1436 destroyPQExpBuffer(query);
1440 * Find the OIDs of all extensions matching the given list of patterns,
1441 * and append them to the given OID list.
1443 static void
1444 expand_extension_name_patterns(Archive *fout,
1445 SimpleStringList *patterns,
1446 SimpleOidList *oids,
1447 bool strict_names)
1449 PQExpBuffer query;
1450 PGresult *res;
1451 SimpleStringListCell *cell;
1452 int i;
1454 if (patterns->head == NULL)
1455 return; /* nothing to do */
1457 query = createPQExpBuffer();
1460 * The loop below runs multiple SELECTs might sometimes result in
1461 * duplicate entries in the OID list, but we don't care.
1463 for (cell = patterns->head; cell; cell = cell->next)
1465 int dotcnt;
1467 appendPQExpBufferStr(query,
1468 "SELECT oid FROM pg_catalog.pg_extension e\n");
1469 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1470 false, NULL, "e.extname", NULL, NULL, NULL,
1471 &dotcnt);
1472 if (dotcnt > 0)
1473 pg_fatal("improper qualified name (too many dotted names): %s",
1474 cell->val);
1476 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1477 if (strict_names && PQntuples(res) == 0)
1478 pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1480 for (i = 0; i < PQntuples(res); i++)
1482 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1485 PQclear(res);
1486 resetPQExpBuffer(query);
1489 destroyPQExpBuffer(query);
1493 * Find the OIDs of all foreign servers matching the given list of patterns,
1494 * and append them to the given OID list.
1496 static void
1497 expand_foreign_server_name_patterns(Archive *fout,
1498 SimpleStringList *patterns,
1499 SimpleOidList *oids)
1501 PQExpBuffer query;
1502 PGresult *res;
1503 SimpleStringListCell *cell;
1504 int i;
1506 if (patterns->head == NULL)
1507 return; /* nothing to do */
1509 query = createPQExpBuffer();
1512 * The loop below runs multiple SELECTs might sometimes result in
1513 * duplicate entries in the OID list, but we don't care.
1516 for (cell = patterns->head; cell; cell = cell->next)
1518 int dotcnt;
1520 appendPQExpBufferStr(query,
1521 "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1522 processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1523 false, NULL, "s.srvname", NULL, NULL, NULL,
1524 &dotcnt);
1525 if (dotcnt > 0)
1526 pg_fatal("improper qualified name (too many dotted names): %s",
1527 cell->val);
1529 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1530 if (PQntuples(res) == 0)
1531 pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1533 for (i = 0; i < PQntuples(res); i++)
1534 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1536 PQclear(res);
1537 resetPQExpBuffer(query);
1540 destroyPQExpBuffer(query);
1544 * Find the OIDs of all tables matching the given list of patterns,
1545 * and append them to the given OID list. See also expand_dbname_patterns()
1546 * in pg_dumpall.c
1548 static void
1549 expand_table_name_patterns(Archive *fout,
1550 SimpleStringList *patterns, SimpleOidList *oids,
1551 bool strict_names, bool with_child_tables)
1553 PQExpBuffer query;
1554 PGresult *res;
1555 SimpleStringListCell *cell;
1556 int i;
1558 if (patterns->head == NULL)
1559 return; /* nothing to do */
1561 query = createPQExpBuffer();
1564 * this might sometimes result in duplicate entries in the OID list, but
1565 * we don't care.
1568 for (cell = patterns->head; cell; cell = cell->next)
1570 PQExpBufferData dbbuf;
1571 int dotcnt;
1574 * Query must remain ABSOLUTELY devoid of unqualified names. This
1575 * would be unnecessary given a pg_table_is_visible() variant taking a
1576 * search_path argument.
1578 * For with_child_tables, we start with the basic query's results and
1579 * recursively search the inheritance tree to add child tables.
1581 if (with_child_tables)
1583 appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1586 appendPQExpBuffer(query,
1587 "SELECT c.oid"
1588 "\nFROM pg_catalog.pg_class c"
1589 "\n LEFT JOIN pg_catalog.pg_namespace n"
1590 "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1591 "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1592 "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1593 RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1594 RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1595 RELKIND_PARTITIONED_TABLE);
1596 initPQExpBuffer(&dbbuf);
1597 processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1598 false, "n.nspname", "c.relname", NULL,
1599 "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1600 &dotcnt);
1601 if (dotcnt > 2)
1602 pg_fatal("improper relation name (too many dotted names): %s",
1603 cell->val);
1604 else if (dotcnt == 2)
1605 prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1606 termPQExpBuffer(&dbbuf);
1608 if (with_child_tables)
1610 appendPQExpBuffer(query, "UNION"
1611 "\nSELECT i.inhrelid"
1612 "\nFROM partition_tree p"
1613 "\n JOIN pg_catalog.pg_inherits i"
1614 "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1615 "\n)"
1616 "\nSELECT relid FROM partition_tree");
1619 ExecuteSqlStatement(fout, "RESET search_path");
1620 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1621 PQclear(ExecuteSqlQueryForSingleRow(fout,
1622 ALWAYS_SECURE_SEARCH_PATH_SQL));
1623 if (strict_names && PQntuples(res) == 0)
1624 pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1626 for (i = 0; i < PQntuples(res); i++)
1628 simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1631 PQclear(res);
1632 resetPQExpBuffer(query);
1635 destroyPQExpBuffer(query);
1639 * Verifies that the connected database name matches the given database name,
1640 * and if not, dies with an error about the given pattern.
1642 * The 'dbname' argument should be a literal name parsed from 'pattern'.
1644 static void
1645 prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1647 const char *db;
1649 db = PQdb(conn);
1650 if (db == NULL)
1651 pg_fatal("You are currently not connected to a database.");
1653 if (strcmp(db, dbname) != 0)
1654 pg_fatal("cross-database references are not implemented: %s",
1655 pattern);
1659 * checkExtensionMembership
1660 * Determine whether object is an extension member, and if so,
1661 * record an appropriate dependency and set the object's dump flag.
1663 * It's important to call this for each object that could be an extension
1664 * member. Generally, we integrate this with determining the object's
1665 * to-be-dumped-ness, since extension membership overrides other rules for that.
1667 * Returns true if object is an extension member, else false.
1669 static bool
1670 checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1672 ExtensionInfo *ext = findOwningExtension(dobj->catId);
1674 if (ext == NULL)
1675 return false;
1677 dobj->ext_member = true;
1679 /* Record dependency so that getDependencies needn't deal with that */
1680 addObjectDependency(dobj, ext->dobj.dumpId);
1683 * In 9.6 and above, mark the member object to have any non-initial ACL,
1684 * policies, and security labels dumped.
1686 * Note that any initial ACLs (see pg_init_privs) will be removed when we
1687 * extract the information about the object. We don't provide support for
1688 * initial policies and security labels and it seems unlikely for those to
1689 * ever exist, but we may have to revisit this later.
1691 * Prior to 9.6, we do not include any extension member components.
1693 * In binary upgrades, we still dump all components of the members
1694 * individually, since the idea is to exactly reproduce the database
1695 * contents rather than replace the extension contents with something
1696 * different.
1698 if (fout->dopt->binary_upgrade)
1699 dobj->dump = ext->dobj.dump;
1700 else
1702 if (fout->remoteVersion < 90600)
1703 dobj->dump = DUMP_COMPONENT_NONE;
1704 else
1705 dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL |
1706 DUMP_COMPONENT_SECLABEL |
1707 DUMP_COMPONENT_POLICY);
1710 return true;
1714 * selectDumpableNamespace: policy-setting subroutine
1715 * Mark a namespace as to be dumped or not
1717 static void
1718 selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1721 * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1722 * and (for --clean) a DROP SCHEMA statement. (In the absence of
1723 * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1725 nsinfo->create = true;
1728 * If specific tables are being dumped, do not dump any complete
1729 * namespaces. If specific namespaces are being dumped, dump just those
1730 * namespaces. Otherwise, dump all non-system namespaces.
1732 if (table_include_oids.head != NULL)
1733 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1734 else if (schema_include_oids.head != NULL)
1735 nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1736 simple_oid_list_member(&schema_include_oids,
1737 nsinfo->dobj.catId.oid) ?
1738 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1739 else if (fout->remoteVersion >= 90600 &&
1740 strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1743 * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1744 * they are interesting (and not the original ACLs which were set at
1745 * initdb time, see pg_init_privs).
1747 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1749 else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1750 strcmp(nsinfo->dobj.name, "information_schema") == 0)
1752 /* Other system schemas don't get dumped */
1753 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1755 else if (strcmp(nsinfo->dobj.name, "public") == 0)
1758 * The public schema is a strange beast that sits in a sort of
1759 * no-mans-land between being a system object and a user object.
1760 * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1761 * a comment and an indication of ownership. If the owner is the
1762 * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1763 * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1765 nsinfo->create = false;
1766 nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1767 if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1768 nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1769 nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1772 * Also, make like it has a comment even if it doesn't; this is so
1773 * that we'll emit a command to drop the comment, if appropriate.
1774 * (Without this, we'd not call dumpCommentExtended for it.)
1776 nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1778 else
1779 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1782 * In any case, a namespace can be excluded by an exclusion switch
1784 if (nsinfo->dobj.dump_contains &&
1785 simple_oid_list_member(&schema_exclude_oids,
1786 nsinfo->dobj.catId.oid))
1787 nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1790 * If the schema belongs to an extension, allow extension membership to
1791 * override the dump decision for the schema itself. However, this does
1792 * not change dump_contains, so this won't change what we do with objects
1793 * within the schema. (If they belong to the extension, they'll get
1794 * suppressed by it, otherwise not.)
1796 (void) checkExtensionMembership(&nsinfo->dobj, fout);
1800 * selectDumpableTable: policy-setting subroutine
1801 * Mark a table as to be dumped or not
1803 static void
1804 selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1806 if (checkExtensionMembership(&tbinfo->dobj, fout))
1807 return; /* extension membership overrides all else */
1810 * If specific tables are being dumped, dump just those tables; else, dump
1811 * according to the parent namespace's dump flag.
1813 if (table_include_oids.head != NULL)
1814 tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1815 tbinfo->dobj.catId.oid) ?
1816 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1817 else
1818 tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1821 * In any case, a table can be excluded by an exclusion switch
1823 if (tbinfo->dobj.dump &&
1824 simple_oid_list_member(&table_exclude_oids,
1825 tbinfo->dobj.catId.oid))
1826 tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1830 * selectDumpableType: policy-setting subroutine
1831 * Mark a type as to be dumped or not
1833 * If it's a table's rowtype or an autogenerated array type, we also apply a
1834 * special type code to facilitate sorting into the desired order. (We don't
1835 * want to consider those to be ordinary types because that would bring tables
1836 * up into the datatype part of the dump order.) We still set the object's
1837 * dump flag; that's not going to cause the dummy type to be dumped, but we
1838 * need it so that casts involving such types will be dumped correctly -- see
1839 * dumpCast. This means the flag should be set the same as for the underlying
1840 * object (the table or base type).
1842 static void
1843 selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1845 /* skip complex types, except for standalone composite types */
1846 if (OidIsValid(tyinfo->typrelid) &&
1847 tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1849 TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1851 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1852 if (tytable != NULL)
1853 tyinfo->dobj.dump = tytable->dobj.dump;
1854 else
1855 tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1856 return;
1859 /* skip auto-generated array types */
1860 if (tyinfo->isArray || tyinfo->isMultirange)
1862 tyinfo->dobj.objType = DO_DUMMY_TYPE;
1865 * Fall through to set the dump flag; we assume that the subsequent
1866 * rules will do the same thing as they would for the array's base
1867 * type. (We cannot reliably look up the base type here, since
1868 * getTypes may not have processed it yet.)
1872 if (checkExtensionMembership(&tyinfo->dobj, fout))
1873 return; /* extension membership overrides all else */
1875 /* Dump based on if the contents of the namespace are being dumped */
1876 tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1880 * selectDumpableDefaultACL: policy-setting subroutine
1881 * Mark a default ACL as to be dumped or not
1883 * For per-schema default ACLs, dump if the schema is to be dumped.
1884 * Otherwise dump if we are dumping "everything". Note that dataOnly
1885 * and aclsSkip are checked separately.
1887 static void
1888 selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
1890 /* Default ACLs can't be extension members */
1892 if (dinfo->dobj.namespace)
1893 /* default ACLs are considered part of the namespace */
1894 dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
1895 else
1896 dinfo->dobj.dump = dopt->include_everything ?
1897 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1901 * selectDumpableCast: policy-setting subroutine
1902 * Mark a cast as to be dumped or not
1904 * Casts do not belong to any particular namespace (since they haven't got
1905 * names), nor do they have identifiable owners. To distinguish user-defined
1906 * casts from built-in ones, we must resort to checking whether the cast's
1907 * OID is in the range reserved for initdb.
1909 static void
1910 selectDumpableCast(CastInfo *cast, Archive *fout)
1912 if (checkExtensionMembership(&cast->dobj, fout))
1913 return; /* extension membership overrides all else */
1916 * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
1917 * support ACLs currently.
1919 if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1920 cast->dobj.dump = DUMP_COMPONENT_NONE;
1921 else
1922 cast->dobj.dump = fout->dopt->include_everything ?
1923 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1927 * selectDumpableProcLang: policy-setting subroutine
1928 * Mark a procedural language as to be dumped or not
1930 * Procedural languages do not belong to any particular namespace. To
1931 * identify built-in languages, we must resort to checking whether the
1932 * language's OID is in the range reserved for initdb.
1934 static void
1935 selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
1937 if (checkExtensionMembership(&plang->dobj, fout))
1938 return; /* extension membership overrides all else */
1941 * Only include procedural languages when we are dumping everything.
1943 * For from-initdb procedural languages, only include ACLs, as we do for
1944 * the pg_catalog namespace. We need this because procedural languages do
1945 * not live in any namespace.
1947 if (!fout->dopt->include_everything)
1948 plang->dobj.dump = DUMP_COMPONENT_NONE;
1949 else
1951 if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1952 plang->dobj.dump = fout->remoteVersion < 90600 ?
1953 DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
1954 else
1955 plang->dobj.dump = DUMP_COMPONENT_ALL;
1960 * selectDumpableAccessMethod: policy-setting subroutine
1961 * Mark an access method as to be dumped or not
1963 * Access methods do not belong to any particular namespace. To identify
1964 * built-in access methods, we must resort to checking whether the
1965 * method's OID is in the range reserved for initdb.
1967 static void
1968 selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
1970 if (checkExtensionMembership(&method->dobj, fout))
1971 return; /* extension membership overrides all else */
1974 * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
1975 * they do not support ACLs currently.
1977 if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1978 method->dobj.dump = DUMP_COMPONENT_NONE;
1979 else
1980 method->dobj.dump = fout->dopt->include_everything ?
1981 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1985 * selectDumpableExtension: policy-setting subroutine
1986 * Mark an extension as to be dumped or not
1988 * Built-in extensions should be skipped except for checking ACLs, since we
1989 * assume those will already be installed in the target database. We identify
1990 * such extensions by their having OIDs in the range reserved for initdb.
1991 * We dump all user-added extensions by default. No extensions are dumped
1992 * if include_everything is false (i.e., a --schema or --table switch was
1993 * given), except if --extension specifies a list of extensions to dump.
1995 static void
1996 selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
1999 * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2000 * change permissions on their member objects, if they wish to, and have
2001 * those changes preserved.
2003 if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2004 extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2005 else
2007 /* check if there is a list of extensions to dump */
2008 if (extension_include_oids.head != NULL)
2009 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2010 simple_oid_list_member(&extension_include_oids,
2011 extinfo->dobj.catId.oid) ?
2012 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2013 else
2014 extinfo->dobj.dump = extinfo->dobj.dump_contains =
2015 dopt->include_everything ?
2016 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2021 * selectDumpablePublicationObject: policy-setting subroutine
2022 * Mark a publication object as to be dumped or not
2024 * A publication can have schemas and tables which have schemas, but those are
2025 * ignored in decision making, because publications are only dumped when we are
2026 * dumping everything.
2028 static void
2029 selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2031 if (checkExtensionMembership(dobj, fout))
2032 return; /* extension membership overrides all else */
2034 dobj->dump = fout->dopt->include_everything ?
2035 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2039 * selectDumpableObject: policy-setting subroutine
2040 * Mark a generic dumpable object as to be dumped or not
2042 * Use this only for object types without a special-case routine above.
2044 static void
2045 selectDumpableObject(DumpableObject *dobj, Archive *fout)
2047 if (checkExtensionMembership(dobj, fout))
2048 return; /* extension membership overrides all else */
2051 * Default policy is to dump if parent namespace is dumpable, or for
2052 * non-namespace-associated items, dump if we're dumping "everything".
2054 if (dobj->namespace)
2055 dobj->dump = dobj->namespace->dobj.dump_contains;
2056 else
2057 dobj->dump = fout->dopt->include_everything ?
2058 DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2062 * Dump a table's contents for loading using the COPY command
2063 * - this routine is called by the Archiver when it wants the table
2064 * to be dumped.
2066 static int
2067 dumpTableData_copy(Archive *fout, const void *dcontext)
2069 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2070 TableInfo *tbinfo = tdinfo->tdtable;
2071 const char *classname = tbinfo->dobj.name;
2072 PQExpBuffer q = createPQExpBuffer();
2075 * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2076 * which uses it already.
2078 PQExpBuffer clistBuf = createPQExpBuffer();
2079 PGconn *conn = GetConnection(fout);
2080 PGresult *res;
2081 int ret;
2082 char *copybuf;
2083 const char *column_list;
2085 pg_log_info("dumping contents of table \"%s.%s\"",
2086 tbinfo->dobj.namespace->dobj.name, classname);
2089 * Specify the column list explicitly so that we have no possibility of
2090 * retrieving data in the wrong column order. (The default column
2091 * ordering of COPY will not be what we want in certain corner cases
2092 * involving ADD COLUMN and inheritance.)
2094 column_list = fmtCopyColumnList(tbinfo, clistBuf);
2097 * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2098 * a filter condition was specified. For other cases a simple COPY
2099 * suffices.
2101 if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2103 appendPQExpBufferStr(q, "COPY (SELECT ");
2104 /* klugery to get rid of parens in column list */
2105 if (strlen(column_list) > 2)
2107 appendPQExpBufferStr(q, column_list + 1);
2108 q->data[q->len - 1] = ' ';
2110 else
2111 appendPQExpBufferStr(q, "* ");
2113 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2114 fmtQualifiedDumpable(tbinfo),
2115 tdinfo->filtercond ? tdinfo->filtercond : "");
2117 else
2119 appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2120 fmtQualifiedDumpable(tbinfo),
2121 column_list);
2123 res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2124 PQclear(res);
2125 destroyPQExpBuffer(clistBuf);
2127 for (;;)
2129 ret = PQgetCopyData(conn, &copybuf, 0);
2131 if (ret < 0)
2132 break; /* done or error */
2134 if (copybuf)
2136 WriteData(fout, copybuf, ret);
2137 PQfreemem(copybuf);
2140 /* ----------
2141 * THROTTLE:
2143 * There was considerable discussion in late July, 2000 regarding
2144 * slowing down pg_dump when backing up large tables. Users with both
2145 * slow & fast (multi-processor) machines experienced performance
2146 * degradation when doing a backup.
2148 * Initial attempts based on sleeping for a number of ms for each ms
2149 * of work were deemed too complex, then a simple 'sleep in each loop'
2150 * implementation was suggested. The latter failed because the loop
2151 * was too tight. Finally, the following was implemented:
2153 * If throttle is non-zero, then
2154 * See how long since the last sleep.
2155 * Work out how long to sleep (based on ratio).
2156 * If sleep is more than 100ms, then
2157 * sleep
2158 * reset timer
2159 * EndIf
2160 * EndIf
2162 * where the throttle value was the number of ms to sleep per ms of
2163 * work. The calculation was done in each loop.
2165 * Most of the hard work is done in the backend, and this solution
2166 * still did not work particularly well: on slow machines, the ratio
2167 * was 50:1, and on medium paced machines, 1:1, and on fast
2168 * multi-processor machines, it had little or no effect, for reasons
2169 * that were unclear.
2171 * Further discussion ensued, and the proposal was dropped.
2173 * For those people who want this feature, it can be implemented using
2174 * gettimeofday in each loop, calculating the time since last sleep,
2175 * multiplying that by the sleep ratio, then if the result is more
2176 * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2177 * function to sleep for a subsecond period ie.
2179 * select(0, NULL, NULL, NULL, &tvi);
2181 * This will return after the interval specified in the structure tvi.
2182 * Finally, call gettimeofday again to save the 'last sleep time'.
2183 * ----------
2186 archprintf(fout, "\\.\n\n\n");
2188 if (ret == -2)
2190 /* copy data transfer failed */
2191 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2192 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2193 pg_log_error_detail("Command was: %s", q->data);
2194 exit_nicely(1);
2197 /* Check command status and return to normal libpq state */
2198 res = PQgetResult(conn);
2199 if (PQresultStatus(res) != PGRES_COMMAND_OK)
2201 pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2202 pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2203 pg_log_error_detail("Command was: %s", q->data);
2204 exit_nicely(1);
2206 PQclear(res);
2208 /* Do this to ensure we've pumped libpq back to idle state */
2209 if (PQgetResult(conn) != NULL)
2210 pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2211 classname);
2213 destroyPQExpBuffer(q);
2214 return 1;
2218 * Dump table data using INSERT commands.
2220 * Caution: when we restore from an archive file direct to database, the
2221 * INSERT commands emitted by this function have to be parsed by
2222 * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2223 * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2225 static int
2226 dumpTableData_insert(Archive *fout, const void *dcontext)
2228 TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2229 TableInfo *tbinfo = tdinfo->tdtable;
2230 DumpOptions *dopt = fout->dopt;
2231 PQExpBuffer q = createPQExpBuffer();
2232 PQExpBuffer insertStmt = NULL;
2233 char *attgenerated;
2234 PGresult *res;
2235 int nfields,
2237 int rows_per_statement = dopt->dump_inserts;
2238 int rows_this_statement = 0;
2241 * If we're going to emit INSERTs with column names, the most efficient
2242 * way to deal with generated columns is to exclude them entirely. For
2243 * INSERTs without column names, we have to emit DEFAULT rather than the
2244 * actual column value --- but we can save a few cycles by fetching nulls
2245 * rather than the uninteresting-to-us value.
2247 attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2248 appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2249 nfields = 0;
2250 for (i = 0; i < tbinfo->numatts; i++)
2252 if (tbinfo->attisdropped[i])
2253 continue;
2254 if (tbinfo->attgenerated[i] && dopt->column_inserts)
2255 continue;
2256 if (nfields > 0)
2257 appendPQExpBufferStr(q, ", ");
2258 if (tbinfo->attgenerated[i])
2259 appendPQExpBufferStr(q, "NULL");
2260 else
2261 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2262 attgenerated[nfields] = tbinfo->attgenerated[i];
2263 nfields++;
2265 /* Servers before 9.4 will complain about zero-column SELECT */
2266 if (nfields == 0)
2267 appendPQExpBufferStr(q, "NULL");
2268 appendPQExpBuffer(q, " FROM ONLY %s",
2269 fmtQualifiedDumpable(tbinfo));
2270 if (tdinfo->filtercond)
2271 appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2273 ExecuteSqlStatement(fout, q->data);
2275 while (1)
2277 res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2278 PGRES_TUPLES_OK);
2280 /* cross-check field count, allowing for dummy NULL if any */
2281 if (nfields != PQnfields(res) &&
2282 !(nfields == 0 && PQnfields(res) == 1))
2283 pg_fatal("wrong number of fields retrieved from table \"%s\"",
2284 tbinfo->dobj.name);
2287 * First time through, we build as much of the INSERT statement as
2288 * possible in "insertStmt", which we can then just print for each
2289 * statement. If the table happens to have zero dumpable columns then
2290 * this will be a complete statement, otherwise it will end in
2291 * "VALUES" and be ready to have the row's column values printed.
2293 if (insertStmt == NULL)
2295 TableInfo *targettab;
2297 insertStmt = createPQExpBuffer();
2300 * When load-via-partition-root is set or forced, get the root
2301 * table name for the partition table, so that we can reload data
2302 * through the root table.
2304 if (tbinfo->ispartition &&
2305 (dopt->load_via_partition_root ||
2306 forcePartitionRootLoad(tbinfo)))
2307 targettab = getRootTableInfo(tbinfo);
2308 else
2309 targettab = tbinfo;
2311 appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2312 fmtQualifiedDumpable(targettab));
2314 /* corner case for zero-column table */
2315 if (nfields == 0)
2317 appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2319 else
2321 /* append the list of column names if required */
2322 if (dopt->column_inserts)
2324 appendPQExpBufferChar(insertStmt, '(');
2325 for (int field = 0; field < nfields; field++)
2327 if (field > 0)
2328 appendPQExpBufferStr(insertStmt, ", ");
2329 appendPQExpBufferStr(insertStmt,
2330 fmtId(PQfname(res, field)));
2332 appendPQExpBufferStr(insertStmt, ") ");
2335 if (tbinfo->needs_override)
2336 appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2338 appendPQExpBufferStr(insertStmt, "VALUES");
2342 for (int tuple = 0; tuple < PQntuples(res); tuple++)
2344 /* Write the INSERT if not in the middle of a multi-row INSERT. */
2345 if (rows_this_statement == 0)
2346 archputs(insertStmt->data, fout);
2349 * If it is zero-column table then we've already written the
2350 * complete statement, which will mean we've disobeyed
2351 * --rows-per-insert when it's set greater than 1. We do support
2352 * a way to make this multi-row with: SELECT UNION ALL SELECT
2353 * UNION ALL ... but that's non-standard so we should avoid it
2354 * given that using INSERTs is mostly only ever needed for
2355 * cross-database exports.
2357 if (nfields == 0)
2358 continue;
2360 /* Emit a row heading */
2361 if (rows_per_statement == 1)
2362 archputs(" (", fout);
2363 else if (rows_this_statement > 0)
2364 archputs(",\n\t(", fout);
2365 else
2366 archputs("\n\t(", fout);
2368 for (int field = 0; field < nfields; field++)
2370 if (field > 0)
2371 archputs(", ", fout);
2372 if (attgenerated[field])
2374 archputs("DEFAULT", fout);
2375 continue;
2377 if (PQgetisnull(res, tuple, field))
2379 archputs("NULL", fout);
2380 continue;
2383 /* XXX This code is partially duplicated in ruleutils.c */
2384 switch (PQftype(res, field))
2386 case INT2OID:
2387 case INT4OID:
2388 case INT8OID:
2389 case OIDOID:
2390 case FLOAT4OID:
2391 case FLOAT8OID:
2392 case NUMERICOID:
2395 * These types are printed without quotes unless
2396 * they contain values that aren't accepted by the
2397 * scanner unquoted (e.g., 'NaN'). Note that
2398 * strtod() and friends might accept NaN, so we
2399 * can't use that to test.
2401 * In reality we only need to defend against
2402 * infinity and NaN, so we need not get too crazy
2403 * about pattern matching here.
2405 const char *s = PQgetvalue(res, tuple, field);
2407 if (strspn(s, "0123456789 +-eE.") == strlen(s))
2408 archputs(s, fout);
2409 else
2410 archprintf(fout, "'%s'", s);
2412 break;
2414 case BITOID:
2415 case VARBITOID:
2416 archprintf(fout, "B'%s'",
2417 PQgetvalue(res, tuple, field));
2418 break;
2420 case BOOLOID:
2421 if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2422 archputs("true", fout);
2423 else
2424 archputs("false", fout);
2425 break;
2427 default:
2428 /* All other types are printed as string literals. */
2429 resetPQExpBuffer(q);
2430 appendStringLiteralAH(q,
2431 PQgetvalue(res, tuple, field),
2432 fout);
2433 archputs(q->data, fout);
2434 break;
2438 /* Terminate the row ... */
2439 archputs(")", fout);
2441 /* ... and the statement, if the target no. of rows is reached */
2442 if (++rows_this_statement >= rows_per_statement)
2444 if (dopt->do_nothing)
2445 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2446 else
2447 archputs(";\n", fout);
2448 /* Reset the row counter */
2449 rows_this_statement = 0;
2453 if (PQntuples(res) <= 0)
2455 PQclear(res);
2456 break;
2458 PQclear(res);
2461 /* Terminate any statements that didn't make the row count. */
2462 if (rows_this_statement > 0)
2464 if (dopt->do_nothing)
2465 archputs(" ON CONFLICT DO NOTHING;\n", fout);
2466 else
2467 archputs(";\n", fout);
2470 archputs("\n\n", fout);
2472 ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2474 destroyPQExpBuffer(q);
2475 if (insertStmt != NULL)
2476 destroyPQExpBuffer(insertStmt);
2477 free(attgenerated);
2479 return 1;
2483 * getRootTableInfo:
2484 * get the root TableInfo for the given partition table.
2486 static TableInfo *
2487 getRootTableInfo(const TableInfo *tbinfo)
2489 TableInfo *parentTbinfo;
2491 Assert(tbinfo->ispartition);
2492 Assert(tbinfo->numParents == 1);
2494 parentTbinfo = tbinfo->parents[0];
2495 while (parentTbinfo->ispartition)
2497 Assert(parentTbinfo->numParents == 1);
2498 parentTbinfo = parentTbinfo->parents[0];
2501 return parentTbinfo;
2505 * forcePartitionRootLoad
2506 * Check if we must force load_via_partition_root for this partition.
2508 * This is required if any level of ancestral partitioned table has an
2509 * unsafe partitioning scheme.
2511 static bool
2512 forcePartitionRootLoad(const TableInfo *tbinfo)
2514 TableInfo *parentTbinfo;
2516 Assert(tbinfo->ispartition);
2517 Assert(tbinfo->numParents == 1);
2519 parentTbinfo = tbinfo->parents[0];
2520 if (parentTbinfo->unsafe_partitions)
2521 return true;
2522 while (parentTbinfo->ispartition)
2524 Assert(parentTbinfo->numParents == 1);
2525 parentTbinfo = parentTbinfo->parents[0];
2526 if (parentTbinfo->unsafe_partitions)
2527 return true;
2530 return false;
2534 * dumpTableData -
2535 * dump the contents of a single table
2537 * Actually, this just makes an ArchiveEntry for the table contents.
2539 static void
2540 dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2542 DumpOptions *dopt = fout->dopt;
2543 TableInfo *tbinfo = tdinfo->tdtable;
2544 PQExpBuffer copyBuf = createPQExpBuffer();
2545 PQExpBuffer clistBuf = createPQExpBuffer();
2546 DataDumperPtr dumpFn;
2547 char *tdDefn = NULL;
2548 char *copyStmt;
2549 const char *copyFrom;
2551 /* We had better have loaded per-column details about this table */
2552 Assert(tbinfo->interesting);
2555 * When load-via-partition-root is set or forced, get the root table name
2556 * for the partition table, so that we can reload data through the root
2557 * table. Then construct a comment to be inserted into the TOC entry's
2558 * defn field, so that such cases can be identified reliably.
2560 if (tbinfo->ispartition &&
2561 (dopt->load_via_partition_root ||
2562 forcePartitionRootLoad(tbinfo)))
2564 TableInfo *parentTbinfo;
2566 parentTbinfo = getRootTableInfo(tbinfo);
2567 copyFrom = fmtQualifiedDumpable(parentTbinfo);
2568 printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2569 copyFrom);
2570 tdDefn = pg_strdup(copyBuf->data);
2572 else
2573 copyFrom = fmtQualifiedDumpable(tbinfo);
2575 if (dopt->dump_inserts == 0)
2577 /* Dump/restore using COPY */
2578 dumpFn = dumpTableData_copy;
2579 /* must use 2 steps here 'cause fmtId is nonreentrant */
2580 printfPQExpBuffer(copyBuf, "COPY %s ",
2581 copyFrom);
2582 appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2583 fmtCopyColumnList(tbinfo, clistBuf));
2584 copyStmt = copyBuf->data;
2586 else
2588 /* Restore using INSERT */
2589 dumpFn = dumpTableData_insert;
2590 copyStmt = NULL;
2594 * Note: although the TableDataInfo is a full DumpableObject, we treat its
2595 * dependency on its table as "special" and pass it to ArchiveEntry now.
2596 * See comments for BuildArchiveDependencies.
2598 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2600 TocEntry *te;
2602 te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2603 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2604 .namespace = tbinfo->dobj.namespace->dobj.name,
2605 .owner = tbinfo->rolname,
2606 .description = "TABLE DATA",
2607 .section = SECTION_DATA,
2608 .createStmt = tdDefn,
2609 .copyStmt = copyStmt,
2610 .deps = &(tbinfo->dobj.dumpId),
2611 .nDeps = 1,
2612 .dumpFn = dumpFn,
2613 .dumpArg = tdinfo));
2616 * Set the TocEntry's dataLength in case we are doing a parallel dump
2617 * and want to order dump jobs by table size. We choose to measure
2618 * dataLength in table pages (including TOAST pages) during dump, so
2619 * no scaling is needed.
2621 * However, relpages is declared as "integer" in pg_class, and hence
2622 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2623 * Cast so that we get the right interpretation of table sizes
2624 * exceeding INT_MAX pages.
2626 te->dataLength = (BlockNumber) tbinfo->relpages;
2627 te->dataLength += (BlockNumber) tbinfo->toastpages;
2630 * If pgoff_t is only 32 bits wide, the above refinement is useless,
2631 * and instead we'd better worry about integer overflow. Clamp to
2632 * INT_MAX if the correct result exceeds that.
2634 if (sizeof(te->dataLength) == 4 &&
2635 (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2636 te->dataLength < 0))
2637 te->dataLength = INT_MAX;
2640 destroyPQExpBuffer(copyBuf);
2641 destroyPQExpBuffer(clistBuf);
2645 * refreshMatViewData -
2646 * load or refresh the contents of a single materialized view
2648 * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2649 * statement.
2651 static void
2652 refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2654 TableInfo *tbinfo = tdinfo->tdtable;
2655 PQExpBuffer q;
2657 /* If the materialized view is not flagged as populated, skip this. */
2658 if (!tbinfo->relispopulated)
2659 return;
2661 q = createPQExpBuffer();
2663 appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2664 fmtQualifiedDumpable(tbinfo));
2666 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2667 ArchiveEntry(fout,
2668 tdinfo->dobj.catId, /* catalog ID */
2669 tdinfo->dobj.dumpId, /* dump ID */
2670 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2671 .namespace = tbinfo->dobj.namespace->dobj.name,
2672 .owner = tbinfo->rolname,
2673 .description = "MATERIALIZED VIEW DATA",
2674 .section = SECTION_POST_DATA,
2675 .createStmt = q->data,
2676 .deps = tdinfo->dobj.dependencies,
2677 .nDeps = tdinfo->dobj.nDeps));
2679 destroyPQExpBuffer(q);
2683 * getTableData -
2684 * set up dumpable objects representing the contents of tables
2686 static void
2687 getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2689 int i;
2691 for (i = 0; i < numTables; i++)
2693 if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2694 (!relkind || tblinfo[i].relkind == relkind))
2695 makeTableDataInfo(dopt, &(tblinfo[i]));
2700 * Make a dumpable object for the data of this specific table
2702 * Note: we make a TableDataInfo if and only if we are going to dump the
2703 * table data; the "dump" field in such objects isn't very interesting.
2705 static void
2706 makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2708 TableDataInfo *tdinfo;
2711 * Nothing to do if we already decided to dump the table. This will
2712 * happen for "config" tables.
2714 if (tbinfo->dataObj != NULL)
2715 return;
2717 /* Skip VIEWs (no data to dump) */
2718 if (tbinfo->relkind == RELKIND_VIEW)
2719 return;
2720 /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2721 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2722 (foreign_servers_include_oids.head == NULL ||
2723 !simple_oid_list_member(&foreign_servers_include_oids,
2724 tbinfo->foreign_server)))
2725 return;
2726 /* Skip partitioned tables (data in partitions) */
2727 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2728 return;
2730 /* Don't dump data in unlogged tables, if so requested */
2731 if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2732 dopt->no_unlogged_table_data)
2733 return;
2735 /* Check that the data is not explicitly excluded */
2736 if (simple_oid_list_member(&tabledata_exclude_oids,
2737 tbinfo->dobj.catId.oid))
2738 return;
2740 /* OK, let's dump it */
2741 tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2743 if (tbinfo->relkind == RELKIND_MATVIEW)
2744 tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2745 else if (tbinfo->relkind == RELKIND_SEQUENCE)
2746 tdinfo->dobj.objType = DO_SEQUENCE_SET;
2747 else
2748 tdinfo->dobj.objType = DO_TABLE_DATA;
2751 * Note: use tableoid 0 so that this object won't be mistaken for
2752 * something that pg_depend entries apply to.
2754 tdinfo->dobj.catId.tableoid = 0;
2755 tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2756 AssignDumpId(&tdinfo->dobj);
2757 tdinfo->dobj.name = tbinfo->dobj.name;
2758 tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2759 tdinfo->tdtable = tbinfo;
2760 tdinfo->filtercond = NULL; /* might get set later */
2761 addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2763 /* A TableDataInfo contains data, of course */
2764 tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
2766 tbinfo->dataObj = tdinfo;
2768 /* Make sure that we'll collect per-column info for this table. */
2769 tbinfo->interesting = true;
2773 * The refresh for a materialized view must be dependent on the refresh for
2774 * any materialized view that this one is dependent on.
2776 * This must be called after all the objects are created, but before they are
2777 * sorted.
2779 static void
2780 buildMatViewRefreshDependencies(Archive *fout)
2782 PQExpBuffer query;
2783 PGresult *res;
2784 int ntups,
2786 int i_classid,
2787 i_objid,
2788 i_refobjid;
2790 /* No Mat Views before 9.3. */
2791 if (fout->remoteVersion < 90300)
2792 return;
2794 query = createPQExpBuffer();
2796 appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2797 "( "
2798 "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2799 "FROM pg_depend d1 "
2800 "JOIN pg_class c1 ON c1.oid = d1.objid "
2801 "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2802 " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2803 "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2804 "AND d2.objid = r1.oid "
2805 "AND d2.refobjid <> d1.objid "
2806 "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2807 "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2808 CppAsString2(RELKIND_VIEW) ") "
2809 "WHERE d1.classid = 'pg_class'::regclass "
2810 "UNION "
2811 "SELECT w.objid, d3.refobjid, c3.relkind "
2812 "FROM w "
2813 "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2814 "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2815 "AND d3.objid = r3.oid "
2816 "AND d3.refobjid <> w.refobjid "
2817 "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2818 "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2819 CppAsString2(RELKIND_VIEW) ") "
2820 ") "
2821 "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2822 "FROM w "
2823 "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2825 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2827 ntups = PQntuples(res);
2829 i_classid = PQfnumber(res, "classid");
2830 i_objid = PQfnumber(res, "objid");
2831 i_refobjid = PQfnumber(res, "refobjid");
2833 for (i = 0; i < ntups; i++)
2835 CatalogId objId;
2836 CatalogId refobjId;
2837 DumpableObject *dobj;
2838 DumpableObject *refdobj;
2839 TableInfo *tbinfo;
2840 TableInfo *reftbinfo;
2842 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2843 objId.oid = atooid(PQgetvalue(res, i, i_objid));
2844 refobjId.tableoid = objId.tableoid;
2845 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
2847 dobj = findObjectByCatalogId(objId);
2848 if (dobj == NULL)
2849 continue;
2851 Assert(dobj->objType == DO_TABLE);
2852 tbinfo = (TableInfo *) dobj;
2853 Assert(tbinfo->relkind == RELKIND_MATVIEW);
2854 dobj = (DumpableObject *) tbinfo->dataObj;
2855 if (dobj == NULL)
2856 continue;
2857 Assert(dobj->objType == DO_REFRESH_MATVIEW);
2859 refdobj = findObjectByCatalogId(refobjId);
2860 if (refdobj == NULL)
2861 continue;
2863 Assert(refdobj->objType == DO_TABLE);
2864 reftbinfo = (TableInfo *) refdobj;
2865 Assert(reftbinfo->relkind == RELKIND_MATVIEW);
2866 refdobj = (DumpableObject *) reftbinfo->dataObj;
2867 if (refdobj == NULL)
2868 continue;
2869 Assert(refdobj->objType == DO_REFRESH_MATVIEW);
2871 addObjectDependency(dobj, refdobj->dumpId);
2873 if (!reftbinfo->relispopulated)
2874 tbinfo->relispopulated = false;
2877 PQclear(res);
2879 destroyPQExpBuffer(query);
2883 * getTableDataFKConstraints -
2884 * add dump-order dependencies reflecting foreign key constraints
2886 * This code is executed only in a data-only dump --- in schema+data dumps
2887 * we handle foreign key issues by not creating the FK constraints until
2888 * after the data is loaded. In a data-only dump, however, we want to
2889 * order the table data objects in such a way that a table's referenced
2890 * tables are restored first. (In the presence of circular references or
2891 * self-references this may be impossible; we'll detect and complain about
2892 * that during the dependency sorting step.)
2894 static void
2895 getTableDataFKConstraints(void)
2897 DumpableObject **dobjs;
2898 int numObjs;
2899 int i;
2901 /* Search through all the dumpable objects for FK constraints */
2902 getDumpableObjects(&dobjs, &numObjs);
2903 for (i = 0; i < numObjs; i++)
2905 if (dobjs[i]->objType == DO_FK_CONSTRAINT)
2907 ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
2908 TableInfo *ftable;
2910 /* Not interesting unless both tables are to be dumped */
2911 if (cinfo->contable == NULL ||
2912 cinfo->contable->dataObj == NULL)
2913 continue;
2914 ftable = findTableByOid(cinfo->confrelid);
2915 if (ftable == NULL ||
2916 ftable->dataObj == NULL)
2917 continue;
2920 * Okay, make referencing table's TABLE_DATA object depend on the
2921 * referenced table's TABLE_DATA object.
2923 addObjectDependency(&cinfo->contable->dataObj->dobj,
2924 ftable->dataObj->dobj.dumpId);
2927 free(dobjs);
2932 * dumpDatabase:
2933 * dump the database definition
2935 static void
2936 dumpDatabase(Archive *fout)
2938 DumpOptions *dopt = fout->dopt;
2939 PQExpBuffer dbQry = createPQExpBuffer();
2940 PQExpBuffer delQry = createPQExpBuffer();
2941 PQExpBuffer creaQry = createPQExpBuffer();
2942 PQExpBuffer labelq = createPQExpBuffer();
2943 PGconn *conn = GetConnection(fout);
2944 PGresult *res;
2945 int i_tableoid,
2946 i_oid,
2947 i_datname,
2948 i_datdba,
2949 i_encoding,
2950 i_datlocprovider,
2951 i_collate,
2952 i_ctype,
2953 i_daticulocale,
2954 i_daticurules,
2955 i_frozenxid,
2956 i_minmxid,
2957 i_datacl,
2958 i_acldefault,
2959 i_datistemplate,
2960 i_datconnlimit,
2961 i_datcollversion,
2962 i_tablespace;
2963 CatalogId dbCatId;
2964 DumpId dbDumpId;
2965 DumpableAcl dbdacl;
2966 const char *datname,
2967 *dba,
2968 *encoding,
2969 *datlocprovider,
2970 *collate,
2971 *ctype,
2972 *iculocale,
2973 *icurules,
2974 *datistemplate,
2975 *datconnlimit,
2976 *tablespace;
2977 uint32 frozenxid,
2978 minmxid;
2979 char *qdatname;
2981 pg_log_info("saving database definition");
2984 * Fetch the database-level properties for this database.
2986 appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
2987 "datdba, "
2988 "pg_encoding_to_char(encoding) AS encoding, "
2989 "datcollate, datctype, datfrozenxid, "
2990 "datacl, acldefault('d', datdba) AS acldefault, "
2991 "datistemplate, datconnlimit, ");
2992 if (fout->remoteVersion >= 90300)
2993 appendPQExpBufferStr(dbQry, "datminmxid, ");
2994 else
2995 appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
2996 if (fout->remoteVersion >= 150000)
2997 appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale, datcollversion, ");
2998 else
2999 appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS daticulocale, NULL AS datcollversion, ");
3000 if (fout->remoteVersion >= 160000)
3001 appendPQExpBufferStr(dbQry, "daticurules, ");
3002 else
3003 appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3004 appendPQExpBufferStr(dbQry,
3005 "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3006 "shobj_description(oid, 'pg_database') AS description "
3007 "FROM pg_database "
3008 "WHERE datname = current_database()");
3010 res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3012 i_tableoid = PQfnumber(res, "tableoid");
3013 i_oid = PQfnumber(res, "oid");
3014 i_datname = PQfnumber(res, "datname");
3015 i_datdba = PQfnumber(res, "datdba");
3016 i_encoding = PQfnumber(res, "encoding");
3017 i_datlocprovider = PQfnumber(res, "datlocprovider");
3018 i_collate = PQfnumber(res, "datcollate");
3019 i_ctype = PQfnumber(res, "datctype");
3020 i_daticulocale = PQfnumber(res, "daticulocale");
3021 i_daticurules = PQfnumber(res, "daticurules");
3022 i_frozenxid = PQfnumber(res, "datfrozenxid");
3023 i_minmxid = PQfnumber(res, "datminmxid");
3024 i_datacl = PQfnumber(res, "datacl");
3025 i_acldefault = PQfnumber(res, "acldefault");
3026 i_datistemplate = PQfnumber(res, "datistemplate");
3027 i_datconnlimit = PQfnumber(res, "datconnlimit");
3028 i_datcollversion = PQfnumber(res, "datcollversion");
3029 i_tablespace = PQfnumber(res, "tablespace");
3031 dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3032 dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3033 datname = PQgetvalue(res, 0, i_datname);
3034 dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3035 encoding = PQgetvalue(res, 0, i_encoding);
3036 datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3037 collate = PQgetvalue(res, 0, i_collate);
3038 ctype = PQgetvalue(res, 0, i_ctype);
3039 if (!PQgetisnull(res, 0, i_daticulocale))
3040 iculocale = PQgetvalue(res, 0, i_daticulocale);
3041 else
3042 iculocale = NULL;
3043 if (!PQgetisnull(res, 0, i_daticurules))
3044 icurules = PQgetvalue(res, 0, i_daticurules);
3045 else
3046 icurules = NULL;
3047 frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3048 minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3049 dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3050 dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3051 datistemplate = PQgetvalue(res, 0, i_datistemplate);
3052 datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3053 tablespace = PQgetvalue(res, 0, i_tablespace);
3055 qdatname = pg_strdup(fmtId(datname));
3058 * Prepare the CREATE DATABASE command. We must specify OID (if we want
3059 * to preserve that), as well as the encoding, locale, and tablespace
3060 * since those can't be altered later. Other DB properties are left to
3061 * the DATABASE PROPERTIES entry, so that they can be applied after
3062 * reconnecting to the target DB.
3064 if (dopt->binary_upgrade)
3066 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0 OID = %u",
3067 qdatname, dbCatId.oid);
3069 else
3071 appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3072 qdatname);
3074 if (strlen(encoding) > 0)
3076 appendPQExpBufferStr(creaQry, " ENCODING = ");
3077 appendStringLiteralAH(creaQry, encoding, fout);
3080 appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3081 if (datlocprovider[0] == 'c')
3082 appendPQExpBufferStr(creaQry, "libc");
3083 else if (datlocprovider[0] == 'i')
3084 appendPQExpBufferStr(creaQry, "icu");
3085 else
3086 pg_fatal("unrecognized locale provider: %s",
3087 datlocprovider);
3089 if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3091 appendPQExpBufferStr(creaQry, " LOCALE = ");
3092 appendStringLiteralAH(creaQry, collate, fout);
3094 else
3096 if (strlen(collate) > 0)
3098 appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3099 appendStringLiteralAH(creaQry, collate, fout);
3101 if (strlen(ctype) > 0)
3103 appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3104 appendStringLiteralAH(creaQry, ctype, fout);
3107 if (iculocale)
3109 appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3110 appendStringLiteralAH(creaQry, iculocale, fout);
3112 if (icurules)
3114 appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3115 appendStringLiteralAH(creaQry, icurules, fout);
3119 * For binary upgrade, carry over the collation version. For normal
3120 * dump/restore, omit the version, so that it is computed upon restore.
3122 if (dopt->binary_upgrade)
3124 if (!PQgetisnull(res, 0, i_datcollversion))
3126 appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3127 appendStringLiteralAH(creaQry,
3128 PQgetvalue(res, 0, i_datcollversion),
3129 fout);
3134 * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3135 * thing; the decision whether to specify a tablespace should be left till
3136 * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3137 * label the DATABASE entry with the tablespace and let the normal
3138 * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3139 * attention to default_tablespace, so that won't work.
3141 if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3142 !dopt->outputNoTablespaces)
3143 appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3144 fmtId(tablespace));
3145 appendPQExpBufferStr(creaQry, ";\n");
3147 appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3148 qdatname);
3150 dbDumpId = createDumpId();
3152 ArchiveEntry(fout,
3153 dbCatId, /* catalog ID */
3154 dbDumpId, /* dump ID */
3155 ARCHIVE_OPTS(.tag = datname,
3156 .owner = dba,
3157 .description = "DATABASE",
3158 .section = SECTION_PRE_DATA,
3159 .createStmt = creaQry->data,
3160 .dropStmt = delQry->data));
3162 /* Compute correct tag for archive entry */
3163 appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3165 /* Dump DB comment if any */
3168 * 8.2 and up keep comments on shared objects in a shared table, so we
3169 * cannot use the dumpComment() code used for other database objects.
3170 * Be careful that the ArchiveEntry parameters match that function.
3172 char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3174 if (comment && *comment && !dopt->no_comments)
3176 resetPQExpBuffer(dbQry);
3179 * Generates warning when loaded into a differently-named
3180 * database.
3182 appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3183 appendStringLiteralAH(dbQry, comment, fout);
3184 appendPQExpBufferStr(dbQry, ";\n");
3186 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3187 ARCHIVE_OPTS(.tag = labelq->data,
3188 .owner = dba,
3189 .description = "COMMENT",
3190 .section = SECTION_NONE,
3191 .createStmt = dbQry->data,
3192 .deps = &dbDumpId,
3193 .nDeps = 1));
3197 /* Dump DB security label, if enabled */
3198 if (!dopt->no_security_labels)
3200 PGresult *shres;
3201 PQExpBuffer seclabelQry;
3203 seclabelQry = createPQExpBuffer();
3205 buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3206 shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3207 resetPQExpBuffer(seclabelQry);
3208 emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3209 if (seclabelQry->len > 0)
3210 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3211 ARCHIVE_OPTS(.tag = labelq->data,
3212 .owner = dba,
3213 .description = "SECURITY LABEL",
3214 .section = SECTION_NONE,
3215 .createStmt = seclabelQry->data,
3216 .deps = &dbDumpId,
3217 .nDeps = 1));
3218 destroyPQExpBuffer(seclabelQry);
3219 PQclear(shres);
3223 * Dump ACL if any. Note that we do not support initial privileges
3224 * (pg_init_privs) on databases.
3226 dbdacl.privtype = 0;
3227 dbdacl.initprivs = NULL;
3229 dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3230 qdatname, NULL, NULL,
3231 dba, &dbdacl);
3234 * Now construct a DATABASE PROPERTIES archive entry to restore any
3235 * non-default database-level properties. (The reason this must be
3236 * separate is that we cannot put any additional commands into the TOC
3237 * entry that has CREATE DATABASE. pg_restore would execute such a group
3238 * in an implicit transaction block, and the backend won't allow CREATE
3239 * DATABASE in that context.)
3241 resetPQExpBuffer(creaQry);
3242 resetPQExpBuffer(delQry);
3244 if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3245 appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3246 qdatname, datconnlimit);
3248 if (strcmp(datistemplate, "t") == 0)
3250 appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3251 qdatname);
3254 * The backend won't accept DROP DATABASE on a template database. We
3255 * can deal with that by removing the template marking before the DROP
3256 * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3257 * since no such command is currently supported, fake it with a direct
3258 * UPDATE on pg_database.
3260 appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3261 "SET datistemplate = false WHERE datname = ");
3262 appendStringLiteralAH(delQry, datname, fout);
3263 appendPQExpBufferStr(delQry, ";\n");
3267 * We do not restore pg_database.dathasloginevt because it is set
3268 * automatically on login event trigger creation.
3271 /* Add database-specific SET options */
3272 dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3275 * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3276 * entry, too, for lack of a better place.
3278 if (dopt->binary_upgrade)
3280 appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3281 appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3282 "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3283 "WHERE datname = ",
3284 frozenxid, minmxid);
3285 appendStringLiteralAH(creaQry, datname, fout);
3286 appendPQExpBufferStr(creaQry, ";\n");
3289 if (creaQry->len > 0)
3290 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3291 ARCHIVE_OPTS(.tag = datname,
3292 .owner = dba,
3293 .description = "DATABASE PROPERTIES",
3294 .section = SECTION_PRE_DATA,
3295 .createStmt = creaQry->data,
3296 .dropStmt = delQry->data,
3297 .deps = &dbDumpId));
3300 * pg_largeobject comes from the old system intact, so set its
3301 * relfrozenxids, relminmxids and relfilenode.
3303 if (dopt->binary_upgrade)
3305 PGresult *lo_res;
3306 PQExpBuffer loFrozenQry = createPQExpBuffer();
3307 PQExpBuffer loOutQry = createPQExpBuffer();
3308 PQExpBuffer loHorizonQry = createPQExpBuffer();
3309 int ii_relfrozenxid,
3310 ii_relfilenode,
3311 ii_oid,
3312 ii_relminmxid;
3315 * pg_largeobject
3317 if (fout->remoteVersion >= 90300)
3318 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3319 "FROM pg_catalog.pg_class\n"
3320 "WHERE oid IN (%u, %u);\n",
3321 LargeObjectRelationId, LargeObjectLOidPNIndexId);
3322 else
3323 appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3324 "FROM pg_catalog.pg_class\n"
3325 "WHERE oid IN (%u, %u);\n",
3326 LargeObjectRelationId, LargeObjectLOidPNIndexId);
3328 lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3330 ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3331 ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3332 ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3333 ii_oid = PQfnumber(lo_res, "oid");
3335 appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3336 appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3337 for (int i = 0; i < PQntuples(lo_res); ++i)
3339 Oid oid;
3340 RelFileNumber relfilenumber;
3342 appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3343 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3344 "WHERE oid = %u;\n",
3345 atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3346 atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3347 atooid(PQgetvalue(lo_res, i, ii_oid)));
3349 oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3350 relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3352 if (oid == LargeObjectRelationId)
3353 appendPQExpBuffer(loOutQry,
3354 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3355 relfilenumber);
3356 else if (oid == LargeObjectLOidPNIndexId)
3357 appendPQExpBuffer(loOutQry,
3358 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3359 relfilenumber);
3362 appendPQExpBufferStr(loOutQry,
3363 "TRUNCATE pg_catalog.pg_largeobject;\n");
3364 appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3366 ArchiveEntry(fout, nilCatalogId, createDumpId(),
3367 ARCHIVE_OPTS(.tag = "pg_largeobject",
3368 .description = "pg_largeobject",
3369 .section = SECTION_PRE_DATA,
3370 .createStmt = loOutQry->data));
3372 PQclear(lo_res);
3374 destroyPQExpBuffer(loFrozenQry);
3375 destroyPQExpBuffer(loHorizonQry);
3376 destroyPQExpBuffer(loOutQry);
3379 PQclear(res);
3381 free(qdatname);
3382 destroyPQExpBuffer(dbQry);
3383 destroyPQExpBuffer(delQry);
3384 destroyPQExpBuffer(creaQry);
3385 destroyPQExpBuffer(labelq);
3389 * Collect any database-specific or role-and-database-specific SET options
3390 * for this database, and append them to outbuf.
3392 static void
3393 dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3394 const char *dbname, Oid dboid)
3396 PGconn *conn = GetConnection(AH);
3397 PQExpBuffer buf = createPQExpBuffer();
3398 PGresult *res;
3400 /* First collect database-specific options */
3401 printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3402 "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3403 dboid);
3405 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3407 for (int i = 0; i < PQntuples(res); i++)
3408 makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3409 "DATABASE", dbname, NULL, NULL,
3410 outbuf);
3412 PQclear(res);
3414 /* Now look for role-and-database-specific options */
3415 printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3416 "FROM pg_db_role_setting s, pg_roles r "
3417 "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3418 dboid);
3420 res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3422 for (int i = 0; i < PQntuples(res); i++)
3423 makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3424 "ROLE", PQgetvalue(res, i, 0),
3425 "DATABASE", dbname,
3426 outbuf);
3428 PQclear(res);
3430 destroyPQExpBuffer(buf);
3434 * dumpEncoding: put the correct encoding into the archive
3436 static void
3437 dumpEncoding(Archive *AH)
3439 const char *encname = pg_encoding_to_char(AH->encoding);
3440 PQExpBuffer qry = createPQExpBuffer();
3442 pg_log_info("saving encoding = %s", encname);
3444 appendPQExpBufferStr(qry, "SET client_encoding = ");
3445 appendStringLiteralAH(qry, encname, AH);
3446 appendPQExpBufferStr(qry, ";\n");
3448 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3449 ARCHIVE_OPTS(.tag = "ENCODING",
3450 .description = "ENCODING",
3451 .section = SECTION_PRE_DATA,
3452 .createStmt = qry->data));
3454 destroyPQExpBuffer(qry);
3459 * dumpStdStrings: put the correct escape string behavior into the archive
3461 static void
3462 dumpStdStrings(Archive *AH)
3464 const char *stdstrings = AH->std_strings ? "on" : "off";
3465 PQExpBuffer qry = createPQExpBuffer();
3467 pg_log_info("saving standard_conforming_strings = %s",
3468 stdstrings);
3470 appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3471 stdstrings);
3473 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3474 ARCHIVE_OPTS(.tag = "STDSTRINGS",
3475 .description = "STDSTRINGS",
3476 .section = SECTION_PRE_DATA,
3477 .createStmt = qry->data));
3479 destroyPQExpBuffer(qry);
3483 * dumpSearchPath: record the active search_path in the archive
3485 static void
3486 dumpSearchPath(Archive *AH)
3488 PQExpBuffer qry = createPQExpBuffer();
3489 PQExpBuffer path = createPQExpBuffer();
3490 PGresult *res;
3491 char **schemanames = NULL;
3492 int nschemanames = 0;
3493 int i;
3496 * We use the result of current_schemas(), not the search_path GUC,
3497 * because that might contain wildcards such as "$user", which won't
3498 * necessarily have the same value during restore. Also, this way avoids
3499 * listing schemas that may appear in search_path but not actually exist,
3500 * which seems like a prudent exclusion.
3502 res = ExecuteSqlQueryForSingleRow(AH,
3503 "SELECT pg_catalog.current_schemas(false)");
3505 if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3506 pg_fatal("could not parse result of current_schemas()");
3509 * We use set_config(), not a simple "SET search_path" command, because
3510 * the latter has less-clean behavior if the search path is empty. While
3511 * that's likely to get fixed at some point, it seems like a good idea to
3512 * be as backwards-compatible as possible in what we put into archives.
3514 for (i = 0; i < nschemanames; i++)
3516 if (i > 0)
3517 appendPQExpBufferStr(path, ", ");
3518 appendPQExpBufferStr(path, fmtId(schemanames[i]));
3521 appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3522 appendStringLiteralAH(qry, path->data, AH);
3523 appendPQExpBufferStr(qry, ", false);\n");
3525 pg_log_info("saving search_path = %s", path->data);
3527 ArchiveEntry(AH, nilCatalogId, createDumpId(),
3528 ARCHIVE_OPTS(.tag = "SEARCHPATH",
3529 .description = "SEARCHPATH",
3530 .section = SECTION_PRE_DATA,
3531 .createStmt = qry->data));
3533 /* Also save it in AH->searchpath, in case we're doing plain text dump */
3534 AH->searchpath = pg_strdup(qry->data);
3536 free(schemanames);
3537 PQclear(res);
3538 destroyPQExpBuffer(qry);
3539 destroyPQExpBuffer(path);
3544 * getLOs:
3545 * Collect schema-level data about large objects
3547 static void
3548 getLOs(Archive *fout)
3550 DumpOptions *dopt = fout->dopt;
3551 PQExpBuffer loQry = createPQExpBuffer();
3552 LoInfo *loinfo;
3553 DumpableObject *lodata;
3554 PGresult *res;
3555 int ntups;
3556 int i;
3557 int i_oid;
3558 int i_lomowner;
3559 int i_lomacl;
3560 int i_acldefault;
3562 pg_log_info("reading large objects");
3564 /* Fetch LO OIDs, and owner/ACL data */
3565 appendPQExpBufferStr(loQry,
3566 "SELECT oid, lomowner, lomacl, "
3567 "acldefault('L', lomowner) AS acldefault "
3568 "FROM pg_largeobject_metadata");
3570 res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3572 i_oid = PQfnumber(res, "oid");
3573 i_lomowner = PQfnumber(res, "lomowner");
3574 i_lomacl = PQfnumber(res, "lomacl");
3575 i_acldefault = PQfnumber(res, "acldefault");
3577 ntups = PQntuples(res);
3580 * Each large object has its own "BLOB" archive entry.
3582 loinfo = (LoInfo *) pg_malloc(ntups * sizeof(LoInfo));
3584 for (i = 0; i < ntups; i++)
3586 loinfo[i].dobj.objType = DO_LARGE_OBJECT;
3587 loinfo[i].dobj.catId.tableoid = LargeObjectRelationId;
3588 loinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3589 AssignDumpId(&loinfo[i].dobj);
3591 loinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid));
3592 loinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lomacl));
3593 loinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3594 loinfo[i].dacl.privtype = 0;
3595 loinfo[i].dacl.initprivs = NULL;
3596 loinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_lomowner));
3598 /* LOs have data */
3599 loinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
3601 /* Mark whether LO has an ACL */
3602 if (!PQgetisnull(res, i, i_lomacl))
3603 loinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
3606 * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3607 * as it will be copied by pg_upgrade, which simply copies the
3608 * pg_largeobject table. We *do* however dump out anything but the
3609 * data, as pg_upgrade copies just pg_largeobject, but not
3610 * pg_largeobject_metadata, after the dump is restored.
3612 if (dopt->binary_upgrade)
3613 loinfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA;
3617 * If we have any large objects, a "BLOBS" archive entry is needed. This
3618 * is just a placeholder for sorting; it carries no data now.
3620 if (ntups > 0)
3622 lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3623 lodata->objType = DO_LARGE_OBJECT_DATA;
3624 lodata->catId = nilCatalogId;
3625 AssignDumpId(lodata);
3626 lodata->name = pg_strdup("BLOBS");
3627 lodata->components |= DUMP_COMPONENT_DATA;
3630 PQclear(res);
3631 destroyPQExpBuffer(loQry);
3635 * dumpLO
3637 * dump the definition (metadata) of the given large object
3639 static void
3640 dumpLO(Archive *fout, const LoInfo *loinfo)
3642 PQExpBuffer cquery = createPQExpBuffer();
3643 PQExpBuffer dquery = createPQExpBuffer();
3645 appendPQExpBuffer(cquery,
3646 "SELECT pg_catalog.lo_create('%s');\n",
3647 loinfo->dobj.name);
3649 appendPQExpBuffer(dquery,
3650 "SELECT pg_catalog.lo_unlink('%s');\n",
3651 loinfo->dobj.name);
3653 if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3654 ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3655 ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3656 .owner = loinfo->rolname,
3657 .description = "BLOB",
3658 .section = SECTION_PRE_DATA,
3659 .createStmt = cquery->data,
3660 .dropStmt = dquery->data));
3662 /* Dump comment if any */
3663 if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3664 dumpComment(fout, "LARGE OBJECT", loinfo->dobj.name,
3665 NULL, loinfo->rolname,
3666 loinfo->dobj.catId, 0, loinfo->dobj.dumpId);
3668 /* Dump security label if any */
3669 if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3670 dumpSecLabel(fout, "LARGE OBJECT", loinfo->dobj.name,
3671 NULL, loinfo->rolname,
3672 loinfo->dobj.catId, 0, loinfo->dobj.dumpId);
3674 /* Dump ACL if any */
3675 if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
3676 dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT",
3677 loinfo->dobj.name, NULL,
3678 NULL, loinfo->rolname, &loinfo->dacl);
3680 destroyPQExpBuffer(cquery);
3681 destroyPQExpBuffer(dquery);
3685 * dumpLOs:
3686 * dump the data contents of all large objects
3688 static int
3689 dumpLOs(Archive *fout, const void *arg)
3691 const char *loQry;
3692 const char *loFetchQry;
3693 PGconn *conn = GetConnection(fout);
3694 PGresult *res;
3695 char buf[LOBBUFSIZE];
3696 int ntups;
3697 int i;
3698 int cnt;
3700 pg_log_info("saving large objects");
3703 * Currently, we re-fetch all LO OIDs using a cursor. Consider scanning
3704 * the already-in-memory dumpable objects instead...
3706 loQry =
3707 "DECLARE looid CURSOR FOR "
3708 "SELECT oid FROM pg_largeobject_metadata ORDER BY 1";
3710 ExecuteSqlStatement(fout, loQry);
3712 /* Command to fetch from cursor */
3713 loFetchQry = "FETCH 1000 IN looid";
3717 /* Do a fetch */
3718 res = ExecuteSqlQuery(fout, loFetchQry, PGRES_TUPLES_OK);
3720 /* Process the tuples, if any */
3721 ntups = PQntuples(res);
3722 for (i = 0; i < ntups; i++)
3724 Oid loOid;
3725 int loFd;
3727 loOid = atooid(PQgetvalue(res, i, 0));
3728 /* Open the LO */
3729 loFd = lo_open(conn, loOid, INV_READ);
3730 if (loFd == -1)
3731 pg_fatal("could not open large object %u: %s",
3732 loOid, PQerrorMessage(conn));
3734 StartLO(fout, loOid);
3736 /* Now read it in chunks, sending data to archive */
3739 cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3740 if (cnt < 0)
3741 pg_fatal("error reading large object %u: %s",
3742 loOid, PQerrorMessage(conn));
3744 WriteData(fout, buf, cnt);
3745 } while (cnt > 0);
3747 lo_close(conn, loFd);
3749 EndLO(fout, loOid);
3752 PQclear(res);
3753 } while (ntups > 0);
3755 return 1;
3759 * getPolicies
3760 * get information about all RLS policies on dumpable tables.
3762 void
3763 getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3765 PQExpBuffer query;
3766 PQExpBuffer tbloids;
3767 PGresult *res;
3768 PolicyInfo *polinfo;
3769 int i_oid;
3770 int i_tableoid;
3771 int i_polrelid;
3772 int i_polname;
3773 int i_polcmd;
3774 int i_polpermissive;
3775 int i_polroles;
3776 int i_polqual;
3777 int i_polwithcheck;
3778 int i,
3780 ntups;
3782 /* No policies before 9.5 */
3783 if (fout->remoteVersion < 90500)
3784 return;
3786 query = createPQExpBuffer();
3787 tbloids = createPQExpBuffer();
3790 * Identify tables of interest, and check which ones have RLS enabled.
3792 appendPQExpBufferChar(tbloids, '{');
3793 for (i = 0; i < numTables; i++)
3795 TableInfo *tbinfo = &tblinfo[i];
3797 /* Ignore row security on tables not to be dumped */
3798 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
3799 continue;
3801 /* It can't have RLS or policies if it's not a table */
3802 if (tbinfo->relkind != RELKIND_RELATION &&
3803 tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
3804 continue;
3806 /* Add it to the list of table OIDs to be probed below */
3807 if (tbloids->len > 1) /* do we have more than the '{'? */
3808 appendPQExpBufferChar(tbloids, ',');
3809 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
3811 /* Is RLS enabled? (That's separate from whether it has policies) */
3812 if (tbinfo->rowsec)
3814 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
3817 * We represent RLS being enabled on a table by creating a
3818 * PolicyInfo object with null polname.
3820 * Note: use tableoid 0 so that this object won't be mistaken for
3821 * something that pg_depend entries apply to.
3823 polinfo = pg_malloc(sizeof(PolicyInfo));
3824 polinfo->dobj.objType = DO_POLICY;
3825 polinfo->dobj.catId.tableoid = 0;
3826 polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3827 AssignDumpId(&polinfo->dobj);
3828 polinfo->dobj.namespace = tbinfo->dobj.namespace;
3829 polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
3830 polinfo->poltable = tbinfo;
3831 polinfo->polname = NULL;
3832 polinfo->polcmd = '\0';
3833 polinfo->polpermissive = 0;
3834 polinfo->polroles = NULL;
3835 polinfo->polqual = NULL;
3836 polinfo->polwithcheck = NULL;
3839 appendPQExpBufferChar(tbloids, '}');
3842 * Now, read all RLS policies belonging to the tables of interest, and
3843 * create PolicyInfo objects for them. (Note that we must filter the
3844 * results server-side not locally, because we dare not apply pg_get_expr
3845 * to tables we don't have lock on.)
3847 pg_log_info("reading row-level security policies");
3849 printfPQExpBuffer(query,
3850 "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
3851 if (fout->remoteVersion >= 100000)
3852 appendPQExpBufferStr(query, "pol.polpermissive, ");
3853 else
3854 appendPQExpBufferStr(query, "'t' as polpermissive, ");
3855 appendPQExpBuffer(query,
3856 "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
3857 " 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, "
3858 "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
3859 "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
3860 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
3861 "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
3862 tbloids->data);
3864 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3866 ntups = PQntuples(res);
3867 if (ntups > 0)
3869 i_oid = PQfnumber(res, "oid");
3870 i_tableoid = PQfnumber(res, "tableoid");
3871 i_polrelid = PQfnumber(res, "polrelid");
3872 i_polname = PQfnumber(res, "polname");
3873 i_polcmd = PQfnumber(res, "polcmd");
3874 i_polpermissive = PQfnumber(res, "polpermissive");
3875 i_polroles = PQfnumber(res, "polroles");
3876 i_polqual = PQfnumber(res, "polqual");
3877 i_polwithcheck = PQfnumber(res, "polwithcheck");
3879 polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
3881 for (j = 0; j < ntups; j++)
3883 Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
3884 TableInfo *tbinfo = findTableByOid(polrelid);
3886 tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
3888 polinfo[j].dobj.objType = DO_POLICY;
3889 polinfo[j].dobj.catId.tableoid =
3890 atooid(PQgetvalue(res, j, i_tableoid));
3891 polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
3892 AssignDumpId(&polinfo[j].dobj);
3893 polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3894 polinfo[j].poltable = tbinfo;
3895 polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
3896 polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
3898 polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
3899 polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
3901 if (PQgetisnull(res, j, i_polroles))
3902 polinfo[j].polroles = NULL;
3903 else
3904 polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
3906 if (PQgetisnull(res, j, i_polqual))
3907 polinfo[j].polqual = NULL;
3908 else
3909 polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
3911 if (PQgetisnull(res, j, i_polwithcheck))
3912 polinfo[j].polwithcheck = NULL;
3913 else
3914 polinfo[j].polwithcheck
3915 = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
3919 PQclear(res);
3921 destroyPQExpBuffer(query);
3922 destroyPQExpBuffer(tbloids);
3926 * dumpPolicy
3927 * dump the definition of the given policy
3929 static void
3930 dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
3932 DumpOptions *dopt = fout->dopt;
3933 TableInfo *tbinfo = polinfo->poltable;
3934 PQExpBuffer query;
3935 PQExpBuffer delqry;
3936 PQExpBuffer polprefix;
3937 char *qtabname;
3938 const char *cmd;
3939 char *tag;
3941 /* Do nothing in data-only dump */
3942 if (dopt->dataOnly)
3943 return;
3946 * If polname is NULL, then this record is just indicating that ROW LEVEL
3947 * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
3948 * ROW LEVEL SECURITY.
3950 if (polinfo->polname == NULL)
3952 query = createPQExpBuffer();
3954 appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
3955 fmtQualifiedDumpable(tbinfo));
3958 * We must emit the ROW SECURITY object's dependency on its table
3959 * explicitly, because it will not match anything in pg_depend (unlike
3960 * the case for other PolicyInfo objects).
3962 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3963 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
3964 ARCHIVE_OPTS(.tag = polinfo->dobj.name,
3965 .namespace = polinfo->dobj.namespace->dobj.name,
3966 .owner = tbinfo->rolname,
3967 .description = "ROW SECURITY",
3968 .section = SECTION_POST_DATA,
3969 .createStmt = query->data,
3970 .deps = &(tbinfo->dobj.dumpId),
3971 .nDeps = 1));
3973 destroyPQExpBuffer(query);
3974 return;
3977 if (polinfo->polcmd == '*')
3978 cmd = "";
3979 else if (polinfo->polcmd == 'r')
3980 cmd = " FOR SELECT";
3981 else if (polinfo->polcmd == 'a')
3982 cmd = " FOR INSERT";
3983 else if (polinfo->polcmd == 'w')
3984 cmd = " FOR UPDATE";
3985 else if (polinfo->polcmd == 'd')
3986 cmd = " FOR DELETE";
3987 else
3988 pg_fatal("unexpected policy command type: %c",
3989 polinfo->polcmd);
3991 query = createPQExpBuffer();
3992 delqry = createPQExpBuffer();
3993 polprefix = createPQExpBuffer();
3995 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
3997 appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
3999 appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4000 !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4002 if (polinfo->polroles != NULL)
4003 appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4005 if (polinfo->polqual != NULL)
4006 appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4008 if (polinfo->polwithcheck != NULL)
4009 appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4011 appendPQExpBufferStr(query, ";\n");
4013 appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4014 appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4016 appendPQExpBuffer(polprefix, "POLICY %s ON",
4017 fmtId(polinfo->polname));
4019 tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4021 if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4022 ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4023 ARCHIVE_OPTS(.tag = tag,
4024 .namespace = polinfo->dobj.namespace->dobj.name,
4025 .owner = tbinfo->rolname,
4026 .description = "POLICY",
4027 .section = SECTION_POST_DATA,
4028 .createStmt = query->data,
4029 .dropStmt = delqry->data));
4031 if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4032 dumpComment(fout, polprefix->data, qtabname,
4033 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4034 polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4036 free(tag);
4037 destroyPQExpBuffer(query);
4038 destroyPQExpBuffer(delqry);
4039 destroyPQExpBuffer(polprefix);
4040 free(qtabname);
4044 * getPublications
4045 * get information about publications
4047 PublicationInfo *
4048 getPublications(Archive *fout, int *numPublications)
4050 DumpOptions *dopt = fout->dopt;
4051 PQExpBuffer query;
4052 PGresult *res;
4053 PublicationInfo *pubinfo;
4054 int i_tableoid;
4055 int i_oid;
4056 int i_pubname;
4057 int i_pubowner;
4058 int i_puballtables;
4059 int i_pubinsert;
4060 int i_pubupdate;
4061 int i_pubdelete;
4062 int i_pubtruncate;
4063 int i_pubviaroot;
4064 int i,
4065 ntups;
4067 if (dopt->no_publications || fout->remoteVersion < 100000)
4069 *numPublications = 0;
4070 return NULL;
4073 query = createPQExpBuffer();
4075 resetPQExpBuffer(query);
4077 /* Get the publications. */
4078 if (fout->remoteVersion >= 130000)
4079 appendPQExpBufferStr(query,
4080 "SELECT p.tableoid, p.oid, p.pubname, "
4081 "p.pubowner, "
4082 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
4083 "FROM pg_publication p");
4084 else if (fout->remoteVersion >= 110000)
4085 appendPQExpBufferStr(query,
4086 "SELECT p.tableoid, p.oid, p.pubname, "
4087 "p.pubowner, "
4088 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
4089 "FROM pg_publication p");
4090 else
4091 appendPQExpBufferStr(query,
4092 "SELECT p.tableoid, p.oid, p.pubname, "
4093 "p.pubowner, "
4094 "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
4095 "FROM pg_publication p");
4097 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4099 ntups = PQntuples(res);
4101 i_tableoid = PQfnumber(res, "tableoid");
4102 i_oid = PQfnumber(res, "oid");
4103 i_pubname = PQfnumber(res, "pubname");
4104 i_pubowner = PQfnumber(res, "pubowner");
4105 i_puballtables = PQfnumber(res, "puballtables");
4106 i_pubinsert = PQfnumber(res, "pubinsert");
4107 i_pubupdate = PQfnumber(res, "pubupdate");
4108 i_pubdelete = PQfnumber(res, "pubdelete");
4109 i_pubtruncate = PQfnumber(res, "pubtruncate");
4110 i_pubviaroot = PQfnumber(res, "pubviaroot");
4112 pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4114 for (i = 0; i < ntups; i++)
4116 pubinfo[i].dobj.objType = DO_PUBLICATION;
4117 pubinfo[i].dobj.catId.tableoid =
4118 atooid(PQgetvalue(res, i, i_tableoid));
4119 pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4120 AssignDumpId(&pubinfo[i].dobj);
4121 pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4122 pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4123 pubinfo[i].puballtables =
4124 (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4125 pubinfo[i].pubinsert =
4126 (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4127 pubinfo[i].pubupdate =
4128 (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4129 pubinfo[i].pubdelete =
4130 (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4131 pubinfo[i].pubtruncate =
4132 (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4133 pubinfo[i].pubviaroot =
4134 (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4136 /* Decide whether we want to dump it */
4137 selectDumpableObject(&(pubinfo[i].dobj), fout);
4139 PQclear(res);
4141 destroyPQExpBuffer(query);
4143 *numPublications = ntups;
4144 return pubinfo;
4148 * dumpPublication
4149 * dump the definition of the given publication
4151 static void
4152 dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4154 DumpOptions *dopt = fout->dopt;
4155 PQExpBuffer delq;
4156 PQExpBuffer query;
4157 char *qpubname;
4158 bool first = true;
4160 /* Do nothing in data-only dump */
4161 if (dopt->dataOnly)
4162 return;
4164 delq = createPQExpBuffer();
4165 query = createPQExpBuffer();
4167 qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4169 appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4170 qpubname);
4172 appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4173 qpubname);
4175 if (pubinfo->puballtables)
4176 appendPQExpBufferStr(query, " FOR ALL TABLES");
4178 appendPQExpBufferStr(query, " WITH (publish = '");
4179 if (pubinfo->pubinsert)
4181 appendPQExpBufferStr(query, "insert");
4182 first = false;
4185 if (pubinfo->pubupdate)
4187 if (!first)
4188 appendPQExpBufferStr(query, ", ");
4190 appendPQExpBufferStr(query, "update");
4191 first = false;
4194 if (pubinfo->pubdelete)
4196 if (!first)
4197 appendPQExpBufferStr(query, ", ");
4199 appendPQExpBufferStr(query, "delete");
4200 first = false;
4203 if (pubinfo->pubtruncate)
4205 if (!first)
4206 appendPQExpBufferStr(query, ", ");
4208 appendPQExpBufferStr(query, "truncate");
4209 first = false;
4212 appendPQExpBufferChar(query, '\'');
4214 if (pubinfo->pubviaroot)
4215 appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4217 appendPQExpBufferStr(query, ");\n");
4219 if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4220 ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4221 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4222 .owner = pubinfo->rolname,
4223 .description = "PUBLICATION",
4224 .section = SECTION_POST_DATA,
4225 .createStmt = query->data,
4226 .dropStmt = delq->data));
4228 if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4229 dumpComment(fout, "PUBLICATION", qpubname,
4230 NULL, pubinfo->rolname,
4231 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4233 if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4234 dumpSecLabel(fout, "PUBLICATION", qpubname,
4235 NULL, pubinfo->rolname,
4236 pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4238 destroyPQExpBuffer(delq);
4239 destroyPQExpBuffer(query);
4240 free(qpubname);
4244 * getPublicationNamespaces
4245 * get information about publication membership for dumpable schemas.
4247 void
4248 getPublicationNamespaces(Archive *fout)
4250 PQExpBuffer query;
4251 PGresult *res;
4252 PublicationSchemaInfo *pubsinfo;
4253 DumpOptions *dopt = fout->dopt;
4254 int i_tableoid;
4255 int i_oid;
4256 int i_pnpubid;
4257 int i_pnnspid;
4258 int i,
4260 ntups;
4262 if (dopt->no_publications || fout->remoteVersion < 150000)
4263 return;
4265 query = createPQExpBuffer();
4267 /* Collect all publication membership info. */
4268 appendPQExpBufferStr(query,
4269 "SELECT tableoid, oid, pnpubid, pnnspid "
4270 "FROM pg_catalog.pg_publication_namespace");
4271 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4273 ntups = PQntuples(res);
4275 i_tableoid = PQfnumber(res, "tableoid");
4276 i_oid = PQfnumber(res, "oid");
4277 i_pnpubid = PQfnumber(res, "pnpubid");
4278 i_pnnspid = PQfnumber(res, "pnnspid");
4280 /* this allocation may be more than we need */
4281 pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4282 j = 0;
4284 for (i = 0; i < ntups; i++)
4286 Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4287 Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4288 PublicationInfo *pubinfo;
4289 NamespaceInfo *nspinfo;
4292 * Ignore any entries for which we aren't interested in either the
4293 * publication or the rel.
4295 pubinfo = findPublicationByOid(pnpubid);
4296 if (pubinfo == NULL)
4297 continue;
4298 nspinfo = findNamespaceByOid(pnnspid);
4299 if (nspinfo == NULL)
4300 continue;
4303 * We always dump publication namespaces unless the corresponding
4304 * namespace is excluded from the dump.
4306 if (nspinfo->dobj.dump == DUMP_COMPONENT_NONE)
4307 continue;
4309 /* OK, make a DumpableObject for this relationship */
4310 pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4311 pubsinfo[j].dobj.catId.tableoid =
4312 atooid(PQgetvalue(res, i, i_tableoid));
4313 pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4314 AssignDumpId(&pubsinfo[j].dobj);
4315 pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4316 pubsinfo[j].dobj.name = nspinfo->dobj.name;
4317 pubsinfo[j].publication = pubinfo;
4318 pubsinfo[j].pubschema = nspinfo;
4320 /* Decide whether we want to dump it */
4321 selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4323 j++;
4326 PQclear(res);
4327 destroyPQExpBuffer(query);
4331 * getPublicationTables
4332 * get information about publication membership for dumpable tables.
4334 void
4335 getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4337 PQExpBuffer query;
4338 PGresult *res;
4339 PublicationRelInfo *pubrinfo;
4340 DumpOptions *dopt = fout->dopt;
4341 int i_tableoid;
4342 int i_oid;
4343 int i_prpubid;
4344 int i_prrelid;
4345 int i_prrelqual;
4346 int i_prattrs;
4347 int i,
4349 ntups;
4351 if (dopt->no_publications || fout->remoteVersion < 100000)
4352 return;
4354 query = createPQExpBuffer();
4356 /* Collect all publication membership info. */
4357 if (fout->remoteVersion >= 150000)
4358 appendPQExpBufferStr(query,
4359 "SELECT tableoid, oid, prpubid, prrelid, "
4360 "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4361 "(CASE\n"
4362 " WHEN pr.prattrs IS NOT NULL THEN\n"
4363 " (SELECT array_agg(attname)\n"
4364 " FROM\n"
4365 " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4366 " pg_catalog.pg_attribute\n"
4367 " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4368 " ELSE NULL END) prattrs "
4369 "FROM pg_catalog.pg_publication_rel pr");
4370 else
4371 appendPQExpBufferStr(query,
4372 "SELECT tableoid, oid, prpubid, prrelid, "
4373 "NULL AS prrelqual, NULL AS prattrs "
4374 "FROM pg_catalog.pg_publication_rel");
4375 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4377 ntups = PQntuples(res);
4379 i_tableoid = PQfnumber(res, "tableoid");
4380 i_oid = PQfnumber(res, "oid");
4381 i_prpubid = PQfnumber(res, "prpubid");
4382 i_prrelid = PQfnumber(res, "prrelid");
4383 i_prrelqual = PQfnumber(res, "prrelqual");
4384 i_prattrs = PQfnumber(res, "prattrs");
4386 /* this allocation may be more than we need */
4387 pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4388 j = 0;
4390 for (i = 0; i < ntups; i++)
4392 Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4393 Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4394 PublicationInfo *pubinfo;
4395 TableInfo *tbinfo;
4398 * Ignore any entries for which we aren't interested in either the
4399 * publication or the rel.
4401 pubinfo = findPublicationByOid(prpubid);
4402 if (pubinfo == NULL)
4403 continue;
4404 tbinfo = findTableByOid(prrelid);
4405 if (tbinfo == NULL)
4406 continue;
4409 * Ignore publication membership of tables whose definitions are not
4410 * to be dumped.
4412 if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4413 continue;
4415 /* OK, make a DumpableObject for this relationship */
4416 pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4417 pubrinfo[j].dobj.catId.tableoid =
4418 atooid(PQgetvalue(res, i, i_tableoid));
4419 pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4420 AssignDumpId(&pubrinfo[j].dobj);
4421 pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4422 pubrinfo[j].dobj.name = tbinfo->dobj.name;
4423 pubrinfo[j].publication = pubinfo;
4424 pubrinfo[j].pubtable = tbinfo;
4425 if (PQgetisnull(res, i, i_prrelqual))
4426 pubrinfo[j].pubrelqual = NULL;
4427 else
4428 pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4430 if (!PQgetisnull(res, i, i_prattrs))
4432 char **attnames;
4433 int nattnames;
4434 PQExpBuffer attribs;
4436 if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4437 &attnames, &nattnames))
4438 pg_fatal("could not parse %s array", "prattrs");
4439 attribs = createPQExpBuffer();
4440 for (int k = 0; k < nattnames; k++)
4442 if (k > 0)
4443 appendPQExpBufferStr(attribs, ", ");
4445 appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4447 pubrinfo[j].pubrattrs = attribs->data;
4449 else
4450 pubrinfo[j].pubrattrs = NULL;
4452 /* Decide whether we want to dump it */
4453 selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4455 j++;
4458 PQclear(res);
4459 destroyPQExpBuffer(query);
4463 * dumpPublicationNamespace
4464 * dump the definition of the given publication schema mapping.
4466 static void
4467 dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4469 DumpOptions *dopt = fout->dopt;
4470 NamespaceInfo *schemainfo = pubsinfo->pubschema;
4471 PublicationInfo *pubinfo = pubsinfo->publication;
4472 PQExpBuffer query;
4473 char *tag;
4475 /* Do nothing in data-only dump */
4476 if (dopt->dataOnly)
4477 return;
4479 tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4481 query = createPQExpBuffer();
4483 appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4484 appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4487 * There is no point in creating drop query as the drop is done by schema
4488 * drop.
4490 if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4491 ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4492 ARCHIVE_OPTS(.tag = tag,
4493 .namespace = schemainfo->dobj.name,
4494 .owner = pubinfo->rolname,
4495 .description = "PUBLICATION TABLES IN SCHEMA",
4496 .section = SECTION_POST_DATA,
4497 .createStmt = query->data));
4499 /* These objects can't currently have comments or seclabels */
4501 free(tag);
4502 destroyPQExpBuffer(query);
4506 * dumpPublicationTable
4507 * dump the definition of the given publication table mapping
4509 static void
4510 dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4512 DumpOptions *dopt = fout->dopt;
4513 PublicationInfo *pubinfo = pubrinfo->publication;
4514 TableInfo *tbinfo = pubrinfo->pubtable;
4515 PQExpBuffer query;
4516 char *tag;
4518 /* Do nothing in data-only dump */
4519 if (dopt->dataOnly)
4520 return;
4522 tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4524 query = createPQExpBuffer();
4526 appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4527 fmtId(pubinfo->dobj.name));
4528 appendPQExpBuffer(query, " %s",
4529 fmtQualifiedDumpable(tbinfo));
4531 if (pubrinfo->pubrattrs)
4532 appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4534 if (pubrinfo->pubrelqual)
4537 * It's necessary to add parentheses around the expression because
4538 * pg_get_expr won't supply the parentheses for things like WHERE
4539 * TRUE.
4541 appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4543 appendPQExpBufferStr(query, ";\n");
4546 * There is no point in creating a drop query as the drop is done by table
4547 * drop. (If you think to change this, see also _printTocEntry().)
4548 * Although this object doesn't really have ownership as such, set the
4549 * owner field anyway to ensure that the command is run by the correct
4550 * role at restore time.
4552 if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4553 ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4554 ARCHIVE_OPTS(.tag = tag,
4555 .namespace = tbinfo->dobj.namespace->dobj.name,
4556 .owner = pubinfo->rolname,
4557 .description = "PUBLICATION TABLE",
4558 .section = SECTION_POST_DATA,
4559 .createStmt = query->data));
4561 /* These objects can't currently have comments or seclabels */
4563 free(tag);
4564 destroyPQExpBuffer(query);
4568 * Is the currently connected user a superuser?
4570 static bool
4571 is_superuser(Archive *fout)
4573 ArchiveHandle *AH = (ArchiveHandle *) fout;
4574 const char *val;
4576 val = PQparameterStatus(AH->connection, "is_superuser");
4578 if (val && strcmp(val, "on") == 0)
4579 return true;
4581 return false;
4585 * getSubscriptions
4586 * get information about subscriptions
4588 void
4589 getSubscriptions(Archive *fout)
4591 DumpOptions *dopt = fout->dopt;
4592 PQExpBuffer query;
4593 PGresult *res;
4594 SubscriptionInfo *subinfo;
4595 int i_tableoid;
4596 int i_oid;
4597 int i_subname;
4598 int i_subowner;
4599 int i_subbinary;
4600 int i_substream;
4601 int i_subtwophasestate;
4602 int i_subdisableonerr;
4603 int i_subpasswordrequired;
4604 int i_subrunasowner;
4605 int i_subconninfo;
4606 int i_subslotname;
4607 int i_subsynccommit;
4608 int i_subpublications;
4609 int i_suborigin;
4610 int i,
4611 ntups;
4613 if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4614 return;
4616 if (!is_superuser(fout))
4618 int n;
4620 res = ExecuteSqlQuery(fout,
4621 "SELECT count(*) FROM pg_subscription "
4622 "WHERE subdbid = (SELECT oid FROM pg_database"
4623 " WHERE datname = current_database())",
4624 PGRES_TUPLES_OK);
4625 n = atoi(PQgetvalue(res, 0, 0));
4626 if (n > 0)
4627 pg_log_warning("subscriptions not dumped because current user is not a superuser");
4628 PQclear(res);
4629 return;
4632 query = createPQExpBuffer();
4634 /* Get the subscriptions in current database. */
4635 appendPQExpBufferStr(query,
4636 "SELECT s.tableoid, s.oid, s.subname,\n"
4637 " s.subowner,\n"
4638 " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4639 " s.subpublications,\n");
4641 if (fout->remoteVersion >= 140000)
4642 appendPQExpBufferStr(query, " s.subbinary,\n");
4643 else
4644 appendPQExpBufferStr(query, " false AS subbinary,\n");
4646 if (fout->remoteVersion >= 140000)
4647 appendPQExpBufferStr(query, " s.substream,\n");
4648 else
4649 appendPQExpBufferStr(query, " 'f' AS substream,\n");
4651 if (fout->remoteVersion >= 150000)
4652 appendPQExpBufferStr(query,
4653 " s.subtwophasestate,\n"
4654 " s.subdisableonerr,\n");
4655 else
4656 appendPQExpBuffer(query,
4657 " '%c' AS subtwophasestate,\n"
4658 " false AS subdisableonerr,\n",
4659 LOGICALREP_TWOPHASE_STATE_DISABLED);
4661 if (fout->remoteVersion >= 160000)
4662 appendPQExpBufferStr(query,
4663 " s.subpasswordrequired,\n"
4664 " s.subrunasowner,\n"
4665 " s.suborigin\n");
4666 else
4667 appendPQExpBuffer(query,
4668 " 't' AS subpasswordrequired,\n"
4669 " 't' AS subrunasowner,\n"
4670 " '%s' AS suborigin\n",
4671 LOGICALREP_ORIGIN_ANY);
4673 appendPQExpBufferStr(query,
4674 "FROM pg_subscription s\n"
4675 "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
4676 " WHERE datname = current_database())");
4678 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4680 ntups = PQntuples(res);
4683 * Get subscription fields. We don't include subskiplsn in the dump as
4684 * after restoring the dump this value may no longer be relevant.
4686 i_tableoid = PQfnumber(res, "tableoid");
4687 i_oid = PQfnumber(res, "oid");
4688 i_subname = PQfnumber(res, "subname");
4689 i_subowner = PQfnumber(res, "subowner");
4690 i_subbinary = PQfnumber(res, "subbinary");
4691 i_substream = PQfnumber(res, "substream");
4692 i_subtwophasestate = PQfnumber(res, "subtwophasestate");
4693 i_subdisableonerr = PQfnumber(res, "subdisableonerr");
4694 i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
4695 i_subrunasowner = PQfnumber(res, "subrunasowner");
4696 i_subconninfo = PQfnumber(res, "subconninfo");
4697 i_subslotname = PQfnumber(res, "subslotname");
4698 i_subsynccommit = PQfnumber(res, "subsynccommit");
4699 i_subpublications = PQfnumber(res, "subpublications");
4700 i_suborigin = PQfnumber(res, "suborigin");
4702 subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
4704 for (i = 0; i < ntups; i++)
4706 subinfo[i].dobj.objType = DO_SUBSCRIPTION;
4707 subinfo[i].dobj.catId.tableoid =
4708 atooid(PQgetvalue(res, i, i_tableoid));
4709 subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4710 AssignDumpId(&subinfo[i].dobj);
4711 subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
4712 subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
4714 subinfo[i].subbinary =
4715 pg_strdup(PQgetvalue(res, i, i_subbinary));
4716 subinfo[i].substream =
4717 pg_strdup(PQgetvalue(res, i, i_substream));
4718 subinfo[i].subtwophasestate =
4719 pg_strdup(PQgetvalue(res, i, i_subtwophasestate));
4720 subinfo[i].subdisableonerr =
4721 pg_strdup(PQgetvalue(res, i, i_subdisableonerr));
4722 subinfo[i].subpasswordrequired =
4723 pg_strdup(PQgetvalue(res, i, i_subpasswordrequired));
4724 subinfo[i].subrunasowner =
4725 pg_strdup(PQgetvalue(res, i, i_subrunasowner));
4726 subinfo[i].subconninfo =
4727 pg_strdup(PQgetvalue(res, i, i_subconninfo));
4728 if (PQgetisnull(res, i, i_subslotname))
4729 subinfo[i].subslotname = NULL;
4730 else
4731 subinfo[i].subslotname =
4732 pg_strdup(PQgetvalue(res, i, i_subslotname));
4733 subinfo[i].subsynccommit =
4734 pg_strdup(PQgetvalue(res, i, i_subsynccommit));
4735 subinfo[i].subpublications =
4736 pg_strdup(PQgetvalue(res, i, i_subpublications));
4737 subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
4739 /* Decide whether we want to dump it */
4740 selectDumpableObject(&(subinfo[i].dobj), fout);
4742 PQclear(res);
4744 destroyPQExpBuffer(query);
4748 * dumpSubscription
4749 * dump the definition of the given subscription
4751 static void
4752 dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
4754 DumpOptions *dopt = fout->dopt;
4755 PQExpBuffer delq;
4756 PQExpBuffer query;
4757 PQExpBuffer publications;
4758 char *qsubname;
4759 char **pubnames = NULL;
4760 int npubnames = 0;
4761 int i;
4762 char two_phase_disabled[] = {LOGICALREP_TWOPHASE_STATE_DISABLED, '\0'};
4764 /* Do nothing in data-only dump */
4765 if (dopt->dataOnly)
4766 return;
4768 delq = createPQExpBuffer();
4769 query = createPQExpBuffer();
4771 qsubname = pg_strdup(fmtId(subinfo->dobj.name));
4773 appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
4774 qsubname);
4776 appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
4777 qsubname);
4778 appendStringLiteralAH(query, subinfo->subconninfo, fout);
4780 /* Build list of quoted publications and append them to query. */
4781 if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
4782 pg_fatal("could not parse %s array", "subpublications");
4784 publications = createPQExpBuffer();
4785 for (i = 0; i < npubnames; i++)
4787 if (i > 0)
4788 appendPQExpBufferStr(publications, ", ");
4790 appendPQExpBufferStr(publications, fmtId(pubnames[i]));
4793 appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
4794 if (subinfo->subslotname)
4795 appendStringLiteralAH(query, subinfo->subslotname, fout);
4796 else
4797 appendPQExpBufferStr(query, "NONE");
4799 if (strcmp(subinfo->subbinary, "t") == 0)
4800 appendPQExpBufferStr(query, ", binary = true");
4802 if (strcmp(subinfo->substream, "t") == 0)
4803 appendPQExpBufferStr(query, ", streaming = on");
4804 else if (strcmp(subinfo->substream, "p") == 0)
4805 appendPQExpBufferStr(query, ", streaming = parallel");
4807 if (strcmp(subinfo->subtwophasestate, two_phase_disabled) != 0)
4808 appendPQExpBufferStr(query, ", two_phase = on");
4810 if (strcmp(subinfo->subdisableonerr, "t") == 0)
4811 appendPQExpBufferStr(query, ", disable_on_error = true");
4813 if (strcmp(subinfo->subpasswordrequired, "t") != 0)
4814 appendPQExpBuffer(query, ", password_required = false");
4816 if (strcmp(subinfo->subrunasowner, "t") == 0)
4817 appendPQExpBufferStr(query, ", run_as_owner = true");
4819 if (strcmp(subinfo->subsynccommit, "off") != 0)
4820 appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
4822 if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
4823 appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
4825 appendPQExpBufferStr(query, ");\n");
4827 if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4828 ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
4829 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
4830 .owner = subinfo->rolname,
4831 .description = "SUBSCRIPTION",
4832 .section = SECTION_POST_DATA,
4833 .createStmt = query->data,
4834 .dropStmt = delq->data));
4836 if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4837 dumpComment(fout, "SUBSCRIPTION", qsubname,
4838 NULL, subinfo->rolname,
4839 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
4841 if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4842 dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
4843 NULL, subinfo->rolname,
4844 subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
4846 destroyPQExpBuffer(publications);
4847 free(pubnames);
4849 destroyPQExpBuffer(delq);
4850 destroyPQExpBuffer(query);
4851 free(qsubname);
4855 * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
4856 * the object needs.
4858 static void
4859 append_depends_on_extension(Archive *fout,
4860 PQExpBuffer create,
4861 const DumpableObject *dobj,
4862 const char *catalog,
4863 const char *keyword,
4864 const char *objname)
4866 if (dobj->depends_on_ext)
4868 char *nm;
4869 PGresult *res;
4870 PQExpBuffer query;
4871 int ntups;
4872 int i_extname;
4873 int i;
4875 /* dodge fmtId() non-reentrancy */
4876 nm = pg_strdup(objname);
4878 query = createPQExpBuffer();
4879 appendPQExpBuffer(query,
4880 "SELECT e.extname "
4881 "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
4882 "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
4883 "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
4884 "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
4885 catalog,
4886 dobj->catId.oid);
4887 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4888 ntups = PQntuples(res);
4889 i_extname = PQfnumber(res, "extname");
4890 for (i = 0; i < ntups; i++)
4892 appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
4893 keyword, nm,
4894 fmtId(PQgetvalue(res, i, i_extname)));
4897 PQclear(res);
4898 destroyPQExpBuffer(query);
4899 pg_free(nm);
4903 static Oid
4904 get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
4907 * If the old version didn't assign an array type, but the new version
4908 * does, we must select an unused type OID to assign. This currently only
4909 * happens for domains, when upgrading pre-v11 to v11 and up.
4911 * Note: local state here is kind of ugly, but we must have some, since we
4912 * mustn't choose the same unused OID more than once.
4914 static Oid next_possible_free_oid = FirstNormalObjectId;
4915 PGresult *res;
4916 bool is_dup;
4920 ++next_possible_free_oid;
4921 printfPQExpBuffer(upgrade_query,
4922 "SELECT EXISTS(SELECT 1 "
4923 "FROM pg_catalog.pg_type "
4924 "WHERE oid = '%u'::pg_catalog.oid);",
4925 next_possible_free_oid);
4926 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4927 is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
4928 PQclear(res);
4929 } while (is_dup);
4931 return next_possible_free_oid;
4934 static void
4935 binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
4936 PQExpBuffer upgrade_buffer,
4937 Oid pg_type_oid,
4938 bool force_array_type,
4939 bool include_multirange_type)
4941 PQExpBuffer upgrade_query = createPQExpBuffer();
4942 PGresult *res;
4943 Oid pg_type_array_oid;
4944 Oid pg_type_multirange_oid;
4945 Oid pg_type_multirange_array_oid;
4947 appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
4948 appendPQExpBuffer(upgrade_buffer,
4949 "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4950 pg_type_oid);
4952 appendPQExpBuffer(upgrade_query,
4953 "SELECT typarray "
4954 "FROM pg_catalog.pg_type "
4955 "WHERE oid = '%u'::pg_catalog.oid;",
4956 pg_type_oid);
4958 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4960 pg_type_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
4962 PQclear(res);
4964 if (!OidIsValid(pg_type_array_oid) && force_array_type)
4965 pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
4967 if (OidIsValid(pg_type_array_oid))
4969 appendPQExpBufferStr(upgrade_buffer,
4970 "\n-- For binary upgrade, must preserve pg_type array oid\n");
4971 appendPQExpBuffer(upgrade_buffer,
4972 "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
4973 pg_type_array_oid);
4977 * Pre-set the multirange type oid and its own array type oid.
4979 if (include_multirange_type)
4981 if (fout->remoteVersion >= 140000)
4983 printfPQExpBuffer(upgrade_query,
4984 "SELECT t.oid, t.typarray "
4985 "FROM pg_catalog.pg_type t "
4986 "JOIN pg_catalog.pg_range r "
4987 "ON t.oid = r.rngmultitypid "
4988 "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
4989 pg_type_oid);
4991 res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
4993 pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
4994 pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
4996 PQclear(res);
4998 else
5000 pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5001 pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5004 appendPQExpBufferStr(upgrade_buffer,
5005 "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5006 appendPQExpBuffer(upgrade_buffer,
5007 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5008 pg_type_multirange_oid);
5009 appendPQExpBufferStr(upgrade_buffer,
5010 "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5011 appendPQExpBuffer(upgrade_buffer,
5012 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5013 pg_type_multirange_array_oid);
5016 destroyPQExpBuffer(upgrade_query);
5019 static void
5020 binary_upgrade_set_type_oids_by_rel(Archive *fout,
5021 PQExpBuffer upgrade_buffer,
5022 const TableInfo *tbinfo)
5024 Oid pg_type_oid = tbinfo->reltype;
5026 if (OidIsValid(pg_type_oid))
5027 binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5028 pg_type_oid, false, false);
5031 static void
5032 binary_upgrade_set_pg_class_oids(Archive *fout,
5033 PQExpBuffer upgrade_buffer, Oid pg_class_oid,
5034 bool is_index)
5036 PQExpBuffer upgrade_query = createPQExpBuffer();
5037 PGresult *upgrade_res;
5038 RelFileNumber relfilenumber;
5039 Oid toast_oid;
5040 RelFileNumber toast_relfilenumber;
5041 char relkind;
5042 Oid toast_index_oid;
5043 RelFileNumber toast_index_relfilenumber;
5046 * Preserve the OID and relfilenumber of the table, table's index, table's
5047 * toast table and toast table's index if any.
5049 * One complexity is that the current table definition might not require
5050 * the creation of a TOAST table, but the old database might have a TOAST
5051 * table that was created earlier, before some wide columns were dropped.
5052 * By setting the TOAST oid we force creation of the TOAST heap and index
5053 * by the new backend, so we can copy the files during binary upgrade
5054 * without worrying about this case.
5056 appendPQExpBuffer(upgrade_query,
5057 "SELECT c.relkind, c.relfilenode, c.reltoastrelid, ct.relfilenode AS toast_relfilenode, i.indexrelid, cti.relfilenode AS toast_index_relfilenode "
5058 "FROM pg_catalog.pg_class c LEFT JOIN "
5059 "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5060 "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5061 "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5062 "WHERE c.oid = '%u'::pg_catalog.oid;",
5063 pg_class_oid);
5065 upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5067 relkind = *PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "relkind"));
5069 relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5070 PQfnumber(upgrade_res, "relfilenode")));
5071 toast_oid = atooid(PQgetvalue(upgrade_res, 0,
5072 PQfnumber(upgrade_res, "reltoastrelid")));
5073 toast_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5074 PQfnumber(upgrade_res, "toast_relfilenode")));
5075 toast_index_oid = atooid(PQgetvalue(upgrade_res, 0,
5076 PQfnumber(upgrade_res, "indexrelid")));
5077 toast_index_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5078 PQfnumber(upgrade_res, "toast_index_relfilenode")));
5080 appendPQExpBufferStr(upgrade_buffer,
5081 "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5083 if (!is_index)
5085 appendPQExpBuffer(upgrade_buffer,
5086 "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5087 pg_class_oid);
5090 * Not every relation has storage. Also, in a pre-v12 database,
5091 * partitioned tables have a relfilenumber, which should not be
5092 * preserved when upgrading.
5094 if (RelFileNumberIsValid(relfilenumber) && relkind != RELKIND_PARTITIONED_TABLE)
5095 appendPQExpBuffer(upgrade_buffer,
5096 "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5097 relfilenumber);
5100 * In a pre-v12 database, partitioned tables might be marked as having
5101 * toast tables, but we should ignore them if so.
5103 if (OidIsValid(toast_oid) &&
5104 relkind != RELKIND_PARTITIONED_TABLE)
5106 appendPQExpBuffer(upgrade_buffer,
5107 "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5108 toast_oid);
5109 appendPQExpBuffer(upgrade_buffer,
5110 "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5111 toast_relfilenumber);
5113 /* every toast table has an index */
5114 appendPQExpBuffer(upgrade_buffer,
5115 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5116 toast_index_oid);
5117 appendPQExpBuffer(upgrade_buffer,
5118 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5119 toast_index_relfilenumber);
5122 PQclear(upgrade_res);
5124 else
5126 /* Preserve the OID and relfilenumber of the index */
5127 appendPQExpBuffer(upgrade_buffer,
5128 "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5129 pg_class_oid);
5130 appendPQExpBuffer(upgrade_buffer,
5131 "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5132 relfilenumber);
5135 appendPQExpBufferChar(upgrade_buffer, '\n');
5137 destroyPQExpBuffer(upgrade_query);
5141 * If the DumpableObject is a member of an extension, add a suitable
5142 * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5144 * For somewhat historical reasons, objname should already be quoted,
5145 * but not objnamespace (if any).
5147 static void
5148 binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5149 const DumpableObject *dobj,
5150 const char *objtype,
5151 const char *objname,
5152 const char *objnamespace)
5154 DumpableObject *extobj = NULL;
5155 int i;
5157 if (!dobj->ext_member)
5158 return;
5161 * Find the parent extension. We could avoid this search if we wanted to
5162 * add a link field to DumpableObject, but the space costs of that would
5163 * be considerable. We assume that member objects could only have a
5164 * direct dependency on their own extension, not any others.
5166 for (i = 0; i < dobj->nDeps; i++)
5168 extobj = findObjectByDumpId(dobj->dependencies[i]);
5169 if (extobj && extobj->objType == DO_EXTENSION)
5170 break;
5171 extobj = NULL;
5173 if (extobj == NULL)
5174 pg_fatal("could not find parent extension for %s %s",
5175 objtype, objname);
5177 appendPQExpBufferStr(upgrade_buffer,
5178 "\n-- For binary upgrade, handle extension membership the hard way\n");
5179 appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5180 fmtId(extobj->name),
5181 objtype);
5182 if (objnamespace && *objnamespace)
5183 appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5184 appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5188 * getNamespaces:
5189 * read all namespaces in the system catalogs and return them in the
5190 * NamespaceInfo* structure
5192 * numNamespaces is set to the number of namespaces read in
5194 NamespaceInfo *
5195 getNamespaces(Archive *fout, int *numNamespaces)
5197 PGresult *res;
5198 int ntups;
5199 int i;
5200 PQExpBuffer query;
5201 NamespaceInfo *nsinfo;
5202 int i_tableoid;
5203 int i_oid;
5204 int i_nspname;
5205 int i_nspowner;
5206 int i_nspacl;
5207 int i_acldefault;
5209 query = createPQExpBuffer();
5212 * we fetch all namespaces including system ones, so that every object we
5213 * read in can be linked to a containing namespace.
5215 appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5216 "n.nspowner, "
5217 "n.nspacl, "
5218 "acldefault('n', n.nspowner) AS acldefault "
5219 "FROM pg_namespace n");
5221 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5223 ntups = PQntuples(res);
5225 nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5227 i_tableoid = PQfnumber(res, "tableoid");
5228 i_oid = PQfnumber(res, "oid");
5229 i_nspname = PQfnumber(res, "nspname");
5230 i_nspowner = PQfnumber(res, "nspowner");
5231 i_nspacl = PQfnumber(res, "nspacl");
5232 i_acldefault = PQfnumber(res, "acldefault");
5234 for (i = 0; i < ntups; i++)
5236 const char *nspowner;
5238 nsinfo[i].dobj.objType = DO_NAMESPACE;
5239 nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5240 nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5241 AssignDumpId(&nsinfo[i].dobj);
5242 nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5243 nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5244 nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5245 nsinfo[i].dacl.privtype = 0;
5246 nsinfo[i].dacl.initprivs = NULL;
5247 nspowner = PQgetvalue(res, i, i_nspowner);
5248 nsinfo[i].nspowner = atooid(nspowner);
5249 nsinfo[i].rolname = getRoleName(nspowner);
5251 /* Decide whether to dump this namespace */
5252 selectDumpableNamespace(&nsinfo[i], fout);
5254 /* Mark whether namespace has an ACL */
5255 if (!PQgetisnull(res, i, i_nspacl))
5256 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5259 * We ignore any pg_init_privs.initprivs entry for the public schema
5260 * and assume a predetermined default, for several reasons. First,
5261 * dropping and recreating the schema removes its pg_init_privs entry,
5262 * but an empty destination database starts with this ACL nonetheless.
5263 * Second, we support dump/reload of public schema ownership changes.
5264 * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5265 * initprivs continues to reflect the initial owner. Hence,
5266 * synthesize the value that nspacl will have after the restore's
5267 * ALTER SCHEMA OWNER. Third, this makes the destination database
5268 * match the source's ACL, even if the latter was an initdb-default
5269 * ACL, which changed in v15. An upgrade pulls in changes to most
5270 * system object ACLs that the DBA had not customized. We've made the
5271 * public schema depart from that, because changing its ACL so easily
5272 * breaks applications.
5274 if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5276 PQExpBuffer aclarray = createPQExpBuffer();
5277 PQExpBuffer aclitem = createPQExpBuffer();
5279 /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5280 appendPQExpBufferChar(aclarray, '{');
5281 quoteAclUserName(aclitem, nsinfo[i].rolname);
5282 appendPQExpBufferStr(aclitem, "=UC/");
5283 quoteAclUserName(aclitem, nsinfo[i].rolname);
5284 appendPGArray(aclarray, aclitem->data);
5285 resetPQExpBuffer(aclitem);
5286 appendPQExpBufferStr(aclitem, "=U/");
5287 quoteAclUserName(aclitem, nsinfo[i].rolname);
5288 appendPGArray(aclarray, aclitem->data);
5289 appendPQExpBufferChar(aclarray, '}');
5291 nsinfo[i].dacl.privtype = 'i';
5292 nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5293 nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5295 destroyPQExpBuffer(aclarray);
5296 destroyPQExpBuffer(aclitem);
5300 PQclear(res);
5301 destroyPQExpBuffer(query);
5303 *numNamespaces = ntups;
5305 return nsinfo;
5309 * findNamespace:
5310 * given a namespace OID, look up the info read by getNamespaces
5312 static NamespaceInfo *
5313 findNamespace(Oid nsoid)
5315 NamespaceInfo *nsinfo;
5317 nsinfo = findNamespaceByOid(nsoid);
5318 if (nsinfo == NULL)
5319 pg_fatal("schema with OID %u does not exist", nsoid);
5320 return nsinfo;
5324 * getExtensions:
5325 * read all extensions in the system catalogs and return them in the
5326 * ExtensionInfo* structure
5328 * numExtensions is set to the number of extensions read in
5330 ExtensionInfo *
5331 getExtensions(Archive *fout, int *numExtensions)
5333 DumpOptions *dopt = fout->dopt;
5334 PGresult *res;
5335 int ntups;
5336 int i;
5337 PQExpBuffer query;
5338 ExtensionInfo *extinfo;
5339 int i_tableoid;
5340 int i_oid;
5341 int i_extname;
5342 int i_nspname;
5343 int i_extrelocatable;
5344 int i_extversion;
5345 int i_extconfig;
5346 int i_extcondition;
5348 query = createPQExpBuffer();
5350 appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5351 "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5352 "FROM pg_extension x "
5353 "JOIN pg_namespace n ON n.oid = x.extnamespace");
5355 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5357 ntups = PQntuples(res);
5359 extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5361 i_tableoid = PQfnumber(res, "tableoid");
5362 i_oid = PQfnumber(res, "oid");
5363 i_extname = PQfnumber(res, "extname");
5364 i_nspname = PQfnumber(res, "nspname");
5365 i_extrelocatable = PQfnumber(res, "extrelocatable");
5366 i_extversion = PQfnumber(res, "extversion");
5367 i_extconfig = PQfnumber(res, "extconfig");
5368 i_extcondition = PQfnumber(res, "extcondition");
5370 for (i = 0; i < ntups; i++)
5372 extinfo[i].dobj.objType = DO_EXTENSION;
5373 extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5374 extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5375 AssignDumpId(&extinfo[i].dobj);
5376 extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5377 extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5378 extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5379 extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5380 extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5381 extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5383 /* Decide whether we want to dump it */
5384 selectDumpableExtension(&(extinfo[i]), dopt);
5387 PQclear(res);
5388 destroyPQExpBuffer(query);
5390 *numExtensions = ntups;
5392 return extinfo;
5396 * getTypes:
5397 * read all types in the system catalogs and return them in the
5398 * TypeInfo* structure
5400 * numTypes is set to the number of types read in
5402 * NB: this must run after getFuncs() because we assume we can do
5403 * findFuncByOid().
5405 TypeInfo *
5406 getTypes(Archive *fout, int *numTypes)
5408 PGresult *res;
5409 int ntups;
5410 int i;
5411 PQExpBuffer query = createPQExpBuffer();
5412 TypeInfo *tyinfo;
5413 ShellTypeInfo *stinfo;
5414 int i_tableoid;
5415 int i_oid;
5416 int i_typname;
5417 int i_typnamespace;
5418 int i_typacl;
5419 int i_acldefault;
5420 int i_typowner;
5421 int i_typelem;
5422 int i_typrelid;
5423 int i_typrelkind;
5424 int i_typtype;
5425 int i_typisdefined;
5426 int i_isarray;
5429 * we include even the built-in types because those may be used as array
5430 * elements by user-defined types
5432 * we filter out the built-in types when we dump out the types
5434 * same approach for undefined (shell) types and array types
5436 * Note: as of 8.3 we can reliably detect whether a type is an
5437 * auto-generated array type by checking the element type's typarray.
5438 * (Before that the test is capable of generating false positives.) We
5439 * still check for name beginning with '_', though, so as to avoid the
5440 * cost of the subselect probe for all standard types. This would have to
5441 * be revisited if the backend ever allows renaming of array types.
5443 appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
5444 "typnamespace, typacl, "
5445 "acldefault('T', typowner) AS acldefault, "
5446 "typowner, "
5447 "typelem, typrelid, "
5448 "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
5449 "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
5450 "typtype, typisdefined, "
5451 "typname[0] = '_' AND typelem != 0 AND "
5452 "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
5453 "FROM pg_type");
5455 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5457 ntups = PQntuples(res);
5459 tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
5461 i_tableoid = PQfnumber(res, "tableoid");
5462 i_oid = PQfnumber(res, "oid");
5463 i_typname = PQfnumber(res, "typname");
5464 i_typnamespace = PQfnumber(res, "typnamespace");
5465 i_typacl = PQfnumber(res, "typacl");
5466 i_acldefault = PQfnumber(res, "acldefault");
5467 i_typowner = PQfnumber(res, "typowner");
5468 i_typelem = PQfnumber(res, "typelem");
5469 i_typrelid = PQfnumber(res, "typrelid");
5470 i_typrelkind = PQfnumber(res, "typrelkind");
5471 i_typtype = PQfnumber(res, "typtype");
5472 i_typisdefined = PQfnumber(res, "typisdefined");
5473 i_isarray = PQfnumber(res, "isarray");
5475 for (i = 0; i < ntups; i++)
5477 tyinfo[i].dobj.objType = DO_TYPE;
5478 tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5479 tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5480 AssignDumpId(&tyinfo[i].dobj);
5481 tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
5482 tyinfo[i].dobj.namespace =
5483 findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
5484 tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
5485 tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5486 tyinfo[i].dacl.privtype = 0;
5487 tyinfo[i].dacl.initprivs = NULL;
5488 tyinfo[i].ftypname = NULL; /* may get filled later */
5489 tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
5490 tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
5491 tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
5492 tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
5493 tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
5494 tyinfo[i].shellType = NULL;
5496 if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
5497 tyinfo[i].isDefined = true;
5498 else
5499 tyinfo[i].isDefined = false;
5501 if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
5502 tyinfo[i].isArray = true;
5503 else
5504 tyinfo[i].isArray = false;
5506 if (tyinfo[i].typtype == 'm')
5507 tyinfo[i].isMultirange = true;
5508 else
5509 tyinfo[i].isMultirange = false;
5511 /* Decide whether we want to dump it */
5512 selectDumpableType(&tyinfo[i], fout);
5514 /* Mark whether type has an ACL */
5515 if (!PQgetisnull(res, i, i_typacl))
5516 tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5519 * If it's a domain, fetch info about its constraints, if any
5521 tyinfo[i].nDomChecks = 0;
5522 tyinfo[i].domChecks = NULL;
5523 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
5524 tyinfo[i].typtype == TYPTYPE_DOMAIN)
5525 getDomainConstraints(fout, &(tyinfo[i]));
5528 * If it's a base type, make a DumpableObject representing a shell
5529 * definition of the type. We will need to dump that ahead of the I/O
5530 * functions for the type. Similarly, range types need a shell
5531 * definition in case they have a canonicalize function.
5533 * Note: the shell type doesn't have a catId. You might think it
5534 * should copy the base type's catId, but then it might capture the
5535 * pg_depend entries for the type, which we don't want.
5537 if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
5538 (tyinfo[i].typtype == TYPTYPE_BASE ||
5539 tyinfo[i].typtype == TYPTYPE_RANGE))
5541 stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
5542 stinfo->dobj.objType = DO_SHELL_TYPE;
5543 stinfo->dobj.catId = nilCatalogId;
5544 AssignDumpId(&stinfo->dobj);
5545 stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
5546 stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
5547 stinfo->baseType = &(tyinfo[i]);
5548 tyinfo[i].shellType = stinfo;
5551 * Initially mark the shell type as not to be dumped. We'll only
5552 * dump it if the I/O or canonicalize functions need to be dumped;
5553 * this is taken care of while sorting dependencies.
5555 stinfo->dobj.dump = DUMP_COMPONENT_NONE;
5559 *numTypes = ntups;
5561 PQclear(res);
5563 destroyPQExpBuffer(query);
5565 return tyinfo;
5569 * getOperators:
5570 * read all operators in the system catalogs and return them in the
5571 * OprInfo* structure
5573 * numOprs is set to the number of operators read in
5575 OprInfo *
5576 getOperators(Archive *fout, int *numOprs)
5578 PGresult *res;
5579 int ntups;
5580 int i;
5581 PQExpBuffer query = createPQExpBuffer();
5582 OprInfo *oprinfo;
5583 int i_tableoid;
5584 int i_oid;
5585 int i_oprname;
5586 int i_oprnamespace;
5587 int i_oprowner;
5588 int i_oprkind;
5589 int i_oprcode;
5592 * find all operators, including builtin operators; we filter out
5593 * system-defined operators at dump-out time.
5596 appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
5597 "oprnamespace, "
5598 "oprowner, "
5599 "oprkind, "
5600 "oprcode::oid AS oprcode "
5601 "FROM pg_operator");
5603 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5605 ntups = PQntuples(res);
5606 *numOprs = ntups;
5608 oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
5610 i_tableoid = PQfnumber(res, "tableoid");
5611 i_oid = PQfnumber(res, "oid");
5612 i_oprname = PQfnumber(res, "oprname");
5613 i_oprnamespace = PQfnumber(res, "oprnamespace");
5614 i_oprowner = PQfnumber(res, "oprowner");
5615 i_oprkind = PQfnumber(res, "oprkind");
5616 i_oprcode = PQfnumber(res, "oprcode");
5618 for (i = 0; i < ntups; i++)
5620 oprinfo[i].dobj.objType = DO_OPERATOR;
5621 oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5622 oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5623 AssignDumpId(&oprinfo[i].dobj);
5624 oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
5625 oprinfo[i].dobj.namespace =
5626 findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
5627 oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
5628 oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
5629 oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
5631 /* Decide whether we want to dump it */
5632 selectDumpableObject(&(oprinfo[i].dobj), fout);
5635 PQclear(res);
5637 destroyPQExpBuffer(query);
5639 return oprinfo;
5643 * getCollations:
5644 * read all collations in the system catalogs and return them in the
5645 * CollInfo* structure
5647 * numCollations is set to the number of collations read in
5649 CollInfo *
5650 getCollations(Archive *fout, int *numCollations)
5652 PGresult *res;
5653 int ntups;
5654 int i;
5655 PQExpBuffer query;
5656 CollInfo *collinfo;
5657 int i_tableoid;
5658 int i_oid;
5659 int i_collname;
5660 int i_collnamespace;
5661 int i_collowner;
5663 query = createPQExpBuffer();
5666 * find all collations, including builtin collations; we filter out
5667 * system-defined collations at dump-out time.
5670 appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
5671 "collnamespace, "
5672 "collowner "
5673 "FROM pg_collation");
5675 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5677 ntups = PQntuples(res);
5678 *numCollations = ntups;
5680 collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
5682 i_tableoid = PQfnumber(res, "tableoid");
5683 i_oid = PQfnumber(res, "oid");
5684 i_collname = PQfnumber(res, "collname");
5685 i_collnamespace = PQfnumber(res, "collnamespace");
5686 i_collowner = PQfnumber(res, "collowner");
5688 for (i = 0; i < ntups; i++)
5690 collinfo[i].dobj.objType = DO_COLLATION;
5691 collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5692 collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5693 AssignDumpId(&collinfo[i].dobj);
5694 collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
5695 collinfo[i].dobj.namespace =
5696 findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
5697 collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
5699 /* Decide whether we want to dump it */
5700 selectDumpableObject(&(collinfo[i].dobj), fout);
5703 PQclear(res);
5705 destroyPQExpBuffer(query);
5707 return collinfo;
5711 * getConversions:
5712 * read all conversions in the system catalogs and return them in the
5713 * ConvInfo* structure
5715 * numConversions is set to the number of conversions read in
5717 ConvInfo *
5718 getConversions(Archive *fout, int *numConversions)
5720 PGresult *res;
5721 int ntups;
5722 int i;
5723 PQExpBuffer query;
5724 ConvInfo *convinfo;
5725 int i_tableoid;
5726 int i_oid;
5727 int i_conname;
5728 int i_connamespace;
5729 int i_conowner;
5731 query = createPQExpBuffer();
5734 * find all conversions, including builtin conversions; we filter out
5735 * system-defined conversions at dump-out time.
5738 appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
5739 "connamespace, "
5740 "conowner "
5741 "FROM pg_conversion");
5743 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5745 ntups = PQntuples(res);
5746 *numConversions = ntups;
5748 convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
5750 i_tableoid = PQfnumber(res, "tableoid");
5751 i_oid = PQfnumber(res, "oid");
5752 i_conname = PQfnumber(res, "conname");
5753 i_connamespace = PQfnumber(res, "connamespace");
5754 i_conowner = PQfnumber(res, "conowner");
5756 for (i = 0; i < ntups; i++)
5758 convinfo[i].dobj.objType = DO_CONVERSION;
5759 convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5760 convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5761 AssignDumpId(&convinfo[i].dobj);
5762 convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
5763 convinfo[i].dobj.namespace =
5764 findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
5765 convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
5767 /* Decide whether we want to dump it */
5768 selectDumpableObject(&(convinfo[i].dobj), fout);
5771 PQclear(res);
5773 destroyPQExpBuffer(query);
5775 return convinfo;
5779 * getAccessMethods:
5780 * read all user-defined access methods in the system catalogs and return
5781 * them in the AccessMethodInfo* structure
5783 * numAccessMethods is set to the number of access methods read in
5785 AccessMethodInfo *
5786 getAccessMethods(Archive *fout, int *numAccessMethods)
5788 PGresult *res;
5789 int ntups;
5790 int i;
5791 PQExpBuffer query;
5792 AccessMethodInfo *aminfo;
5793 int i_tableoid;
5794 int i_oid;
5795 int i_amname;
5796 int i_amhandler;
5797 int i_amtype;
5799 /* Before 9.6, there are no user-defined access methods */
5800 if (fout->remoteVersion < 90600)
5802 *numAccessMethods = 0;
5803 return NULL;
5806 query = createPQExpBuffer();
5808 /* Select all access methods from pg_am table */
5809 appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
5810 "amhandler::pg_catalog.regproc AS amhandler "
5811 "FROM pg_am");
5813 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5815 ntups = PQntuples(res);
5816 *numAccessMethods = ntups;
5818 aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
5820 i_tableoid = PQfnumber(res, "tableoid");
5821 i_oid = PQfnumber(res, "oid");
5822 i_amname = PQfnumber(res, "amname");
5823 i_amhandler = PQfnumber(res, "amhandler");
5824 i_amtype = PQfnumber(res, "amtype");
5826 for (i = 0; i < ntups; i++)
5828 aminfo[i].dobj.objType = DO_ACCESS_METHOD;
5829 aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5830 aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5831 AssignDumpId(&aminfo[i].dobj);
5832 aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
5833 aminfo[i].dobj.namespace = NULL;
5834 aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
5835 aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
5837 /* Decide whether we want to dump it */
5838 selectDumpableAccessMethod(&(aminfo[i]), fout);
5841 PQclear(res);
5843 destroyPQExpBuffer(query);
5845 return aminfo;
5850 * getOpclasses:
5851 * read all opclasses in the system catalogs and return them in the
5852 * OpclassInfo* structure
5854 * numOpclasses is set to the number of opclasses read in
5856 OpclassInfo *
5857 getOpclasses(Archive *fout, int *numOpclasses)
5859 PGresult *res;
5860 int ntups;
5861 int i;
5862 PQExpBuffer query = createPQExpBuffer();
5863 OpclassInfo *opcinfo;
5864 int i_tableoid;
5865 int i_oid;
5866 int i_opcname;
5867 int i_opcnamespace;
5868 int i_opcowner;
5871 * find all opclasses, including builtin opclasses; we filter out
5872 * system-defined opclasses at dump-out time.
5875 appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
5876 "opcnamespace, "
5877 "opcowner "
5878 "FROM pg_opclass");
5880 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5882 ntups = PQntuples(res);
5883 *numOpclasses = ntups;
5885 opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
5887 i_tableoid = PQfnumber(res, "tableoid");
5888 i_oid = PQfnumber(res, "oid");
5889 i_opcname = PQfnumber(res, "opcname");
5890 i_opcnamespace = PQfnumber(res, "opcnamespace");
5891 i_opcowner = PQfnumber(res, "opcowner");
5893 for (i = 0; i < ntups; i++)
5895 opcinfo[i].dobj.objType = DO_OPCLASS;
5896 opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5897 opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5898 AssignDumpId(&opcinfo[i].dobj);
5899 opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
5900 opcinfo[i].dobj.namespace =
5901 findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
5902 opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
5904 /* Decide whether we want to dump it */
5905 selectDumpableObject(&(opcinfo[i].dobj), fout);
5908 PQclear(res);
5910 destroyPQExpBuffer(query);
5912 return opcinfo;
5916 * getOpfamilies:
5917 * read all opfamilies in the system catalogs and return them in the
5918 * OpfamilyInfo* structure
5920 * numOpfamilies is set to the number of opfamilies read in
5922 OpfamilyInfo *
5923 getOpfamilies(Archive *fout, int *numOpfamilies)
5925 PGresult *res;
5926 int ntups;
5927 int i;
5928 PQExpBuffer query;
5929 OpfamilyInfo *opfinfo;
5930 int i_tableoid;
5931 int i_oid;
5932 int i_opfname;
5933 int i_opfnamespace;
5934 int i_opfowner;
5936 query = createPQExpBuffer();
5939 * find all opfamilies, including builtin opfamilies; we filter out
5940 * system-defined opfamilies at dump-out time.
5943 appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
5944 "opfnamespace, "
5945 "opfowner "
5946 "FROM pg_opfamily");
5948 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5950 ntups = PQntuples(res);
5951 *numOpfamilies = ntups;
5953 opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
5955 i_tableoid = PQfnumber(res, "tableoid");
5956 i_oid = PQfnumber(res, "oid");
5957 i_opfname = PQfnumber(res, "opfname");
5958 i_opfnamespace = PQfnumber(res, "opfnamespace");
5959 i_opfowner = PQfnumber(res, "opfowner");
5961 for (i = 0; i < ntups; i++)
5963 opfinfo[i].dobj.objType = DO_OPFAMILY;
5964 opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5965 opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5966 AssignDumpId(&opfinfo[i].dobj);
5967 opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
5968 opfinfo[i].dobj.namespace =
5969 findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
5970 opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
5972 /* Decide whether we want to dump it */
5973 selectDumpableObject(&(opfinfo[i].dobj), fout);
5976 PQclear(res);
5978 destroyPQExpBuffer(query);
5980 return opfinfo;
5984 * getAggregates:
5985 * read all the user-defined aggregates in the system catalogs and
5986 * return them in the AggInfo* structure
5988 * numAggs is set to the number of aggregates read in
5990 AggInfo *
5991 getAggregates(Archive *fout, int *numAggs)
5993 DumpOptions *dopt = fout->dopt;
5994 PGresult *res;
5995 int ntups;
5996 int i;
5997 PQExpBuffer query = createPQExpBuffer();
5998 AggInfo *agginfo;
5999 int i_tableoid;
6000 int i_oid;
6001 int i_aggname;
6002 int i_aggnamespace;
6003 int i_pronargs;
6004 int i_proargtypes;
6005 int i_proowner;
6006 int i_aggacl;
6007 int i_acldefault;
6010 * Find all interesting aggregates. See comment in getFuncs() for the
6011 * rationale behind the filtering logic.
6013 if (fout->remoteVersion >= 90600)
6015 const char *agg_check;
6017 agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6018 : "p.proisagg");
6020 appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6021 "p.proname AS aggname, "
6022 "p.pronamespace AS aggnamespace, "
6023 "p.pronargs, p.proargtypes, "
6024 "p.proowner, "
6025 "p.proacl AS aggacl, "
6026 "acldefault('f', p.proowner) AS acldefault "
6027 "FROM pg_proc p "
6028 "LEFT JOIN pg_init_privs pip ON "
6029 "(p.oid = pip.objoid "
6030 "AND pip.classoid = 'pg_proc'::regclass "
6031 "AND pip.objsubid = 0) "
6032 "WHERE %s AND ("
6033 "p.pronamespace != "
6034 "(SELECT oid FROM pg_namespace "
6035 "WHERE nspname = 'pg_catalog') OR "
6036 "p.proacl IS DISTINCT FROM pip.initprivs",
6037 agg_check);
6038 if (dopt->binary_upgrade)
6039 appendPQExpBufferStr(query,
6040 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6041 "classid = 'pg_proc'::regclass AND "
6042 "objid = p.oid AND "
6043 "refclassid = 'pg_extension'::regclass AND "
6044 "deptype = 'e')");
6045 appendPQExpBufferChar(query, ')');
6047 else
6049 appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6050 "pronamespace AS aggnamespace, "
6051 "pronargs, proargtypes, "
6052 "proowner, "
6053 "proacl AS aggacl, "
6054 "acldefault('f', proowner) AS acldefault "
6055 "FROM pg_proc p "
6056 "WHERE proisagg AND ("
6057 "pronamespace != "
6058 "(SELECT oid FROM pg_namespace "
6059 "WHERE nspname = 'pg_catalog')");
6060 if (dopt->binary_upgrade)
6061 appendPQExpBufferStr(query,
6062 " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6063 "classid = 'pg_proc'::regclass AND "
6064 "objid = p.oid AND "
6065 "refclassid = 'pg_extension'::regclass AND "
6066 "deptype = 'e')");
6067 appendPQExpBufferChar(query, ')');
6070 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6072 ntups = PQntuples(res);
6073 *numAggs = ntups;
6075 agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6077 i_tableoid = PQfnumber(res, "tableoid");
6078 i_oid = PQfnumber(res, "oid");
6079 i_aggname = PQfnumber(res, "aggname");
6080 i_aggnamespace = PQfnumber(res, "aggnamespace");
6081 i_pronargs = PQfnumber(res, "pronargs");
6082 i_proargtypes = PQfnumber(res, "proargtypes");
6083 i_proowner = PQfnumber(res, "proowner");
6084 i_aggacl = PQfnumber(res, "aggacl");
6085 i_acldefault = PQfnumber(res, "acldefault");
6087 for (i = 0; i < ntups; i++)
6089 agginfo[i].aggfn.dobj.objType = DO_AGG;
6090 agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6091 agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6092 AssignDumpId(&agginfo[i].aggfn.dobj);
6093 agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6094 agginfo[i].aggfn.dobj.namespace =
6095 findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6096 agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6097 agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6098 agginfo[i].aggfn.dacl.privtype = 0;
6099 agginfo[i].aggfn.dacl.initprivs = NULL;
6100 agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6101 agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6102 agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6103 agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6104 if (agginfo[i].aggfn.nargs == 0)
6105 agginfo[i].aggfn.argtypes = NULL;
6106 else
6108 agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6109 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6110 agginfo[i].aggfn.argtypes,
6111 agginfo[i].aggfn.nargs);
6113 agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6115 /* Decide whether we want to dump it */
6116 selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6118 /* Mark whether aggregate has an ACL */
6119 if (!PQgetisnull(res, i, i_aggacl))
6120 agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6123 PQclear(res);
6125 destroyPQExpBuffer(query);
6127 return agginfo;
6131 * getFuncs:
6132 * read all the user-defined functions in the system catalogs and
6133 * return them in the FuncInfo* structure
6135 * numFuncs is set to the number of functions read in
6137 FuncInfo *
6138 getFuncs(Archive *fout, int *numFuncs)
6140 DumpOptions *dopt = fout->dopt;
6141 PGresult *res;
6142 int ntups;
6143 int i;
6144 PQExpBuffer query = createPQExpBuffer();
6145 FuncInfo *finfo;
6146 int i_tableoid;
6147 int i_oid;
6148 int i_proname;
6149 int i_pronamespace;
6150 int i_proowner;
6151 int i_prolang;
6152 int i_pronargs;
6153 int i_proargtypes;
6154 int i_prorettype;
6155 int i_proacl;
6156 int i_acldefault;
6159 * Find all interesting functions. This is a bit complicated:
6161 * 1. Always exclude aggregates; those are handled elsewhere.
6163 * 2. Always exclude functions that are internally dependent on something
6164 * else, since presumably those will be created as a result of creating
6165 * the something else. This currently acts only to suppress constructor
6166 * functions for range types. Note this is OK only because the
6167 * constructors don't have any dependencies the range type doesn't have;
6168 * otherwise we might not get creation ordering correct.
6170 * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6171 * they're members of extensions and we are in binary-upgrade mode then
6172 * include them, since we want to dump extension members individually in
6173 * that mode. Also, if they are used by casts or transforms then we need
6174 * to gather the information about them, though they won't be dumped if
6175 * they are built-in. Also, in 9.6 and up, include functions in
6176 * pg_catalog if they have an ACL different from what's shown in
6177 * pg_init_privs (so we have to join to pg_init_privs; annoying).
6179 if (fout->remoteVersion >= 90600)
6181 const char *not_agg_check;
6183 not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6184 : "NOT p.proisagg");
6186 appendPQExpBuffer(query,
6187 "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6188 "p.pronargs, p.proargtypes, p.prorettype, "
6189 "p.proacl, "
6190 "acldefault('f', p.proowner) AS acldefault, "
6191 "p.pronamespace, "
6192 "p.proowner "
6193 "FROM pg_proc p "
6194 "LEFT JOIN pg_init_privs pip ON "
6195 "(p.oid = pip.objoid "
6196 "AND pip.classoid = 'pg_proc'::regclass "
6197 "AND pip.objsubid = 0) "
6198 "WHERE %s"
6199 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6200 "WHERE classid = 'pg_proc'::regclass AND "
6201 "objid = p.oid AND deptype = 'i')"
6202 "\n AND ("
6203 "\n pronamespace != "
6204 "(SELECT oid FROM pg_namespace "
6205 "WHERE nspname = 'pg_catalog')"
6206 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6207 "\n WHERE pg_cast.oid > %u "
6208 "\n AND p.oid = pg_cast.castfunc)"
6209 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6210 "\n WHERE pg_transform.oid > %u AND "
6211 "\n (p.oid = pg_transform.trffromsql"
6212 "\n OR p.oid = pg_transform.trftosql))",
6213 not_agg_check,
6214 g_last_builtin_oid,
6215 g_last_builtin_oid);
6216 if (dopt->binary_upgrade)
6217 appendPQExpBufferStr(query,
6218 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6219 "classid = 'pg_proc'::regclass AND "
6220 "objid = p.oid AND "
6221 "refclassid = 'pg_extension'::regclass AND "
6222 "deptype = 'e')");
6223 appendPQExpBufferStr(query,
6224 "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6225 appendPQExpBufferChar(query, ')');
6227 else
6229 appendPQExpBuffer(query,
6230 "SELECT tableoid, oid, proname, prolang, "
6231 "pronargs, proargtypes, prorettype, proacl, "
6232 "acldefault('f', proowner) AS acldefault, "
6233 "pronamespace, "
6234 "proowner "
6235 "FROM pg_proc p "
6236 "WHERE NOT proisagg"
6237 "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6238 "WHERE classid = 'pg_proc'::regclass AND "
6239 "objid = p.oid AND deptype = 'i')"
6240 "\n AND ("
6241 "\n pronamespace != "
6242 "(SELECT oid FROM pg_namespace "
6243 "WHERE nspname = 'pg_catalog')"
6244 "\n OR EXISTS (SELECT 1 FROM pg_cast"
6245 "\n WHERE pg_cast.oid > '%u'::oid"
6246 "\n AND p.oid = pg_cast.castfunc)",
6247 g_last_builtin_oid);
6249 if (fout->remoteVersion >= 90500)
6250 appendPQExpBuffer(query,
6251 "\n OR EXISTS (SELECT 1 FROM pg_transform"
6252 "\n WHERE pg_transform.oid > '%u'::oid"
6253 "\n AND (p.oid = pg_transform.trffromsql"
6254 "\n OR p.oid = pg_transform.trftosql))",
6255 g_last_builtin_oid);
6257 if (dopt->binary_upgrade)
6258 appendPQExpBufferStr(query,
6259 "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6260 "classid = 'pg_proc'::regclass AND "
6261 "objid = p.oid AND "
6262 "refclassid = 'pg_extension'::regclass AND "
6263 "deptype = 'e')");
6264 appendPQExpBufferChar(query, ')');
6267 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6269 ntups = PQntuples(res);
6271 *numFuncs = ntups;
6273 finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6275 i_tableoid = PQfnumber(res, "tableoid");
6276 i_oid = PQfnumber(res, "oid");
6277 i_proname = PQfnumber(res, "proname");
6278 i_pronamespace = PQfnumber(res, "pronamespace");
6279 i_proowner = PQfnumber(res, "proowner");
6280 i_prolang = PQfnumber(res, "prolang");
6281 i_pronargs = PQfnumber(res, "pronargs");
6282 i_proargtypes = PQfnumber(res, "proargtypes");
6283 i_prorettype = PQfnumber(res, "prorettype");
6284 i_proacl = PQfnumber(res, "proacl");
6285 i_acldefault = PQfnumber(res, "acldefault");
6287 for (i = 0; i < ntups; i++)
6289 finfo[i].dobj.objType = DO_FUNC;
6290 finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6291 finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6292 AssignDumpId(&finfo[i].dobj);
6293 finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6294 finfo[i].dobj.namespace =
6295 findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6296 finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6297 finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6298 finfo[i].dacl.privtype = 0;
6299 finfo[i].dacl.initprivs = NULL;
6300 finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6301 finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6302 finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6303 finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6304 if (finfo[i].nargs == 0)
6305 finfo[i].argtypes = NULL;
6306 else
6308 finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6309 parseOidArray(PQgetvalue(res, i, i_proargtypes),
6310 finfo[i].argtypes, finfo[i].nargs);
6312 finfo[i].postponed_def = false; /* might get set during sort */
6314 /* Decide whether we want to dump it */
6315 selectDumpableObject(&(finfo[i].dobj), fout);
6317 /* Mark whether function has an ACL */
6318 if (!PQgetisnull(res, i, i_proacl))
6319 finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6322 PQclear(res);
6324 destroyPQExpBuffer(query);
6326 return finfo;
6330 * getTables
6331 * read all the tables (no indexes) in the system catalogs,
6332 * and return them as an array of TableInfo structures
6334 * *numTables is set to the number of tables read in
6336 TableInfo *
6337 getTables(Archive *fout, int *numTables)
6339 DumpOptions *dopt = fout->dopt;
6340 PGresult *res;
6341 int ntups;
6342 int i;
6343 PQExpBuffer query = createPQExpBuffer();
6344 TableInfo *tblinfo;
6345 int i_reltableoid;
6346 int i_reloid;
6347 int i_relname;
6348 int i_relnamespace;
6349 int i_relkind;
6350 int i_reltype;
6351 int i_relowner;
6352 int i_relchecks;
6353 int i_relhasindex;
6354 int i_relhasrules;
6355 int i_relpages;
6356 int i_toastpages;
6357 int i_owning_tab;
6358 int i_owning_col;
6359 int i_reltablespace;
6360 int i_relhasoids;
6361 int i_relhastriggers;
6362 int i_relpersistence;
6363 int i_relispopulated;
6364 int i_relreplident;
6365 int i_relrowsec;
6366 int i_relforcerowsec;
6367 int i_relfrozenxid;
6368 int i_toastfrozenxid;
6369 int i_toastoid;
6370 int i_relminmxid;
6371 int i_toastminmxid;
6372 int i_reloptions;
6373 int i_checkoption;
6374 int i_toastreloptions;
6375 int i_reloftype;
6376 int i_foreignserver;
6377 int i_amname;
6378 int i_is_identity_sequence;
6379 int i_relacl;
6380 int i_acldefault;
6381 int i_ispartition;
6384 * Find all the tables and table-like objects.
6386 * We must fetch all tables in this phase because otherwise we cannot
6387 * correctly identify inherited columns, owned sequences, etc.
6389 * We include system catalogs, so that we can work if a user table is
6390 * defined to inherit from a system catalog (pretty weird, but...)
6392 * Note: in this phase we should collect only a minimal amount of
6393 * information about each table, basically just enough to decide if it is
6394 * interesting. In particular, since we do not yet have lock on any user
6395 * table, we MUST NOT invoke any server-side data collection functions
6396 * (for instance, pg_get_partkeydef()). Those are likely to fail or give
6397 * wrong answers if any concurrent DDL is happening.
6400 appendPQExpBufferStr(query,
6401 "SELECT c.tableoid, c.oid, c.relname, "
6402 "c.relnamespace, c.relkind, c.reltype, "
6403 "c.relowner, "
6404 "c.relchecks, "
6405 "c.relhasindex, c.relhasrules, c.relpages, "
6406 "c.relhastriggers, "
6407 "c.relpersistence, "
6408 "c.reloftype, "
6409 "c.relacl, "
6410 "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6411 " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
6412 "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
6413 "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6414 "ELSE 0 END AS foreignserver, "
6415 "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
6416 "tc.oid AS toid, "
6417 "tc.relpages AS toastpages, "
6418 "tc.reloptions AS toast_reloptions, "
6419 "d.refobjid AS owning_tab, "
6420 "d.refobjsubid AS owning_col, "
6421 "tsp.spcname AS reltablespace, ");
6423 if (fout->remoteVersion >= 120000)
6424 appendPQExpBufferStr(query,
6425 "false AS relhasoids, ");
6426 else
6427 appendPQExpBufferStr(query,
6428 "c.relhasoids, ");
6430 if (fout->remoteVersion >= 90300)
6431 appendPQExpBufferStr(query,
6432 "c.relispopulated, ");
6433 else
6434 appendPQExpBufferStr(query,
6435 "'t' as relispopulated, ");
6437 if (fout->remoteVersion >= 90400)
6438 appendPQExpBufferStr(query,
6439 "c.relreplident, ");
6440 else
6441 appendPQExpBufferStr(query,
6442 "'d' AS relreplident, ");
6444 if (fout->remoteVersion >= 90500)
6445 appendPQExpBufferStr(query,
6446 "c.relrowsecurity, c.relforcerowsecurity, ");
6447 else
6448 appendPQExpBufferStr(query,
6449 "false AS relrowsecurity, "
6450 "false AS relforcerowsecurity, ");
6452 if (fout->remoteVersion >= 90300)
6453 appendPQExpBufferStr(query,
6454 "c.relminmxid, tc.relminmxid AS tminmxid, ");
6455 else
6456 appendPQExpBufferStr(query,
6457 "0 AS relminmxid, 0 AS tminmxid, ");
6459 if (fout->remoteVersion >= 90300)
6460 appendPQExpBufferStr(query,
6461 "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6462 "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6463 "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
6464 else
6465 appendPQExpBufferStr(query,
6466 "c.reloptions, NULL AS checkoption, ");
6468 if (fout->remoteVersion >= 90600)
6469 appendPQExpBufferStr(query,
6470 "am.amname, ");
6471 else
6472 appendPQExpBufferStr(query,
6473 "NULL AS amname, ");
6475 if (fout->remoteVersion >= 90600)
6476 appendPQExpBufferStr(query,
6477 "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
6478 else
6479 appendPQExpBufferStr(query,
6480 "false AS is_identity_sequence, ");
6482 if (fout->remoteVersion >= 100000)
6483 appendPQExpBufferStr(query,
6484 "c.relispartition AS ispartition ");
6485 else
6486 appendPQExpBufferStr(query,
6487 "false AS ispartition ");
6490 * Left join to pg_depend to pick up dependency info linking sequences to
6491 * their owning column, if any (note this dependency is AUTO except for
6492 * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
6493 * collect the spcname.
6495 appendPQExpBufferStr(query,
6496 "\nFROM pg_class c\n"
6497 "LEFT JOIN pg_depend d ON "
6498 "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
6499 "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
6500 "d.objsubid = 0 AND "
6501 "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
6502 "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
6505 * In 9.6 and up, left join to pg_am to pick up the amname.
6507 if (fout->remoteVersion >= 90600)
6508 appendPQExpBufferStr(query,
6509 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
6512 * We purposefully ignore toast OIDs for partitioned tables; the reason is
6513 * that versions 10 and 11 have them, but later versions do not, so
6514 * emitting them causes the upgrade to fail.
6516 appendPQExpBufferStr(query,
6517 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
6518 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
6519 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
6522 * Restrict to interesting relkinds (in particular, not indexes). Not all
6523 * relkinds are possible in older servers, but it's not worth the trouble
6524 * to emit a version-dependent list.
6526 * Composite-type table entries won't be dumped as such, but we have to
6527 * make a DumpableObject for them so that we can track dependencies of the
6528 * composite type (pg_depend entries for columns of the composite type
6529 * link to the pg_class entry not the pg_type entry).
6531 appendPQExpBufferStr(query,
6532 "WHERE c.relkind IN ("
6533 CppAsString2(RELKIND_RELATION) ", "
6534 CppAsString2(RELKIND_SEQUENCE) ", "
6535 CppAsString2(RELKIND_VIEW) ", "
6536 CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
6537 CppAsString2(RELKIND_MATVIEW) ", "
6538 CppAsString2(RELKIND_FOREIGN_TABLE) ", "
6539 CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
6540 "ORDER BY c.oid");
6542 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6544 ntups = PQntuples(res);
6546 *numTables = ntups;
6549 * Extract data from result and lock dumpable tables. We do the locking
6550 * before anything else, to minimize the window wherein a table could
6551 * disappear under us.
6553 * Note that we have to save info about all tables here, even when dumping
6554 * only one, because we don't yet know which tables might be inheritance
6555 * ancestors of the target table.
6557 tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
6559 i_reltableoid = PQfnumber(res, "tableoid");
6560 i_reloid = PQfnumber(res, "oid");
6561 i_relname = PQfnumber(res, "relname");
6562 i_relnamespace = PQfnumber(res, "relnamespace");
6563 i_relkind = PQfnumber(res, "relkind");
6564 i_reltype = PQfnumber(res, "reltype");
6565 i_relowner = PQfnumber(res, "relowner");
6566 i_relchecks = PQfnumber(res, "relchecks");
6567 i_relhasindex = PQfnumber(res, "relhasindex");
6568 i_relhasrules = PQfnumber(res, "relhasrules");
6569 i_relpages = PQfnumber(res, "relpages");
6570 i_toastpages = PQfnumber(res, "toastpages");
6571 i_owning_tab = PQfnumber(res, "owning_tab");
6572 i_owning_col = PQfnumber(res, "owning_col");
6573 i_reltablespace = PQfnumber(res, "reltablespace");
6574 i_relhasoids = PQfnumber(res, "relhasoids");
6575 i_relhastriggers = PQfnumber(res, "relhastriggers");
6576 i_relpersistence = PQfnumber(res, "relpersistence");
6577 i_relispopulated = PQfnumber(res, "relispopulated");
6578 i_relreplident = PQfnumber(res, "relreplident");
6579 i_relrowsec = PQfnumber(res, "relrowsecurity");
6580 i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
6581 i_relfrozenxid = PQfnumber(res, "relfrozenxid");
6582 i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
6583 i_toastoid = PQfnumber(res, "toid");
6584 i_relminmxid = PQfnumber(res, "relminmxid");
6585 i_toastminmxid = PQfnumber(res, "tminmxid");
6586 i_reloptions = PQfnumber(res, "reloptions");
6587 i_checkoption = PQfnumber(res, "checkoption");
6588 i_toastreloptions = PQfnumber(res, "toast_reloptions");
6589 i_reloftype = PQfnumber(res, "reloftype");
6590 i_foreignserver = PQfnumber(res, "foreignserver");
6591 i_amname = PQfnumber(res, "amname");
6592 i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
6593 i_relacl = PQfnumber(res, "relacl");
6594 i_acldefault = PQfnumber(res, "acldefault");
6595 i_ispartition = PQfnumber(res, "ispartition");
6597 if (dopt->lockWaitTimeout)
6600 * Arrange to fail instead of waiting forever for a table lock.
6602 * NB: this coding assumes that the only queries issued within the
6603 * following loop are LOCK TABLEs; else the timeout may be undesirably
6604 * applied to other things too.
6606 resetPQExpBuffer(query);
6607 appendPQExpBufferStr(query, "SET statement_timeout = ");
6608 appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
6609 ExecuteSqlStatement(fout, query->data);
6612 resetPQExpBuffer(query);
6614 for (i = 0; i < ntups; i++)
6616 tblinfo[i].dobj.objType = DO_TABLE;
6617 tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
6618 tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
6619 AssignDumpId(&tblinfo[i].dobj);
6620 tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
6621 tblinfo[i].dobj.namespace =
6622 findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
6623 tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
6624 tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6625 tblinfo[i].dacl.privtype = 0;
6626 tblinfo[i].dacl.initprivs = NULL;
6627 tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
6628 tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
6629 tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
6630 tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
6631 tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
6632 tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
6633 tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
6634 if (PQgetisnull(res, i, i_toastpages))
6635 tblinfo[i].toastpages = 0;
6636 else
6637 tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
6638 if (PQgetisnull(res, i, i_owning_tab))
6640 tblinfo[i].owning_tab = InvalidOid;
6641 tblinfo[i].owning_col = 0;
6643 else
6645 tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
6646 tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
6648 tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
6649 tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
6650 tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
6651 tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
6652 tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
6653 tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
6654 tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
6655 tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
6656 tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
6657 tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
6658 tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
6659 tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
6660 tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
6661 tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
6662 if (PQgetisnull(res, i, i_checkoption))
6663 tblinfo[i].checkoption = NULL;
6664 else
6665 tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
6666 tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
6667 tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
6668 tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
6669 if (PQgetisnull(res, i, i_amname))
6670 tblinfo[i].amname = NULL;
6671 else
6672 tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
6673 tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
6674 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
6676 /* other fields were zeroed above */
6679 * Decide whether we want to dump this table.
6681 if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
6682 tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
6683 else
6684 selectDumpableTable(&tblinfo[i], fout);
6687 * Now, consider the table "interesting" if we need to dump its
6688 * definition or its data. Later on, we'll skip a lot of data
6689 * collection for uninteresting tables.
6691 * Note: the "interesting" flag will also be set by flagInhTables for
6692 * parents of interesting tables, so that we collect necessary
6693 * inheritance info even when the parents are not themselves being
6694 * dumped. This is the main reason why we need an "interesting" flag
6695 * that's separate from the components-to-dump bitmask.
6697 tblinfo[i].interesting = (tblinfo[i].dobj.dump &
6698 (DUMP_COMPONENT_DEFINITION |
6699 DUMP_COMPONENT_DATA)) != 0;
6701 tblinfo[i].dummy_view = false; /* might get set during sort */
6702 tblinfo[i].postponed_def = false; /* might get set during sort */
6704 /* Tables have data */
6705 tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
6707 /* Mark whether table has an ACL */
6708 if (!PQgetisnull(res, i, i_relacl))
6709 tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6710 tblinfo[i].hascolumnACLs = false; /* may get set later */
6713 * Read-lock target tables to make sure they aren't DROPPED or altered
6714 * in schema before we get around to dumping them.
6716 * Note that we don't explicitly lock parents of the target tables; we
6717 * assume our lock on the child is enough to prevent schema
6718 * alterations to parent tables.
6720 * NOTE: it'd be kinda nice to lock other relations too, not only
6721 * plain or partitioned tables, but the backend doesn't presently
6722 * allow that.
6724 * We only need to lock the table for certain components; see
6725 * pg_dump.h
6727 if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
6728 (tblinfo[i].relkind == RELKIND_RELATION ||
6729 tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
6732 * Tables are locked in batches. When dumping from a remote
6733 * server this can save a significant amount of time by reducing
6734 * the number of round trips.
6736 if (query->len == 0)
6737 appendPQExpBuffer(query, "LOCK TABLE %s",
6738 fmtQualifiedDumpable(&tblinfo[i]));
6739 else
6741 appendPQExpBuffer(query, ", %s",
6742 fmtQualifiedDumpable(&tblinfo[i]));
6744 /* Arbitrarily end a batch when query length reaches 100K. */
6745 if (query->len >= 100000)
6747 /* Lock another batch of tables. */
6748 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
6749 ExecuteSqlStatement(fout, query->data);
6750 resetPQExpBuffer(query);
6756 if (query->len != 0)
6758 /* Lock the tables in the last batch. */
6759 appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
6760 ExecuteSqlStatement(fout, query->data);
6763 if (dopt->lockWaitTimeout)
6765 ExecuteSqlStatement(fout, "SET statement_timeout = 0");
6768 PQclear(res);
6770 destroyPQExpBuffer(query);
6772 return tblinfo;
6776 * getOwnedSeqs
6777 * identify owned sequences and mark them as dumpable if owning table is
6779 * We used to do this in getTables(), but it's better to do it after the
6780 * index used by findTableByOid() has been set up.
6782 void
6783 getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
6785 int i;
6788 * Force sequences that are "owned" by table columns to be dumped whenever
6789 * their owning table is being dumped.
6791 for (i = 0; i < numTables; i++)
6793 TableInfo *seqinfo = &tblinfo[i];
6794 TableInfo *owning_tab;
6796 if (!OidIsValid(seqinfo->owning_tab))
6797 continue; /* not an owned sequence */
6799 owning_tab = findTableByOid(seqinfo->owning_tab);
6800 if (owning_tab == NULL)
6801 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
6802 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
6805 * Only dump identity sequences if we're going to dump the table that
6806 * it belongs to.
6808 if (owning_tab->dobj.dump == DUMP_COMPONENT_NONE &&
6809 seqinfo->is_identity_sequence)
6811 seqinfo->dobj.dump = DUMP_COMPONENT_NONE;
6812 continue;
6816 * Otherwise we need to dump the components that are being dumped for
6817 * the table and any components which the sequence is explicitly
6818 * marked with.
6820 * We can't simply use the set of components which are being dumped
6821 * for the table as the table might be in an extension (and only the
6822 * non-extension components, eg: ACLs if changed, security labels, and
6823 * policies, are being dumped) while the sequence is not (and
6824 * therefore the definition and other components should also be
6825 * dumped).
6827 * If the sequence is part of the extension then it should be properly
6828 * marked by checkExtensionMembership() and this will be a no-op as
6829 * the table will be equivalently marked.
6831 seqinfo->dobj.dump = seqinfo->dobj.dump | owning_tab->dobj.dump;
6833 if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
6834 seqinfo->interesting = true;
6839 * getInherits
6840 * read all the inheritance information
6841 * from the system catalogs return them in the InhInfo* structure
6843 * numInherits is set to the number of pairs read in
6845 InhInfo *
6846 getInherits(Archive *fout, int *numInherits)
6848 PGresult *res;
6849 int ntups;
6850 int i;
6851 PQExpBuffer query = createPQExpBuffer();
6852 InhInfo *inhinfo;
6854 int i_inhrelid;
6855 int i_inhparent;
6857 /* find all the inheritance information */
6858 appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
6860 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6862 ntups = PQntuples(res);
6864 *numInherits = ntups;
6866 inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
6868 i_inhrelid = PQfnumber(res, "inhrelid");
6869 i_inhparent = PQfnumber(res, "inhparent");
6871 for (i = 0; i < ntups; i++)
6873 inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
6874 inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
6877 PQclear(res);
6879 destroyPQExpBuffer(query);
6881 return inhinfo;
6885 * getPartitioningInfo
6886 * get information about partitioning
6888 * For the most part, we only collect partitioning info about tables we
6889 * intend to dump. However, this function has to consider all partitioned
6890 * tables in the database, because we need to know about parents of partitions
6891 * we are going to dump even if the parents themselves won't be dumped.
6893 * Specifically, what we need to know is whether each partitioned table
6894 * has an "unsafe" partitioning scheme that requires us to force
6895 * load-via-partition-root mode for its children. Currently the only case
6896 * for which we force that is hash partitioning on enum columns, since the
6897 * hash codes depend on enum value OIDs which won't be replicated across
6898 * dump-and-reload. There are other cases in which load-via-partition-root
6899 * might be necessary, but we expect users to cope with them.
6901 void
6902 getPartitioningInfo(Archive *fout)
6904 PQExpBuffer query;
6905 PGresult *res;
6906 int ntups;
6908 /* hash partitioning didn't exist before v11 */
6909 if (fout->remoteVersion < 110000)
6910 return;
6911 /* needn't bother if schema-only dump */
6912 if (fout->dopt->schemaOnly)
6913 return;
6915 query = createPQExpBuffer();
6918 * Unsafe partitioning schemes are exactly those for which hash enum_ops
6919 * appears among the partition opclasses. We needn't check partstrat.
6921 * Note that this query may well retrieve info about tables we aren't
6922 * going to dump and hence have no lock on. That's okay since we need not
6923 * invoke any unsafe server-side functions.
6925 appendPQExpBufferStr(query,
6926 "SELECT partrelid FROM pg_partitioned_table WHERE\n"
6927 "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
6928 "ON c.opcmethod = a.oid\n"
6929 "WHERE opcname = 'enum_ops' "
6930 "AND opcnamespace = 'pg_catalog'::regnamespace "
6931 "AND amname = 'hash') = ANY(partclass)");
6933 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6935 ntups = PQntuples(res);
6937 for (int i = 0; i < ntups; i++)
6939 Oid tabrelid = atooid(PQgetvalue(res, i, 0));
6940 TableInfo *tbinfo;
6942 tbinfo = findTableByOid(tabrelid);
6943 if (tbinfo == NULL)
6944 pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
6945 tabrelid);
6946 tbinfo->unsafe_partitions = true;
6949 PQclear(res);
6951 destroyPQExpBuffer(query);
6955 * getIndexes
6956 * get information about every index on a dumpable table
6958 * Note: index data is not returned directly to the caller, but it
6959 * does get entered into the DumpableObject tables.
6961 void
6962 getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
6964 PQExpBuffer query = createPQExpBuffer();
6965 PQExpBuffer tbloids = createPQExpBuffer();
6966 PGresult *res;
6967 int ntups;
6968 int curtblindx;
6969 IndxInfo *indxinfo;
6970 int i_tableoid,
6971 i_oid,
6972 i_indrelid,
6973 i_indexname,
6974 i_parentidx,
6975 i_indexdef,
6976 i_indnkeyatts,
6977 i_indnatts,
6978 i_indkey,
6979 i_indisclustered,
6980 i_indisreplident,
6981 i_indnullsnotdistinct,
6982 i_contype,
6983 i_conname,
6984 i_condeferrable,
6985 i_condeferred,
6986 i_contableoid,
6987 i_conoid,
6988 i_condef,
6989 i_tablespace,
6990 i_indreloptions,
6991 i_indstatcols,
6992 i_indstatvals;
6995 * We want to perform just one query against pg_index. However, we
6996 * mustn't try to select every row of the catalog and then sort it out on
6997 * the client side, because some of the server-side functions we need
6998 * would be unsafe to apply to tables we don't have lock on. Hence, we
6999 * build an array of the OIDs of tables we care about (and now have lock
7000 * on!), and use a WHERE clause to constrain which rows are selected.
7002 appendPQExpBufferChar(tbloids, '{');
7003 for (int i = 0; i < numTables; i++)
7005 TableInfo *tbinfo = &tblinfo[i];
7007 if (!tbinfo->hasindex)
7008 continue;
7011 * We can ignore indexes of uninteresting tables.
7013 if (!tbinfo->interesting)
7014 continue;
7016 /* OK, we need info for this table */
7017 if (tbloids->len > 1) /* do we have more than the '{'? */
7018 appendPQExpBufferChar(tbloids, ',');
7019 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7021 appendPQExpBufferChar(tbloids, '}');
7023 appendPQExpBufferStr(query,
7024 "SELECT t.tableoid, t.oid, i.indrelid, "
7025 "t.relname AS indexname, "
7026 "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7027 "i.indkey, i.indisclustered, "
7028 "c.contype, c.conname, "
7029 "c.condeferrable, c.condeferred, "
7030 "c.tableoid AS contableoid, "
7031 "c.oid AS conoid, "
7032 "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7033 "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7034 "t.reloptions AS indreloptions, ");
7037 if (fout->remoteVersion >= 90400)
7038 appendPQExpBufferStr(query,
7039 "i.indisreplident, ");
7040 else
7041 appendPQExpBufferStr(query,
7042 "false AS indisreplident, ");
7044 if (fout->remoteVersion >= 110000)
7045 appendPQExpBufferStr(query,
7046 "inh.inhparent AS parentidx, "
7047 "i.indnkeyatts AS indnkeyatts, "
7048 "i.indnatts AS indnatts, "
7049 "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7050 " FROM pg_catalog.pg_attribute "
7051 " WHERE attrelid = i.indexrelid AND "
7052 " attstattarget >= 0) AS indstatcols, "
7053 "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7054 " FROM pg_catalog.pg_attribute "
7055 " WHERE attrelid = i.indexrelid AND "
7056 " attstattarget >= 0) AS indstatvals, ");
7057 else
7058 appendPQExpBufferStr(query,
7059 "0 AS parentidx, "
7060 "i.indnatts AS indnkeyatts, "
7061 "i.indnatts AS indnatts, "
7062 "'' AS indstatcols, "
7063 "'' AS indstatvals, ");
7065 if (fout->remoteVersion >= 150000)
7066 appendPQExpBufferStr(query,
7067 "i.indnullsnotdistinct ");
7068 else
7069 appendPQExpBufferStr(query,
7070 "false AS indnullsnotdistinct ");
7073 * The point of the messy-looking outer join is to find a constraint that
7074 * is related by an internal dependency link to the index. If we find one,
7075 * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7076 * index won't have more than one internal dependency.
7078 * Note: the check on conrelid is redundant, but useful because that
7079 * column is indexed while conindid is not.
7081 if (fout->remoteVersion >= 110000)
7083 appendPQExpBuffer(query,
7084 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7085 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7086 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7087 "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7088 "LEFT JOIN pg_catalog.pg_constraint c "
7089 "ON (i.indrelid = c.conrelid AND "
7090 "i.indexrelid = c.conindid AND "
7091 "c.contype IN ('p','u','x')) "
7092 "LEFT JOIN pg_catalog.pg_inherits inh "
7093 "ON (inh.inhrelid = indexrelid) "
7094 "WHERE (i.indisvalid OR t2.relkind = 'p') "
7095 "AND i.indisready "
7096 "ORDER BY i.indrelid, indexname",
7097 tbloids->data);
7099 else
7102 * the test on indisready is necessary in 9.2, and harmless in
7103 * earlier/later versions
7105 appendPQExpBuffer(query,
7106 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7107 "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7108 "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7109 "LEFT JOIN pg_catalog.pg_constraint c "
7110 "ON (i.indrelid = c.conrelid AND "
7111 "i.indexrelid = c.conindid AND "
7112 "c.contype IN ('p','u','x')) "
7113 "WHERE i.indisvalid AND i.indisready "
7114 "ORDER BY i.indrelid, indexname",
7115 tbloids->data);
7118 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7120 ntups = PQntuples(res);
7122 i_tableoid = PQfnumber(res, "tableoid");
7123 i_oid = PQfnumber(res, "oid");
7124 i_indrelid = PQfnumber(res, "indrelid");
7125 i_indexname = PQfnumber(res, "indexname");
7126 i_parentidx = PQfnumber(res, "parentidx");
7127 i_indexdef = PQfnumber(res, "indexdef");
7128 i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7129 i_indnatts = PQfnumber(res, "indnatts");
7130 i_indkey = PQfnumber(res, "indkey");
7131 i_indisclustered = PQfnumber(res, "indisclustered");
7132 i_indisreplident = PQfnumber(res, "indisreplident");
7133 i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7134 i_contype = PQfnumber(res, "contype");
7135 i_conname = PQfnumber(res, "conname");
7136 i_condeferrable = PQfnumber(res, "condeferrable");
7137 i_condeferred = PQfnumber(res, "condeferred");
7138 i_contableoid = PQfnumber(res, "contableoid");
7139 i_conoid = PQfnumber(res, "conoid");
7140 i_condef = PQfnumber(res, "condef");
7141 i_tablespace = PQfnumber(res, "tablespace");
7142 i_indreloptions = PQfnumber(res, "indreloptions");
7143 i_indstatcols = PQfnumber(res, "indstatcols");
7144 i_indstatvals = PQfnumber(res, "indstatvals");
7146 indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7149 * Outer loop iterates once per table, not once per row. Incrementing of
7150 * j is handled by the inner loop.
7152 curtblindx = -1;
7153 for (int j = 0; j < ntups;)
7155 Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7156 TableInfo *tbinfo = NULL;
7157 int numinds;
7159 /* Count rows for this table */
7160 for (numinds = 1; numinds < ntups - j; numinds++)
7161 if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7162 break;
7165 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7166 * order.
7168 while (++curtblindx < numTables)
7170 tbinfo = &tblinfo[curtblindx];
7171 if (tbinfo->dobj.catId.oid == indrelid)
7172 break;
7174 if (curtblindx >= numTables)
7175 pg_fatal("unrecognized table OID %u", indrelid);
7176 /* cross-check that we only got requested tables */
7177 if (!tbinfo->hasindex ||
7178 !tbinfo->interesting)
7179 pg_fatal("unexpected index data for table \"%s\"",
7180 tbinfo->dobj.name);
7182 /* Save data for this table */
7183 tbinfo->indexes = indxinfo + j;
7184 tbinfo->numIndexes = numinds;
7186 for (int c = 0; c < numinds; c++, j++)
7188 char contype;
7190 indxinfo[j].dobj.objType = DO_INDEX;
7191 indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7192 indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7193 AssignDumpId(&indxinfo[j].dobj);
7194 indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7195 indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7196 indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7197 indxinfo[j].indextable = tbinfo;
7198 indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7199 indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7200 indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7201 indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7202 indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7203 indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7204 indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7205 indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7206 parseOidArray(PQgetvalue(res, j, i_indkey),
7207 indxinfo[j].indkeys, indxinfo[j].indnattrs);
7208 indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7209 indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7210 indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7211 indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7212 indxinfo[j].partattaches = (SimplePtrList)
7214 NULL, NULL
7216 contype = *(PQgetvalue(res, j, i_contype));
7218 if (contype == 'p' || contype == 'u' || contype == 'x')
7221 * If we found a constraint matching the index, create an
7222 * entry for it.
7224 ConstraintInfo *constrinfo;
7226 constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7227 constrinfo->dobj.objType = DO_CONSTRAINT;
7228 constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7229 constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7230 AssignDumpId(&constrinfo->dobj);
7231 constrinfo->dobj.dump = tbinfo->dobj.dump;
7232 constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7233 constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7234 constrinfo->contable = tbinfo;
7235 constrinfo->condomain = NULL;
7236 constrinfo->contype = contype;
7237 if (contype == 'x')
7238 constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7239 else
7240 constrinfo->condef = NULL;
7241 constrinfo->confrelid = InvalidOid;
7242 constrinfo->conindex = indxinfo[j].dobj.dumpId;
7243 constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7244 constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7245 constrinfo->conislocal = true;
7246 constrinfo->separate = true;
7248 indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7250 else
7252 /* Plain secondary index */
7253 indxinfo[j].indexconstraint = 0;
7258 PQclear(res);
7260 destroyPQExpBuffer(query);
7261 destroyPQExpBuffer(tbloids);
7265 * getExtendedStatistics
7266 * get information about extended-statistics objects.
7268 * Note: extended statistics data is not returned directly to the caller, but
7269 * it does get entered into the DumpableObject tables.
7271 void
7272 getExtendedStatistics(Archive *fout)
7274 PQExpBuffer query;
7275 PGresult *res;
7276 StatsExtInfo *statsextinfo;
7277 int ntups;
7278 int i_tableoid;
7279 int i_oid;
7280 int i_stxname;
7281 int i_stxnamespace;
7282 int i_stxowner;
7283 int i_stattarget;
7284 int i;
7286 /* Extended statistics were new in v10 */
7287 if (fout->remoteVersion < 100000)
7288 return;
7290 query = createPQExpBuffer();
7292 if (fout->remoteVersion < 130000)
7293 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7294 "stxnamespace, stxowner, (-1) AS stxstattarget "
7295 "FROM pg_catalog.pg_statistic_ext");
7296 else
7297 appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7298 "stxnamespace, stxowner, stxstattarget "
7299 "FROM pg_catalog.pg_statistic_ext");
7301 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7303 ntups = PQntuples(res);
7305 i_tableoid = PQfnumber(res, "tableoid");
7306 i_oid = PQfnumber(res, "oid");
7307 i_stxname = PQfnumber(res, "stxname");
7308 i_stxnamespace = PQfnumber(res, "stxnamespace");
7309 i_stxowner = PQfnumber(res, "stxowner");
7310 i_stattarget = PQfnumber(res, "stxstattarget");
7312 statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7314 for (i = 0; i < ntups; i++)
7316 statsextinfo[i].dobj.objType = DO_STATSEXT;
7317 statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7318 statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7319 AssignDumpId(&statsextinfo[i].dobj);
7320 statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7321 statsextinfo[i].dobj.namespace =
7322 findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
7323 statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
7324 statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
7326 /* Decide whether we want to dump it */
7327 selectDumpableObject(&(statsextinfo[i].dobj), fout);
7330 PQclear(res);
7331 destroyPQExpBuffer(query);
7335 * getConstraints
7337 * Get info about constraints on dumpable tables.
7339 * Currently handles foreign keys only.
7340 * Unique and primary key constraints are handled with indexes,
7341 * while check constraints are processed in getTableAttrs().
7343 void
7344 getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7346 PQExpBuffer query = createPQExpBuffer();
7347 PQExpBuffer tbloids = createPQExpBuffer();
7348 PGresult *res;
7349 int ntups;
7350 int curtblindx;
7351 TableInfo *tbinfo = NULL;
7352 ConstraintInfo *constrinfo;
7353 int i_contableoid,
7354 i_conoid,
7355 i_conrelid,
7356 i_conname,
7357 i_confrelid,
7358 i_conindid,
7359 i_condef;
7362 * We want to perform just one query against pg_constraint. However, we
7363 * mustn't try to select every row of the catalog and then sort it out on
7364 * the client side, because some of the server-side functions we need
7365 * would be unsafe to apply to tables we don't have lock on. Hence, we
7366 * build an array of the OIDs of tables we care about (and now have lock
7367 * on!), and use a WHERE clause to constrain which rows are selected.
7369 appendPQExpBufferChar(tbloids, '{');
7370 for (int i = 0; i < numTables; i++)
7372 TableInfo *tinfo = &tblinfo[i];
7375 * For partitioned tables, foreign keys have no triggers so they must
7376 * be included anyway in case some foreign keys are defined.
7378 if ((!tinfo->hastriggers &&
7379 tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7380 !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7381 continue;
7383 /* OK, we need info for this table */
7384 if (tbloids->len > 1) /* do we have more than the '{'? */
7385 appendPQExpBufferChar(tbloids, ',');
7386 appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
7388 appendPQExpBufferChar(tbloids, '}');
7390 appendPQExpBufferStr(query,
7391 "SELECT c.tableoid, c.oid, "
7392 "conrelid, conname, confrelid, ");
7393 if (fout->remoteVersion >= 110000)
7394 appendPQExpBufferStr(query, "conindid, ");
7395 else
7396 appendPQExpBufferStr(query, "0 AS conindid, ");
7397 appendPQExpBuffer(query,
7398 "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
7399 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7400 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
7401 "WHERE contype = 'f' ",
7402 tbloids->data);
7403 if (fout->remoteVersion >= 110000)
7404 appendPQExpBufferStr(query,
7405 "AND conparentid = 0 ");
7406 appendPQExpBufferStr(query,
7407 "ORDER BY conrelid, conname");
7409 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7411 ntups = PQntuples(res);
7413 i_contableoid = PQfnumber(res, "tableoid");
7414 i_conoid = PQfnumber(res, "oid");
7415 i_conrelid = PQfnumber(res, "conrelid");
7416 i_conname = PQfnumber(res, "conname");
7417 i_confrelid = PQfnumber(res, "confrelid");
7418 i_conindid = PQfnumber(res, "conindid");
7419 i_condef = PQfnumber(res, "condef");
7421 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7423 curtblindx = -1;
7424 for (int j = 0; j < ntups; j++)
7426 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
7427 TableInfo *reftable;
7430 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7431 * order.
7433 if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
7435 while (++curtblindx < numTables)
7437 tbinfo = &tblinfo[curtblindx];
7438 if (tbinfo->dobj.catId.oid == conrelid)
7439 break;
7441 if (curtblindx >= numTables)
7442 pg_fatal("unrecognized table OID %u", conrelid);
7445 constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
7446 constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7447 constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7448 AssignDumpId(&constrinfo[j].dobj);
7449 constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7450 constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7451 constrinfo[j].contable = tbinfo;
7452 constrinfo[j].condomain = NULL;
7453 constrinfo[j].contype = 'f';
7454 constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7455 constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
7456 constrinfo[j].conindex = 0;
7457 constrinfo[j].condeferrable = false;
7458 constrinfo[j].condeferred = false;
7459 constrinfo[j].conislocal = true;
7460 constrinfo[j].separate = true;
7463 * Restoring an FK that points to a partitioned table requires that
7464 * all partition indexes have been attached beforehand. Ensure that
7465 * happens by making the constraint depend on each index partition
7466 * attach object.
7468 reftable = findTableByOid(constrinfo[j].confrelid);
7469 if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
7471 Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
7473 if (indexOid != InvalidOid)
7475 for (int k = 0; k < reftable->numIndexes; k++)
7477 IndxInfo *refidx;
7479 /* not our index? */
7480 if (reftable->indexes[k].dobj.catId.oid != indexOid)
7481 continue;
7483 refidx = &reftable->indexes[k];
7484 addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
7485 break;
7491 PQclear(res);
7493 destroyPQExpBuffer(query);
7494 destroyPQExpBuffer(tbloids);
7498 * addConstrChildIdxDeps
7500 * Recursive subroutine for getConstraints
7502 * Given an object representing a foreign key constraint and an index on the
7503 * partitioned table it references, mark the constraint object as dependent
7504 * on the DO_INDEX_ATTACH object of each index partition, recursively
7505 * drilling down to their partitions if any. This ensures that the FK is not
7506 * restored until the index is fully marked valid.
7508 static void
7509 addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
7511 SimplePtrListCell *cell;
7513 Assert(dobj->objType == DO_FK_CONSTRAINT);
7515 for (cell = refidx->partattaches.head; cell; cell = cell->next)
7517 IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
7519 addObjectDependency(dobj, attach->dobj.dumpId);
7521 if (attach->partitionIdx->partattaches.head != NULL)
7522 addConstrChildIdxDeps(dobj, attach->partitionIdx);
7527 * getDomainConstraints
7529 * Get info about constraints on a domain.
7531 static void
7532 getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
7534 int i;
7535 ConstraintInfo *constrinfo;
7536 PQExpBuffer query = createPQExpBuffer();
7537 PGresult *res;
7538 int i_tableoid,
7539 i_oid,
7540 i_conname,
7541 i_consrc;
7542 int ntups;
7544 if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
7546 /* Set up query for constraint-specific details */
7547 appendPQExpBufferStr(query,
7548 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
7549 "SELECT tableoid, oid, conname, "
7550 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
7551 "convalidated "
7552 "FROM pg_catalog.pg_constraint "
7553 "WHERE contypid = $1 "
7554 "ORDER BY conname");
7556 ExecuteSqlStatement(fout, query->data);
7558 fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
7561 printfPQExpBuffer(query,
7562 "EXECUTE getDomainConstraints('%u')",
7563 tyinfo->dobj.catId.oid);
7565 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7567 ntups = PQntuples(res);
7569 i_tableoid = PQfnumber(res, "tableoid");
7570 i_oid = PQfnumber(res, "oid");
7571 i_conname = PQfnumber(res, "conname");
7572 i_consrc = PQfnumber(res, "consrc");
7574 constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7576 tyinfo->nDomChecks = ntups;
7577 tyinfo->domChecks = constrinfo;
7579 for (i = 0; i < ntups; i++)
7581 bool validated = PQgetvalue(res, i, 4)[0] == 't';
7583 constrinfo[i].dobj.objType = DO_CONSTRAINT;
7584 constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7585 constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7586 AssignDumpId(&constrinfo[i].dobj);
7587 constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
7588 constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
7589 constrinfo[i].contable = NULL;
7590 constrinfo[i].condomain = tyinfo;
7591 constrinfo[i].contype = 'c';
7592 constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
7593 constrinfo[i].confrelid = InvalidOid;
7594 constrinfo[i].conindex = 0;
7595 constrinfo[i].condeferrable = false;
7596 constrinfo[i].condeferred = false;
7597 constrinfo[i].conislocal = true;
7599 constrinfo[i].separate = !validated;
7602 * Make the domain depend on the constraint, ensuring it won't be
7603 * output till any constraint dependencies are OK. If the constraint
7604 * has not been validated, it's going to be dumped after the domain
7605 * anyway, so this doesn't matter.
7607 if (validated)
7608 addObjectDependency(&tyinfo->dobj,
7609 constrinfo[i].dobj.dumpId);
7612 PQclear(res);
7614 destroyPQExpBuffer(query);
7618 * getRules
7619 * get basic information about every rule in the system
7621 * numRules is set to the number of rules read in
7623 RuleInfo *
7624 getRules(Archive *fout, int *numRules)
7626 PGresult *res;
7627 int ntups;
7628 int i;
7629 PQExpBuffer query = createPQExpBuffer();
7630 RuleInfo *ruleinfo;
7631 int i_tableoid;
7632 int i_oid;
7633 int i_rulename;
7634 int i_ruletable;
7635 int i_ev_type;
7636 int i_is_instead;
7637 int i_ev_enabled;
7639 appendPQExpBufferStr(query, "SELECT "
7640 "tableoid, oid, rulename, "
7641 "ev_class AS ruletable, ev_type, is_instead, "
7642 "ev_enabled "
7643 "FROM pg_rewrite "
7644 "ORDER BY oid");
7646 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7648 ntups = PQntuples(res);
7650 *numRules = ntups;
7652 ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
7654 i_tableoid = PQfnumber(res, "tableoid");
7655 i_oid = PQfnumber(res, "oid");
7656 i_rulename = PQfnumber(res, "rulename");
7657 i_ruletable = PQfnumber(res, "ruletable");
7658 i_ev_type = PQfnumber(res, "ev_type");
7659 i_is_instead = PQfnumber(res, "is_instead");
7660 i_ev_enabled = PQfnumber(res, "ev_enabled");
7662 for (i = 0; i < ntups; i++)
7664 Oid ruletableoid;
7666 ruleinfo[i].dobj.objType = DO_RULE;
7667 ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7668 ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7669 AssignDumpId(&ruleinfo[i].dobj);
7670 ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
7671 ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
7672 ruleinfo[i].ruletable = findTableByOid(ruletableoid);
7673 if (ruleinfo[i].ruletable == NULL)
7674 pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
7675 ruletableoid, ruleinfo[i].dobj.catId.oid);
7676 ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
7677 ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
7678 ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
7679 ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
7680 ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
7681 if (ruleinfo[i].ruletable)
7684 * If the table is a view or materialized view, force its ON
7685 * SELECT rule to be sorted before the view itself --- this
7686 * ensures that any dependencies for the rule affect the table's
7687 * positioning. Other rules are forced to appear after their
7688 * table.
7690 if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
7691 ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
7692 ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
7694 addObjectDependency(&ruleinfo[i].ruletable->dobj,
7695 ruleinfo[i].dobj.dumpId);
7696 /* We'll merge the rule into CREATE VIEW, if possible */
7697 ruleinfo[i].separate = false;
7699 else
7701 addObjectDependency(&ruleinfo[i].dobj,
7702 ruleinfo[i].ruletable->dobj.dumpId);
7703 ruleinfo[i].separate = true;
7706 else
7707 ruleinfo[i].separate = true;
7710 PQclear(res);
7712 destroyPQExpBuffer(query);
7714 return ruleinfo;
7718 * getTriggers
7719 * get information about every trigger on a dumpable table
7721 * Note: trigger data is not returned directly to the caller, but it
7722 * does get entered into the DumpableObject tables.
7724 void
7725 getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
7727 PQExpBuffer query = createPQExpBuffer();
7728 PQExpBuffer tbloids = createPQExpBuffer();
7729 PGresult *res;
7730 int ntups;
7731 int curtblindx;
7732 TriggerInfo *tginfo;
7733 int i_tableoid,
7734 i_oid,
7735 i_tgrelid,
7736 i_tgname,
7737 i_tgfname,
7738 i_tgtype,
7739 i_tgnargs,
7740 i_tgargs,
7741 i_tgisconstraint,
7742 i_tgconstrname,
7743 i_tgconstrrelid,
7744 i_tgconstrrelname,
7745 i_tgenabled,
7746 i_tgispartition,
7747 i_tgdeferrable,
7748 i_tginitdeferred,
7749 i_tgdef;
7752 * We want to perform just one query against pg_trigger. However, we
7753 * mustn't try to select every row of the catalog and then sort it out on
7754 * the client side, because some of the server-side functions we need
7755 * would be unsafe to apply to tables we don't have lock on. Hence, we
7756 * build an array of the OIDs of tables we care about (and now have lock
7757 * on!), and use a WHERE clause to constrain which rows are selected.
7759 appendPQExpBufferChar(tbloids, '{');
7760 for (int i = 0; i < numTables; i++)
7762 TableInfo *tbinfo = &tblinfo[i];
7764 if (!tbinfo->hastriggers ||
7765 !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7766 continue;
7768 /* OK, we need info for this table */
7769 if (tbloids->len > 1) /* do we have more than the '{'? */
7770 appendPQExpBufferChar(tbloids, ',');
7771 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7773 appendPQExpBufferChar(tbloids, '}');
7775 if (fout->remoteVersion >= 150000)
7778 * NB: think not to use pretty=true in pg_get_triggerdef. It could
7779 * result in non-forward-compatible dumps of WHEN clauses due to
7780 * under-parenthesization.
7782 * NB: We need to see partition triggers in case the tgenabled flag
7783 * has been changed from the parent.
7785 appendPQExpBuffer(query,
7786 "SELECT t.tgrelid, t.tgname, "
7787 "t.tgfoid::pg_catalog.regproc AS tgfname, "
7788 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
7789 "t.tgenabled, t.tableoid, t.oid, "
7790 "t.tgparentid <> 0 AS tgispartition\n"
7791 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7792 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
7793 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
7794 "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
7795 "OR t.tgenabled != u.tgenabled) "
7796 "ORDER BY t.tgrelid, t.tgname",
7797 tbloids->data);
7799 else if (fout->remoteVersion >= 130000)
7802 * NB: think not to use pretty=true in pg_get_triggerdef. It could
7803 * result in non-forward-compatible dumps of WHEN clauses due to
7804 * under-parenthesization.
7806 * NB: We need to see tgisinternal triggers in partitions, in case the
7807 * tgenabled flag has been changed from the parent.
7809 appendPQExpBuffer(query,
7810 "SELECT t.tgrelid, t.tgname, "
7811 "t.tgfoid::pg_catalog.regproc AS tgfname, "
7812 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
7813 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
7814 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7815 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
7816 "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
7817 "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
7818 "ORDER BY t.tgrelid, t.tgname",
7819 tbloids->data);
7821 else if (fout->remoteVersion >= 110000)
7824 * NB: We need to see tgisinternal triggers in partitions, in case the
7825 * tgenabled flag has been changed from the parent. No tgparentid in
7826 * version 11-12, so we have to match them via pg_depend.
7828 * See above about pretty=true in pg_get_triggerdef.
7830 appendPQExpBuffer(query,
7831 "SELECT t.tgrelid, t.tgname, "
7832 "t.tgfoid::pg_catalog.regproc AS tgfname, "
7833 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
7834 "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
7835 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7836 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
7837 "LEFT JOIN pg_catalog.pg_depend AS d ON "
7838 " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
7839 " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
7840 " d.objid = t.oid "
7841 "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
7842 "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
7843 "ORDER BY t.tgrelid, t.tgname",
7844 tbloids->data);
7846 else
7848 /* See above about pretty=true in pg_get_triggerdef */
7849 appendPQExpBuffer(query,
7850 "SELECT t.tgrelid, t.tgname, "
7851 "t.tgfoid::pg_catalog.regproc AS tgfname, "
7852 "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
7853 "t.tgenabled, false as tgispartition, "
7854 "t.tableoid, t.oid "
7855 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7856 "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
7857 "WHERE NOT tgisinternal "
7858 "ORDER BY t.tgrelid, t.tgname",
7859 tbloids->data);
7862 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7864 ntups = PQntuples(res);
7866 i_tableoid = PQfnumber(res, "tableoid");
7867 i_oid = PQfnumber(res, "oid");
7868 i_tgrelid = PQfnumber(res, "tgrelid");
7869 i_tgname = PQfnumber(res, "tgname");
7870 i_tgfname = PQfnumber(res, "tgfname");
7871 i_tgtype = PQfnumber(res, "tgtype");
7872 i_tgnargs = PQfnumber(res, "tgnargs");
7873 i_tgargs = PQfnumber(res, "tgargs");
7874 i_tgisconstraint = PQfnumber(res, "tgisconstraint");
7875 i_tgconstrname = PQfnumber(res, "tgconstrname");
7876 i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
7877 i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
7878 i_tgenabled = PQfnumber(res, "tgenabled");
7879 i_tgispartition = PQfnumber(res, "tgispartition");
7880 i_tgdeferrable = PQfnumber(res, "tgdeferrable");
7881 i_tginitdeferred = PQfnumber(res, "tginitdeferred");
7882 i_tgdef = PQfnumber(res, "tgdef");
7884 tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
7887 * Outer loop iterates once per table, not once per row. Incrementing of
7888 * j is handled by the inner loop.
7890 curtblindx = -1;
7891 for (int j = 0; j < ntups;)
7893 Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
7894 TableInfo *tbinfo = NULL;
7895 int numtrigs;
7897 /* Count rows for this table */
7898 for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
7899 if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
7900 break;
7903 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7904 * order.
7906 while (++curtblindx < numTables)
7908 tbinfo = &tblinfo[curtblindx];
7909 if (tbinfo->dobj.catId.oid == tgrelid)
7910 break;
7912 if (curtblindx >= numTables)
7913 pg_fatal("unrecognized table OID %u", tgrelid);
7915 /* Save data for this table */
7916 tbinfo->triggers = tginfo + j;
7917 tbinfo->numTriggers = numtrigs;
7919 for (int c = 0; c < numtrigs; c++, j++)
7921 tginfo[j].dobj.objType = DO_TRIGGER;
7922 tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7923 tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7924 AssignDumpId(&tginfo[j].dobj);
7925 tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
7926 tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
7927 tginfo[j].tgtable = tbinfo;
7928 tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
7929 tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
7930 if (i_tgdef >= 0)
7932 tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
7934 /* remaining fields are not valid if we have tgdef */
7935 tginfo[j].tgfname = NULL;
7936 tginfo[j].tgtype = 0;
7937 tginfo[j].tgnargs = 0;
7938 tginfo[j].tgargs = NULL;
7939 tginfo[j].tgisconstraint = false;
7940 tginfo[j].tgdeferrable = false;
7941 tginfo[j].tginitdeferred = false;
7942 tginfo[j].tgconstrname = NULL;
7943 tginfo[j].tgconstrrelid = InvalidOid;
7944 tginfo[j].tgconstrrelname = NULL;
7946 else
7948 tginfo[j].tgdef = NULL;
7950 tginfo[j].tgfname = pg_strdup(PQgetvalue(res, j, i_tgfname));
7951 tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
7952 tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
7953 tginfo[j].tgargs = pg_strdup(PQgetvalue(res, j, i_tgargs));
7954 tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
7955 tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
7956 tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
7958 if (tginfo[j].tgisconstraint)
7960 tginfo[j].tgconstrname = pg_strdup(PQgetvalue(res, j, i_tgconstrname));
7961 tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
7962 if (OidIsValid(tginfo[j].tgconstrrelid))
7964 if (PQgetisnull(res, j, i_tgconstrrelname))
7965 pg_fatal("query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)",
7966 tginfo[j].dobj.name,
7967 tbinfo->dobj.name,
7968 tginfo[j].tgconstrrelid);
7969 tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname));
7971 else
7972 tginfo[j].tgconstrrelname = NULL;
7974 else
7976 tginfo[j].tgconstrname = NULL;
7977 tginfo[j].tgconstrrelid = InvalidOid;
7978 tginfo[j].tgconstrrelname = NULL;
7984 PQclear(res);
7986 destroyPQExpBuffer(query);
7987 destroyPQExpBuffer(tbloids);
7991 * getEventTriggers
7992 * get information about event triggers
7994 EventTriggerInfo *
7995 getEventTriggers(Archive *fout, int *numEventTriggers)
7997 int i;
7998 PQExpBuffer query;
7999 PGresult *res;
8000 EventTriggerInfo *evtinfo;
8001 int i_tableoid,
8002 i_oid,
8003 i_evtname,
8004 i_evtevent,
8005 i_evtowner,
8006 i_evttags,
8007 i_evtfname,
8008 i_evtenabled;
8009 int ntups;
8011 /* Before 9.3, there are no event triggers */
8012 if (fout->remoteVersion < 90300)
8014 *numEventTriggers = 0;
8015 return NULL;
8018 query = createPQExpBuffer();
8020 appendPQExpBufferStr(query,
8021 "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8022 "evtevent, evtowner, "
8023 "array_to_string(array("
8024 "select quote_literal(x) "
8025 " from unnest(evttags) as t(x)), ', ') as evttags, "
8026 "e.evtfoid::regproc as evtfname "
8027 "FROM pg_event_trigger e "
8028 "ORDER BY e.oid");
8030 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8032 ntups = PQntuples(res);
8034 *numEventTriggers = ntups;
8036 evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8038 i_tableoid = PQfnumber(res, "tableoid");
8039 i_oid = PQfnumber(res, "oid");
8040 i_evtname = PQfnumber(res, "evtname");
8041 i_evtevent = PQfnumber(res, "evtevent");
8042 i_evtowner = PQfnumber(res, "evtowner");
8043 i_evttags = PQfnumber(res, "evttags");
8044 i_evtfname = PQfnumber(res, "evtfname");
8045 i_evtenabled = PQfnumber(res, "evtenabled");
8047 for (i = 0; i < ntups; i++)
8049 evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8050 evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8051 evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8052 AssignDumpId(&evtinfo[i].dobj);
8053 evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8054 evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8055 evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8056 evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8057 evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8058 evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8059 evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8061 /* Decide whether we want to dump it */
8062 selectDumpableObject(&(evtinfo[i].dobj), fout);
8065 PQclear(res);
8067 destroyPQExpBuffer(query);
8069 return evtinfo;
8073 * getProcLangs
8074 * get basic information about every procedural language in the system
8076 * numProcLangs is set to the number of langs read in
8078 * NB: this must run after getFuncs() because we assume we can do
8079 * findFuncByOid().
8081 ProcLangInfo *
8082 getProcLangs(Archive *fout, int *numProcLangs)
8084 PGresult *res;
8085 int ntups;
8086 int i;
8087 PQExpBuffer query = createPQExpBuffer();
8088 ProcLangInfo *planginfo;
8089 int i_tableoid;
8090 int i_oid;
8091 int i_lanname;
8092 int i_lanpltrusted;
8093 int i_lanplcallfoid;
8094 int i_laninline;
8095 int i_lanvalidator;
8096 int i_lanacl;
8097 int i_acldefault;
8098 int i_lanowner;
8100 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8101 "lanname, lanpltrusted, lanplcallfoid, "
8102 "laninline, lanvalidator, "
8103 "lanacl, "
8104 "acldefault('l', lanowner) AS acldefault, "
8105 "lanowner "
8106 "FROM pg_language "
8107 "WHERE lanispl "
8108 "ORDER BY oid");
8110 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8112 ntups = PQntuples(res);
8114 *numProcLangs = ntups;
8116 planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8118 i_tableoid = PQfnumber(res, "tableoid");
8119 i_oid = PQfnumber(res, "oid");
8120 i_lanname = PQfnumber(res, "lanname");
8121 i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8122 i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8123 i_laninline = PQfnumber(res, "laninline");
8124 i_lanvalidator = PQfnumber(res, "lanvalidator");
8125 i_lanacl = PQfnumber(res, "lanacl");
8126 i_acldefault = PQfnumber(res, "acldefault");
8127 i_lanowner = PQfnumber(res, "lanowner");
8129 for (i = 0; i < ntups; i++)
8131 planginfo[i].dobj.objType = DO_PROCLANG;
8132 planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8133 planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8134 AssignDumpId(&planginfo[i].dobj);
8136 planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8137 planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8138 planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8139 planginfo[i].dacl.privtype = 0;
8140 planginfo[i].dacl.initprivs = NULL;
8141 planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8142 planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8143 planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8144 planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8145 planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8147 /* Decide whether we want to dump it */
8148 selectDumpableProcLang(&(planginfo[i]), fout);
8150 /* Mark whether language has an ACL */
8151 if (!PQgetisnull(res, i, i_lanacl))
8152 planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8155 PQclear(res);
8157 destroyPQExpBuffer(query);
8159 return planginfo;
8163 * getCasts
8164 * get basic information about most casts in the system
8166 * numCasts is set to the number of casts read in
8168 * Skip casts from a range to its multirange, since we'll create those
8169 * automatically.
8171 CastInfo *
8172 getCasts(Archive *fout, int *numCasts)
8174 PGresult *res;
8175 int ntups;
8176 int i;
8177 PQExpBuffer query = createPQExpBuffer();
8178 CastInfo *castinfo;
8179 int i_tableoid;
8180 int i_oid;
8181 int i_castsource;
8182 int i_casttarget;
8183 int i_castfunc;
8184 int i_castcontext;
8185 int i_castmethod;
8187 if (fout->remoteVersion >= 140000)
8189 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8190 "castsource, casttarget, castfunc, castcontext, "
8191 "castmethod "
8192 "FROM pg_cast c "
8193 "WHERE NOT EXISTS ( "
8194 "SELECT 1 FROM pg_range r "
8195 "WHERE c.castsource = r.rngtypid "
8196 "AND c.casttarget = r.rngmultitypid "
8197 ") "
8198 "ORDER BY 3,4");
8200 else
8202 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8203 "castsource, casttarget, castfunc, castcontext, "
8204 "castmethod "
8205 "FROM pg_cast ORDER BY 3,4");
8208 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8210 ntups = PQntuples(res);
8212 *numCasts = ntups;
8214 castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8216 i_tableoid = PQfnumber(res, "tableoid");
8217 i_oid = PQfnumber(res, "oid");
8218 i_castsource = PQfnumber(res, "castsource");
8219 i_casttarget = PQfnumber(res, "casttarget");
8220 i_castfunc = PQfnumber(res, "castfunc");
8221 i_castcontext = PQfnumber(res, "castcontext");
8222 i_castmethod = PQfnumber(res, "castmethod");
8224 for (i = 0; i < ntups; i++)
8226 PQExpBufferData namebuf;
8227 TypeInfo *sTypeInfo;
8228 TypeInfo *tTypeInfo;
8230 castinfo[i].dobj.objType = DO_CAST;
8231 castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8232 castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8233 AssignDumpId(&castinfo[i].dobj);
8234 castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8235 castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8236 castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8237 castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8238 castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8241 * Try to name cast as concatenation of typnames. This is only used
8242 * for purposes of sorting. If we fail to find either type, the name
8243 * will be an empty string.
8245 initPQExpBuffer(&namebuf);
8246 sTypeInfo = findTypeByOid(castinfo[i].castsource);
8247 tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8248 if (sTypeInfo && tTypeInfo)
8249 appendPQExpBuffer(&namebuf, "%s %s",
8250 sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8251 castinfo[i].dobj.name = namebuf.data;
8253 /* Decide whether we want to dump it */
8254 selectDumpableCast(&(castinfo[i]), fout);
8257 PQclear(res);
8259 destroyPQExpBuffer(query);
8261 return castinfo;
8264 static char *
8265 get_language_name(Archive *fout, Oid langid)
8267 PQExpBuffer query;
8268 PGresult *res;
8269 char *lanname;
8271 query = createPQExpBuffer();
8272 appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8273 res = ExecuteSqlQueryForSingleRow(fout, query->data);
8274 lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8275 destroyPQExpBuffer(query);
8276 PQclear(res);
8278 return lanname;
8282 * getTransforms
8283 * get basic information about every transform in the system
8285 * numTransforms is set to the number of transforms read in
8287 TransformInfo *
8288 getTransforms(Archive *fout, int *numTransforms)
8290 PGresult *res;
8291 int ntups;
8292 int i;
8293 PQExpBuffer query;
8294 TransformInfo *transforminfo;
8295 int i_tableoid;
8296 int i_oid;
8297 int i_trftype;
8298 int i_trflang;
8299 int i_trffromsql;
8300 int i_trftosql;
8302 /* Transforms didn't exist pre-9.5 */
8303 if (fout->remoteVersion < 90500)
8305 *numTransforms = 0;
8306 return NULL;
8309 query = createPQExpBuffer();
8311 appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8312 "trftype, trflang, trffromsql::oid, trftosql::oid "
8313 "FROM pg_transform "
8314 "ORDER BY 3,4");
8316 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8318 ntups = PQntuples(res);
8320 *numTransforms = ntups;
8322 transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8324 i_tableoid = PQfnumber(res, "tableoid");
8325 i_oid = PQfnumber(res, "oid");
8326 i_trftype = PQfnumber(res, "trftype");
8327 i_trflang = PQfnumber(res, "trflang");
8328 i_trffromsql = PQfnumber(res, "trffromsql");
8329 i_trftosql = PQfnumber(res, "trftosql");
8331 for (i = 0; i < ntups; i++)
8333 PQExpBufferData namebuf;
8334 TypeInfo *typeInfo;
8335 char *lanname;
8337 transforminfo[i].dobj.objType = DO_TRANSFORM;
8338 transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8339 transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8340 AssignDumpId(&transforminfo[i].dobj);
8341 transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8342 transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8343 transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8344 transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8347 * Try to name transform as concatenation of type and language name.
8348 * This is only used for purposes of sorting. If we fail to find
8349 * either, the name will be an empty string.
8351 initPQExpBuffer(&namebuf);
8352 typeInfo = findTypeByOid(transforminfo[i].trftype);
8353 lanname = get_language_name(fout, transforminfo[i].trflang);
8354 if (typeInfo && lanname)
8355 appendPQExpBuffer(&namebuf, "%s %s",
8356 typeInfo->dobj.name, lanname);
8357 transforminfo[i].dobj.name = namebuf.data;
8358 free(lanname);
8360 /* Decide whether we want to dump it */
8361 selectDumpableObject(&(transforminfo[i].dobj), fout);
8364 PQclear(res);
8366 destroyPQExpBuffer(query);
8368 return transforminfo;
8372 * getTableAttrs -
8373 * for each interesting table, read info about its attributes
8374 * (names, types, default values, CHECK constraints, etc)
8376 * modifies tblinfo
8378 void
8379 getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8381 DumpOptions *dopt = fout->dopt;
8382 PQExpBuffer q = createPQExpBuffer();
8383 PQExpBuffer tbloids = createPQExpBuffer();
8384 PQExpBuffer checkoids = createPQExpBuffer();
8385 PGresult *res;
8386 int ntups;
8387 int curtblindx;
8388 int i_attrelid;
8389 int i_attnum;
8390 int i_attname;
8391 int i_atttypname;
8392 int i_attstattarget;
8393 int i_attstorage;
8394 int i_typstorage;
8395 int i_attidentity;
8396 int i_attgenerated;
8397 int i_attisdropped;
8398 int i_attlen;
8399 int i_attalign;
8400 int i_attislocal;
8401 int i_notnull_name;
8402 int i_notnull_noinherit;
8403 int i_notnull_is_pk;
8404 int i_notnull_inh;
8405 int i_attoptions;
8406 int i_attcollation;
8407 int i_attcompression;
8408 int i_attfdwoptions;
8409 int i_attmissingval;
8410 int i_atthasdef;
8413 * We want to perform just one query against pg_attribute, and then just
8414 * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
8415 * (for CHECK constraints and for NOT NULL constraints). However, we
8416 * mustn't try to select every row of those catalogs and then sort it out
8417 * on the client side, because some of the server-side functions we need
8418 * would be unsafe to apply to tables we don't have lock on. Hence, we
8419 * build an array of the OIDs of tables we care about (and now have lock
8420 * on!), and use a WHERE clause to constrain which rows are selected.
8422 appendPQExpBufferChar(tbloids, '{');
8423 appendPQExpBufferChar(checkoids, '{');
8424 for (int i = 0; i < numTables; i++)
8426 TableInfo *tbinfo = &tblinfo[i];
8428 /* Don't bother to collect info for sequences */
8429 if (tbinfo->relkind == RELKIND_SEQUENCE)
8430 continue;
8432 /* Don't bother with uninteresting tables, either */
8433 if (!tbinfo->interesting)
8434 continue;
8436 /* OK, we need info for this table */
8437 if (tbloids->len > 1) /* do we have more than the '{'? */
8438 appendPQExpBufferChar(tbloids, ',');
8439 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8441 if (tbinfo->ncheck > 0)
8443 /* Also make a list of the ones with check constraints */
8444 if (checkoids->len > 1) /* do we have more than the '{'? */
8445 appendPQExpBufferChar(checkoids, ',');
8446 appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
8449 appendPQExpBufferChar(tbloids, '}');
8450 appendPQExpBufferChar(checkoids, '}');
8453 * Find all the user attributes and their types.
8455 * Since we only want to dump COLLATE clauses for attributes whose
8456 * collation is different from their type's default, we use a CASE here to
8457 * suppress uninteresting attcollations cheaply.
8459 appendPQExpBufferStr(q,
8460 "SELECT\n"
8461 "a.attrelid,\n"
8462 "a.attnum,\n"
8463 "a.attname,\n"
8464 "a.attstattarget,\n"
8465 "a.attstorage,\n"
8466 "t.typstorage,\n"
8467 "a.atthasdef,\n"
8468 "a.attisdropped,\n"
8469 "a.attlen,\n"
8470 "a.attalign,\n"
8471 "a.attislocal,\n"
8472 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
8473 "array_to_string(a.attoptions, ', ') AS attoptions,\n"
8474 "CASE WHEN a.attcollation <> t.typcollation "
8475 "THEN a.attcollation ELSE 0 END AS attcollation,\n"
8476 "pg_catalog.array_to_string(ARRAY("
8477 "SELECT pg_catalog.quote_ident(option_name) || "
8478 "' ' || pg_catalog.quote_literal(option_value) "
8479 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8480 "ORDER BY option_name"
8481 "), E',\n ') AS attfdwoptions,\n");
8484 * Find out any NOT NULL markings for each column. In 17 and up we have
8485 * to read pg_constraint, and keep track whether it's NO INHERIT; in older
8486 * versions we rely on pg_attribute.attnotnull.
8488 * We also track whether the constraint was defined directly in this table
8489 * or via an ancestor, for binary upgrade.
8491 * Lastly, we need to know if the PK for the table involves each column;
8492 * for columns that are there we need a NOT NULL marking even if there's
8493 * no explicit constraint, to avoid the table having to be scanned for
8494 * NULLs after the data is loaded when the PK is created, later in the
8495 * dump; for this case we add throwaway constraints that are dropped once
8496 * the PK is created.
8498 if (fout->remoteVersion >= 170000)
8499 appendPQExpBufferStr(q,
8500 "co.conname AS notnull_name,\n"
8501 "co.connoinherit AS notnull_noinherit,\n"
8502 "copk.conname IS NOT NULL as notnull_is_pk,\n"
8503 "coalesce(NOT co.conislocal, true) AS notnull_inh,\n");
8504 else
8505 appendPQExpBufferStr(q,
8506 "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
8507 "false AS notnull_noinherit,\n"
8508 "copk.conname IS NOT NULL AS notnull_is_pk,\n"
8509 "NOT a.attislocal AS notnull_inh,\n");
8511 if (fout->remoteVersion >= 140000)
8512 appendPQExpBufferStr(q,
8513 "a.attcompression AS attcompression,\n");
8514 else
8515 appendPQExpBufferStr(q,
8516 "'' AS attcompression,\n");
8518 if (fout->remoteVersion >= 100000)
8519 appendPQExpBufferStr(q,
8520 "a.attidentity,\n");
8521 else
8522 appendPQExpBufferStr(q,
8523 "'' AS attidentity,\n");
8525 if (fout->remoteVersion >= 110000)
8526 appendPQExpBufferStr(q,
8527 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8528 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8529 else
8530 appendPQExpBufferStr(q,
8531 "NULL AS attmissingval,\n");
8533 if (fout->remoteVersion >= 120000)
8534 appendPQExpBufferStr(q,
8535 "a.attgenerated\n");
8536 else
8537 appendPQExpBufferStr(q,
8538 "'' AS attgenerated\n");
8540 /* need left join to pg_type to not fail on dropped columns ... */
8541 appendPQExpBuffer(q,
8542 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8543 "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
8544 "LEFT JOIN pg_catalog.pg_type t "
8545 "ON (a.atttypid = t.oid)\n",
8546 tbloids->data);
8549 * In versions 16 and up, we need pg_constraint for explicit NOT NULL
8550 * entries. Also, we need to know if the NOT NULL for each column is
8551 * backing a primary key.
8553 if (fout->remoteVersion >= 170000)
8554 appendPQExpBufferStr(q,
8555 " LEFT JOIN pg_catalog.pg_constraint co ON "
8556 "(a.attrelid = co.conrelid\n"
8557 " AND co.contype = 'n' AND "
8558 "co.conkey = array[a.attnum])\n");
8560 appendPQExpBufferStr(q,
8561 "LEFT JOIN pg_catalog.pg_constraint copk ON "
8562 "(copk.conrelid = src.tbloid\n"
8563 " AND copk.contype = 'p' AND "
8564 "copk.conkey @> array[a.attnum])\n"
8565 "WHERE a.attnum > 0::pg_catalog.int2\n"
8566 "ORDER BY a.attrelid, a.attnum");
8568 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8570 ntups = PQntuples(res);
8572 i_attrelid = PQfnumber(res, "attrelid");
8573 i_attnum = PQfnumber(res, "attnum");
8574 i_attname = PQfnumber(res, "attname");
8575 i_atttypname = PQfnumber(res, "atttypname");
8576 i_attstattarget = PQfnumber(res, "attstattarget");
8577 i_attstorage = PQfnumber(res, "attstorage");
8578 i_typstorage = PQfnumber(res, "typstorage");
8579 i_attidentity = PQfnumber(res, "attidentity");
8580 i_attgenerated = PQfnumber(res, "attgenerated");
8581 i_attisdropped = PQfnumber(res, "attisdropped");
8582 i_attlen = PQfnumber(res, "attlen");
8583 i_attalign = PQfnumber(res, "attalign");
8584 i_attislocal = PQfnumber(res, "attislocal");
8585 i_notnull_name = PQfnumber(res, "notnull_name");
8586 i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
8587 i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
8588 i_notnull_inh = PQfnumber(res, "notnull_inh");
8589 i_attoptions = PQfnumber(res, "attoptions");
8590 i_attcollation = PQfnumber(res, "attcollation");
8591 i_attcompression = PQfnumber(res, "attcompression");
8592 i_attfdwoptions = PQfnumber(res, "attfdwoptions");
8593 i_attmissingval = PQfnumber(res, "attmissingval");
8594 i_atthasdef = PQfnumber(res, "atthasdef");
8596 /* Within the next loop, we'll accumulate OIDs of tables with defaults */
8597 resetPQExpBuffer(tbloids);
8598 appendPQExpBufferChar(tbloids, '{');
8601 * Outer loop iterates once per table, not once per row. Incrementing of
8602 * r is handled by the inner loop.
8604 curtblindx = -1;
8605 for (int r = 0; r < ntups;)
8607 Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
8608 TableInfo *tbinfo = NULL;
8609 int numatts;
8610 bool hasdefaults;
8611 int notnullcount;
8613 /* Count rows for this table */
8614 for (numatts = 1; numatts < ntups - r; numatts++)
8615 if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
8616 break;
8619 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8620 * order.
8622 while (++curtblindx < numTables)
8624 tbinfo = &tblinfo[curtblindx];
8625 if (tbinfo->dobj.catId.oid == attrelid)
8626 break;
8628 if (curtblindx >= numTables)
8629 pg_fatal("unrecognized table OID %u", attrelid);
8630 /* cross-check that we only got requested tables */
8631 if (tbinfo->relkind == RELKIND_SEQUENCE ||
8632 !tbinfo->interesting)
8633 pg_fatal("unexpected column data for table \"%s\"",
8634 tbinfo->dobj.name);
8636 notnullcount = 0;
8638 /* Save data for this table */
8639 tbinfo->numatts = numatts;
8640 tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
8641 tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
8642 tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
8643 tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
8644 tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
8645 tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
8646 tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
8647 tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
8648 tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
8649 tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
8650 tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
8651 tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
8652 tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
8653 tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
8654 tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
8655 tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
8656 tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
8657 tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
8658 tbinfo->notnull_throwaway = (bool *) pg_malloc(numatts * sizeof(bool));
8659 tbinfo->notnull_inh = (bool *) pg_malloc(numatts * sizeof(bool));
8660 tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
8661 hasdefaults = false;
8663 for (int j = 0; j < numatts; j++, r++)
8665 bool use_named_notnull = false;
8666 bool use_unnamed_notnull = false;
8667 bool use_throwaway_notnull = false;
8669 if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
8670 pg_fatal("invalid column numbering in table \"%s\"",
8671 tbinfo->dobj.name);
8672 tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
8673 tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
8674 tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
8675 tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
8676 tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
8677 tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
8678 tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
8679 tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
8680 tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
8681 tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
8682 tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
8683 tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
8686 * Not-null constraints require a jumping through a few hoops.
8687 * First, if the user has specified a constraint name that's not
8688 * the system-assigned default name, then we need to preserve
8689 * that. But if they haven't, then we don't want to use the
8690 * verbose syntax in the dump output. (Also, in versions prior to
8691 * 17, there was no constraint name at all.)
8693 * (XXX Comparing the name this way to a supposed default name is
8694 * a bit of a hack, but it beats having to store a boolean flag in
8695 * pg_constraint just for this, or having to compute the knowledge
8696 * at pg_dump time from the server.)
8698 * We also need to know if a column is part of the primary key. In
8699 * that case, we want to mark the column as not-null at table
8700 * creation time, so that the table doesn't have to be scanned to
8701 * check for nulls when the PK is created afterwards; this is
8702 * especially critical during pg_upgrade (where the data would not
8703 * be scanned at all otherwise.) If the column is part of the PK
8704 * and does not have any other not-null constraint, then we
8705 * fabricate a throwaway constraint name that we later use to
8706 * remove the constraint after the PK has been created.
8708 * For inheritance child tables, we don't want to print not-null
8709 * when the constraint was defined at the parent level instead of
8710 * locally.
8714 * We use notnull_inh to suppress unwanted not-null constraints in
8715 * inheritance children, when said constraints come from the
8716 * parent(s).
8718 tbinfo->notnull_inh[j] = PQgetvalue(res, r, i_notnull_inh)[0] == 't';
8720 if (fout->remoteVersion < 170000)
8722 if (!PQgetisnull(res, r, i_notnull_name) &&
8723 dopt->binary_upgrade &&
8724 !tbinfo->ispartition &&
8725 tbinfo->notnull_inh[j])
8727 use_named_notnull = true;
8728 /* XXX should match ChooseConstraintName better */
8729 tbinfo->notnull_constrs[j] =
8730 psprintf("%s_%s_not_null", tbinfo->dobj.name,
8731 tbinfo->attnames[j]);
8733 else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
8734 use_throwaway_notnull = true;
8735 else if (!PQgetisnull(res, r, i_notnull_name))
8736 use_unnamed_notnull = true;
8738 else
8740 if (!PQgetisnull(res, r, i_notnull_name))
8743 * In binary upgrade of inheritance child tables, must
8744 * have a constraint name that we can UPDATE later.
8746 if (dopt->binary_upgrade &&
8747 !tbinfo->ispartition &&
8748 tbinfo->notnull_inh[j])
8750 use_named_notnull = true;
8751 tbinfo->notnull_constrs[j] =
8752 pstrdup(PQgetvalue(res, r, i_notnull_name));
8755 else
8757 char *default_name;
8759 /* XXX should match ChooseConstraintName better */
8760 default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
8761 tbinfo->attnames[j]);
8762 if (strcmp(default_name,
8763 PQgetvalue(res, r, i_notnull_name)) == 0)
8764 use_unnamed_notnull = true;
8765 else
8767 use_named_notnull = true;
8768 tbinfo->notnull_constrs[j] =
8769 pstrdup(PQgetvalue(res, r, i_notnull_name));
8773 else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
8774 use_throwaway_notnull = true;
8777 if (use_unnamed_notnull)
8779 tbinfo->notnull_constrs[j] = "";
8780 tbinfo->notnull_throwaway[j] = false;
8782 else if (use_named_notnull)
8784 /* The name itself has already been determined */
8785 tbinfo->notnull_throwaway[j] = false;
8787 else if (use_throwaway_notnull)
8789 tbinfo->notnull_constrs[j] =
8790 psprintf("pgdump_throwaway_notnull_%d", notnullcount++);
8791 tbinfo->notnull_throwaway[j] = true;
8792 tbinfo->notnull_inh[j] = false;
8794 else
8796 tbinfo->notnull_constrs[j] = NULL;
8797 tbinfo->notnull_throwaway[j] = false;
8801 * Throwaway constraints must always be NO INHERIT; otherwise do
8802 * what the catalog says.
8804 tbinfo->notnull_noinh[j] = use_throwaway_notnull ||
8805 PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
8807 tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
8808 tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
8809 tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
8810 tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
8811 tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
8812 tbinfo->attrdefs[j] = NULL; /* fix below */
8813 if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
8814 hasdefaults = true;
8817 if (hasdefaults)
8819 /* Collect OIDs of interesting tables that have defaults */
8820 if (tbloids->len > 1) /* do we have more than the '{'? */
8821 appendPQExpBufferChar(tbloids, ',');
8822 appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8826 PQclear(res);
8829 * Now get info about column defaults. This is skipped for a data-only
8830 * dump, as it is only needed for table schemas.
8832 if (!dopt->dataOnly && tbloids->len > 1)
8834 AttrDefInfo *attrdefs;
8835 int numDefaults;
8836 TableInfo *tbinfo = NULL;
8838 pg_log_info("finding table default expressions");
8840 appendPQExpBufferChar(tbloids, '}');
8842 printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
8843 "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
8844 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8845 "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
8846 "ORDER BY a.adrelid, a.adnum",
8847 tbloids->data);
8849 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8851 numDefaults = PQntuples(res);
8852 attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
8854 curtblindx = -1;
8855 for (int j = 0; j < numDefaults; j++)
8857 Oid adtableoid = atooid(PQgetvalue(res, j, 0));
8858 Oid adoid = atooid(PQgetvalue(res, j, 1));
8859 Oid adrelid = atooid(PQgetvalue(res, j, 2));
8860 int adnum = atoi(PQgetvalue(res, j, 3));
8861 char *adsrc = PQgetvalue(res, j, 4);
8864 * Locate the associated TableInfo; we rely on tblinfo[] being in
8865 * OID order.
8867 if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
8869 while (++curtblindx < numTables)
8871 tbinfo = &tblinfo[curtblindx];
8872 if (tbinfo->dobj.catId.oid == adrelid)
8873 break;
8875 if (curtblindx >= numTables)
8876 pg_fatal("unrecognized table OID %u", adrelid);
8879 if (adnum <= 0 || adnum > tbinfo->numatts)
8880 pg_fatal("invalid adnum value %d for table \"%s\"",
8881 adnum, tbinfo->dobj.name);
8884 * dropped columns shouldn't have defaults, but just in case,
8885 * ignore 'em
8887 if (tbinfo->attisdropped[adnum - 1])
8888 continue;
8890 attrdefs[j].dobj.objType = DO_ATTRDEF;
8891 attrdefs[j].dobj.catId.tableoid = adtableoid;
8892 attrdefs[j].dobj.catId.oid = adoid;
8893 AssignDumpId(&attrdefs[j].dobj);
8894 attrdefs[j].adtable = tbinfo;
8895 attrdefs[j].adnum = adnum;
8896 attrdefs[j].adef_expr = pg_strdup(adsrc);
8898 attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
8899 attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
8901 attrdefs[j].dobj.dump = tbinfo->dobj.dump;
8904 * Figure out whether the default/generation expression should be
8905 * dumped as part of the main CREATE TABLE (or similar) command or
8906 * as a separate ALTER TABLE (or similar) command. The preference
8907 * is to put it into the CREATE command, but in some cases that's
8908 * not possible.
8910 if (tbinfo->attgenerated[adnum - 1])
8913 * Column generation expressions cannot be dumped separately,
8914 * because there is no syntax for it. By setting separate to
8915 * false here we prevent the "default" from being processed as
8916 * its own dumpable object. Later, flagInhAttrs() will mark
8917 * it as not to be dumped at all, if possible (that is, if it
8918 * can be inherited from a parent).
8920 attrdefs[j].separate = false;
8922 else if (tbinfo->relkind == RELKIND_VIEW)
8925 * Defaults on a VIEW must always be dumped as separate ALTER
8926 * TABLE commands.
8928 attrdefs[j].separate = true;
8930 else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
8932 /* column will be suppressed, print default separately */
8933 attrdefs[j].separate = true;
8935 else
8937 attrdefs[j].separate = false;
8940 if (!attrdefs[j].separate)
8943 * Mark the default as needing to appear before the table, so
8944 * that any dependencies it has must be emitted before the
8945 * CREATE TABLE. If this is not possible, we'll change to
8946 * "separate" mode while sorting dependencies.
8948 addObjectDependency(&tbinfo->dobj,
8949 attrdefs[j].dobj.dumpId);
8952 tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
8955 PQclear(res);
8959 * Get info about table CHECK constraints. This is skipped for a
8960 * data-only dump, as it is only needed for table schemas.
8962 if (!dopt->dataOnly && checkoids->len > 2)
8964 ConstraintInfo *constrs;
8965 int numConstrs;
8966 int i_tableoid;
8967 int i_oid;
8968 int i_conrelid;
8969 int i_conname;
8970 int i_consrc;
8971 int i_conislocal;
8972 int i_convalidated;
8974 pg_log_info("finding table check constraints");
8976 resetPQExpBuffer(q);
8977 appendPQExpBuffer(q,
8978 "SELECT c.tableoid, c.oid, conrelid, conname, "
8979 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
8980 "conislocal, convalidated "
8981 "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8982 "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8983 "WHERE contype = 'c' "
8984 "ORDER BY c.conrelid, c.conname",
8985 checkoids->data);
8987 res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8989 numConstrs = PQntuples(res);
8990 constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
8992 i_tableoid = PQfnumber(res, "tableoid");
8993 i_oid = PQfnumber(res, "oid");
8994 i_conrelid = PQfnumber(res, "conrelid");
8995 i_conname = PQfnumber(res, "conname");
8996 i_consrc = PQfnumber(res, "consrc");
8997 i_conislocal = PQfnumber(res, "conislocal");
8998 i_convalidated = PQfnumber(res, "convalidated");
9000 /* As above, this loop iterates once per table, not once per row */
9001 curtblindx = -1;
9002 for (int j = 0; j < numConstrs;)
9004 Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9005 TableInfo *tbinfo = NULL;
9006 int numcons;
9008 /* Count rows for this table */
9009 for (numcons = 1; numcons < numConstrs - j; numcons++)
9010 if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9011 break;
9014 * Locate the associated TableInfo; we rely on tblinfo[] being in
9015 * OID order.
9017 while (++curtblindx < numTables)
9019 tbinfo = &tblinfo[curtblindx];
9020 if (tbinfo->dobj.catId.oid == conrelid)
9021 break;
9023 if (curtblindx >= numTables)
9024 pg_fatal("unrecognized table OID %u", conrelid);
9026 if (numcons != tbinfo->ncheck)
9028 pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9029 "expected %d check constraints on table \"%s\" but found %d",
9030 tbinfo->ncheck),
9031 tbinfo->ncheck, tbinfo->dobj.name, numcons);
9032 pg_log_error_hint("The system catalogs might be corrupted.");
9033 exit_nicely(1);
9036 tbinfo->checkexprs = constrs + j;
9038 for (int c = 0; c < numcons; c++, j++)
9040 bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9042 constrs[j].dobj.objType = DO_CONSTRAINT;
9043 constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9044 constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9045 AssignDumpId(&constrs[j].dobj);
9046 constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9047 constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9048 constrs[j].contable = tbinfo;
9049 constrs[j].condomain = NULL;
9050 constrs[j].contype = 'c';
9051 constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9052 constrs[j].confrelid = InvalidOid;
9053 constrs[j].conindex = 0;
9054 constrs[j].condeferrable = false;
9055 constrs[j].condeferred = false;
9056 constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9059 * An unvalidated constraint needs to be dumped separately, so
9060 * that potentially-violating existing data is loaded before
9061 * the constraint.
9063 constrs[j].separate = !validated;
9065 constrs[j].dobj.dump = tbinfo->dobj.dump;
9068 * Mark the constraint as needing to appear before the table
9069 * --- this is so that any other dependencies of the
9070 * constraint will be emitted before we try to create the
9071 * table. If the constraint is to be dumped separately, it
9072 * will be dumped after data is loaded anyway, so don't do it.
9073 * (There's an automatic dependency in the opposite direction
9074 * anyway, so don't need to add one manually here.)
9076 if (!constrs[j].separate)
9077 addObjectDependency(&tbinfo->dobj,
9078 constrs[j].dobj.dumpId);
9081 * We will detect later whether the constraint must be split
9082 * out from the table definition.
9087 PQclear(res);
9090 destroyPQExpBuffer(q);
9091 destroyPQExpBuffer(tbloids);
9092 destroyPQExpBuffer(checkoids);
9096 * Test whether a column should be printed as part of table's CREATE TABLE.
9097 * Column number is zero-based.
9099 * Normally this is always true, but it's false for dropped columns, as well
9100 * as those that were inherited without any local definition. (If we print
9101 * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9102 * For partitions, it's always true, because we want the partitions to be
9103 * created independently and ATTACH PARTITION used afterwards.
9105 * In binary_upgrade mode, we must print all columns and fix the attislocal/
9106 * attisdropped state later, so as to keep control of the physical column
9107 * order.
9109 * This function exists because there are scattered nonobvious places that
9110 * must be kept in sync with this decision.
9112 bool
9113 shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9115 if (dopt->binary_upgrade)
9116 return true;
9117 if (tbinfo->attisdropped[colno])
9118 return false;
9119 return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9124 * getTSParsers:
9125 * read all text search parsers in the system catalogs and return them
9126 * in the TSParserInfo* structure
9128 * numTSParsers is set to the number of parsers read in
9130 TSParserInfo *
9131 getTSParsers(Archive *fout, int *numTSParsers)
9133 PGresult *res;
9134 int ntups;
9135 int i;
9136 PQExpBuffer query;
9137 TSParserInfo *prsinfo;
9138 int i_tableoid;
9139 int i_oid;
9140 int i_prsname;
9141 int i_prsnamespace;
9142 int i_prsstart;
9143 int i_prstoken;
9144 int i_prsend;
9145 int i_prsheadline;
9146 int i_prslextype;
9148 query = createPQExpBuffer();
9151 * find all text search objects, including builtin ones; we filter out
9152 * system-defined objects at dump-out time.
9155 appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9156 "prsstart::oid, prstoken::oid, "
9157 "prsend::oid, prsheadline::oid, prslextype::oid "
9158 "FROM pg_ts_parser");
9160 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9162 ntups = PQntuples(res);
9163 *numTSParsers = ntups;
9165 prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9167 i_tableoid = PQfnumber(res, "tableoid");
9168 i_oid = PQfnumber(res, "oid");
9169 i_prsname = PQfnumber(res, "prsname");
9170 i_prsnamespace = PQfnumber(res, "prsnamespace");
9171 i_prsstart = PQfnumber(res, "prsstart");
9172 i_prstoken = PQfnumber(res, "prstoken");
9173 i_prsend = PQfnumber(res, "prsend");
9174 i_prsheadline = PQfnumber(res, "prsheadline");
9175 i_prslextype = PQfnumber(res, "prslextype");
9177 for (i = 0; i < ntups; i++)
9179 prsinfo[i].dobj.objType = DO_TSPARSER;
9180 prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9181 prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9182 AssignDumpId(&prsinfo[i].dobj);
9183 prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9184 prsinfo[i].dobj.namespace =
9185 findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9186 prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9187 prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9188 prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9189 prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9190 prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9192 /* Decide whether we want to dump it */
9193 selectDumpableObject(&(prsinfo[i].dobj), fout);
9196 PQclear(res);
9198 destroyPQExpBuffer(query);
9200 return prsinfo;
9204 * getTSDictionaries:
9205 * read all text search dictionaries in the system catalogs and return them
9206 * in the TSDictInfo* structure
9208 * numTSDicts is set to the number of dictionaries read in
9210 TSDictInfo *
9211 getTSDictionaries(Archive *fout, int *numTSDicts)
9213 PGresult *res;
9214 int ntups;
9215 int i;
9216 PQExpBuffer query;
9217 TSDictInfo *dictinfo;
9218 int i_tableoid;
9219 int i_oid;
9220 int i_dictname;
9221 int i_dictnamespace;
9222 int i_dictowner;
9223 int i_dicttemplate;
9224 int i_dictinitoption;
9226 query = createPQExpBuffer();
9228 appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9229 "dictnamespace, dictowner, "
9230 "dicttemplate, dictinitoption "
9231 "FROM pg_ts_dict");
9233 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9235 ntups = PQntuples(res);
9236 *numTSDicts = ntups;
9238 dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9240 i_tableoid = PQfnumber(res, "tableoid");
9241 i_oid = PQfnumber(res, "oid");
9242 i_dictname = PQfnumber(res, "dictname");
9243 i_dictnamespace = PQfnumber(res, "dictnamespace");
9244 i_dictowner = PQfnumber(res, "dictowner");
9245 i_dictinitoption = PQfnumber(res, "dictinitoption");
9246 i_dicttemplate = PQfnumber(res, "dicttemplate");
9248 for (i = 0; i < ntups; i++)
9250 dictinfo[i].dobj.objType = DO_TSDICT;
9251 dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9252 dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9253 AssignDumpId(&dictinfo[i].dobj);
9254 dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9255 dictinfo[i].dobj.namespace =
9256 findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9257 dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9258 dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9259 if (PQgetisnull(res, i, i_dictinitoption))
9260 dictinfo[i].dictinitoption = NULL;
9261 else
9262 dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9264 /* Decide whether we want to dump it */
9265 selectDumpableObject(&(dictinfo[i].dobj), fout);
9268 PQclear(res);
9270 destroyPQExpBuffer(query);
9272 return dictinfo;
9276 * getTSTemplates:
9277 * read all text search templates in the system catalogs and return them
9278 * in the TSTemplateInfo* structure
9280 * numTSTemplates is set to the number of templates read in
9282 TSTemplateInfo *
9283 getTSTemplates(Archive *fout, int *numTSTemplates)
9285 PGresult *res;
9286 int ntups;
9287 int i;
9288 PQExpBuffer query;
9289 TSTemplateInfo *tmplinfo;
9290 int i_tableoid;
9291 int i_oid;
9292 int i_tmplname;
9293 int i_tmplnamespace;
9294 int i_tmplinit;
9295 int i_tmpllexize;
9297 query = createPQExpBuffer();
9299 appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9300 "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9301 "FROM pg_ts_template");
9303 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9305 ntups = PQntuples(res);
9306 *numTSTemplates = ntups;
9308 tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9310 i_tableoid = PQfnumber(res, "tableoid");
9311 i_oid = PQfnumber(res, "oid");
9312 i_tmplname = PQfnumber(res, "tmplname");
9313 i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9314 i_tmplinit = PQfnumber(res, "tmplinit");
9315 i_tmpllexize = PQfnumber(res, "tmpllexize");
9317 for (i = 0; i < ntups; i++)
9319 tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9320 tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9321 tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9322 AssignDumpId(&tmplinfo[i].dobj);
9323 tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9324 tmplinfo[i].dobj.namespace =
9325 findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9326 tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9327 tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9329 /* Decide whether we want to dump it */
9330 selectDumpableObject(&(tmplinfo[i].dobj), fout);
9333 PQclear(res);
9335 destroyPQExpBuffer(query);
9337 return tmplinfo;
9341 * getTSConfigurations:
9342 * read all text search configurations in the system catalogs and return
9343 * them in the TSConfigInfo* structure
9345 * numTSConfigs is set to the number of configurations read in
9347 TSConfigInfo *
9348 getTSConfigurations(Archive *fout, int *numTSConfigs)
9350 PGresult *res;
9351 int ntups;
9352 int i;
9353 PQExpBuffer query;
9354 TSConfigInfo *cfginfo;
9355 int i_tableoid;
9356 int i_oid;
9357 int i_cfgname;
9358 int i_cfgnamespace;
9359 int i_cfgowner;
9360 int i_cfgparser;
9362 query = createPQExpBuffer();
9364 appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9365 "cfgnamespace, cfgowner, cfgparser "
9366 "FROM pg_ts_config");
9368 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9370 ntups = PQntuples(res);
9371 *numTSConfigs = ntups;
9373 cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9375 i_tableoid = PQfnumber(res, "tableoid");
9376 i_oid = PQfnumber(res, "oid");
9377 i_cfgname = PQfnumber(res, "cfgname");
9378 i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9379 i_cfgowner = PQfnumber(res, "cfgowner");
9380 i_cfgparser = PQfnumber(res, "cfgparser");
9382 for (i = 0; i < ntups; i++)
9384 cfginfo[i].dobj.objType = DO_TSCONFIG;
9385 cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9386 cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9387 AssignDumpId(&cfginfo[i].dobj);
9388 cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9389 cfginfo[i].dobj.namespace =
9390 findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9391 cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9392 cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9394 /* Decide whether we want to dump it */
9395 selectDumpableObject(&(cfginfo[i].dobj), fout);
9398 PQclear(res);
9400 destroyPQExpBuffer(query);
9402 return cfginfo;
9406 * getForeignDataWrappers:
9407 * read all foreign-data wrappers in the system catalogs and return
9408 * them in the FdwInfo* structure
9410 * numForeignDataWrappers is set to the number of fdws read in
9412 FdwInfo *
9413 getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
9415 PGresult *res;
9416 int ntups;
9417 int i;
9418 PQExpBuffer query;
9419 FdwInfo *fdwinfo;
9420 int i_tableoid;
9421 int i_oid;
9422 int i_fdwname;
9423 int i_fdwowner;
9424 int i_fdwhandler;
9425 int i_fdwvalidator;
9426 int i_fdwacl;
9427 int i_acldefault;
9428 int i_fdwoptions;
9430 query = createPQExpBuffer();
9432 appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9433 "fdwowner, "
9434 "fdwhandler::pg_catalog.regproc, "
9435 "fdwvalidator::pg_catalog.regproc, "
9436 "fdwacl, "
9437 "acldefault('F', fdwowner) AS acldefault, "
9438 "array_to_string(ARRAY("
9439 "SELECT quote_ident(option_name) || ' ' || "
9440 "quote_literal(option_value) "
9441 "FROM pg_options_to_table(fdwoptions) "
9442 "ORDER BY option_name"
9443 "), E',\n ') AS fdwoptions "
9444 "FROM pg_foreign_data_wrapper");
9446 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9448 ntups = PQntuples(res);
9449 *numForeignDataWrappers = ntups;
9451 fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9453 i_tableoid = PQfnumber(res, "tableoid");
9454 i_oid = PQfnumber(res, "oid");
9455 i_fdwname = PQfnumber(res, "fdwname");
9456 i_fdwowner = PQfnumber(res, "fdwowner");
9457 i_fdwhandler = PQfnumber(res, "fdwhandler");
9458 i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9459 i_fdwacl = PQfnumber(res, "fdwacl");
9460 i_acldefault = PQfnumber(res, "acldefault");
9461 i_fdwoptions = PQfnumber(res, "fdwoptions");
9463 for (i = 0; i < ntups; i++)
9465 fdwinfo[i].dobj.objType = DO_FDW;
9466 fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9467 fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9468 AssignDumpId(&fdwinfo[i].dobj);
9469 fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9470 fdwinfo[i].dobj.namespace = NULL;
9471 fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9472 fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9473 fdwinfo[i].dacl.privtype = 0;
9474 fdwinfo[i].dacl.initprivs = NULL;
9475 fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
9476 fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9477 fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9478 fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9480 /* Decide whether we want to dump it */
9481 selectDumpableObject(&(fdwinfo[i].dobj), fout);
9483 /* Mark whether FDW has an ACL */
9484 if (!PQgetisnull(res, i, i_fdwacl))
9485 fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9488 PQclear(res);
9490 destroyPQExpBuffer(query);
9492 return fdwinfo;
9496 * getForeignServers:
9497 * read all foreign servers in the system catalogs and return
9498 * them in the ForeignServerInfo * structure
9500 * numForeignServers is set to the number of servers read in
9502 ForeignServerInfo *
9503 getForeignServers(Archive *fout, int *numForeignServers)
9505 PGresult *res;
9506 int ntups;
9507 int i;
9508 PQExpBuffer query;
9509 ForeignServerInfo *srvinfo;
9510 int i_tableoid;
9511 int i_oid;
9512 int i_srvname;
9513 int i_srvowner;
9514 int i_srvfdw;
9515 int i_srvtype;
9516 int i_srvversion;
9517 int i_srvacl;
9518 int i_acldefault;
9519 int i_srvoptions;
9521 query = createPQExpBuffer();
9523 appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
9524 "srvowner, "
9525 "srvfdw, srvtype, srvversion, srvacl, "
9526 "acldefault('S', srvowner) AS acldefault, "
9527 "array_to_string(ARRAY("
9528 "SELECT quote_ident(option_name) || ' ' || "
9529 "quote_literal(option_value) "
9530 "FROM pg_options_to_table(srvoptions) "
9531 "ORDER BY option_name"
9532 "), E',\n ') AS srvoptions "
9533 "FROM pg_foreign_server");
9535 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9537 ntups = PQntuples(res);
9538 *numForeignServers = ntups;
9540 srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9542 i_tableoid = PQfnumber(res, "tableoid");
9543 i_oid = PQfnumber(res, "oid");
9544 i_srvname = PQfnumber(res, "srvname");
9545 i_srvowner = PQfnumber(res, "srvowner");
9546 i_srvfdw = PQfnumber(res, "srvfdw");
9547 i_srvtype = PQfnumber(res, "srvtype");
9548 i_srvversion = PQfnumber(res, "srvversion");
9549 i_srvacl = PQfnumber(res, "srvacl");
9550 i_acldefault = PQfnumber(res, "acldefault");
9551 i_srvoptions = PQfnumber(res, "srvoptions");
9553 for (i = 0; i < ntups; i++)
9555 srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9556 srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9557 srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9558 AssignDumpId(&srvinfo[i].dobj);
9559 srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9560 srvinfo[i].dobj.namespace = NULL;
9561 srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9562 srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9563 srvinfo[i].dacl.privtype = 0;
9564 srvinfo[i].dacl.initprivs = NULL;
9565 srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
9566 srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9567 srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9568 srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9569 srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9571 /* Decide whether we want to dump it */
9572 selectDumpableObject(&(srvinfo[i].dobj), fout);
9574 /* Servers have user mappings */
9575 srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
9577 /* Mark whether server has an ACL */
9578 if (!PQgetisnull(res, i, i_srvacl))
9579 srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9582 PQclear(res);
9584 destroyPQExpBuffer(query);
9586 return srvinfo;
9590 * getDefaultACLs:
9591 * read all default ACL information in the system catalogs and return
9592 * them in the DefaultACLInfo structure
9594 * numDefaultACLs is set to the number of ACLs read in
9596 DefaultACLInfo *
9597 getDefaultACLs(Archive *fout, int *numDefaultACLs)
9599 DumpOptions *dopt = fout->dopt;
9600 DefaultACLInfo *daclinfo;
9601 PQExpBuffer query;
9602 PGresult *res;
9603 int i_oid;
9604 int i_tableoid;
9605 int i_defaclrole;
9606 int i_defaclnamespace;
9607 int i_defaclobjtype;
9608 int i_defaclacl;
9609 int i_acldefault;
9610 int i,
9611 ntups;
9613 query = createPQExpBuffer();
9616 * Global entries (with defaclnamespace=0) replace the hard-wired default
9617 * ACL for their object type. We should dump them as deltas from the
9618 * default ACL, since that will be used as a starting point for
9619 * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
9620 * non-global entries can only add privileges not revoke them. We must
9621 * dump those as-is (i.e., as deltas from an empty ACL).
9623 * We can use defaclobjtype as the object type for acldefault(), except
9624 * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
9625 * 's'.
9627 appendPQExpBufferStr(query,
9628 "SELECT oid, tableoid, "
9629 "defaclrole, "
9630 "defaclnamespace, "
9631 "defaclobjtype, "
9632 "defaclacl, "
9633 "CASE WHEN defaclnamespace = 0 THEN "
9634 "acldefault(CASE WHEN defaclobjtype = 'S' "
9635 "THEN 's'::\"char\" ELSE defaclobjtype END, "
9636 "defaclrole) ELSE '{}' END AS acldefault "
9637 "FROM pg_default_acl");
9639 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9641 ntups = PQntuples(res);
9642 *numDefaultACLs = ntups;
9644 daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9646 i_oid = PQfnumber(res, "oid");
9647 i_tableoid = PQfnumber(res, "tableoid");
9648 i_defaclrole = PQfnumber(res, "defaclrole");
9649 i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9650 i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9651 i_defaclacl = PQfnumber(res, "defaclacl");
9652 i_acldefault = PQfnumber(res, "acldefault");
9654 for (i = 0; i < ntups; i++)
9656 Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9658 daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9659 daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9660 daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9661 AssignDumpId(&daclinfo[i].dobj);
9662 /* cheesy ... is it worth coming up with a better object name? */
9663 daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
9665 if (nspid != InvalidOid)
9666 daclinfo[i].dobj.namespace = findNamespace(nspid);
9667 else
9668 daclinfo[i].dobj.namespace = NULL;
9670 daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
9671 daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9672 daclinfo[i].dacl.privtype = 0;
9673 daclinfo[i].dacl.initprivs = NULL;
9674 daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
9675 daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
9677 /* Default ACLs are ACLs, of course */
9678 daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9680 /* Decide whether we want to dump it */
9681 selectDumpableDefaultACL(&(daclinfo[i]), dopt);
9684 PQclear(res);
9686 destroyPQExpBuffer(query);
9688 return daclinfo;
9692 * getRoleName -- look up the name of a role, given its OID
9694 * In current usage, we don't expect failures, so error out for a bad OID.
9696 static const char *
9697 getRoleName(const char *roleoid_str)
9699 Oid roleoid = atooid(roleoid_str);
9702 * Do binary search to find the appropriate item.
9704 if (nrolenames > 0)
9706 RoleNameItem *low = &rolenames[0];
9707 RoleNameItem *high = &rolenames[nrolenames - 1];
9709 while (low <= high)
9711 RoleNameItem *middle = low + (high - low) / 2;
9713 if (roleoid < middle->roleoid)
9714 high = middle - 1;
9715 else if (roleoid > middle->roleoid)
9716 low = middle + 1;
9717 else
9718 return middle->rolename; /* found a match */
9722 pg_fatal("role with OID %u does not exist", roleoid);
9723 return NULL; /* keep compiler quiet */
9727 * collectRoleNames --
9729 * Construct a table of all known roles.
9730 * The table is sorted by OID for speed in lookup.
9732 static void
9733 collectRoleNames(Archive *fout)
9735 PGresult *res;
9736 const char *query;
9737 int i;
9739 query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
9741 res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
9743 nrolenames = PQntuples(res);
9745 rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
9747 for (i = 0; i < nrolenames; i++)
9749 rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
9750 rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
9753 PQclear(res);
9757 * getAdditionalACLs
9759 * We have now created all the DumpableObjects, and collected the ACL data
9760 * that appears in the directly-associated catalog entries. However, there's
9761 * more ACL-related info to collect. If any of a table's columns have ACLs,
9762 * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
9763 * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
9764 * Also, in versions having the pg_init_privs catalog, read that and load the
9765 * information into the relevant DumpableObjects.
9767 static void
9768 getAdditionalACLs(Archive *fout)
9770 PQExpBuffer query = createPQExpBuffer();
9771 PGresult *res;
9772 int ntups,
9775 /* Check for per-column ACLs */
9776 appendPQExpBufferStr(query,
9777 "SELECT DISTINCT attrelid FROM pg_attribute "
9778 "WHERE attacl IS NOT NULL");
9780 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9782 ntups = PQntuples(res);
9783 for (i = 0; i < ntups; i++)
9785 Oid relid = atooid(PQgetvalue(res, i, 0));
9786 TableInfo *tblinfo;
9788 tblinfo = findTableByOid(relid);
9789 /* OK to ignore tables we haven't got a DumpableObject for */
9790 if (tblinfo)
9792 tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
9793 tblinfo->hascolumnACLs = true;
9796 PQclear(res);
9798 /* Fetch initial-privileges data */
9799 if (fout->remoteVersion >= 90600)
9801 printfPQExpBuffer(query,
9802 "SELECT objoid, classoid, objsubid, privtype, initprivs "
9803 "FROM pg_init_privs");
9805 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9807 ntups = PQntuples(res);
9808 for (i = 0; i < ntups; i++)
9810 Oid objoid = atooid(PQgetvalue(res, i, 0));
9811 Oid classoid = atooid(PQgetvalue(res, i, 1));
9812 int objsubid = atoi(PQgetvalue(res, i, 2));
9813 char privtype = *(PQgetvalue(res, i, 3));
9814 char *initprivs = PQgetvalue(res, i, 4);
9815 CatalogId objId;
9816 DumpableObject *dobj;
9818 objId.tableoid = classoid;
9819 objId.oid = objoid;
9820 dobj = findObjectByCatalogId(objId);
9821 /* OK to ignore entries we haven't got a DumpableObject for */
9822 if (dobj)
9824 /* Cope with sub-object initprivs */
9825 if (objsubid != 0)
9827 if (dobj->objType == DO_TABLE)
9829 /* For a column initprivs, set the table's ACL flags */
9830 dobj->components |= DUMP_COMPONENT_ACL;
9831 ((TableInfo *) dobj)->hascolumnACLs = true;
9833 else
9834 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
9835 classoid, objoid, objsubid);
9836 continue;
9840 * We ignore any pg_init_privs.initprivs entry for the public
9841 * schema, as explained in getNamespaces().
9843 if (dobj->objType == DO_NAMESPACE &&
9844 strcmp(dobj->name, "public") == 0)
9845 continue;
9847 /* Else it had better be of a type we think has ACLs */
9848 if (dobj->objType == DO_NAMESPACE ||
9849 dobj->objType == DO_TYPE ||
9850 dobj->objType == DO_FUNC ||
9851 dobj->objType == DO_AGG ||
9852 dobj->objType == DO_TABLE ||
9853 dobj->objType == DO_PROCLANG ||
9854 dobj->objType == DO_FDW ||
9855 dobj->objType == DO_FOREIGN_SERVER)
9857 DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
9859 daobj->dacl.privtype = privtype;
9860 daobj->dacl.initprivs = pstrdup(initprivs);
9862 else
9863 pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
9864 classoid, objoid, objsubid);
9867 PQclear(res);
9870 destroyPQExpBuffer(query);
9874 * dumpCommentExtended --
9876 * This routine is used to dump any comments associated with the
9877 * object handed to this routine. The routine takes the object type
9878 * and object name (ready to print, except for schema decoration), plus
9879 * the namespace and owner of the object (for labeling the ArchiveEntry),
9880 * plus catalog ID and subid which are the lookup key for pg_description,
9881 * plus the dump ID for the object (for setting a dependency).
9882 * If a matching pg_description entry is found, it is dumped.
9884 * Note: in some cases, such as comments for triggers and rules, the "type"
9885 * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
9886 * but it doesn't seem worth complicating the API for all callers to make
9887 * it cleaner.
9889 * Note: although this routine takes a dumpId for dependency purposes,
9890 * that purpose is just to mark the dependency in the emitted dump file
9891 * for possible future use by pg_restore. We do NOT use it for determining
9892 * ordering of the comment in the dump file, because this routine is called
9893 * after dependency sorting occurs. This routine should be called just after
9894 * calling ArchiveEntry() for the specified object.
9896 static void
9897 dumpCommentExtended(Archive *fout, const char *type,
9898 const char *name, const char *namespace,
9899 const char *owner, CatalogId catalogId,
9900 int subid, DumpId dumpId,
9901 const char *initdb_comment)
9903 DumpOptions *dopt = fout->dopt;
9904 CommentItem *comments;
9905 int ncomments;
9907 /* do nothing, if --no-comments is supplied */
9908 if (dopt->no_comments)
9909 return;
9911 /* Comments are schema not data ... except LO comments are data */
9912 if (strcmp(type, "LARGE OBJECT") != 0)
9914 if (dopt->dataOnly)
9915 return;
9917 else
9919 /* We do dump LO comments in binary-upgrade mode */
9920 if (dopt->schemaOnly && !dopt->binary_upgrade)
9921 return;
9924 /* Search for comments associated with catalogId, using table */
9925 ncomments = findComments(catalogId.tableoid, catalogId.oid,
9926 &comments);
9928 /* Is there one matching the subid? */
9929 while (ncomments > 0)
9931 if (comments->objsubid == subid)
9932 break;
9933 comments++;
9934 ncomments--;
9937 if (initdb_comment != NULL)
9939 static CommentItem empty_comment = {.descr = ""};
9942 * initdb creates this object with a comment. Skip dumping the
9943 * initdb-provided comment, which would complicate matters for
9944 * non-superuser use of pg_dump. When the DBA has removed initdb's
9945 * comment, replicate that.
9947 if (ncomments == 0)
9949 comments = &empty_comment;
9950 ncomments = 1;
9952 else if (strcmp(comments->descr, initdb_comment) == 0)
9953 ncomments = 0;
9956 /* If a comment exists, build COMMENT ON statement */
9957 if (ncomments > 0)
9959 PQExpBuffer query = createPQExpBuffer();
9960 PQExpBuffer tag = createPQExpBuffer();
9962 appendPQExpBuffer(query, "COMMENT ON %s ", type);
9963 if (namespace && *namespace)
9964 appendPQExpBuffer(query, "%s.", fmtId(namespace));
9965 appendPQExpBuffer(query, "%s IS ", name);
9966 appendStringLiteralAH(query, comments->descr, fout);
9967 appendPQExpBufferStr(query, ";\n");
9969 appendPQExpBuffer(tag, "%s %s", type, name);
9972 * We mark comments as SECTION_NONE because they really belong in the
9973 * same section as their parent, whether that is pre-data or
9974 * post-data.
9976 ArchiveEntry(fout, nilCatalogId, createDumpId(),
9977 ARCHIVE_OPTS(.tag = tag->data,
9978 .namespace = namespace,
9979 .owner = owner,
9980 .description = "COMMENT",
9981 .section = SECTION_NONE,
9982 .createStmt = query->data,
9983 .deps = &dumpId,
9984 .nDeps = 1));
9986 destroyPQExpBuffer(query);
9987 destroyPQExpBuffer(tag);
9992 * dumpComment --
9994 * Typical simplification of the above function.
9996 static inline void
9997 dumpComment(Archive *fout, const char *type,
9998 const char *name, const char *namespace,
9999 const char *owner, CatalogId catalogId,
10000 int subid, DumpId dumpId)
10002 dumpCommentExtended(fout, type, name, namespace, owner,
10003 catalogId, subid, dumpId, NULL);
10007 * dumpTableComment --
10009 * As above, but dump comments for both the specified table (or view)
10010 * and its columns.
10012 static void
10013 dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10014 const char *reltypename)
10016 DumpOptions *dopt = fout->dopt;
10017 CommentItem *comments;
10018 int ncomments;
10019 PQExpBuffer query;
10020 PQExpBuffer tag;
10022 /* do nothing, if --no-comments is supplied */
10023 if (dopt->no_comments)
10024 return;
10026 /* Comments are SCHEMA not data */
10027 if (dopt->dataOnly)
10028 return;
10030 /* Search for comments associated with relation, using table */
10031 ncomments = findComments(tbinfo->dobj.catId.tableoid,
10032 tbinfo->dobj.catId.oid,
10033 &comments);
10035 /* If comments exist, build COMMENT ON statements */
10036 if (ncomments <= 0)
10037 return;
10039 query = createPQExpBuffer();
10040 tag = createPQExpBuffer();
10042 while (ncomments > 0)
10044 const char *descr = comments->descr;
10045 int objsubid = comments->objsubid;
10047 if (objsubid == 0)
10049 resetPQExpBuffer(tag);
10050 appendPQExpBuffer(tag, "%s %s", reltypename,
10051 fmtId(tbinfo->dobj.name));
10053 resetPQExpBuffer(query);
10054 appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10055 fmtQualifiedDumpable(tbinfo));
10056 appendStringLiteralAH(query, descr, fout);
10057 appendPQExpBufferStr(query, ";\n");
10059 ArchiveEntry(fout, nilCatalogId, createDumpId(),
10060 ARCHIVE_OPTS(.tag = tag->data,
10061 .namespace = tbinfo->dobj.namespace->dobj.name,
10062 .owner = tbinfo->rolname,
10063 .description = "COMMENT",
10064 .section = SECTION_NONE,
10065 .createStmt = query->data,
10066 .deps = &(tbinfo->dobj.dumpId),
10067 .nDeps = 1));
10069 else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10071 resetPQExpBuffer(tag);
10072 appendPQExpBuffer(tag, "COLUMN %s.",
10073 fmtId(tbinfo->dobj.name));
10074 appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10076 resetPQExpBuffer(query);
10077 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10078 fmtQualifiedDumpable(tbinfo));
10079 appendPQExpBuffer(query, "%s IS ",
10080 fmtId(tbinfo->attnames[objsubid - 1]));
10081 appendStringLiteralAH(query, descr, fout);
10082 appendPQExpBufferStr(query, ";\n");
10084 ArchiveEntry(fout, nilCatalogId, createDumpId(),
10085 ARCHIVE_OPTS(.tag = tag->data,
10086 .namespace = tbinfo->dobj.namespace->dobj.name,
10087 .owner = tbinfo->rolname,
10088 .description = "COMMENT",
10089 .section = SECTION_NONE,
10090 .createStmt = query->data,
10091 .deps = &(tbinfo->dobj.dumpId),
10092 .nDeps = 1));
10095 comments++;
10096 ncomments--;
10099 destroyPQExpBuffer(query);
10100 destroyPQExpBuffer(tag);
10104 * findComments --
10106 * Find the comment(s), if any, associated with the given object. All the
10107 * objsubid values associated with the given classoid/objoid are found with
10108 * one search.
10110 static int
10111 findComments(Oid classoid, Oid objoid, CommentItem **items)
10113 CommentItem *middle = NULL;
10114 CommentItem *low;
10115 CommentItem *high;
10116 int nmatch;
10119 * Do binary search to find some item matching the object.
10121 low = &comments[0];
10122 high = &comments[ncomments - 1];
10123 while (low <= high)
10125 middle = low + (high - low) / 2;
10127 if (classoid < middle->classoid)
10128 high = middle - 1;
10129 else if (classoid > middle->classoid)
10130 low = middle + 1;
10131 else if (objoid < middle->objoid)
10132 high = middle - 1;
10133 else if (objoid > middle->objoid)
10134 low = middle + 1;
10135 else
10136 break; /* found a match */
10139 if (low > high) /* no matches */
10141 *items = NULL;
10142 return 0;
10146 * Now determine how many items match the object. The search loop
10147 * invariant still holds: only items between low and high inclusive could
10148 * match.
10150 nmatch = 1;
10151 while (middle > low)
10153 if (classoid != middle[-1].classoid ||
10154 objoid != middle[-1].objoid)
10155 break;
10156 middle--;
10157 nmatch++;
10160 *items = middle;
10162 middle += nmatch;
10163 while (middle <= high)
10165 if (classoid != middle->classoid ||
10166 objoid != middle->objoid)
10167 break;
10168 middle++;
10169 nmatch++;
10172 return nmatch;
10176 * collectComments --
10178 * Construct a table of all comments available for database objects;
10179 * also set the has-comment component flag for each relevant object.
10181 * We used to do per-object queries for the comments, but it's much faster
10182 * to pull them all over at once, and on most databases the memory cost
10183 * isn't high.
10185 * The table is sorted by classoid/objid/objsubid for speed in lookup.
10187 static void
10188 collectComments(Archive *fout)
10190 PGresult *res;
10191 PQExpBuffer query;
10192 int i_description;
10193 int i_classoid;
10194 int i_objoid;
10195 int i_objsubid;
10196 int ntups;
10197 int i;
10198 DumpableObject *dobj;
10200 query = createPQExpBuffer();
10202 appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10203 "FROM pg_catalog.pg_description "
10204 "ORDER BY classoid, objoid, objsubid");
10206 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10208 /* Construct lookup table containing OIDs in numeric form */
10210 i_description = PQfnumber(res, "description");
10211 i_classoid = PQfnumber(res, "classoid");
10212 i_objoid = PQfnumber(res, "objoid");
10213 i_objsubid = PQfnumber(res, "objsubid");
10215 ntups = PQntuples(res);
10217 comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10218 ncomments = 0;
10219 dobj = NULL;
10221 for (i = 0; i < ntups; i++)
10223 CatalogId objId;
10224 int subid;
10226 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
10227 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
10228 subid = atoi(PQgetvalue(res, i, i_objsubid));
10230 /* We needn't remember comments that don't match any dumpable object */
10231 if (dobj == NULL ||
10232 dobj->catId.tableoid != objId.tableoid ||
10233 dobj->catId.oid != objId.oid)
10234 dobj = findObjectByCatalogId(objId);
10235 if (dobj == NULL)
10236 continue;
10239 * Comments on columns of composite types are linked to the type's
10240 * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
10241 * in the type's own DumpableObject.
10243 if (subid != 0 && dobj->objType == DO_TABLE &&
10244 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
10246 TypeInfo *cTypeInfo;
10248 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
10249 if (cTypeInfo)
10250 cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
10252 else
10253 dobj->components |= DUMP_COMPONENT_COMMENT;
10255 comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
10256 comments[ncomments].classoid = objId.tableoid;
10257 comments[ncomments].objoid = objId.oid;
10258 comments[ncomments].objsubid = subid;
10259 ncomments++;
10262 PQclear(res);
10263 destroyPQExpBuffer(query);
10267 * dumpDumpableObject
10269 * This routine and its subsidiaries are responsible for creating
10270 * ArchiveEntries (TOC objects) for each object to be dumped.
10272 static void
10273 dumpDumpableObject(Archive *fout, DumpableObject *dobj)
10276 * Clear any dump-request bits for components that don't exist for this
10277 * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
10278 * request for every kind of object.)
10280 dobj->dump &= dobj->components;
10282 /* Now, short-circuit if there's nothing to be done here. */
10283 if (dobj->dump == 0)
10284 return;
10286 switch (dobj->objType)
10288 case DO_NAMESPACE:
10289 dumpNamespace(fout, (const NamespaceInfo *) dobj);
10290 break;
10291 case DO_EXTENSION:
10292 dumpExtension(fout, (const ExtensionInfo *) dobj);
10293 break;
10294 case DO_TYPE:
10295 dumpType(fout, (const TypeInfo *) dobj);
10296 break;
10297 case DO_SHELL_TYPE:
10298 dumpShellType(fout, (const ShellTypeInfo *) dobj);
10299 break;
10300 case DO_FUNC:
10301 dumpFunc(fout, (const FuncInfo *) dobj);
10302 break;
10303 case DO_AGG:
10304 dumpAgg(fout, (const AggInfo *) dobj);
10305 break;
10306 case DO_OPERATOR:
10307 dumpOpr(fout, (const OprInfo *) dobj);
10308 break;
10309 case DO_ACCESS_METHOD:
10310 dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
10311 break;
10312 case DO_OPCLASS:
10313 dumpOpclass(fout, (const OpclassInfo *) dobj);
10314 break;
10315 case DO_OPFAMILY:
10316 dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
10317 break;
10318 case DO_COLLATION:
10319 dumpCollation(fout, (const CollInfo *) dobj);
10320 break;
10321 case DO_CONVERSION:
10322 dumpConversion(fout, (const ConvInfo *) dobj);
10323 break;
10324 case DO_TABLE:
10325 dumpTable(fout, (const TableInfo *) dobj);
10326 break;
10327 case DO_TABLE_ATTACH:
10328 dumpTableAttach(fout, (const TableAttachInfo *) dobj);
10329 break;
10330 case DO_ATTRDEF:
10331 dumpAttrDef(fout, (const AttrDefInfo *) dobj);
10332 break;
10333 case DO_INDEX:
10334 dumpIndex(fout, (const IndxInfo *) dobj);
10335 break;
10336 case DO_INDEX_ATTACH:
10337 dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
10338 break;
10339 case DO_STATSEXT:
10340 dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
10341 break;
10342 case DO_REFRESH_MATVIEW:
10343 refreshMatViewData(fout, (const TableDataInfo *) dobj);
10344 break;
10345 case DO_RULE:
10346 dumpRule(fout, (const RuleInfo *) dobj);
10347 break;
10348 case DO_TRIGGER:
10349 dumpTrigger(fout, (const TriggerInfo *) dobj);
10350 break;
10351 case DO_EVENT_TRIGGER:
10352 dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
10353 break;
10354 case DO_CONSTRAINT:
10355 dumpConstraint(fout, (const ConstraintInfo *) dobj);
10356 break;
10357 case DO_FK_CONSTRAINT:
10358 dumpConstraint(fout, (const ConstraintInfo *) dobj);
10359 break;
10360 case DO_PROCLANG:
10361 dumpProcLang(fout, (const ProcLangInfo *) dobj);
10362 break;
10363 case DO_CAST:
10364 dumpCast(fout, (const CastInfo *) dobj);
10365 break;
10366 case DO_TRANSFORM:
10367 dumpTransform(fout, (const TransformInfo *) dobj);
10368 break;
10369 case DO_SEQUENCE_SET:
10370 dumpSequenceData(fout, (const TableDataInfo *) dobj);
10371 break;
10372 case DO_TABLE_DATA:
10373 dumpTableData(fout, (const TableDataInfo *) dobj);
10374 break;
10375 case DO_DUMMY_TYPE:
10376 /* table rowtypes and array types are never dumped separately */
10377 break;
10378 case DO_TSPARSER:
10379 dumpTSParser(fout, (const TSParserInfo *) dobj);
10380 break;
10381 case DO_TSDICT:
10382 dumpTSDictionary(fout, (const TSDictInfo *) dobj);
10383 break;
10384 case DO_TSTEMPLATE:
10385 dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
10386 break;
10387 case DO_TSCONFIG:
10388 dumpTSConfig(fout, (const TSConfigInfo *) dobj);
10389 break;
10390 case DO_FDW:
10391 dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
10392 break;
10393 case DO_FOREIGN_SERVER:
10394 dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
10395 break;
10396 case DO_DEFAULT_ACL:
10397 dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
10398 break;
10399 case DO_LARGE_OBJECT:
10400 dumpLO(fout, (const LoInfo *) dobj);
10401 break;
10402 case DO_LARGE_OBJECT_DATA:
10403 if (dobj->dump & DUMP_COMPONENT_DATA)
10405 TocEntry *te;
10407 te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
10408 ARCHIVE_OPTS(.tag = dobj->name,
10409 .description = "BLOBS",
10410 .section = SECTION_DATA,
10411 .dumpFn = dumpLOs));
10414 * Set the TocEntry's dataLength in case we are doing a
10415 * parallel dump and want to order dump jobs by table size.
10416 * (We need some size estimate for every TocEntry with a
10417 * DataDumper function.) We don't currently have any cheap
10418 * way to estimate the size of LOs, but it doesn't matter;
10419 * let's just set the size to a large value so parallel dumps
10420 * will launch this job first. If there's lots of LOs, we
10421 * win, and if there aren't, we don't lose much. (If you want
10422 * to improve on this, really what you should be thinking
10423 * about is allowing LO dumping to be parallelized, not just
10424 * getting a smarter estimate for the single TOC entry.)
10426 te->dataLength = INT_MAX;
10428 break;
10429 case DO_POLICY:
10430 dumpPolicy(fout, (const PolicyInfo *) dobj);
10431 break;
10432 case DO_PUBLICATION:
10433 dumpPublication(fout, (const PublicationInfo *) dobj);
10434 break;
10435 case DO_PUBLICATION_REL:
10436 dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
10437 break;
10438 case DO_PUBLICATION_TABLE_IN_SCHEMA:
10439 dumpPublicationNamespace(fout,
10440 (const PublicationSchemaInfo *) dobj);
10441 break;
10442 case DO_SUBSCRIPTION:
10443 dumpSubscription(fout, (const SubscriptionInfo *) dobj);
10444 break;
10445 case DO_PRE_DATA_BOUNDARY:
10446 case DO_POST_DATA_BOUNDARY:
10447 /* never dumped, nothing to do */
10448 break;
10453 * dumpNamespace
10454 * writes out to fout the queries to recreate a user-defined namespace
10456 static void
10457 dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
10459 DumpOptions *dopt = fout->dopt;
10460 PQExpBuffer q;
10461 PQExpBuffer delq;
10462 char *qnspname;
10464 /* Do nothing in data-only dump */
10465 if (dopt->dataOnly)
10466 return;
10468 q = createPQExpBuffer();
10469 delq = createPQExpBuffer();
10471 qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
10473 if (nspinfo->create)
10475 appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
10476 appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
10478 else
10480 /* see selectDumpableNamespace() */
10481 appendPQExpBufferStr(delq,
10482 "-- *not* dropping schema, since initdb creates it\n");
10483 appendPQExpBufferStr(q,
10484 "-- *not* creating schema, since initdb creates it\n");
10487 if (dopt->binary_upgrade)
10488 binary_upgrade_extension_member(q, &nspinfo->dobj,
10489 "SCHEMA", qnspname, NULL);
10491 if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10492 ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
10493 ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
10494 .owner = nspinfo->rolname,
10495 .description = "SCHEMA",
10496 .section = SECTION_PRE_DATA,
10497 .createStmt = q->data,
10498 .dropStmt = delq->data));
10500 /* Dump Schema Comments and Security Labels */
10501 if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10503 const char *initdb_comment = NULL;
10505 if (!nspinfo->create && strcmp(qnspname, "public") == 0)
10506 initdb_comment = "standard public schema";
10507 dumpCommentExtended(fout, "SCHEMA", qnspname,
10508 NULL, nspinfo->rolname,
10509 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
10510 initdb_comment);
10513 if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10514 dumpSecLabel(fout, "SCHEMA", qnspname,
10515 NULL, nspinfo->rolname,
10516 nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
10518 if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
10519 dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
10520 qnspname, NULL, NULL,
10521 nspinfo->rolname, &nspinfo->dacl);
10523 free(qnspname);
10525 destroyPQExpBuffer(q);
10526 destroyPQExpBuffer(delq);
10530 * dumpExtension
10531 * writes out to fout the queries to recreate an extension
10533 static void
10534 dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
10536 DumpOptions *dopt = fout->dopt;
10537 PQExpBuffer q;
10538 PQExpBuffer delq;
10539 char *qextname;
10541 /* Do nothing in data-only dump */
10542 if (dopt->dataOnly)
10543 return;
10545 q = createPQExpBuffer();
10546 delq = createPQExpBuffer();
10548 qextname = pg_strdup(fmtId(extinfo->dobj.name));
10550 appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
10552 if (!dopt->binary_upgrade)
10555 * In a regular dump, we simply create the extension, intentionally
10556 * not specifying a version, so that the destination installation's
10557 * default version is used.
10559 * Use of IF NOT EXISTS here is unlike our behavior for other object
10560 * types; but there are various scenarios in which it's convenient to
10561 * manually create the desired extension before restoring, so we
10562 * prefer to allow it to exist already.
10564 appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
10565 qextname, fmtId(extinfo->namespace));
10567 else
10570 * In binary-upgrade mode, it's critical to reproduce the state of the
10571 * database exactly, so our procedure is to create an empty extension,
10572 * restore all the contained objects normally, and add them to the
10573 * extension one by one. This function performs just the first of
10574 * those steps. binary_upgrade_extension_member() takes care of
10575 * adding member objects as they're created.
10577 int i;
10578 int n;
10580 appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
10583 * We unconditionally create the extension, so we must drop it if it
10584 * exists. This could happen if the user deleted 'plpgsql' and then
10585 * readded it, causing its oid to be greater than g_last_builtin_oid.
10587 appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
10589 appendPQExpBufferStr(q,
10590 "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
10591 appendStringLiteralAH(q, extinfo->dobj.name, fout);
10592 appendPQExpBufferStr(q, ", ");
10593 appendStringLiteralAH(q, extinfo->namespace, fout);
10594 appendPQExpBufferStr(q, ", ");
10595 appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
10596 appendStringLiteralAH(q, extinfo->extversion, fout);
10597 appendPQExpBufferStr(q, ", ");
10600 * Note that we're pushing extconfig (an OID array) back into
10601 * pg_extension exactly as-is. This is OK because pg_class OIDs are
10602 * preserved in binary upgrade.
10604 if (strlen(extinfo->extconfig) > 2)
10605 appendStringLiteralAH(q, extinfo->extconfig, fout);
10606 else
10607 appendPQExpBufferStr(q, "NULL");
10608 appendPQExpBufferStr(q, ", ");
10609 if (strlen(extinfo->extcondition) > 2)
10610 appendStringLiteralAH(q, extinfo->extcondition, fout);
10611 else
10612 appendPQExpBufferStr(q, "NULL");
10613 appendPQExpBufferStr(q, ", ");
10614 appendPQExpBufferStr(q, "ARRAY[");
10615 n = 0;
10616 for (i = 0; i < extinfo->dobj.nDeps; i++)
10618 DumpableObject *extobj;
10620 extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
10621 if (extobj && extobj->objType == DO_EXTENSION)
10623 if (n++ > 0)
10624 appendPQExpBufferChar(q, ',');
10625 appendStringLiteralAH(q, extobj->name, fout);
10628 appendPQExpBufferStr(q, "]::pg_catalog.text[]");
10629 appendPQExpBufferStr(q, ");\n");
10632 if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10633 ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
10634 ARCHIVE_OPTS(.tag = extinfo->dobj.name,
10635 .description = "EXTENSION",
10636 .section = SECTION_PRE_DATA,
10637 .createStmt = q->data,
10638 .dropStmt = delq->data));
10640 /* Dump Extension Comments and Security Labels */
10641 if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10642 dumpComment(fout, "EXTENSION", qextname,
10643 NULL, "",
10644 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10646 if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10647 dumpSecLabel(fout, "EXTENSION", qextname,
10648 NULL, "",
10649 extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10651 free(qextname);
10653 destroyPQExpBuffer(q);
10654 destroyPQExpBuffer(delq);
10658 * dumpType
10659 * writes out to fout the queries to recreate a user-defined type
10661 static void
10662 dumpType(Archive *fout, const TypeInfo *tyinfo)
10664 DumpOptions *dopt = fout->dopt;
10666 /* Do nothing in data-only dump */
10667 if (dopt->dataOnly)
10668 return;
10670 /* Dump out in proper style */
10671 if (tyinfo->typtype == TYPTYPE_BASE)
10672 dumpBaseType(fout, tyinfo);
10673 else if (tyinfo->typtype == TYPTYPE_DOMAIN)
10674 dumpDomain(fout, tyinfo);
10675 else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
10676 dumpCompositeType(fout, tyinfo);
10677 else if (tyinfo->typtype == TYPTYPE_ENUM)
10678 dumpEnumType(fout, tyinfo);
10679 else if (tyinfo->typtype == TYPTYPE_RANGE)
10680 dumpRangeType(fout, tyinfo);
10681 else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
10682 dumpUndefinedType(fout, tyinfo);
10683 else
10684 pg_log_warning("typtype of data type \"%s\" appears to be invalid",
10685 tyinfo->dobj.name);
10689 * dumpEnumType
10690 * writes out to fout the queries to recreate a user-defined enum type
10692 static void
10693 dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
10695 DumpOptions *dopt = fout->dopt;
10696 PQExpBuffer q = createPQExpBuffer();
10697 PQExpBuffer delq = createPQExpBuffer();
10698 PQExpBuffer query = createPQExpBuffer();
10699 PGresult *res;
10700 int num,
10702 Oid enum_oid;
10703 char *qtypname;
10704 char *qualtypname;
10705 char *label;
10706 int i_enumlabel;
10707 int i_oid;
10709 if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
10711 /* Set up query for enum-specific details */
10712 appendPQExpBufferStr(query,
10713 "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
10714 "SELECT oid, enumlabel "
10715 "FROM pg_catalog.pg_enum "
10716 "WHERE enumtypid = $1 "
10717 "ORDER BY enumsortorder");
10719 ExecuteSqlStatement(fout, query->data);
10721 fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
10724 printfPQExpBuffer(query,
10725 "EXECUTE dumpEnumType('%u')",
10726 tyinfo->dobj.catId.oid);
10728 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10730 num = PQntuples(res);
10732 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10733 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10736 * CASCADE shouldn't be required here as for normal types since the I/O
10737 * functions are generic and do not get dropped.
10739 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10741 if (dopt->binary_upgrade)
10742 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10743 tyinfo->dobj.catId.oid,
10744 false, false);
10746 appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
10747 qualtypname);
10749 if (!dopt->binary_upgrade)
10751 i_enumlabel = PQfnumber(res, "enumlabel");
10753 /* Labels with server-assigned oids */
10754 for (i = 0; i < num; i++)
10756 label = PQgetvalue(res, i, i_enumlabel);
10757 if (i > 0)
10758 appendPQExpBufferChar(q, ',');
10759 appendPQExpBufferStr(q, "\n ");
10760 appendStringLiteralAH(q, label, fout);
10764 appendPQExpBufferStr(q, "\n);\n");
10766 if (dopt->binary_upgrade)
10768 i_oid = PQfnumber(res, "oid");
10769 i_enumlabel = PQfnumber(res, "enumlabel");
10771 /* Labels with dump-assigned (preserved) oids */
10772 for (i = 0; i < num; i++)
10774 enum_oid = atooid(PQgetvalue(res, i, i_oid));
10775 label = PQgetvalue(res, i, i_enumlabel);
10777 if (i == 0)
10778 appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
10779 appendPQExpBuffer(q,
10780 "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
10781 enum_oid);
10782 appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
10783 appendStringLiteralAH(q, label, fout);
10784 appendPQExpBufferStr(q, ";\n\n");
10788 if (dopt->binary_upgrade)
10789 binary_upgrade_extension_member(q, &tyinfo->dobj,
10790 "TYPE", qtypname,
10791 tyinfo->dobj.namespace->dobj.name);
10793 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10794 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10795 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10796 .namespace = tyinfo->dobj.namespace->dobj.name,
10797 .owner = tyinfo->rolname,
10798 .description = "TYPE",
10799 .section = SECTION_PRE_DATA,
10800 .createStmt = q->data,
10801 .dropStmt = delq->data));
10803 /* Dump Type Comments and Security Labels */
10804 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10805 dumpComment(fout, "TYPE", qtypname,
10806 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10807 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10809 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10810 dumpSecLabel(fout, "TYPE", qtypname,
10811 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10812 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10814 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10815 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10816 qtypname, NULL,
10817 tyinfo->dobj.namespace->dobj.name,
10818 tyinfo->rolname, &tyinfo->dacl);
10820 PQclear(res);
10821 destroyPQExpBuffer(q);
10822 destroyPQExpBuffer(delq);
10823 destroyPQExpBuffer(query);
10824 free(qtypname);
10825 free(qualtypname);
10829 * dumpRangeType
10830 * writes out to fout the queries to recreate a user-defined range type
10832 static void
10833 dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
10835 DumpOptions *dopt = fout->dopt;
10836 PQExpBuffer q = createPQExpBuffer();
10837 PQExpBuffer delq = createPQExpBuffer();
10838 PQExpBuffer query = createPQExpBuffer();
10839 PGresult *res;
10840 Oid collationOid;
10841 char *qtypname;
10842 char *qualtypname;
10843 char *procname;
10845 if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
10847 /* Set up query for range-specific details */
10848 appendPQExpBufferStr(query,
10849 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
10851 appendPQExpBufferStr(query,
10852 "SELECT ");
10854 if (fout->remoteVersion >= 140000)
10855 appendPQExpBufferStr(query,
10856 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
10857 else
10858 appendPQExpBufferStr(query,
10859 "NULL AS rngmultitype, ");
10861 appendPQExpBufferStr(query,
10862 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
10863 "opc.opcname AS opcname, "
10864 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
10865 " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
10866 "opc.opcdefault, "
10867 "CASE WHEN rngcollation = st.typcollation THEN 0 "
10868 " ELSE rngcollation END AS collation, "
10869 "rngcanonical, rngsubdiff "
10870 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
10871 " pg_catalog.pg_opclass opc "
10872 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
10873 "rngtypid = $1");
10875 ExecuteSqlStatement(fout, query->data);
10877 fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
10880 printfPQExpBuffer(query,
10881 "EXECUTE dumpRangeType('%u')",
10882 tyinfo->dobj.catId.oid);
10884 res = ExecuteSqlQueryForSingleRow(fout, query->data);
10886 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10887 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10890 * CASCADE shouldn't be required here as for normal types since the I/O
10891 * functions are generic and do not get dropped.
10893 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10895 if (dopt->binary_upgrade)
10896 binary_upgrade_set_type_oids_by_type_oid(fout, q,
10897 tyinfo->dobj.catId.oid,
10898 false, true);
10900 appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
10901 qualtypname);
10903 appendPQExpBuffer(q, "\n subtype = %s",
10904 PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
10906 if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
10907 appendPQExpBuffer(q, ",\n multirange_type_name = %s",
10908 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
10910 /* print subtype_opclass only if not default for subtype */
10911 if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
10913 char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
10914 char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
10916 appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
10917 fmtId(nspname));
10918 appendPQExpBufferStr(q, fmtId(opcname));
10921 collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
10922 if (OidIsValid(collationOid))
10924 CollInfo *coll = findCollationByOid(collationOid);
10926 if (coll)
10927 appendPQExpBuffer(q, ",\n collation = %s",
10928 fmtQualifiedDumpable(coll));
10931 procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
10932 if (strcmp(procname, "-") != 0)
10933 appendPQExpBuffer(q, ",\n canonical = %s", procname);
10935 procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
10936 if (strcmp(procname, "-") != 0)
10937 appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
10939 appendPQExpBufferStr(q, "\n);\n");
10941 if (dopt->binary_upgrade)
10942 binary_upgrade_extension_member(q, &tyinfo->dobj,
10943 "TYPE", qtypname,
10944 tyinfo->dobj.namespace->dobj.name);
10946 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10947 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
10948 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
10949 .namespace = tyinfo->dobj.namespace->dobj.name,
10950 .owner = tyinfo->rolname,
10951 .description = "TYPE",
10952 .section = SECTION_PRE_DATA,
10953 .createStmt = q->data,
10954 .dropStmt = delq->data));
10956 /* Dump Type Comments and Security Labels */
10957 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10958 dumpComment(fout, "TYPE", qtypname,
10959 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10960 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10962 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10963 dumpSecLabel(fout, "TYPE", qtypname,
10964 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
10965 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
10967 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
10968 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
10969 qtypname, NULL,
10970 tyinfo->dobj.namespace->dobj.name,
10971 tyinfo->rolname, &tyinfo->dacl);
10973 PQclear(res);
10974 destroyPQExpBuffer(q);
10975 destroyPQExpBuffer(delq);
10976 destroyPQExpBuffer(query);
10977 free(qtypname);
10978 free(qualtypname);
10982 * dumpUndefinedType
10983 * writes out to fout the queries to recreate a !typisdefined type
10985 * This is a shell type, but we use different terminology to distinguish
10986 * this case from where we have to emit a shell type definition to break
10987 * circular dependencies. An undefined type shouldn't ever have anything
10988 * depending on it.
10990 static void
10991 dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
10993 DumpOptions *dopt = fout->dopt;
10994 PQExpBuffer q = createPQExpBuffer();
10995 PQExpBuffer delq = createPQExpBuffer();
10996 char *qtypname;
10997 char *qualtypname;
10999 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11000 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11002 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11004 if (dopt->binary_upgrade)
11005 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11006 tyinfo->dobj.catId.oid,
11007 false, false);
11009 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11010 qualtypname);
11012 if (dopt->binary_upgrade)
11013 binary_upgrade_extension_member(q, &tyinfo->dobj,
11014 "TYPE", qtypname,
11015 tyinfo->dobj.namespace->dobj.name);
11017 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11018 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11019 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11020 .namespace = tyinfo->dobj.namespace->dobj.name,
11021 .owner = tyinfo->rolname,
11022 .description = "TYPE",
11023 .section = SECTION_PRE_DATA,
11024 .createStmt = q->data,
11025 .dropStmt = delq->data));
11027 /* Dump Type Comments and Security Labels */
11028 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11029 dumpComment(fout, "TYPE", qtypname,
11030 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11031 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11033 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11034 dumpSecLabel(fout, "TYPE", qtypname,
11035 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11036 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11038 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11039 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11040 qtypname, NULL,
11041 tyinfo->dobj.namespace->dobj.name,
11042 tyinfo->rolname, &tyinfo->dacl);
11044 destroyPQExpBuffer(q);
11045 destroyPQExpBuffer(delq);
11046 free(qtypname);
11047 free(qualtypname);
11051 * dumpBaseType
11052 * writes out to fout the queries to recreate a user-defined base type
11054 static void
11055 dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11057 DumpOptions *dopt = fout->dopt;
11058 PQExpBuffer q = createPQExpBuffer();
11059 PQExpBuffer delq = createPQExpBuffer();
11060 PQExpBuffer query = createPQExpBuffer();
11061 PGresult *res;
11062 char *qtypname;
11063 char *qualtypname;
11064 char *typlen;
11065 char *typinput;
11066 char *typoutput;
11067 char *typreceive;
11068 char *typsend;
11069 char *typmodin;
11070 char *typmodout;
11071 char *typanalyze;
11072 char *typsubscript;
11073 Oid typreceiveoid;
11074 Oid typsendoid;
11075 Oid typmodinoid;
11076 Oid typmodoutoid;
11077 Oid typanalyzeoid;
11078 Oid typsubscriptoid;
11079 char *typcategory;
11080 char *typispreferred;
11081 char *typdelim;
11082 char *typbyval;
11083 char *typalign;
11084 char *typstorage;
11085 char *typcollatable;
11086 char *typdefault;
11087 bool typdefault_is_literal = false;
11089 if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11091 /* Set up query for type-specific details */
11092 appendPQExpBufferStr(query,
11093 "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11094 "SELECT typlen, "
11095 "typinput, typoutput, typreceive, typsend, "
11096 "typreceive::pg_catalog.oid AS typreceiveoid, "
11097 "typsend::pg_catalog.oid AS typsendoid, "
11098 "typanalyze, "
11099 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11100 "typdelim, typbyval, typalign, typstorage, "
11101 "typmodin, typmodout, "
11102 "typmodin::pg_catalog.oid AS typmodinoid, "
11103 "typmodout::pg_catalog.oid AS typmodoutoid, "
11104 "typcategory, typispreferred, "
11105 "(typcollation <> 0) AS typcollatable, "
11106 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11108 if (fout->remoteVersion >= 140000)
11109 appendPQExpBufferStr(query,
11110 "typsubscript, "
11111 "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11112 else
11113 appendPQExpBufferStr(query,
11114 "'-' AS typsubscript, 0 AS typsubscriptoid ");
11116 appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11117 "WHERE oid = $1");
11119 ExecuteSqlStatement(fout, query->data);
11121 fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11124 printfPQExpBuffer(query,
11125 "EXECUTE dumpBaseType('%u')",
11126 tyinfo->dobj.catId.oid);
11128 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11130 typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11131 typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11132 typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11133 typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11134 typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11135 typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11136 typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11137 typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11138 typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11139 typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11140 typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11141 typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11142 typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11143 typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11144 typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11145 typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11146 typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11147 typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11148 typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11149 typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11150 typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11151 typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11152 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11153 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11154 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11156 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11157 typdefault_is_literal = true; /* it needs quotes */
11159 else
11160 typdefault = NULL;
11162 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11163 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11166 * The reason we include CASCADE is that the circular dependency between
11167 * the type and its I/O functions makes it impossible to drop the type any
11168 * other way.
11170 appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11173 * We might already have a shell type, but setting pg_type_oid is
11174 * harmless, and in any case we'd better set the array type OID.
11176 if (dopt->binary_upgrade)
11177 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11178 tyinfo->dobj.catId.oid,
11179 false, false);
11181 appendPQExpBuffer(q,
11182 "CREATE TYPE %s (\n"
11183 " INTERNALLENGTH = %s",
11184 qualtypname,
11185 (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11187 /* regproc result is sufficiently quoted already */
11188 appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11189 appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11190 if (OidIsValid(typreceiveoid))
11191 appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11192 if (OidIsValid(typsendoid))
11193 appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11194 if (OidIsValid(typmodinoid))
11195 appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11196 if (OidIsValid(typmodoutoid))
11197 appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11198 if (OidIsValid(typanalyzeoid))
11199 appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11201 if (strcmp(typcollatable, "t") == 0)
11202 appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11204 if (typdefault != NULL)
11206 appendPQExpBufferStr(q, ",\n DEFAULT = ");
11207 if (typdefault_is_literal)
11208 appendStringLiteralAH(q, typdefault, fout);
11209 else
11210 appendPQExpBufferStr(q, typdefault);
11213 if (OidIsValid(typsubscriptoid))
11214 appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
11216 if (OidIsValid(tyinfo->typelem))
11217 appendPQExpBuffer(q, ",\n ELEMENT = %s",
11218 getFormattedTypeName(fout, tyinfo->typelem,
11219 zeroIsError));
11221 if (strcmp(typcategory, "U") != 0)
11223 appendPQExpBufferStr(q, ",\n CATEGORY = ");
11224 appendStringLiteralAH(q, typcategory, fout);
11227 if (strcmp(typispreferred, "t") == 0)
11228 appendPQExpBufferStr(q, ",\n PREFERRED = true");
11230 if (typdelim && strcmp(typdelim, ",") != 0)
11232 appendPQExpBufferStr(q, ",\n DELIMITER = ");
11233 appendStringLiteralAH(q, typdelim, fout);
11236 if (*typalign == TYPALIGN_CHAR)
11237 appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
11238 else if (*typalign == TYPALIGN_SHORT)
11239 appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
11240 else if (*typalign == TYPALIGN_INT)
11241 appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
11242 else if (*typalign == TYPALIGN_DOUBLE)
11243 appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
11245 if (*typstorage == TYPSTORAGE_PLAIN)
11246 appendPQExpBufferStr(q, ",\n STORAGE = plain");
11247 else if (*typstorage == TYPSTORAGE_EXTERNAL)
11248 appendPQExpBufferStr(q, ",\n STORAGE = external");
11249 else if (*typstorage == TYPSTORAGE_EXTENDED)
11250 appendPQExpBufferStr(q, ",\n STORAGE = extended");
11251 else if (*typstorage == TYPSTORAGE_MAIN)
11252 appendPQExpBufferStr(q, ",\n STORAGE = main");
11254 if (strcmp(typbyval, "t") == 0)
11255 appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
11257 appendPQExpBufferStr(q, "\n);\n");
11259 if (dopt->binary_upgrade)
11260 binary_upgrade_extension_member(q, &tyinfo->dobj,
11261 "TYPE", qtypname,
11262 tyinfo->dobj.namespace->dobj.name);
11264 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11265 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11266 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11267 .namespace = tyinfo->dobj.namespace->dobj.name,
11268 .owner = tyinfo->rolname,
11269 .description = "TYPE",
11270 .section = SECTION_PRE_DATA,
11271 .createStmt = q->data,
11272 .dropStmt = delq->data));
11274 /* Dump Type Comments and Security Labels */
11275 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11276 dumpComment(fout, "TYPE", qtypname,
11277 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11278 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11280 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11281 dumpSecLabel(fout, "TYPE", qtypname,
11282 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11283 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11285 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11286 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11287 qtypname, NULL,
11288 tyinfo->dobj.namespace->dobj.name,
11289 tyinfo->rolname, &tyinfo->dacl);
11291 PQclear(res);
11292 destroyPQExpBuffer(q);
11293 destroyPQExpBuffer(delq);
11294 destroyPQExpBuffer(query);
11295 free(qtypname);
11296 free(qualtypname);
11300 * dumpDomain
11301 * writes out to fout the queries to recreate a user-defined domain
11303 static void
11304 dumpDomain(Archive *fout, const TypeInfo *tyinfo)
11306 DumpOptions *dopt = fout->dopt;
11307 PQExpBuffer q = createPQExpBuffer();
11308 PQExpBuffer delq = createPQExpBuffer();
11309 PQExpBuffer query = createPQExpBuffer();
11310 PGresult *res;
11311 int i;
11312 char *qtypname;
11313 char *qualtypname;
11314 char *typnotnull;
11315 char *typdefn;
11316 char *typdefault;
11317 Oid typcollation;
11318 bool typdefault_is_literal = false;
11320 if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
11322 /* Set up query for domain-specific details */
11323 appendPQExpBufferStr(query,
11324 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
11326 appendPQExpBufferStr(query, "SELECT t.typnotnull, "
11327 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
11328 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
11329 "t.typdefault, "
11330 "CASE WHEN t.typcollation <> u.typcollation "
11331 "THEN t.typcollation ELSE 0 END AS typcollation "
11332 "FROM pg_catalog.pg_type t "
11333 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
11334 "WHERE t.oid = $1");
11336 ExecuteSqlStatement(fout, query->data);
11338 fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
11341 printfPQExpBuffer(query,
11342 "EXECUTE dumpDomain('%u')",
11343 tyinfo->dobj.catId.oid);
11345 res = ExecuteSqlQueryForSingleRow(fout, query->data);
11347 typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
11348 typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
11349 if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11350 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11351 else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11353 typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11354 typdefault_is_literal = true; /* it needs quotes */
11356 else
11357 typdefault = NULL;
11358 typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
11360 if (dopt->binary_upgrade)
11361 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11362 tyinfo->dobj.catId.oid,
11363 true, /* force array type */
11364 false); /* force multirange type */
11366 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11367 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11369 appendPQExpBuffer(q,
11370 "CREATE DOMAIN %s AS %s",
11371 qualtypname,
11372 typdefn);
11374 /* Print collation only if different from base type's collation */
11375 if (OidIsValid(typcollation))
11377 CollInfo *coll;
11379 coll = findCollationByOid(typcollation);
11380 if (coll)
11381 appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
11384 if (typnotnull[0] == 't')
11385 appendPQExpBufferStr(q, " NOT NULL");
11387 if (typdefault != NULL)
11389 appendPQExpBufferStr(q, " DEFAULT ");
11390 if (typdefault_is_literal)
11391 appendStringLiteralAH(q, typdefault, fout);
11392 else
11393 appendPQExpBufferStr(q, typdefault);
11396 PQclear(res);
11399 * Add any CHECK constraints for the domain
11401 for (i = 0; i < tyinfo->nDomChecks; i++)
11403 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11405 if (!domcheck->separate)
11406 appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
11407 fmtId(domcheck->dobj.name), domcheck->condef);
11410 appendPQExpBufferStr(q, ";\n");
11412 appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
11414 if (dopt->binary_upgrade)
11415 binary_upgrade_extension_member(q, &tyinfo->dobj,
11416 "DOMAIN", qtypname,
11417 tyinfo->dobj.namespace->dobj.name);
11419 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11420 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11421 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11422 .namespace = tyinfo->dobj.namespace->dobj.name,
11423 .owner = tyinfo->rolname,
11424 .description = "DOMAIN",
11425 .section = SECTION_PRE_DATA,
11426 .createStmt = q->data,
11427 .dropStmt = delq->data));
11429 /* Dump Domain Comments and Security Labels */
11430 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11431 dumpComment(fout, "DOMAIN", qtypname,
11432 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11433 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11435 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11436 dumpSecLabel(fout, "DOMAIN", qtypname,
11437 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11438 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11440 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11441 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11442 qtypname, NULL,
11443 tyinfo->dobj.namespace->dobj.name,
11444 tyinfo->rolname, &tyinfo->dacl);
11446 /* Dump any per-constraint comments */
11447 for (i = 0; i < tyinfo->nDomChecks; i++)
11449 ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11450 PQExpBuffer conprefix = createPQExpBuffer();
11452 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
11453 fmtId(domcheck->dobj.name));
11455 if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
11456 dumpComment(fout, conprefix->data, qtypname,
11457 tyinfo->dobj.namespace->dobj.name,
11458 tyinfo->rolname,
11459 domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
11461 destroyPQExpBuffer(conprefix);
11464 destroyPQExpBuffer(q);
11465 destroyPQExpBuffer(delq);
11466 destroyPQExpBuffer(query);
11467 free(qtypname);
11468 free(qualtypname);
11472 * dumpCompositeType
11473 * writes out to fout the queries to recreate a user-defined stand-alone
11474 * composite type
11476 static void
11477 dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
11479 DumpOptions *dopt = fout->dopt;
11480 PQExpBuffer q = createPQExpBuffer();
11481 PQExpBuffer dropped = createPQExpBuffer();
11482 PQExpBuffer delq = createPQExpBuffer();
11483 PQExpBuffer query = createPQExpBuffer();
11484 PGresult *res;
11485 char *qtypname;
11486 char *qualtypname;
11487 int ntups;
11488 int i_attname;
11489 int i_atttypdefn;
11490 int i_attlen;
11491 int i_attalign;
11492 int i_attisdropped;
11493 int i_attcollation;
11494 int i;
11495 int actual_atts;
11497 if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
11500 * Set up query for type-specific details.
11502 * Since we only want to dump COLLATE clauses for attributes whose
11503 * collation is different from their type's default, we use a CASE
11504 * here to suppress uninteresting attcollations cheaply. atttypid
11505 * will be 0 for dropped columns; collation does not matter for those.
11507 appendPQExpBufferStr(query,
11508 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
11509 "SELECT a.attname, a.attnum, "
11510 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
11511 "a.attlen, a.attalign, a.attisdropped, "
11512 "CASE WHEN a.attcollation <> at.typcollation "
11513 "THEN a.attcollation ELSE 0 END AS attcollation "
11514 "FROM pg_catalog.pg_type ct "
11515 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
11516 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
11517 "WHERE ct.oid = $1 "
11518 "ORDER BY a.attnum");
11520 ExecuteSqlStatement(fout, query->data);
11522 fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
11525 printfPQExpBuffer(query,
11526 "EXECUTE dumpCompositeType('%u')",
11527 tyinfo->dobj.catId.oid);
11529 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11531 ntups = PQntuples(res);
11533 i_attname = PQfnumber(res, "attname");
11534 i_atttypdefn = PQfnumber(res, "atttypdefn");
11535 i_attlen = PQfnumber(res, "attlen");
11536 i_attalign = PQfnumber(res, "attalign");
11537 i_attisdropped = PQfnumber(res, "attisdropped");
11538 i_attcollation = PQfnumber(res, "attcollation");
11540 if (dopt->binary_upgrade)
11542 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11543 tyinfo->dobj.catId.oid,
11544 false, false);
11545 binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
11548 qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11549 qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11551 appendPQExpBuffer(q, "CREATE TYPE %s AS (",
11552 qualtypname);
11554 actual_atts = 0;
11555 for (i = 0; i < ntups; i++)
11557 char *attname;
11558 char *atttypdefn;
11559 char *attlen;
11560 char *attalign;
11561 bool attisdropped;
11562 Oid attcollation;
11564 attname = PQgetvalue(res, i, i_attname);
11565 atttypdefn = PQgetvalue(res, i, i_atttypdefn);
11566 attlen = PQgetvalue(res, i, i_attlen);
11567 attalign = PQgetvalue(res, i, i_attalign);
11568 attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
11569 attcollation = atooid(PQgetvalue(res, i, i_attcollation));
11571 if (attisdropped && !dopt->binary_upgrade)
11572 continue;
11574 /* Format properly if not first attr */
11575 if (actual_atts++ > 0)
11576 appendPQExpBufferChar(q, ',');
11577 appendPQExpBufferStr(q, "\n\t");
11579 if (!attisdropped)
11581 appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
11583 /* Add collation if not default for the column type */
11584 if (OidIsValid(attcollation))
11586 CollInfo *coll;
11588 coll = findCollationByOid(attcollation);
11589 if (coll)
11590 appendPQExpBuffer(q, " COLLATE %s",
11591 fmtQualifiedDumpable(coll));
11594 else
11597 * This is a dropped attribute and we're in binary_upgrade mode.
11598 * Insert a placeholder for it in the CREATE TYPE command, and set
11599 * length and alignment with direct UPDATE to the catalogs
11600 * afterwards. See similar code in dumpTableSchema().
11602 appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
11604 /* stash separately for insertion after the CREATE TYPE */
11605 appendPQExpBufferStr(dropped,
11606 "\n-- For binary upgrade, recreate dropped column.\n");
11607 appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
11608 "SET attlen = %s, "
11609 "attalign = '%s', attbyval = false\n"
11610 "WHERE attname = ", attlen, attalign);
11611 appendStringLiteralAH(dropped, attname, fout);
11612 appendPQExpBufferStr(dropped, "\n AND attrelid = ");
11613 appendStringLiteralAH(dropped, qualtypname, fout);
11614 appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
11616 appendPQExpBuffer(dropped, "ALTER TYPE %s ",
11617 qualtypname);
11618 appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
11619 fmtId(attname));
11622 appendPQExpBufferStr(q, "\n);\n");
11623 appendPQExpBufferStr(q, dropped->data);
11625 appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11627 if (dopt->binary_upgrade)
11628 binary_upgrade_extension_member(q, &tyinfo->dobj,
11629 "TYPE", qtypname,
11630 tyinfo->dobj.namespace->dobj.name);
11632 if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11633 ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11634 ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11635 .namespace = tyinfo->dobj.namespace->dobj.name,
11636 .owner = tyinfo->rolname,
11637 .description = "TYPE",
11638 .section = SECTION_PRE_DATA,
11639 .createStmt = q->data,
11640 .dropStmt = delq->data));
11643 /* Dump Type Comments and Security Labels */
11644 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11645 dumpComment(fout, "TYPE", qtypname,
11646 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11647 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11649 if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11650 dumpSecLabel(fout, "TYPE", qtypname,
11651 tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11652 tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11654 if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11655 dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11656 qtypname, NULL,
11657 tyinfo->dobj.namespace->dobj.name,
11658 tyinfo->rolname, &tyinfo->dacl);
11660 /* Dump any per-column comments */
11661 if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11662 dumpCompositeTypeColComments(fout, tyinfo, res);
11664 PQclear(res);
11665 destroyPQExpBuffer(q);
11666 destroyPQExpBuffer(dropped);
11667 destroyPQExpBuffer(delq);
11668 destroyPQExpBuffer(query);
11669 free(qtypname);
11670 free(qualtypname);
11674 * dumpCompositeTypeColComments
11675 * writes out to fout the queries to recreate comments on the columns of
11676 * a user-defined stand-alone composite type.
11678 * The caller has already made a query to collect the names and attnums
11679 * of the type's columns, so we just pass that result into here rather
11680 * than reading them again.
11682 static void
11683 dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
11684 PGresult *res)
11686 CommentItem *comments;
11687 int ncomments;
11688 PQExpBuffer query;
11689 PQExpBuffer target;
11690 int i;
11691 int ntups;
11692 int i_attname;
11693 int i_attnum;
11694 int i_attisdropped;
11696 /* do nothing, if --no-comments is supplied */
11697 if (fout->dopt->no_comments)
11698 return;
11700 /* Search for comments associated with type's pg_class OID */
11701 ncomments = findComments(RelationRelationId, tyinfo->typrelid,
11702 &comments);
11704 /* If no comments exist, we're done */
11705 if (ncomments <= 0)
11706 return;
11708 /* Build COMMENT ON statements */
11709 query = createPQExpBuffer();
11710 target = createPQExpBuffer();
11712 ntups = PQntuples(res);
11713 i_attnum = PQfnumber(res, "attnum");
11714 i_attname = PQfnumber(res, "attname");
11715 i_attisdropped = PQfnumber(res, "attisdropped");
11716 while (ncomments > 0)
11718 const char *attname;
11720 attname = NULL;
11721 for (i = 0; i < ntups; i++)
11723 if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
11724 PQgetvalue(res, i, i_attisdropped)[0] != 't')
11726 attname = PQgetvalue(res, i, i_attname);
11727 break;
11730 if (attname) /* just in case we don't find it */
11732 const char *descr = comments->descr;
11734 resetPQExpBuffer(target);
11735 appendPQExpBuffer(target, "COLUMN %s.",
11736 fmtId(tyinfo->dobj.name));
11737 appendPQExpBufferStr(target, fmtId(attname));
11739 resetPQExpBuffer(query);
11740 appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11741 fmtQualifiedDumpable(tyinfo));
11742 appendPQExpBuffer(query, "%s IS ", fmtId(attname));
11743 appendStringLiteralAH(query, descr, fout);
11744 appendPQExpBufferStr(query, ";\n");
11746 ArchiveEntry(fout, nilCatalogId, createDumpId(),
11747 ARCHIVE_OPTS(.tag = target->data,
11748 .namespace = tyinfo->dobj.namespace->dobj.name,
11749 .owner = tyinfo->rolname,
11750 .description = "COMMENT",
11751 .section = SECTION_NONE,
11752 .createStmt = query->data,
11753 .deps = &(tyinfo->dobj.dumpId),
11754 .nDeps = 1));
11757 comments++;
11758 ncomments--;
11761 destroyPQExpBuffer(query);
11762 destroyPQExpBuffer(target);
11766 * dumpShellType
11767 * writes out to fout the queries to create a shell type
11769 * We dump a shell definition in advance of the I/O functions for the type.
11771 static void
11772 dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
11774 DumpOptions *dopt = fout->dopt;
11775 PQExpBuffer q;
11777 /* Do nothing in data-only dump */
11778 if (dopt->dataOnly)
11779 return;
11781 q = createPQExpBuffer();
11784 * Note the lack of a DROP command for the shell type; any required DROP
11785 * is driven off the base type entry, instead. This interacts with
11786 * _printTocEntry()'s use of the presence of a DROP command to decide
11787 * whether an entry needs an ALTER OWNER command. We don't want to alter
11788 * the shell type's owner immediately on creation; that should happen only
11789 * after it's filled in, otherwise the backend complains.
11792 if (dopt->binary_upgrade)
11793 binary_upgrade_set_type_oids_by_type_oid(fout, q,
11794 stinfo->baseType->dobj.catId.oid,
11795 false, false);
11797 appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11798 fmtQualifiedDumpable(stinfo));
11800 if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11801 ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
11802 ARCHIVE_OPTS(.tag = stinfo->dobj.name,
11803 .namespace = stinfo->dobj.namespace->dobj.name,
11804 .owner = stinfo->baseType->rolname,
11805 .description = "SHELL TYPE",
11806 .section = SECTION_PRE_DATA,
11807 .createStmt = q->data));
11809 destroyPQExpBuffer(q);
11813 * dumpProcLang
11814 * writes out to fout the queries to recreate a user-defined
11815 * procedural language
11817 static void
11818 dumpProcLang(Archive *fout, const ProcLangInfo *plang)
11820 DumpOptions *dopt = fout->dopt;
11821 PQExpBuffer defqry;
11822 PQExpBuffer delqry;
11823 bool useParams;
11824 char *qlanname;
11825 FuncInfo *funcInfo;
11826 FuncInfo *inlineInfo = NULL;
11827 FuncInfo *validatorInfo = NULL;
11829 /* Do nothing in data-only dump */
11830 if (dopt->dataOnly)
11831 return;
11834 * Try to find the support function(s). It is not an error if we don't
11835 * find them --- if the functions are in the pg_catalog schema, as is
11836 * standard in 8.1 and up, then we won't have loaded them. (In this case
11837 * we will emit a parameterless CREATE LANGUAGE command, which will
11838 * require PL template knowledge in the backend to reload.)
11841 funcInfo = findFuncByOid(plang->lanplcallfoid);
11842 if (funcInfo != NULL && !funcInfo->dobj.dump)
11843 funcInfo = NULL; /* treat not-dumped same as not-found */
11845 if (OidIsValid(plang->laninline))
11847 inlineInfo = findFuncByOid(plang->laninline);
11848 if (inlineInfo != NULL && !inlineInfo->dobj.dump)
11849 inlineInfo = NULL;
11852 if (OidIsValid(plang->lanvalidator))
11854 validatorInfo = findFuncByOid(plang->lanvalidator);
11855 if (validatorInfo != NULL && !validatorInfo->dobj.dump)
11856 validatorInfo = NULL;
11860 * If the functions are dumpable then emit a complete CREATE LANGUAGE with
11861 * parameters. Otherwise, we'll write a parameterless command, which will
11862 * be interpreted as CREATE EXTENSION.
11864 useParams = (funcInfo != NULL &&
11865 (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
11866 (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
11868 defqry = createPQExpBuffer();
11869 delqry = createPQExpBuffer();
11871 qlanname = pg_strdup(fmtId(plang->dobj.name));
11873 appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
11874 qlanname);
11876 if (useParams)
11878 appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
11879 plang->lanpltrusted ? "TRUSTED " : "",
11880 qlanname);
11881 appendPQExpBuffer(defqry, " HANDLER %s",
11882 fmtQualifiedDumpable(funcInfo));
11883 if (OidIsValid(plang->laninline))
11884 appendPQExpBuffer(defqry, " INLINE %s",
11885 fmtQualifiedDumpable(inlineInfo));
11886 if (OidIsValid(plang->lanvalidator))
11887 appendPQExpBuffer(defqry, " VALIDATOR %s",
11888 fmtQualifiedDumpable(validatorInfo));
11890 else
11893 * If not dumping parameters, then use CREATE OR REPLACE so that the
11894 * command will not fail if the language is preinstalled in the target
11895 * database.
11897 * Modern servers will interpret this as CREATE EXTENSION IF NOT
11898 * EXISTS; perhaps we should emit that instead? But it might just add
11899 * confusion.
11901 appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
11902 qlanname);
11904 appendPQExpBufferStr(defqry, ";\n");
11906 if (dopt->binary_upgrade)
11907 binary_upgrade_extension_member(defqry, &plang->dobj,
11908 "LANGUAGE", qlanname, NULL);
11910 if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
11911 ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
11912 ARCHIVE_OPTS(.tag = plang->dobj.name,
11913 .owner = plang->lanowner,
11914 .description = "PROCEDURAL LANGUAGE",
11915 .section = SECTION_PRE_DATA,
11916 .createStmt = defqry->data,
11917 .dropStmt = delqry->data,
11920 /* Dump Proc Lang Comments and Security Labels */
11921 if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
11922 dumpComment(fout, "LANGUAGE", qlanname,
11923 NULL, plang->lanowner,
11924 plang->dobj.catId, 0, plang->dobj.dumpId);
11926 if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
11927 dumpSecLabel(fout, "LANGUAGE", qlanname,
11928 NULL, plang->lanowner,
11929 plang->dobj.catId, 0, plang->dobj.dumpId);
11931 if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
11932 dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
11933 qlanname, NULL, NULL,
11934 plang->lanowner, &plang->dacl);
11936 free(qlanname);
11938 destroyPQExpBuffer(defqry);
11939 destroyPQExpBuffer(delqry);
11943 * format_function_arguments: generate function name and argument list
11945 * This is used when we can rely on pg_get_function_arguments to format
11946 * the argument list. Note, however, that pg_get_function_arguments
11947 * does not special-case zero-argument aggregates.
11949 static char *
11950 format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
11952 PQExpBufferData fn;
11954 initPQExpBuffer(&fn);
11955 appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
11956 if (is_agg && finfo->nargs == 0)
11957 appendPQExpBufferStr(&fn, "(*)");
11958 else
11959 appendPQExpBuffer(&fn, "(%s)", funcargs);
11960 return fn.data;
11964 * format_function_signature: generate function name and argument list
11966 * Only a minimal list of input argument types is generated; this is
11967 * sufficient to reference the function, but not to define it.
11969 * If honor_quotes is false then the function name is never quoted.
11970 * This is appropriate for use in TOC tags, but not in SQL commands.
11972 static char *
11973 format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
11975 PQExpBufferData fn;
11976 int j;
11978 initPQExpBuffer(&fn);
11979 if (honor_quotes)
11980 appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
11981 else
11982 appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
11983 for (j = 0; j < finfo->nargs; j++)
11985 if (j > 0)
11986 appendPQExpBufferStr(&fn, ", ");
11988 appendPQExpBufferStr(&fn,
11989 getFormattedTypeName(fout, finfo->argtypes[j],
11990 zeroIsError));
11992 appendPQExpBufferChar(&fn, ')');
11993 return fn.data;
11998 * dumpFunc:
11999 * dump out one function
12001 static void
12002 dumpFunc(Archive *fout, const FuncInfo *finfo)
12004 DumpOptions *dopt = fout->dopt;
12005 PQExpBuffer query;
12006 PQExpBuffer q;
12007 PQExpBuffer delqry;
12008 PQExpBuffer asPart;
12009 PGresult *res;
12010 char *funcsig; /* identity signature */
12011 char *funcfullsig = NULL; /* full signature */
12012 char *funcsig_tag;
12013 char *qual_funcsig;
12014 char *proretset;
12015 char *prosrc;
12016 char *probin;
12017 char *prosqlbody;
12018 char *funcargs;
12019 char *funciargs;
12020 char *funcresult;
12021 char *protrftypes;
12022 char *prokind;
12023 char *provolatile;
12024 char *proisstrict;
12025 char *prosecdef;
12026 char *proleakproof;
12027 char *proconfig;
12028 char *procost;
12029 char *prorows;
12030 char *prosupport;
12031 char *proparallel;
12032 char *lanname;
12033 char **configitems = NULL;
12034 int nconfigitems = 0;
12035 const char *keyword;
12037 /* Do nothing in data-only dump */
12038 if (dopt->dataOnly)
12039 return;
12041 query = createPQExpBuffer();
12042 q = createPQExpBuffer();
12043 delqry = createPQExpBuffer();
12044 asPart = createPQExpBuffer();
12046 if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12048 /* Set up query for function-specific details */
12049 appendPQExpBufferStr(query,
12050 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12052 appendPQExpBufferStr(query,
12053 "SELECT\n"
12054 "proretset,\n"
12055 "prosrc,\n"
12056 "probin,\n"
12057 "provolatile,\n"
12058 "proisstrict,\n"
12059 "prosecdef,\n"
12060 "lanname,\n"
12061 "proconfig,\n"
12062 "procost,\n"
12063 "prorows,\n"
12064 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12065 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12066 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12067 "proleakproof,\n");
12069 if (fout->remoteVersion >= 90500)
12070 appendPQExpBufferStr(query,
12071 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12072 else
12073 appendPQExpBufferStr(query,
12074 "NULL AS protrftypes,\n");
12076 if (fout->remoteVersion >= 90600)
12077 appendPQExpBufferStr(query,
12078 "proparallel,\n");
12079 else
12080 appendPQExpBufferStr(query,
12081 "'u' AS proparallel,\n");
12083 if (fout->remoteVersion >= 110000)
12084 appendPQExpBufferStr(query,
12085 "prokind,\n");
12086 else
12087 appendPQExpBufferStr(query,
12088 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12090 if (fout->remoteVersion >= 120000)
12091 appendPQExpBufferStr(query,
12092 "prosupport,\n");
12093 else
12094 appendPQExpBufferStr(query,
12095 "'-' AS prosupport,\n");
12097 if (fout->remoteVersion >= 140000)
12098 appendPQExpBufferStr(query,
12099 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12100 else
12101 appendPQExpBufferStr(query,
12102 "NULL AS prosqlbody\n");
12104 appendPQExpBufferStr(query,
12105 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12106 "WHERE p.oid = $1 "
12107 "AND l.oid = p.prolang");
12109 ExecuteSqlStatement(fout, query->data);
12111 fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12114 printfPQExpBuffer(query,
12115 "EXECUTE dumpFunc('%u')",
12116 finfo->dobj.catId.oid);
12118 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12120 proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12121 if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12123 prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12124 probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12125 prosqlbody = NULL;
12127 else
12129 prosrc = NULL;
12130 probin = NULL;
12131 prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12133 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12134 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12135 funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12136 protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12137 prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12138 provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12139 proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12140 prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12141 proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12142 proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12143 procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12144 prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12145 prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12146 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12147 lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12150 * See backend/commands/functioncmds.c for details of how the 'AS' clause
12151 * is used.
12153 if (prosqlbody)
12155 appendPQExpBufferStr(asPart, prosqlbody);
12157 else if (probin[0] != '\0')
12159 appendPQExpBufferStr(asPart, "AS ");
12160 appendStringLiteralAH(asPart, probin, fout);
12161 if (prosrc[0] != '\0')
12163 appendPQExpBufferStr(asPart, ", ");
12166 * where we have bin, use dollar quoting if allowed and src
12167 * contains quote or backslash; else use regular quoting.
12169 if (dopt->disable_dollar_quoting ||
12170 (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12171 appendStringLiteralAH(asPart, prosrc, fout);
12172 else
12173 appendStringLiteralDQ(asPart, prosrc, NULL);
12176 else
12178 appendPQExpBufferStr(asPart, "AS ");
12179 /* with no bin, dollar quote src unconditionally if allowed */
12180 if (dopt->disable_dollar_quoting)
12181 appendStringLiteralAH(asPart, prosrc, fout);
12182 else
12183 appendStringLiteralDQ(asPart, prosrc, NULL);
12186 if (*proconfig)
12188 if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12189 pg_fatal("could not parse %s array", "proconfig");
12191 else
12193 configitems = NULL;
12194 nconfigitems = 0;
12197 funcfullsig = format_function_arguments(finfo, funcargs, false);
12198 funcsig = format_function_arguments(finfo, funciargs, false);
12200 funcsig_tag = format_function_signature(fout, finfo, false);
12202 qual_funcsig = psprintf("%s.%s",
12203 fmtId(finfo->dobj.namespace->dobj.name),
12204 funcsig);
12206 if (prokind[0] == PROKIND_PROCEDURE)
12207 keyword = "PROCEDURE";
12208 else
12209 keyword = "FUNCTION"; /* works for window functions too */
12211 appendPQExpBuffer(delqry, "DROP %s %s;\n",
12212 keyword, qual_funcsig);
12214 appendPQExpBuffer(q, "CREATE %s %s.%s",
12215 keyword,
12216 fmtId(finfo->dobj.namespace->dobj.name),
12217 funcfullsig ? funcfullsig :
12218 funcsig);
12220 if (prokind[0] == PROKIND_PROCEDURE)
12221 /* no result type to output */ ;
12222 else if (funcresult)
12223 appendPQExpBuffer(q, " RETURNS %s", funcresult);
12224 else
12225 appendPQExpBuffer(q, " RETURNS %s%s",
12226 (proretset[0] == 't') ? "SETOF " : "",
12227 getFormattedTypeName(fout, finfo->prorettype,
12228 zeroIsError));
12230 appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
12232 if (*protrftypes)
12234 Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid));
12235 int i;
12237 appendPQExpBufferStr(q, " TRANSFORM ");
12238 parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
12239 for (i = 0; typeids[i]; i++)
12241 if (i != 0)
12242 appendPQExpBufferStr(q, ", ");
12243 appendPQExpBuffer(q, "FOR TYPE %s",
12244 getFormattedTypeName(fout, typeids[i], zeroAsNone));
12248 if (prokind[0] == PROKIND_WINDOW)
12249 appendPQExpBufferStr(q, " WINDOW");
12251 if (provolatile[0] != PROVOLATILE_VOLATILE)
12253 if (provolatile[0] == PROVOLATILE_IMMUTABLE)
12254 appendPQExpBufferStr(q, " IMMUTABLE");
12255 else if (provolatile[0] == PROVOLATILE_STABLE)
12256 appendPQExpBufferStr(q, " STABLE");
12257 else if (provolatile[0] != PROVOLATILE_VOLATILE)
12258 pg_fatal("unrecognized provolatile value for function \"%s\"",
12259 finfo->dobj.name);
12262 if (proisstrict[0] == 't')
12263 appendPQExpBufferStr(q, " STRICT");
12265 if (prosecdef[0] == 't')
12266 appendPQExpBufferStr(q, " SECURITY DEFINER");
12268 if (proleakproof[0] == 't')
12269 appendPQExpBufferStr(q, " LEAKPROOF");
12272 * COST and ROWS are emitted only if present and not default, so as not to
12273 * break backwards-compatibility of the dump without need. Keep this code
12274 * in sync with the defaults in functioncmds.c.
12276 if (strcmp(procost, "0") != 0)
12278 if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12280 /* default cost is 1 */
12281 if (strcmp(procost, "1") != 0)
12282 appendPQExpBuffer(q, " COST %s", procost);
12284 else
12286 /* default cost is 100 */
12287 if (strcmp(procost, "100") != 0)
12288 appendPQExpBuffer(q, " COST %s", procost);
12291 if (proretset[0] == 't' &&
12292 strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12293 appendPQExpBuffer(q, " ROWS %s", prorows);
12295 if (strcmp(prosupport, "-") != 0)
12297 /* We rely on regprocout to provide quoting and qualification */
12298 appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12301 if (proparallel[0] != PROPARALLEL_UNSAFE)
12303 if (proparallel[0] == PROPARALLEL_SAFE)
12304 appendPQExpBufferStr(q, " PARALLEL SAFE");
12305 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12306 appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12307 else if (proparallel[0] != PROPARALLEL_UNSAFE)
12308 pg_fatal("unrecognized proparallel value for function \"%s\"",
12309 finfo->dobj.name);
12312 for (int i = 0; i < nconfigitems; i++)
12314 /* we feel free to scribble on configitems[] here */
12315 char *configitem = configitems[i];
12316 char *pos;
12318 pos = strchr(configitem, '=');
12319 if (pos == NULL)
12320 continue;
12321 *pos++ = '\0';
12322 appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
12325 * Variables that are marked GUC_LIST_QUOTE were already fully quoted
12326 * by flatten_set_variable_args() before they were put into the
12327 * proconfig array. However, because the quoting rules used there
12328 * aren't exactly like SQL's, we have to break the list value apart
12329 * and then quote the elements as string literals. (The elements may
12330 * be double-quoted as-is, but we can't just feed them to the SQL
12331 * parser; it would do the wrong thing with elements that are
12332 * zero-length or longer than NAMEDATALEN.)
12334 * Variables that are not so marked should just be emitted as simple
12335 * string literals. If the variable is not known to
12336 * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
12337 * to use GUC_LIST_QUOTE for extension variables.
12339 if (variable_is_guc_list_quote(configitem))
12341 char **namelist;
12342 char **nameptr;
12344 /* Parse string into list of identifiers */
12345 /* this shouldn't fail really */
12346 if (SplitGUCList(pos, ',', &namelist))
12348 for (nameptr = namelist; *nameptr; nameptr++)
12350 if (nameptr != namelist)
12351 appendPQExpBufferStr(q, ", ");
12352 appendStringLiteralAH(q, *nameptr, fout);
12355 pg_free(namelist);
12357 else
12358 appendStringLiteralAH(q, pos, fout);
12361 appendPQExpBuffer(q, "\n %s;\n", asPart->data);
12363 append_depends_on_extension(fout, q, &finfo->dobj,
12364 "pg_catalog.pg_proc", keyword,
12365 qual_funcsig);
12367 if (dopt->binary_upgrade)
12368 binary_upgrade_extension_member(q, &finfo->dobj,
12369 keyword, funcsig,
12370 finfo->dobj.namespace->dobj.name);
12372 if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12373 ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
12374 ARCHIVE_OPTS(.tag = funcsig_tag,
12375 .namespace = finfo->dobj.namespace->dobj.name,
12376 .owner = finfo->rolname,
12377 .description = keyword,
12378 .section = finfo->postponed_def ?
12379 SECTION_POST_DATA : SECTION_PRE_DATA,
12380 .createStmt = q->data,
12381 .dropStmt = delqry->data));
12383 /* Dump Function Comments and Security Labels */
12384 if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12385 dumpComment(fout, keyword, funcsig,
12386 finfo->dobj.namespace->dobj.name, finfo->rolname,
12387 finfo->dobj.catId, 0, finfo->dobj.dumpId);
12389 if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12390 dumpSecLabel(fout, keyword, funcsig,
12391 finfo->dobj.namespace->dobj.name, finfo->rolname,
12392 finfo->dobj.catId, 0, finfo->dobj.dumpId);
12394 if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12395 dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
12396 funcsig, NULL,
12397 finfo->dobj.namespace->dobj.name,
12398 finfo->rolname, &finfo->dacl);
12400 PQclear(res);
12402 destroyPQExpBuffer(query);
12403 destroyPQExpBuffer(q);
12404 destroyPQExpBuffer(delqry);
12405 destroyPQExpBuffer(asPart);
12406 free(funcsig);
12407 free(funcfullsig);
12408 free(funcsig_tag);
12409 free(qual_funcsig);
12410 free(configitems);
12415 * Dump a user-defined cast
12417 static void
12418 dumpCast(Archive *fout, const CastInfo *cast)
12420 DumpOptions *dopt = fout->dopt;
12421 PQExpBuffer defqry;
12422 PQExpBuffer delqry;
12423 PQExpBuffer labelq;
12424 PQExpBuffer castargs;
12425 FuncInfo *funcInfo = NULL;
12426 const char *sourceType;
12427 const char *targetType;
12429 /* Do nothing in data-only dump */
12430 if (dopt->dataOnly)
12431 return;
12433 /* Cannot dump if we don't have the cast function's info */
12434 if (OidIsValid(cast->castfunc))
12436 funcInfo = findFuncByOid(cast->castfunc);
12437 if (funcInfo == NULL)
12438 pg_fatal("could not find function definition for function with OID %u",
12439 cast->castfunc);
12442 defqry = createPQExpBuffer();
12443 delqry = createPQExpBuffer();
12444 labelq = createPQExpBuffer();
12445 castargs = createPQExpBuffer();
12447 sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
12448 targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
12449 appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
12450 sourceType, targetType);
12452 appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
12453 sourceType, targetType);
12455 switch (cast->castmethod)
12457 case COERCION_METHOD_BINARY:
12458 appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
12459 break;
12460 case COERCION_METHOD_INOUT:
12461 appendPQExpBufferStr(defqry, "WITH INOUT");
12462 break;
12463 case COERCION_METHOD_FUNCTION:
12464 if (funcInfo)
12466 char *fsig = format_function_signature(fout, funcInfo, true);
12469 * Always qualify the function name (format_function_signature
12470 * won't qualify it).
12472 appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
12473 fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
12474 free(fsig);
12476 else
12477 pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
12478 break;
12479 default:
12480 pg_log_warning("bogus value in pg_cast.castmethod field");
12483 if (cast->castcontext == 'a')
12484 appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
12485 else if (cast->castcontext == 'i')
12486 appendPQExpBufferStr(defqry, " AS IMPLICIT");
12487 appendPQExpBufferStr(defqry, ";\n");
12489 appendPQExpBuffer(labelq, "CAST (%s AS %s)",
12490 sourceType, targetType);
12492 appendPQExpBuffer(castargs, "(%s AS %s)",
12493 sourceType, targetType);
12495 if (dopt->binary_upgrade)
12496 binary_upgrade_extension_member(defqry, &cast->dobj,
12497 "CAST", castargs->data, NULL);
12499 if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
12500 ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
12501 ARCHIVE_OPTS(.tag = labelq->data,
12502 .description = "CAST",
12503 .section = SECTION_PRE_DATA,
12504 .createStmt = defqry->data,
12505 .dropStmt = delqry->data));
12507 /* Dump Cast Comments */
12508 if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
12509 dumpComment(fout, "CAST", castargs->data,
12510 NULL, "",
12511 cast->dobj.catId, 0, cast->dobj.dumpId);
12513 destroyPQExpBuffer(defqry);
12514 destroyPQExpBuffer(delqry);
12515 destroyPQExpBuffer(labelq);
12516 destroyPQExpBuffer(castargs);
12520 * Dump a transform
12522 static void
12523 dumpTransform(Archive *fout, const TransformInfo *transform)
12525 DumpOptions *dopt = fout->dopt;
12526 PQExpBuffer defqry;
12527 PQExpBuffer delqry;
12528 PQExpBuffer labelq;
12529 PQExpBuffer transformargs;
12530 FuncInfo *fromsqlFuncInfo = NULL;
12531 FuncInfo *tosqlFuncInfo = NULL;
12532 char *lanname;
12533 const char *transformType;
12535 /* Do nothing in data-only dump */
12536 if (dopt->dataOnly)
12537 return;
12539 /* Cannot dump if we don't have the transform functions' info */
12540 if (OidIsValid(transform->trffromsql))
12542 fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
12543 if (fromsqlFuncInfo == NULL)
12544 pg_fatal("could not find function definition for function with OID %u",
12545 transform->trffromsql);
12547 if (OidIsValid(transform->trftosql))
12549 tosqlFuncInfo = findFuncByOid(transform->trftosql);
12550 if (tosqlFuncInfo == NULL)
12551 pg_fatal("could not find function definition for function with OID %u",
12552 transform->trftosql);
12555 defqry = createPQExpBuffer();
12556 delqry = createPQExpBuffer();
12557 labelq = createPQExpBuffer();
12558 transformargs = createPQExpBuffer();
12560 lanname = get_language_name(fout, transform->trflang);
12561 transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
12563 appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
12564 transformType, lanname);
12566 appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
12567 transformType, lanname);
12569 if (!transform->trffromsql && !transform->trftosql)
12570 pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
12572 if (transform->trffromsql)
12574 if (fromsqlFuncInfo)
12576 char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
12579 * Always qualify the function name (format_function_signature
12580 * won't qualify it).
12582 appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
12583 fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
12584 free(fsig);
12586 else
12587 pg_log_warning("bogus value in pg_transform.trffromsql field");
12590 if (transform->trftosql)
12592 if (transform->trffromsql)
12593 appendPQExpBufferStr(defqry, ", ");
12595 if (tosqlFuncInfo)
12597 char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
12600 * Always qualify the function name (format_function_signature
12601 * won't qualify it).
12603 appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
12604 fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
12605 free(fsig);
12607 else
12608 pg_log_warning("bogus value in pg_transform.trftosql field");
12611 appendPQExpBufferStr(defqry, ");\n");
12613 appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
12614 transformType, lanname);
12616 appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
12617 transformType, lanname);
12619 if (dopt->binary_upgrade)
12620 binary_upgrade_extension_member(defqry, &transform->dobj,
12621 "TRANSFORM", transformargs->data, NULL);
12623 if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
12624 ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
12625 ARCHIVE_OPTS(.tag = labelq->data,
12626 .description = "TRANSFORM",
12627 .section = SECTION_PRE_DATA,
12628 .createStmt = defqry->data,
12629 .dropStmt = delqry->data,
12630 .deps = transform->dobj.dependencies,
12631 .nDeps = transform->dobj.nDeps));
12633 /* Dump Transform Comments */
12634 if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
12635 dumpComment(fout, "TRANSFORM", transformargs->data,
12636 NULL, "",
12637 transform->dobj.catId, 0, transform->dobj.dumpId);
12639 free(lanname);
12640 destroyPQExpBuffer(defqry);
12641 destroyPQExpBuffer(delqry);
12642 destroyPQExpBuffer(labelq);
12643 destroyPQExpBuffer(transformargs);
12648 * dumpOpr
12649 * write out a single operator definition
12651 static void
12652 dumpOpr(Archive *fout, const OprInfo *oprinfo)
12654 DumpOptions *dopt = fout->dopt;
12655 PQExpBuffer query;
12656 PQExpBuffer q;
12657 PQExpBuffer delq;
12658 PQExpBuffer oprid;
12659 PQExpBuffer details;
12660 PGresult *res;
12661 int i_oprkind;
12662 int i_oprcode;
12663 int i_oprleft;
12664 int i_oprright;
12665 int i_oprcom;
12666 int i_oprnegate;
12667 int i_oprrest;
12668 int i_oprjoin;
12669 int i_oprcanmerge;
12670 int i_oprcanhash;
12671 char *oprkind;
12672 char *oprcode;
12673 char *oprleft;
12674 char *oprright;
12675 char *oprcom;
12676 char *oprnegate;
12677 char *oprrest;
12678 char *oprjoin;
12679 char *oprcanmerge;
12680 char *oprcanhash;
12681 char *oprregproc;
12682 char *oprref;
12684 /* Do nothing in data-only dump */
12685 if (dopt->dataOnly)
12686 return;
12689 * some operators are invalid because they were the result of user
12690 * defining operators before commutators exist
12692 if (!OidIsValid(oprinfo->oprcode))
12693 return;
12695 query = createPQExpBuffer();
12696 q = createPQExpBuffer();
12697 delq = createPQExpBuffer();
12698 oprid = createPQExpBuffer();
12699 details = createPQExpBuffer();
12701 if (!fout->is_prepared[PREPQUERY_DUMPOPR])
12703 /* Set up query for operator-specific details */
12704 appendPQExpBufferStr(query,
12705 "PREPARE dumpOpr(pg_catalog.oid) AS\n"
12706 "SELECT oprkind, "
12707 "oprcode::pg_catalog.regprocedure, "
12708 "oprleft::pg_catalog.regtype, "
12709 "oprright::pg_catalog.regtype, "
12710 "oprcom, "
12711 "oprnegate, "
12712 "oprrest::pg_catalog.regprocedure, "
12713 "oprjoin::pg_catalog.regprocedure, "
12714 "oprcanmerge, oprcanhash "
12715 "FROM pg_catalog.pg_operator "
12716 "WHERE oid = $1");
12718 ExecuteSqlStatement(fout, query->data);
12720 fout->is_prepared[PREPQUERY_DUMPOPR] = true;
12723 printfPQExpBuffer(query,
12724 "EXECUTE dumpOpr('%u')",
12725 oprinfo->dobj.catId.oid);
12727 res = ExecuteSqlQueryForSingleRow(fout, query->data);
12729 i_oprkind = PQfnumber(res, "oprkind");
12730 i_oprcode = PQfnumber(res, "oprcode");
12731 i_oprleft = PQfnumber(res, "oprleft");
12732 i_oprright = PQfnumber(res, "oprright");
12733 i_oprcom = PQfnumber(res, "oprcom");
12734 i_oprnegate = PQfnumber(res, "oprnegate");
12735 i_oprrest = PQfnumber(res, "oprrest");
12736 i_oprjoin = PQfnumber(res, "oprjoin");
12737 i_oprcanmerge = PQfnumber(res, "oprcanmerge");
12738 i_oprcanhash = PQfnumber(res, "oprcanhash");
12740 oprkind = PQgetvalue(res, 0, i_oprkind);
12741 oprcode = PQgetvalue(res, 0, i_oprcode);
12742 oprleft = PQgetvalue(res, 0, i_oprleft);
12743 oprright = PQgetvalue(res, 0, i_oprright);
12744 oprcom = PQgetvalue(res, 0, i_oprcom);
12745 oprnegate = PQgetvalue(res, 0, i_oprnegate);
12746 oprrest = PQgetvalue(res, 0, i_oprrest);
12747 oprjoin = PQgetvalue(res, 0, i_oprjoin);
12748 oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
12749 oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
12751 /* In PG14 upwards postfix operator support does not exist anymore. */
12752 if (strcmp(oprkind, "r") == 0)
12753 pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
12754 oprcode);
12756 oprregproc = convertRegProcReference(oprcode);
12757 if (oprregproc)
12759 appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
12760 free(oprregproc);
12763 appendPQExpBuffer(oprid, "%s (",
12764 oprinfo->dobj.name);
12767 * right unary means there's a left arg and left unary means there's a
12768 * right arg. (Although the "r" case is dead code for PG14 and later,
12769 * continue to support it in case we're dumping from an old server.)
12771 if (strcmp(oprkind, "r") == 0 ||
12772 strcmp(oprkind, "b") == 0)
12774 appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
12775 appendPQExpBufferStr(oprid, oprleft);
12777 else
12778 appendPQExpBufferStr(oprid, "NONE");
12780 if (strcmp(oprkind, "l") == 0 ||
12781 strcmp(oprkind, "b") == 0)
12783 appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
12784 appendPQExpBuffer(oprid, ", %s)", oprright);
12786 else
12787 appendPQExpBufferStr(oprid, ", NONE)");
12789 oprref = getFormattedOperatorName(oprcom);
12790 if (oprref)
12792 appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
12793 free(oprref);
12796 oprref = getFormattedOperatorName(oprnegate);
12797 if (oprref)
12799 appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
12800 free(oprref);
12803 if (strcmp(oprcanmerge, "t") == 0)
12804 appendPQExpBufferStr(details, ",\n MERGES");
12806 if (strcmp(oprcanhash, "t") == 0)
12807 appendPQExpBufferStr(details, ",\n HASHES");
12809 oprregproc = convertRegProcReference(oprrest);
12810 if (oprregproc)
12812 appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
12813 free(oprregproc);
12816 oprregproc = convertRegProcReference(oprjoin);
12817 if (oprregproc)
12819 appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
12820 free(oprregproc);
12823 appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
12824 fmtId(oprinfo->dobj.namespace->dobj.name),
12825 oprid->data);
12827 appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
12828 fmtId(oprinfo->dobj.namespace->dobj.name),
12829 oprinfo->dobj.name, details->data);
12831 if (dopt->binary_upgrade)
12832 binary_upgrade_extension_member(q, &oprinfo->dobj,
12833 "OPERATOR", oprid->data,
12834 oprinfo->dobj.namespace->dobj.name);
12836 if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12837 ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
12838 ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
12839 .namespace = oprinfo->dobj.namespace->dobj.name,
12840 .owner = oprinfo->rolname,
12841 .description = "OPERATOR",
12842 .section = SECTION_PRE_DATA,
12843 .createStmt = q->data,
12844 .dropStmt = delq->data));
12846 /* Dump Operator Comments */
12847 if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12848 dumpComment(fout, "OPERATOR", oprid->data,
12849 oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
12850 oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
12852 PQclear(res);
12854 destroyPQExpBuffer(query);
12855 destroyPQExpBuffer(q);
12856 destroyPQExpBuffer(delq);
12857 destroyPQExpBuffer(oprid);
12858 destroyPQExpBuffer(details);
12862 * Convert a function reference obtained from pg_operator
12864 * Returns allocated string of what to print, or NULL if function references
12865 * is InvalidOid. Returned string is expected to be free'd by the caller.
12867 * The input is a REGPROCEDURE display; we have to strip the argument-types
12868 * part.
12870 static char *
12871 convertRegProcReference(const char *proc)
12873 char *name;
12874 char *paren;
12875 bool inquote;
12877 /* In all cases "-" means a null reference */
12878 if (strcmp(proc, "-") == 0)
12879 return NULL;
12881 name = pg_strdup(proc);
12882 /* find non-double-quoted left paren */
12883 inquote = false;
12884 for (paren = name; *paren; paren++)
12886 if (*paren == '(' && !inquote)
12888 *paren = '\0';
12889 break;
12891 if (*paren == '"')
12892 inquote = !inquote;
12894 return name;
12898 * getFormattedOperatorName - retrieve the operator name for the
12899 * given operator OID (presented in string form).
12901 * Returns an allocated string, or NULL if the given OID is invalid.
12902 * Caller is responsible for free'ing result string.
12904 * What we produce has the format "OPERATOR(schema.oprname)". This is only
12905 * useful in commands where the operator's argument types can be inferred from
12906 * context. We always schema-qualify the name, though. The predecessor to
12907 * this code tried to skip the schema qualification if possible, but that led
12908 * to wrong results in corner cases, such as if an operator and its negator
12909 * are in different schemas.
12911 static char *
12912 getFormattedOperatorName(const char *oproid)
12914 OprInfo *oprInfo;
12916 /* In all cases "0" means a null reference */
12917 if (strcmp(oproid, "0") == 0)
12918 return NULL;
12920 oprInfo = findOprByOid(atooid(oproid));
12921 if (oprInfo == NULL)
12923 pg_log_warning("could not find operator with OID %s",
12924 oproid);
12925 return NULL;
12928 return psprintf("OPERATOR(%s.%s)",
12929 fmtId(oprInfo->dobj.namespace->dobj.name),
12930 oprInfo->dobj.name);
12934 * Convert a function OID obtained from pg_ts_parser or pg_ts_template
12936 * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
12937 * argument lists of these functions are predetermined. Note that the
12938 * caller should ensure we are in the proper schema, because the results
12939 * are search path dependent!
12941 static char *
12942 convertTSFunction(Archive *fout, Oid funcOid)
12944 char *result;
12945 char query[128];
12946 PGresult *res;
12948 snprintf(query, sizeof(query),
12949 "SELECT '%u'::pg_catalog.regproc", funcOid);
12950 res = ExecuteSqlQueryForSingleRow(fout, query);
12952 result = pg_strdup(PQgetvalue(res, 0, 0));
12954 PQclear(res);
12956 return result;
12960 * dumpAccessMethod
12961 * write out a single access method definition
12963 static void
12964 dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
12966 DumpOptions *dopt = fout->dopt;
12967 PQExpBuffer q;
12968 PQExpBuffer delq;
12969 char *qamname;
12971 /* Do nothing in data-only dump */
12972 if (dopt->dataOnly)
12973 return;
12975 q = createPQExpBuffer();
12976 delq = createPQExpBuffer();
12978 qamname = pg_strdup(fmtId(aminfo->dobj.name));
12980 appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
12982 switch (aminfo->amtype)
12984 case AMTYPE_INDEX:
12985 appendPQExpBufferStr(q, "TYPE INDEX ");
12986 break;
12987 case AMTYPE_TABLE:
12988 appendPQExpBufferStr(q, "TYPE TABLE ");
12989 break;
12990 default:
12991 pg_log_warning("invalid type \"%c\" of access method \"%s\"",
12992 aminfo->amtype, qamname);
12993 destroyPQExpBuffer(q);
12994 destroyPQExpBuffer(delq);
12995 free(qamname);
12996 return;
12999 appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13001 appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13002 qamname);
13004 if (dopt->binary_upgrade)
13005 binary_upgrade_extension_member(q, &aminfo->dobj,
13006 "ACCESS METHOD", qamname, NULL);
13008 if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13009 ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13010 ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13011 .description = "ACCESS METHOD",
13012 .section = SECTION_PRE_DATA,
13013 .createStmt = q->data,
13014 .dropStmt = delq->data));
13016 /* Dump Access Method Comments */
13017 if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13018 dumpComment(fout, "ACCESS METHOD", qamname,
13019 NULL, "",
13020 aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13022 destroyPQExpBuffer(q);
13023 destroyPQExpBuffer(delq);
13024 free(qamname);
13028 * dumpOpclass
13029 * write out a single operator class definition
13031 static void
13032 dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13034 DumpOptions *dopt = fout->dopt;
13035 PQExpBuffer query;
13036 PQExpBuffer q;
13037 PQExpBuffer delq;
13038 PQExpBuffer nameusing;
13039 PGresult *res;
13040 int ntups;
13041 int i_opcintype;
13042 int i_opckeytype;
13043 int i_opcdefault;
13044 int i_opcfamily;
13045 int i_opcfamilyname;
13046 int i_opcfamilynsp;
13047 int i_amname;
13048 int i_amopstrategy;
13049 int i_amopopr;
13050 int i_sortfamily;
13051 int i_sortfamilynsp;
13052 int i_amprocnum;
13053 int i_amproc;
13054 int i_amproclefttype;
13055 int i_amprocrighttype;
13056 char *opcintype;
13057 char *opckeytype;
13058 char *opcdefault;
13059 char *opcfamily;
13060 char *opcfamilyname;
13061 char *opcfamilynsp;
13062 char *amname;
13063 char *amopstrategy;
13064 char *amopopr;
13065 char *sortfamily;
13066 char *sortfamilynsp;
13067 char *amprocnum;
13068 char *amproc;
13069 char *amproclefttype;
13070 char *amprocrighttype;
13071 bool needComma;
13072 int i;
13074 /* Do nothing in data-only dump */
13075 if (dopt->dataOnly)
13076 return;
13078 query = createPQExpBuffer();
13079 q = createPQExpBuffer();
13080 delq = createPQExpBuffer();
13081 nameusing = createPQExpBuffer();
13083 /* Get additional fields from the pg_opclass row */
13084 appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13085 "opckeytype::pg_catalog.regtype, "
13086 "opcdefault, opcfamily, "
13087 "opfname AS opcfamilyname, "
13088 "nspname AS opcfamilynsp, "
13089 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13090 "FROM pg_catalog.pg_opclass c "
13091 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13092 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13093 "WHERE c.oid = '%u'::pg_catalog.oid",
13094 opcinfo->dobj.catId.oid);
13096 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13098 i_opcintype = PQfnumber(res, "opcintype");
13099 i_opckeytype = PQfnumber(res, "opckeytype");
13100 i_opcdefault = PQfnumber(res, "opcdefault");
13101 i_opcfamily = PQfnumber(res, "opcfamily");
13102 i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13103 i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13104 i_amname = PQfnumber(res, "amname");
13106 /* opcintype may still be needed after we PQclear res */
13107 opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13108 opckeytype = PQgetvalue(res, 0, i_opckeytype);
13109 opcdefault = PQgetvalue(res, 0, i_opcdefault);
13110 /* opcfamily will still be needed after we PQclear res */
13111 opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13112 opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13113 opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13114 /* amname will still be needed after we PQclear res */
13115 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13117 appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13118 fmtQualifiedDumpable(opcinfo));
13119 appendPQExpBuffer(delq, " USING %s;\n",
13120 fmtId(amname));
13122 /* Build the fixed portion of the CREATE command */
13123 appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13124 fmtQualifiedDumpable(opcinfo));
13125 if (strcmp(opcdefault, "t") == 0)
13126 appendPQExpBufferStr(q, "DEFAULT ");
13127 appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13128 opcintype,
13129 fmtId(amname));
13130 if (strlen(opcfamilyname) > 0)
13132 appendPQExpBufferStr(q, " FAMILY ");
13133 appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13134 appendPQExpBufferStr(q, fmtId(opcfamilyname));
13136 appendPQExpBufferStr(q, " AS\n ");
13138 needComma = false;
13140 if (strcmp(opckeytype, "-") != 0)
13142 appendPQExpBuffer(q, "STORAGE %s",
13143 opckeytype);
13144 needComma = true;
13147 PQclear(res);
13150 * Now fetch and print the OPERATOR entries (pg_amop rows).
13152 * Print only those opfamily members that are tied to the opclass by
13153 * pg_depend entries.
13155 resetPQExpBuffer(query);
13156 appendPQExpBuffer(query, "SELECT amopstrategy, "
13157 "amopopr::pg_catalog.regoperator, "
13158 "opfname AS sortfamily, "
13159 "nspname AS sortfamilynsp "
13160 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13161 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13162 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13163 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13164 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13165 "AND refobjid = '%u'::pg_catalog.oid "
13166 "AND amopfamily = '%s'::pg_catalog.oid "
13167 "ORDER BY amopstrategy",
13168 opcinfo->dobj.catId.oid,
13169 opcfamily);
13171 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13173 ntups = PQntuples(res);
13175 i_amopstrategy = PQfnumber(res, "amopstrategy");
13176 i_amopopr = PQfnumber(res, "amopopr");
13177 i_sortfamily = PQfnumber(res, "sortfamily");
13178 i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13180 for (i = 0; i < ntups; i++)
13182 amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13183 amopopr = PQgetvalue(res, i, i_amopopr);
13184 sortfamily = PQgetvalue(res, i, i_sortfamily);
13185 sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13187 if (needComma)
13188 appendPQExpBufferStr(q, " ,\n ");
13190 appendPQExpBuffer(q, "OPERATOR %s %s",
13191 amopstrategy, amopopr);
13193 if (strlen(sortfamily) > 0)
13195 appendPQExpBufferStr(q, " FOR ORDER BY ");
13196 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13197 appendPQExpBufferStr(q, fmtId(sortfamily));
13200 needComma = true;
13203 PQclear(res);
13206 * Now fetch and print the FUNCTION entries (pg_amproc rows).
13208 * Print only those opfamily members that are tied to the opclass by
13209 * pg_depend entries.
13211 * We print the amproclefttype/amprocrighttype even though in most cases
13212 * the backend could deduce the right values, because of the corner case
13213 * of a btree sort support function for a cross-type comparison.
13215 resetPQExpBuffer(query);
13217 appendPQExpBuffer(query, "SELECT amprocnum, "
13218 "amproc::pg_catalog.regprocedure, "
13219 "amproclefttype::pg_catalog.regtype, "
13220 "amprocrighttype::pg_catalog.regtype "
13221 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13222 "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13223 "AND refobjid = '%u'::pg_catalog.oid "
13224 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13225 "AND objid = ap.oid "
13226 "ORDER BY amprocnum",
13227 opcinfo->dobj.catId.oid);
13229 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13231 ntups = PQntuples(res);
13233 i_amprocnum = PQfnumber(res, "amprocnum");
13234 i_amproc = PQfnumber(res, "amproc");
13235 i_amproclefttype = PQfnumber(res, "amproclefttype");
13236 i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13238 for (i = 0; i < ntups; i++)
13240 amprocnum = PQgetvalue(res, i, i_amprocnum);
13241 amproc = PQgetvalue(res, i, i_amproc);
13242 amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13243 amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13245 if (needComma)
13246 appendPQExpBufferStr(q, " ,\n ");
13248 appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13250 if (*amproclefttype && *amprocrighttype)
13251 appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13253 appendPQExpBuffer(q, " %s", amproc);
13255 needComma = true;
13258 PQclear(res);
13261 * If needComma is still false it means we haven't added anything after
13262 * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13263 * clause with the same datatype. This isn't sanctioned by the
13264 * documentation, but actually DefineOpClass will treat it as a no-op.
13266 if (!needComma)
13267 appendPQExpBuffer(q, "STORAGE %s", opcintype);
13269 appendPQExpBufferStr(q, ";\n");
13271 appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13272 appendPQExpBuffer(nameusing, " USING %s",
13273 fmtId(amname));
13275 if (dopt->binary_upgrade)
13276 binary_upgrade_extension_member(q, &opcinfo->dobj,
13277 "OPERATOR CLASS", nameusing->data,
13278 opcinfo->dobj.namespace->dobj.name);
13280 if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13281 ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13282 ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13283 .namespace = opcinfo->dobj.namespace->dobj.name,
13284 .owner = opcinfo->rolname,
13285 .description = "OPERATOR CLASS",
13286 .section = SECTION_PRE_DATA,
13287 .createStmt = q->data,
13288 .dropStmt = delq->data));
13290 /* Dump Operator Class Comments */
13291 if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13292 dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13293 opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13294 opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13296 free(opcintype);
13297 free(opcfamily);
13298 free(amname);
13299 destroyPQExpBuffer(query);
13300 destroyPQExpBuffer(q);
13301 destroyPQExpBuffer(delq);
13302 destroyPQExpBuffer(nameusing);
13306 * dumpOpfamily
13307 * write out a single operator family definition
13309 * Note: this also dumps any "loose" operator members that aren't bound to a
13310 * specific opclass within the opfamily.
13312 static void
13313 dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
13315 DumpOptions *dopt = fout->dopt;
13316 PQExpBuffer query;
13317 PQExpBuffer q;
13318 PQExpBuffer delq;
13319 PQExpBuffer nameusing;
13320 PGresult *res;
13321 PGresult *res_ops;
13322 PGresult *res_procs;
13323 int ntups;
13324 int i_amname;
13325 int i_amopstrategy;
13326 int i_amopopr;
13327 int i_sortfamily;
13328 int i_sortfamilynsp;
13329 int i_amprocnum;
13330 int i_amproc;
13331 int i_amproclefttype;
13332 int i_amprocrighttype;
13333 char *amname;
13334 char *amopstrategy;
13335 char *amopopr;
13336 char *sortfamily;
13337 char *sortfamilynsp;
13338 char *amprocnum;
13339 char *amproc;
13340 char *amproclefttype;
13341 char *amprocrighttype;
13342 bool needComma;
13343 int i;
13345 /* Do nothing in data-only dump */
13346 if (dopt->dataOnly)
13347 return;
13349 query = createPQExpBuffer();
13350 q = createPQExpBuffer();
13351 delq = createPQExpBuffer();
13352 nameusing = createPQExpBuffer();
13355 * Fetch only those opfamily members that are tied directly to the
13356 * opfamily by pg_depend entries.
13358 appendPQExpBuffer(query, "SELECT amopstrategy, "
13359 "amopopr::pg_catalog.regoperator, "
13360 "opfname AS sortfamily, "
13361 "nspname AS sortfamilynsp "
13362 "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13363 "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13364 "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13365 "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13366 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13367 "AND refobjid = '%u'::pg_catalog.oid "
13368 "AND amopfamily = '%u'::pg_catalog.oid "
13369 "ORDER BY amopstrategy",
13370 opfinfo->dobj.catId.oid,
13371 opfinfo->dobj.catId.oid);
13373 res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13375 resetPQExpBuffer(query);
13377 appendPQExpBuffer(query, "SELECT amprocnum, "
13378 "amproc::pg_catalog.regprocedure, "
13379 "amproclefttype::pg_catalog.regtype, "
13380 "amprocrighttype::pg_catalog.regtype "
13381 "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13382 "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13383 "AND refobjid = '%u'::pg_catalog.oid "
13384 "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13385 "AND objid = ap.oid "
13386 "ORDER BY amprocnum",
13387 opfinfo->dobj.catId.oid);
13389 res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13391 /* Get additional fields from the pg_opfamily row */
13392 resetPQExpBuffer(query);
13394 appendPQExpBuffer(query, "SELECT "
13395 "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
13396 "FROM pg_catalog.pg_opfamily "
13397 "WHERE oid = '%u'::pg_catalog.oid",
13398 opfinfo->dobj.catId.oid);
13400 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13402 i_amname = PQfnumber(res, "amname");
13404 /* amname will still be needed after we PQclear res */
13405 amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13407 appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
13408 fmtQualifiedDumpable(opfinfo));
13409 appendPQExpBuffer(delq, " USING %s;\n",
13410 fmtId(amname));
13412 /* Build the fixed portion of the CREATE command */
13413 appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
13414 fmtQualifiedDumpable(opfinfo));
13415 appendPQExpBuffer(q, " USING %s;\n",
13416 fmtId(amname));
13418 PQclear(res);
13420 /* Do we need an ALTER to add loose members? */
13421 if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
13423 appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
13424 fmtQualifiedDumpable(opfinfo));
13425 appendPQExpBuffer(q, " USING %s ADD\n ",
13426 fmtId(amname));
13428 needComma = false;
13431 * Now fetch and print the OPERATOR entries (pg_amop rows).
13433 ntups = PQntuples(res_ops);
13435 i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
13436 i_amopopr = PQfnumber(res_ops, "amopopr");
13437 i_sortfamily = PQfnumber(res_ops, "sortfamily");
13438 i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
13440 for (i = 0; i < ntups; i++)
13442 amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
13443 amopopr = PQgetvalue(res_ops, i, i_amopopr);
13444 sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
13445 sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
13447 if (needComma)
13448 appendPQExpBufferStr(q, " ,\n ");
13450 appendPQExpBuffer(q, "OPERATOR %s %s",
13451 amopstrategy, amopopr);
13453 if (strlen(sortfamily) > 0)
13455 appendPQExpBufferStr(q, " FOR ORDER BY ");
13456 appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13457 appendPQExpBufferStr(q, fmtId(sortfamily));
13460 needComma = true;
13464 * Now fetch and print the FUNCTION entries (pg_amproc rows).
13466 ntups = PQntuples(res_procs);
13468 i_amprocnum = PQfnumber(res_procs, "amprocnum");
13469 i_amproc = PQfnumber(res_procs, "amproc");
13470 i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
13471 i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
13473 for (i = 0; i < ntups; i++)
13475 amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
13476 amproc = PQgetvalue(res_procs, i, i_amproc);
13477 amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
13478 amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
13480 if (needComma)
13481 appendPQExpBufferStr(q, " ,\n ");
13483 appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
13484 amprocnum, amproclefttype, amprocrighttype,
13485 amproc);
13487 needComma = true;
13490 appendPQExpBufferStr(q, ";\n");
13493 appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
13494 appendPQExpBuffer(nameusing, " USING %s",
13495 fmtId(amname));
13497 if (dopt->binary_upgrade)
13498 binary_upgrade_extension_member(q, &opfinfo->dobj,
13499 "OPERATOR FAMILY", nameusing->data,
13500 opfinfo->dobj.namespace->dobj.name);
13502 if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13503 ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
13504 ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
13505 .namespace = opfinfo->dobj.namespace->dobj.name,
13506 .owner = opfinfo->rolname,
13507 .description = "OPERATOR FAMILY",
13508 .section = SECTION_PRE_DATA,
13509 .createStmt = q->data,
13510 .dropStmt = delq->data));
13512 /* Dump Operator Family Comments */
13513 if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13514 dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
13515 opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
13516 opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
13518 free(amname);
13519 PQclear(res_ops);
13520 PQclear(res_procs);
13521 destroyPQExpBuffer(query);
13522 destroyPQExpBuffer(q);
13523 destroyPQExpBuffer(delq);
13524 destroyPQExpBuffer(nameusing);
13528 * dumpCollation
13529 * write out a single collation definition
13531 static void
13532 dumpCollation(Archive *fout, const CollInfo *collinfo)
13534 DumpOptions *dopt = fout->dopt;
13535 PQExpBuffer query;
13536 PQExpBuffer q;
13537 PQExpBuffer delq;
13538 char *qcollname;
13539 PGresult *res;
13540 int i_collprovider;
13541 int i_collisdeterministic;
13542 int i_collcollate;
13543 int i_collctype;
13544 int i_colliculocale;
13545 int i_collicurules;
13546 const char *collprovider;
13547 const char *collcollate;
13548 const char *collctype;
13549 const char *colliculocale;
13550 const char *collicurules;
13552 /* Do nothing in data-only dump */
13553 if (dopt->dataOnly)
13554 return;
13556 query = createPQExpBuffer();
13557 q = createPQExpBuffer();
13558 delq = createPQExpBuffer();
13560 qcollname = pg_strdup(fmtId(collinfo->dobj.name));
13562 /* Get collation-specific details */
13563 appendPQExpBufferStr(query, "SELECT ");
13565 if (fout->remoteVersion >= 100000)
13566 appendPQExpBufferStr(query,
13567 "collprovider, "
13568 "collversion, ");
13569 else
13570 appendPQExpBufferStr(query,
13571 "'c' AS collprovider, "
13572 "NULL AS collversion, ");
13574 if (fout->remoteVersion >= 120000)
13575 appendPQExpBufferStr(query,
13576 "collisdeterministic, ");
13577 else
13578 appendPQExpBufferStr(query,
13579 "true AS collisdeterministic, ");
13581 if (fout->remoteVersion >= 150000)
13582 appendPQExpBufferStr(query,
13583 "colliculocale, ");
13584 else
13585 appendPQExpBufferStr(query,
13586 "NULL AS colliculocale, ");
13588 if (fout->remoteVersion >= 160000)
13589 appendPQExpBufferStr(query,
13590 "collicurules, ");
13591 else
13592 appendPQExpBufferStr(query,
13593 "NULL AS collicurules, ");
13595 appendPQExpBuffer(query,
13596 "collcollate, "
13597 "collctype "
13598 "FROM pg_catalog.pg_collation c "
13599 "WHERE c.oid = '%u'::pg_catalog.oid",
13600 collinfo->dobj.catId.oid);
13602 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13604 i_collprovider = PQfnumber(res, "collprovider");
13605 i_collisdeterministic = PQfnumber(res, "collisdeterministic");
13606 i_collcollate = PQfnumber(res, "collcollate");
13607 i_collctype = PQfnumber(res, "collctype");
13608 i_colliculocale = PQfnumber(res, "colliculocale");
13609 i_collicurules = PQfnumber(res, "collicurules");
13611 collprovider = PQgetvalue(res, 0, i_collprovider);
13613 if (!PQgetisnull(res, 0, i_collcollate))
13614 collcollate = PQgetvalue(res, 0, i_collcollate);
13615 else
13616 collcollate = NULL;
13618 if (!PQgetisnull(res, 0, i_collctype))
13619 collctype = PQgetvalue(res, 0, i_collctype);
13620 else
13621 collctype = NULL;
13624 * Before version 15, collcollate and collctype were of type NAME and
13625 * non-nullable. Treat empty strings as NULL for consistency.
13627 if (fout->remoteVersion < 150000)
13629 if (collcollate[0] == '\0')
13630 collcollate = NULL;
13631 if (collctype[0] == '\0')
13632 collctype = NULL;
13635 if (!PQgetisnull(res, 0, i_colliculocale))
13636 colliculocale = PQgetvalue(res, 0, i_colliculocale);
13637 else
13638 colliculocale = NULL;
13640 if (!PQgetisnull(res, 0, i_collicurules))
13641 collicurules = PQgetvalue(res, 0, i_collicurules);
13642 else
13643 collicurules = NULL;
13645 appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
13646 fmtQualifiedDumpable(collinfo));
13648 appendPQExpBuffer(q, "CREATE COLLATION %s (",
13649 fmtQualifiedDumpable(collinfo));
13651 appendPQExpBufferStr(q, "provider = ");
13652 if (collprovider[0] == 'c')
13653 appendPQExpBufferStr(q, "libc");
13654 else if (collprovider[0] == 'i')
13655 appendPQExpBufferStr(q, "icu");
13656 else if (collprovider[0] == 'd')
13657 /* to allow dumping pg_catalog; not accepted on input */
13658 appendPQExpBufferStr(q, "default");
13659 else
13660 pg_fatal("unrecognized collation provider: %s",
13661 collprovider);
13663 if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
13664 appendPQExpBufferStr(q, ", deterministic = false");
13666 if (collprovider[0] == 'd')
13668 if (collcollate || collctype || colliculocale || collicurules)
13669 pg_log_warning("invalid collation \"%s\"", qcollname);
13671 /* no locale -- the default collation cannot be reloaded anyway */
13673 else if (collprovider[0] == 'i')
13675 if (fout->remoteVersion >= 150000)
13677 if (collcollate || collctype || !colliculocale)
13678 pg_log_warning("invalid collation \"%s\"", qcollname);
13680 appendPQExpBufferStr(q, ", locale = ");
13681 appendStringLiteralAH(q, colliculocale ? colliculocale : "",
13682 fout);
13684 else
13686 if (!collcollate || !collctype || colliculocale ||
13687 strcmp(collcollate, collctype) != 0)
13688 pg_log_warning("invalid collation \"%s\"", qcollname);
13690 appendPQExpBufferStr(q, ", locale = ");
13691 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
13694 if (collicurules)
13696 appendPQExpBufferStr(q, ", rules = ");
13697 appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
13700 else if (collprovider[0] == 'c')
13702 if (colliculocale || collicurules || !collcollate || !collctype)
13703 pg_log_warning("invalid collation \"%s\"", qcollname);
13705 if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
13707 appendPQExpBufferStr(q, ", locale = ");
13708 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
13710 else
13712 appendPQExpBufferStr(q, ", lc_collate = ");
13713 appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
13714 appendPQExpBufferStr(q, ", lc_ctype = ");
13715 appendStringLiteralAH(q, collctype ? collctype : "", fout);
13718 else
13719 pg_fatal("unrecognized collation provider: %s", collprovider);
13722 * For binary upgrade, carry over the collation version. For normal
13723 * dump/restore, omit the version, so that it is computed upon restore.
13725 if (dopt->binary_upgrade)
13727 int i_collversion;
13729 i_collversion = PQfnumber(res, "collversion");
13730 if (!PQgetisnull(res, 0, i_collversion))
13732 appendPQExpBufferStr(q, ", version = ");
13733 appendStringLiteralAH(q,
13734 PQgetvalue(res, 0, i_collversion),
13735 fout);
13739 appendPQExpBufferStr(q, ");\n");
13741 if (dopt->binary_upgrade)
13742 binary_upgrade_extension_member(q, &collinfo->dobj,
13743 "COLLATION", qcollname,
13744 collinfo->dobj.namespace->dobj.name);
13746 if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13747 ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
13748 ARCHIVE_OPTS(.tag = collinfo->dobj.name,
13749 .namespace = collinfo->dobj.namespace->dobj.name,
13750 .owner = collinfo->rolname,
13751 .description = "COLLATION",
13752 .section = SECTION_PRE_DATA,
13753 .createStmt = q->data,
13754 .dropStmt = delq->data));
13756 /* Dump Collation Comments */
13757 if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13758 dumpComment(fout, "COLLATION", qcollname,
13759 collinfo->dobj.namespace->dobj.name, collinfo->rolname,
13760 collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
13762 PQclear(res);
13764 destroyPQExpBuffer(query);
13765 destroyPQExpBuffer(q);
13766 destroyPQExpBuffer(delq);
13767 free(qcollname);
13771 * dumpConversion
13772 * write out a single conversion definition
13774 static void
13775 dumpConversion(Archive *fout, const ConvInfo *convinfo)
13777 DumpOptions *dopt = fout->dopt;
13778 PQExpBuffer query;
13779 PQExpBuffer q;
13780 PQExpBuffer delq;
13781 char *qconvname;
13782 PGresult *res;
13783 int i_conforencoding;
13784 int i_contoencoding;
13785 int i_conproc;
13786 int i_condefault;
13787 const char *conforencoding;
13788 const char *contoencoding;
13789 const char *conproc;
13790 bool condefault;
13792 /* Do nothing in data-only dump */
13793 if (dopt->dataOnly)
13794 return;
13796 query = createPQExpBuffer();
13797 q = createPQExpBuffer();
13798 delq = createPQExpBuffer();
13800 qconvname = pg_strdup(fmtId(convinfo->dobj.name));
13802 /* Get conversion-specific details */
13803 appendPQExpBuffer(query, "SELECT "
13804 "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
13805 "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
13806 "conproc, condefault "
13807 "FROM pg_catalog.pg_conversion c "
13808 "WHERE c.oid = '%u'::pg_catalog.oid",
13809 convinfo->dobj.catId.oid);
13811 res = ExecuteSqlQueryForSingleRow(fout, query->data);
13813 i_conforencoding = PQfnumber(res, "conforencoding");
13814 i_contoencoding = PQfnumber(res, "contoencoding");
13815 i_conproc = PQfnumber(res, "conproc");
13816 i_condefault = PQfnumber(res, "condefault");
13818 conforencoding = PQgetvalue(res, 0, i_conforencoding);
13819 contoencoding = PQgetvalue(res, 0, i_contoencoding);
13820 conproc = PQgetvalue(res, 0, i_conproc);
13821 condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
13823 appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
13824 fmtQualifiedDumpable(convinfo));
13826 appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
13827 (condefault) ? "DEFAULT " : "",
13828 fmtQualifiedDumpable(convinfo));
13829 appendStringLiteralAH(q, conforencoding, fout);
13830 appendPQExpBufferStr(q, " TO ");
13831 appendStringLiteralAH(q, contoencoding, fout);
13832 /* regproc output is already sufficiently quoted */
13833 appendPQExpBuffer(q, " FROM %s;\n", conproc);
13835 if (dopt->binary_upgrade)
13836 binary_upgrade_extension_member(q, &convinfo->dobj,
13837 "CONVERSION", qconvname,
13838 convinfo->dobj.namespace->dobj.name);
13840 if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13841 ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
13842 ARCHIVE_OPTS(.tag = convinfo->dobj.name,
13843 .namespace = convinfo->dobj.namespace->dobj.name,
13844 .owner = convinfo->rolname,
13845 .description = "CONVERSION",
13846 .section = SECTION_PRE_DATA,
13847 .createStmt = q->data,
13848 .dropStmt = delq->data));
13850 /* Dump Conversion Comments */
13851 if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13852 dumpComment(fout, "CONVERSION", qconvname,
13853 convinfo->dobj.namespace->dobj.name, convinfo->rolname,
13854 convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
13856 PQclear(res);
13858 destroyPQExpBuffer(query);
13859 destroyPQExpBuffer(q);
13860 destroyPQExpBuffer(delq);
13861 free(qconvname);
13865 * format_aggregate_signature: generate aggregate name and argument list
13867 * The argument type names are qualified if needed. The aggregate name
13868 * is never qualified.
13870 static char *
13871 format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
13873 PQExpBufferData buf;
13874 int j;
13876 initPQExpBuffer(&buf);
13877 if (honor_quotes)
13878 appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
13879 else
13880 appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
13882 if (agginfo->aggfn.nargs == 0)
13883 appendPQExpBufferStr(&buf, "(*)");
13884 else
13886 appendPQExpBufferChar(&buf, '(');
13887 for (j = 0; j < agginfo->aggfn.nargs; j++)
13888 appendPQExpBuffer(&buf, "%s%s",
13889 (j > 0) ? ", " : "",
13890 getFormattedTypeName(fout,
13891 agginfo->aggfn.argtypes[j],
13892 zeroIsError));
13893 appendPQExpBufferChar(&buf, ')');
13895 return buf.data;
13899 * dumpAgg
13900 * write out a single aggregate definition
13902 static void
13903 dumpAgg(Archive *fout, const AggInfo *agginfo)
13905 DumpOptions *dopt = fout->dopt;
13906 PQExpBuffer query;
13907 PQExpBuffer q;
13908 PQExpBuffer delq;
13909 PQExpBuffer details;
13910 char *aggsig; /* identity signature */
13911 char *aggfullsig = NULL; /* full signature */
13912 char *aggsig_tag;
13913 PGresult *res;
13914 int i_agginitval;
13915 int i_aggminitval;
13916 const char *aggtransfn;
13917 const char *aggfinalfn;
13918 const char *aggcombinefn;
13919 const char *aggserialfn;
13920 const char *aggdeserialfn;
13921 const char *aggmtransfn;
13922 const char *aggminvtransfn;
13923 const char *aggmfinalfn;
13924 bool aggfinalextra;
13925 bool aggmfinalextra;
13926 char aggfinalmodify;
13927 char aggmfinalmodify;
13928 const char *aggsortop;
13929 char *aggsortconvop;
13930 char aggkind;
13931 const char *aggtranstype;
13932 const char *aggtransspace;
13933 const char *aggmtranstype;
13934 const char *aggmtransspace;
13935 const char *agginitval;
13936 const char *aggminitval;
13937 const char *proparallel;
13938 char defaultfinalmodify;
13940 /* Do nothing in data-only dump */
13941 if (dopt->dataOnly)
13942 return;
13944 query = createPQExpBuffer();
13945 q = createPQExpBuffer();
13946 delq = createPQExpBuffer();
13947 details = createPQExpBuffer();
13949 if (!fout->is_prepared[PREPQUERY_DUMPAGG])
13951 /* Set up query for aggregate-specific details */
13952 appendPQExpBufferStr(query,
13953 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
13955 appendPQExpBufferStr(query,
13956 "SELECT "
13957 "aggtransfn,\n"
13958 "aggfinalfn,\n"
13959 "aggtranstype::pg_catalog.regtype,\n"
13960 "agginitval,\n"
13961 "aggsortop,\n"
13962 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13963 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
13965 if (fout->remoteVersion >= 90400)
13966 appendPQExpBufferStr(query,
13967 "aggkind,\n"
13968 "aggmtransfn,\n"
13969 "aggminvtransfn,\n"
13970 "aggmfinalfn,\n"
13971 "aggmtranstype::pg_catalog.regtype,\n"
13972 "aggfinalextra,\n"
13973 "aggmfinalextra,\n"
13974 "aggtransspace,\n"
13975 "aggmtransspace,\n"
13976 "aggminitval,\n");
13977 else
13978 appendPQExpBufferStr(query,
13979 "'n' AS aggkind,\n"
13980 "'-' AS aggmtransfn,\n"
13981 "'-' AS aggminvtransfn,\n"
13982 "'-' AS aggmfinalfn,\n"
13983 "0 AS aggmtranstype,\n"
13984 "false AS aggfinalextra,\n"
13985 "false AS aggmfinalextra,\n"
13986 "0 AS aggtransspace,\n"
13987 "0 AS aggmtransspace,\n"
13988 "NULL AS aggminitval,\n");
13990 if (fout->remoteVersion >= 90600)
13991 appendPQExpBufferStr(query,
13992 "aggcombinefn,\n"
13993 "aggserialfn,\n"
13994 "aggdeserialfn,\n"
13995 "proparallel,\n");
13996 else
13997 appendPQExpBufferStr(query,
13998 "'-' AS aggcombinefn,\n"
13999 "'-' AS aggserialfn,\n"
14000 "'-' AS aggdeserialfn,\n"
14001 "'u' AS proparallel,\n");
14003 if (fout->remoteVersion >= 110000)
14004 appendPQExpBufferStr(query,
14005 "aggfinalmodify,\n"
14006 "aggmfinalmodify\n");
14007 else
14008 appendPQExpBufferStr(query,
14009 "'0' AS aggfinalmodify,\n"
14010 "'0' AS aggmfinalmodify\n");
14012 appendPQExpBufferStr(query,
14013 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14014 "WHERE a.aggfnoid = p.oid "
14015 "AND p.oid = $1");
14017 ExecuteSqlStatement(fout, query->data);
14019 fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14022 printfPQExpBuffer(query,
14023 "EXECUTE dumpAgg('%u')",
14024 agginfo->aggfn.dobj.catId.oid);
14026 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14028 i_agginitval = PQfnumber(res, "agginitval");
14029 i_aggminitval = PQfnumber(res, "aggminitval");
14031 aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14032 aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14033 aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14034 aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14035 aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14036 aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14037 aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14038 aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14039 aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14040 aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14041 aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14042 aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14043 aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14044 aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14045 aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14046 aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14047 aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14048 aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14049 agginitval = PQgetvalue(res, 0, i_agginitval);
14050 aggminitval = PQgetvalue(res, 0, i_aggminitval);
14051 proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14054 char *funcargs;
14055 char *funciargs;
14057 funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14058 funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14059 aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14060 aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14063 aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14065 /* identify default modify flag for aggkind (must match DefineAggregate) */
14066 defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14067 /* replace omitted flags for old versions */
14068 if (aggfinalmodify == '0')
14069 aggfinalmodify = defaultfinalmodify;
14070 if (aggmfinalmodify == '0')
14071 aggmfinalmodify = defaultfinalmodify;
14073 /* regproc and regtype output is already sufficiently quoted */
14074 appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14075 aggtransfn, aggtranstype);
14077 if (strcmp(aggtransspace, "0") != 0)
14079 appendPQExpBuffer(details, ",\n SSPACE = %s",
14080 aggtransspace);
14083 if (!PQgetisnull(res, 0, i_agginitval))
14085 appendPQExpBufferStr(details, ",\n INITCOND = ");
14086 appendStringLiteralAH(details, agginitval, fout);
14089 if (strcmp(aggfinalfn, "-") != 0)
14091 appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14092 aggfinalfn);
14093 if (aggfinalextra)
14094 appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14095 if (aggfinalmodify != defaultfinalmodify)
14097 switch (aggfinalmodify)
14099 case AGGMODIFY_READ_ONLY:
14100 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14101 break;
14102 case AGGMODIFY_SHAREABLE:
14103 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14104 break;
14105 case AGGMODIFY_READ_WRITE:
14106 appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14107 break;
14108 default:
14109 pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14110 agginfo->aggfn.dobj.name);
14111 break;
14116 if (strcmp(aggcombinefn, "-") != 0)
14117 appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14119 if (strcmp(aggserialfn, "-") != 0)
14120 appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14122 if (strcmp(aggdeserialfn, "-") != 0)
14123 appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14125 if (strcmp(aggmtransfn, "-") != 0)
14127 appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14128 aggmtransfn,
14129 aggminvtransfn,
14130 aggmtranstype);
14133 if (strcmp(aggmtransspace, "0") != 0)
14135 appendPQExpBuffer(details, ",\n MSSPACE = %s",
14136 aggmtransspace);
14139 if (!PQgetisnull(res, 0, i_aggminitval))
14141 appendPQExpBufferStr(details, ",\n MINITCOND = ");
14142 appendStringLiteralAH(details, aggminitval, fout);
14145 if (strcmp(aggmfinalfn, "-") != 0)
14147 appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14148 aggmfinalfn);
14149 if (aggmfinalextra)
14150 appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14151 if (aggmfinalmodify != defaultfinalmodify)
14153 switch (aggmfinalmodify)
14155 case AGGMODIFY_READ_ONLY:
14156 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14157 break;
14158 case AGGMODIFY_SHAREABLE:
14159 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14160 break;
14161 case AGGMODIFY_READ_WRITE:
14162 appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14163 break;
14164 default:
14165 pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14166 agginfo->aggfn.dobj.name);
14167 break;
14172 aggsortconvop = getFormattedOperatorName(aggsortop);
14173 if (aggsortconvop)
14175 appendPQExpBuffer(details, ",\n SORTOP = %s",
14176 aggsortconvop);
14177 free(aggsortconvop);
14180 if (aggkind == AGGKIND_HYPOTHETICAL)
14181 appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14183 if (proparallel[0] != PROPARALLEL_UNSAFE)
14185 if (proparallel[0] == PROPARALLEL_SAFE)
14186 appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14187 else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14188 appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14189 else if (proparallel[0] != PROPARALLEL_UNSAFE)
14190 pg_fatal("unrecognized proparallel value for function \"%s\"",
14191 agginfo->aggfn.dobj.name);
14194 appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14195 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14196 aggsig);
14198 appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14199 fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14200 aggfullsig ? aggfullsig : aggsig, details->data);
14202 if (dopt->binary_upgrade)
14203 binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14204 "AGGREGATE", aggsig,
14205 agginfo->aggfn.dobj.namespace->dobj.name);
14207 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14208 ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14209 agginfo->aggfn.dobj.dumpId,
14210 ARCHIVE_OPTS(.tag = aggsig_tag,
14211 .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14212 .owner = agginfo->aggfn.rolname,
14213 .description = "AGGREGATE",
14214 .section = SECTION_PRE_DATA,
14215 .createStmt = q->data,
14216 .dropStmt = delq->data));
14218 /* Dump Aggregate Comments */
14219 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14220 dumpComment(fout, "AGGREGATE", aggsig,
14221 agginfo->aggfn.dobj.namespace->dobj.name,
14222 agginfo->aggfn.rolname,
14223 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14225 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14226 dumpSecLabel(fout, "AGGREGATE", aggsig,
14227 agginfo->aggfn.dobj.namespace->dobj.name,
14228 agginfo->aggfn.rolname,
14229 agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14232 * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14233 * command look like a function's GRANT; in particular this affects the
14234 * syntax for zero-argument aggregates and ordered-set aggregates.
14236 free(aggsig);
14238 aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14240 if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14241 dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
14242 "FUNCTION", aggsig, NULL,
14243 agginfo->aggfn.dobj.namespace->dobj.name,
14244 agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
14246 free(aggsig);
14247 free(aggfullsig);
14248 free(aggsig_tag);
14250 PQclear(res);
14252 destroyPQExpBuffer(query);
14253 destroyPQExpBuffer(q);
14254 destroyPQExpBuffer(delq);
14255 destroyPQExpBuffer(details);
14259 * dumpTSParser
14260 * write out a single text search parser
14262 static void
14263 dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
14265 DumpOptions *dopt = fout->dopt;
14266 PQExpBuffer q;
14267 PQExpBuffer delq;
14268 char *qprsname;
14270 /* Do nothing in data-only dump */
14271 if (dopt->dataOnly)
14272 return;
14274 q = createPQExpBuffer();
14275 delq = createPQExpBuffer();
14277 qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14279 appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14280 fmtQualifiedDumpable(prsinfo));
14282 appendPQExpBuffer(q, " START = %s,\n",
14283 convertTSFunction(fout, prsinfo->prsstart));
14284 appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14285 convertTSFunction(fout, prsinfo->prstoken));
14286 appendPQExpBuffer(q, " END = %s,\n",
14287 convertTSFunction(fout, prsinfo->prsend));
14288 if (prsinfo->prsheadline != InvalidOid)
14289 appendPQExpBuffer(q, " HEADLINE = %s,\n",
14290 convertTSFunction(fout, prsinfo->prsheadline));
14291 appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14292 convertTSFunction(fout, prsinfo->prslextype));
14294 appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14295 fmtQualifiedDumpable(prsinfo));
14297 if (dopt->binary_upgrade)
14298 binary_upgrade_extension_member(q, &prsinfo->dobj,
14299 "TEXT SEARCH PARSER", qprsname,
14300 prsinfo->dobj.namespace->dobj.name);
14302 if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14303 ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
14304 ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
14305 .namespace = prsinfo->dobj.namespace->dobj.name,
14306 .description = "TEXT SEARCH PARSER",
14307 .section = SECTION_PRE_DATA,
14308 .createStmt = q->data,
14309 .dropStmt = delq->data));
14311 /* Dump Parser Comments */
14312 if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14313 dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
14314 prsinfo->dobj.namespace->dobj.name, "",
14315 prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
14317 destroyPQExpBuffer(q);
14318 destroyPQExpBuffer(delq);
14319 free(qprsname);
14323 * dumpTSDictionary
14324 * write out a single text search dictionary
14326 static void
14327 dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
14329 DumpOptions *dopt = fout->dopt;
14330 PQExpBuffer q;
14331 PQExpBuffer delq;
14332 PQExpBuffer query;
14333 char *qdictname;
14334 PGresult *res;
14335 char *nspname;
14336 char *tmplname;
14338 /* Do nothing in data-only dump */
14339 if (dopt->dataOnly)
14340 return;
14342 q = createPQExpBuffer();
14343 delq = createPQExpBuffer();
14344 query = createPQExpBuffer();
14346 qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
14348 /* Fetch name and namespace of the dictionary's template */
14349 appendPQExpBuffer(query, "SELECT nspname, tmplname "
14350 "FROM pg_ts_template p, pg_namespace n "
14351 "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
14352 dictinfo->dicttemplate);
14353 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14354 nspname = PQgetvalue(res, 0, 0);
14355 tmplname = PQgetvalue(res, 0, 1);
14357 appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
14358 fmtQualifiedDumpable(dictinfo));
14360 appendPQExpBufferStr(q, " TEMPLATE = ");
14361 appendPQExpBuffer(q, "%s.", fmtId(nspname));
14362 appendPQExpBufferStr(q, fmtId(tmplname));
14364 PQclear(res);
14366 /* the dictinitoption can be dumped straight into the command */
14367 if (dictinfo->dictinitoption)
14368 appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
14370 appendPQExpBufferStr(q, " );\n");
14372 appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
14373 fmtQualifiedDumpable(dictinfo));
14375 if (dopt->binary_upgrade)
14376 binary_upgrade_extension_member(q, &dictinfo->dobj,
14377 "TEXT SEARCH DICTIONARY", qdictname,
14378 dictinfo->dobj.namespace->dobj.name);
14380 if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14381 ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
14382 ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
14383 .namespace = dictinfo->dobj.namespace->dobj.name,
14384 .owner = dictinfo->rolname,
14385 .description = "TEXT SEARCH DICTIONARY",
14386 .section = SECTION_PRE_DATA,
14387 .createStmt = q->data,
14388 .dropStmt = delq->data));
14390 /* Dump Dictionary Comments */
14391 if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14392 dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
14393 dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
14394 dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
14396 destroyPQExpBuffer(q);
14397 destroyPQExpBuffer(delq);
14398 destroyPQExpBuffer(query);
14399 free(qdictname);
14403 * dumpTSTemplate
14404 * write out a single text search template
14406 static void
14407 dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
14409 DumpOptions *dopt = fout->dopt;
14410 PQExpBuffer q;
14411 PQExpBuffer delq;
14412 char *qtmplname;
14414 /* Do nothing in data-only dump */
14415 if (dopt->dataOnly)
14416 return;
14418 q = createPQExpBuffer();
14419 delq = createPQExpBuffer();
14421 qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
14423 appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
14424 fmtQualifiedDumpable(tmplinfo));
14426 if (tmplinfo->tmplinit != InvalidOid)
14427 appendPQExpBuffer(q, " INIT = %s,\n",
14428 convertTSFunction(fout, tmplinfo->tmplinit));
14429 appendPQExpBuffer(q, " LEXIZE = %s );\n",
14430 convertTSFunction(fout, tmplinfo->tmpllexize));
14432 appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
14433 fmtQualifiedDumpable(tmplinfo));
14435 if (dopt->binary_upgrade)
14436 binary_upgrade_extension_member(q, &tmplinfo->dobj,
14437 "TEXT SEARCH TEMPLATE", qtmplname,
14438 tmplinfo->dobj.namespace->dobj.name);
14440 if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14441 ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
14442 ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
14443 .namespace = tmplinfo->dobj.namespace->dobj.name,
14444 .description = "TEXT SEARCH TEMPLATE",
14445 .section = SECTION_PRE_DATA,
14446 .createStmt = q->data,
14447 .dropStmt = delq->data));
14449 /* Dump Template Comments */
14450 if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14451 dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
14452 tmplinfo->dobj.namespace->dobj.name, "",
14453 tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
14455 destroyPQExpBuffer(q);
14456 destroyPQExpBuffer(delq);
14457 free(qtmplname);
14461 * dumpTSConfig
14462 * write out a single text search configuration
14464 static void
14465 dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
14467 DumpOptions *dopt = fout->dopt;
14468 PQExpBuffer q;
14469 PQExpBuffer delq;
14470 PQExpBuffer query;
14471 char *qcfgname;
14472 PGresult *res;
14473 char *nspname;
14474 char *prsname;
14475 int ntups,
14477 int i_tokenname;
14478 int i_dictname;
14480 /* Do nothing in data-only dump */
14481 if (dopt->dataOnly)
14482 return;
14484 q = createPQExpBuffer();
14485 delq = createPQExpBuffer();
14486 query = createPQExpBuffer();
14488 qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
14490 /* Fetch name and namespace of the config's parser */
14491 appendPQExpBuffer(query, "SELECT nspname, prsname "
14492 "FROM pg_ts_parser p, pg_namespace n "
14493 "WHERE p.oid = '%u' AND n.oid = prsnamespace",
14494 cfginfo->cfgparser);
14495 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14496 nspname = PQgetvalue(res, 0, 0);
14497 prsname = PQgetvalue(res, 0, 1);
14499 appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
14500 fmtQualifiedDumpable(cfginfo));
14502 appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
14503 appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
14505 PQclear(res);
14507 resetPQExpBuffer(query);
14508 appendPQExpBuffer(query,
14509 "SELECT\n"
14510 " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
14511 " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
14512 " m.mapdict::pg_catalog.regdictionary AS dictname\n"
14513 "FROM pg_catalog.pg_ts_config_map AS m\n"
14514 "WHERE m.mapcfg = '%u'\n"
14515 "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
14516 cfginfo->cfgparser, cfginfo->dobj.catId.oid);
14518 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14519 ntups = PQntuples(res);
14521 i_tokenname = PQfnumber(res, "tokenname");
14522 i_dictname = PQfnumber(res, "dictname");
14524 for (i = 0; i < ntups; i++)
14526 char *tokenname = PQgetvalue(res, i, i_tokenname);
14527 char *dictname = PQgetvalue(res, i, i_dictname);
14529 if (i == 0 ||
14530 strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
14532 /* starting a new token type, so start a new command */
14533 if (i > 0)
14534 appendPQExpBufferStr(q, ";\n");
14535 appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
14536 fmtQualifiedDumpable(cfginfo));
14537 /* tokenname needs quoting, dictname does NOT */
14538 appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
14539 fmtId(tokenname), dictname);
14541 else
14542 appendPQExpBuffer(q, ", %s", dictname);
14545 if (ntups > 0)
14546 appendPQExpBufferStr(q, ";\n");
14548 PQclear(res);
14550 appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
14551 fmtQualifiedDumpable(cfginfo));
14553 if (dopt->binary_upgrade)
14554 binary_upgrade_extension_member(q, &cfginfo->dobj,
14555 "TEXT SEARCH CONFIGURATION", qcfgname,
14556 cfginfo->dobj.namespace->dobj.name);
14558 if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14559 ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
14560 ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
14561 .namespace = cfginfo->dobj.namespace->dobj.name,
14562 .owner = cfginfo->rolname,
14563 .description = "TEXT SEARCH CONFIGURATION",
14564 .section = SECTION_PRE_DATA,
14565 .createStmt = q->data,
14566 .dropStmt = delq->data));
14568 /* Dump Configuration Comments */
14569 if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14570 dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
14571 cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
14572 cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
14574 destroyPQExpBuffer(q);
14575 destroyPQExpBuffer(delq);
14576 destroyPQExpBuffer(query);
14577 free(qcfgname);
14581 * dumpForeignDataWrapper
14582 * write out a single foreign-data wrapper definition
14584 static void
14585 dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
14587 DumpOptions *dopt = fout->dopt;
14588 PQExpBuffer q;
14589 PQExpBuffer delq;
14590 char *qfdwname;
14592 /* Do nothing in data-only dump */
14593 if (dopt->dataOnly)
14594 return;
14596 q = createPQExpBuffer();
14597 delq = createPQExpBuffer();
14599 qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
14601 appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
14602 qfdwname);
14604 if (strcmp(fdwinfo->fdwhandler, "-") != 0)
14605 appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
14607 if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
14608 appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
14610 if (strlen(fdwinfo->fdwoptions) > 0)
14611 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
14613 appendPQExpBufferStr(q, ";\n");
14615 appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
14616 qfdwname);
14618 if (dopt->binary_upgrade)
14619 binary_upgrade_extension_member(q, &fdwinfo->dobj,
14620 "FOREIGN DATA WRAPPER", qfdwname,
14621 NULL);
14623 if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14624 ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14625 ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
14626 .owner = fdwinfo->rolname,
14627 .description = "FOREIGN DATA WRAPPER",
14628 .section = SECTION_PRE_DATA,
14629 .createStmt = q->data,
14630 .dropStmt = delq->data));
14632 /* Dump Foreign Data Wrapper Comments */
14633 if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14634 dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
14635 NULL, fdwinfo->rolname,
14636 fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14638 /* Handle the ACL */
14639 if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14640 dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
14641 "FOREIGN DATA WRAPPER", qfdwname, NULL,
14642 NULL, fdwinfo->rolname, &fdwinfo->dacl);
14644 free(qfdwname);
14646 destroyPQExpBuffer(q);
14647 destroyPQExpBuffer(delq);
14651 * dumpForeignServer
14652 * write out a foreign server definition
14654 static void
14655 dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
14657 DumpOptions *dopt = fout->dopt;
14658 PQExpBuffer q;
14659 PQExpBuffer delq;
14660 PQExpBuffer query;
14661 PGresult *res;
14662 char *qsrvname;
14663 char *fdwname;
14665 /* Do nothing in data-only dump */
14666 if (dopt->dataOnly)
14667 return;
14669 q = createPQExpBuffer();
14670 delq = createPQExpBuffer();
14671 query = createPQExpBuffer();
14673 qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
14675 /* look up the foreign-data wrapper */
14676 appendPQExpBuffer(query, "SELECT fdwname "
14677 "FROM pg_foreign_data_wrapper w "
14678 "WHERE w.oid = '%u'",
14679 srvinfo->srvfdw);
14680 res = ExecuteSqlQueryForSingleRow(fout, query->data);
14681 fdwname = PQgetvalue(res, 0, 0);
14683 appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
14684 if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
14686 appendPQExpBufferStr(q, " TYPE ");
14687 appendStringLiteralAH(q, srvinfo->srvtype, fout);
14689 if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
14691 appendPQExpBufferStr(q, " VERSION ");
14692 appendStringLiteralAH(q, srvinfo->srvversion, fout);
14695 appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
14696 appendPQExpBufferStr(q, fmtId(fdwname));
14698 if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
14699 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
14701 appendPQExpBufferStr(q, ";\n");
14703 appendPQExpBuffer(delq, "DROP SERVER %s;\n",
14704 qsrvname);
14706 if (dopt->binary_upgrade)
14707 binary_upgrade_extension_member(q, &srvinfo->dobj,
14708 "SERVER", qsrvname, NULL);
14710 if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14711 ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
14712 ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
14713 .owner = srvinfo->rolname,
14714 .description = "SERVER",
14715 .section = SECTION_PRE_DATA,
14716 .createStmt = q->data,
14717 .dropStmt = delq->data));
14719 /* Dump Foreign Server Comments */
14720 if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14721 dumpComment(fout, "SERVER", qsrvname,
14722 NULL, srvinfo->rolname,
14723 srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
14725 /* Handle the ACL */
14726 if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
14727 dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
14728 "FOREIGN SERVER", qsrvname, NULL,
14729 NULL, srvinfo->rolname, &srvinfo->dacl);
14731 /* Dump user mappings */
14732 if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
14733 dumpUserMappings(fout,
14734 srvinfo->dobj.name, NULL,
14735 srvinfo->rolname,
14736 srvinfo->dobj.catId, srvinfo->dobj.dumpId);
14738 PQclear(res);
14740 free(qsrvname);
14742 destroyPQExpBuffer(q);
14743 destroyPQExpBuffer(delq);
14744 destroyPQExpBuffer(query);
14748 * dumpUserMappings
14750 * This routine is used to dump any user mappings associated with the
14751 * server handed to this routine. Should be called after ArchiveEntry()
14752 * for the server.
14754 static void
14755 dumpUserMappings(Archive *fout,
14756 const char *servername, const char *namespace,
14757 const char *owner,
14758 CatalogId catalogId, DumpId dumpId)
14760 PQExpBuffer q;
14761 PQExpBuffer delq;
14762 PQExpBuffer query;
14763 PQExpBuffer tag;
14764 PGresult *res;
14765 int ntups;
14766 int i_usename;
14767 int i_umoptions;
14768 int i;
14770 q = createPQExpBuffer();
14771 tag = createPQExpBuffer();
14772 delq = createPQExpBuffer();
14773 query = createPQExpBuffer();
14776 * We read from the publicly accessible view pg_user_mappings, so as not
14777 * to fail if run by a non-superuser. Note that the view will show
14778 * umoptions as null if the user hasn't got privileges for the associated
14779 * server; this means that pg_dump will dump such a mapping, but with no
14780 * OPTIONS clause. A possible alternative is to skip such mappings
14781 * altogether, but it's not clear that that's an improvement.
14783 appendPQExpBuffer(query,
14784 "SELECT usename, "
14785 "array_to_string(ARRAY("
14786 "SELECT quote_ident(option_name) || ' ' || "
14787 "quote_literal(option_value) "
14788 "FROM pg_options_to_table(umoptions) "
14789 "ORDER BY option_name"
14790 "), E',\n ') AS umoptions "
14791 "FROM pg_user_mappings "
14792 "WHERE srvid = '%u' "
14793 "ORDER BY usename",
14794 catalogId.oid);
14796 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14798 ntups = PQntuples(res);
14799 i_usename = PQfnumber(res, "usename");
14800 i_umoptions = PQfnumber(res, "umoptions");
14802 for (i = 0; i < ntups; i++)
14804 char *usename;
14805 char *umoptions;
14807 usename = PQgetvalue(res, i, i_usename);
14808 umoptions = PQgetvalue(res, i, i_umoptions);
14810 resetPQExpBuffer(q);
14811 appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
14812 appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
14814 if (umoptions && strlen(umoptions) > 0)
14815 appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
14817 appendPQExpBufferStr(q, ";\n");
14819 resetPQExpBuffer(delq);
14820 appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
14821 appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
14823 resetPQExpBuffer(tag);
14824 appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
14825 usename, servername);
14827 ArchiveEntry(fout, nilCatalogId, createDumpId(),
14828 ARCHIVE_OPTS(.tag = tag->data,
14829 .namespace = namespace,
14830 .owner = owner,
14831 .description = "USER MAPPING",
14832 .section = SECTION_PRE_DATA,
14833 .createStmt = q->data,
14834 .dropStmt = delq->data));
14837 PQclear(res);
14839 destroyPQExpBuffer(query);
14840 destroyPQExpBuffer(delq);
14841 destroyPQExpBuffer(tag);
14842 destroyPQExpBuffer(q);
14846 * Write out default privileges information
14848 static void
14849 dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
14851 DumpOptions *dopt = fout->dopt;
14852 PQExpBuffer q;
14853 PQExpBuffer tag;
14854 const char *type;
14856 /* Do nothing in data-only dump, or if we're skipping ACLs */
14857 if (dopt->dataOnly || dopt->aclsSkip)
14858 return;
14860 q = createPQExpBuffer();
14861 tag = createPQExpBuffer();
14863 switch (daclinfo->defaclobjtype)
14865 case DEFACLOBJ_RELATION:
14866 type = "TABLES";
14867 break;
14868 case DEFACLOBJ_SEQUENCE:
14869 type = "SEQUENCES";
14870 break;
14871 case DEFACLOBJ_FUNCTION:
14872 type = "FUNCTIONS";
14873 break;
14874 case DEFACLOBJ_TYPE:
14875 type = "TYPES";
14876 break;
14877 case DEFACLOBJ_NAMESPACE:
14878 type = "SCHEMAS";
14879 break;
14880 default:
14881 /* shouldn't get here */
14882 pg_fatal("unrecognized object type in default privileges: %d",
14883 (int) daclinfo->defaclobjtype);
14884 type = ""; /* keep compiler quiet */
14887 appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
14889 /* build the actual command(s) for this tuple */
14890 if (!buildDefaultACLCommands(type,
14891 daclinfo->dobj.namespace != NULL ?
14892 daclinfo->dobj.namespace->dobj.name : NULL,
14893 daclinfo->dacl.acl,
14894 daclinfo->dacl.acldefault,
14895 daclinfo->defaclrole,
14896 fout->remoteVersion,
14898 pg_fatal("could not parse default ACL list (%s)",
14899 daclinfo->dacl.acl);
14901 if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
14902 ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
14903 ARCHIVE_OPTS(.tag = tag->data,
14904 .namespace = daclinfo->dobj.namespace ?
14905 daclinfo->dobj.namespace->dobj.name : NULL,
14906 .owner = daclinfo->defaclrole,
14907 .description = "DEFAULT ACL",
14908 .section = SECTION_POST_DATA,
14909 .createStmt = q->data));
14911 destroyPQExpBuffer(tag);
14912 destroyPQExpBuffer(q);
14915 /*----------
14916 * Write out grant/revoke information
14918 * 'objDumpId' is the dump ID of the underlying object.
14919 * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
14920 * or InvalidDumpId if there is no need for a second dependency.
14921 * 'type' must be one of
14922 * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
14923 * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
14924 * 'name' is the formatted name of the object. Must be quoted etc. already.
14925 * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
14926 * (Currently we assume that subname is only provided for table columns.)
14927 * 'nspname' is the namespace the object is in (NULL if none).
14928 * 'owner' is the owner, NULL if there is no owner (for languages).
14929 * 'dacl' is the DumpableAcl struct fpr the object.
14931 * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
14932 * no ACL entry was created.
14933 *----------
14935 static DumpId
14936 dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
14937 const char *type, const char *name, const char *subname,
14938 const char *nspname, const char *owner,
14939 const DumpableAcl *dacl)
14941 DumpId aclDumpId = InvalidDumpId;
14942 DumpOptions *dopt = fout->dopt;
14943 const char *acls = dacl->acl;
14944 const char *acldefault = dacl->acldefault;
14945 char privtype = dacl->privtype;
14946 const char *initprivs = dacl->initprivs;
14947 const char *baseacls;
14948 PQExpBuffer sql;
14950 /* Do nothing if ACL dump is not enabled */
14951 if (dopt->aclsSkip)
14952 return InvalidDumpId;
14954 /* --data-only skips ACLs *except* large object ACLs */
14955 if (dopt->dataOnly && strcmp(type, "LARGE OBJECT") != 0)
14956 return InvalidDumpId;
14958 sql = createPQExpBuffer();
14961 * In binary upgrade mode, we don't run an extension's script but instead
14962 * dump out the objects independently and then recreate them. To preserve
14963 * any initial privileges which were set on extension objects, we need to
14964 * compute the set of GRANT and REVOKE commands necessary to get from the
14965 * default privileges of an object to its initial privileges as recorded
14966 * in pg_init_privs.
14968 * At restore time, we apply these commands after having called
14969 * binary_upgrade_set_record_init_privs(true). That tells the backend to
14970 * copy the results into pg_init_privs. This is how we preserve the
14971 * contents of that catalog across binary upgrades.
14973 if (dopt->binary_upgrade && privtype == 'e' &&
14974 initprivs && *initprivs != '\0')
14976 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
14977 if (!buildACLCommands(name, subname, nspname, type,
14978 initprivs, acldefault, owner,
14979 "", fout->remoteVersion, sql))
14980 pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
14981 initprivs, acldefault, name, type);
14982 appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
14986 * Now figure the GRANT and REVOKE commands needed to get to the object's
14987 * actual current ACL, starting from the initprivs if given, else from the
14988 * object-type-specific default. Also, while buildACLCommands will assume
14989 * that a NULL/empty acls string means it needn't do anything, what that
14990 * actually represents is the object-type-specific default; so we need to
14991 * substitute the acldefault string to get the right results in that case.
14993 if (initprivs && *initprivs != '\0')
14995 baseacls = initprivs;
14996 if (acls == NULL || *acls == '\0')
14997 acls = acldefault;
14999 else
15000 baseacls = acldefault;
15002 if (!buildACLCommands(name, subname, nspname, type,
15003 acls, baseacls, owner,
15004 "", fout->remoteVersion, sql))
15005 pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15006 acls, baseacls, name, type);
15008 if (sql->len > 0)
15010 PQExpBuffer tag = createPQExpBuffer();
15011 DumpId aclDeps[2];
15012 int nDeps = 0;
15014 if (subname)
15015 appendPQExpBuffer(tag, "COLUMN %s.%s", name, subname);
15016 else
15017 appendPQExpBuffer(tag, "%s %s", type, name);
15019 aclDeps[nDeps++] = objDumpId;
15020 if (altDumpId != InvalidDumpId)
15021 aclDeps[nDeps++] = altDumpId;
15023 aclDumpId = createDumpId();
15025 ArchiveEntry(fout, nilCatalogId, aclDumpId,
15026 ARCHIVE_OPTS(.tag = tag->data,
15027 .namespace = nspname,
15028 .owner = owner,
15029 .description = "ACL",
15030 .section = SECTION_NONE,
15031 .createStmt = sql->data,
15032 .deps = aclDeps,
15033 .nDeps = nDeps));
15035 destroyPQExpBuffer(tag);
15038 destroyPQExpBuffer(sql);
15040 return aclDumpId;
15044 * dumpSecLabel
15046 * This routine is used to dump any security labels associated with the
15047 * object handed to this routine. The routine takes the object type
15048 * and object name (ready to print, except for schema decoration), plus
15049 * the namespace and owner of the object (for labeling the ArchiveEntry),
15050 * plus catalog ID and subid which are the lookup key for pg_seclabel,
15051 * plus the dump ID for the object (for setting a dependency).
15052 * If a matching pg_seclabel entry is found, it is dumped.
15054 * Note: although this routine takes a dumpId for dependency purposes,
15055 * that purpose is just to mark the dependency in the emitted dump file
15056 * for possible future use by pg_restore. We do NOT use it for determining
15057 * ordering of the label in the dump file, because this routine is called
15058 * after dependency sorting occurs. This routine should be called just after
15059 * calling ArchiveEntry() for the specified object.
15061 static void
15062 dumpSecLabel(Archive *fout, const char *type, const char *name,
15063 const char *namespace, const char *owner,
15064 CatalogId catalogId, int subid, DumpId dumpId)
15066 DumpOptions *dopt = fout->dopt;
15067 SecLabelItem *labels;
15068 int nlabels;
15069 int i;
15070 PQExpBuffer query;
15072 /* do nothing, if --no-security-labels is supplied */
15073 if (dopt->no_security_labels)
15074 return;
15077 * Security labels are schema not data ... except large object labels are
15078 * data
15080 if (strcmp(type, "LARGE OBJECT") != 0)
15082 if (dopt->dataOnly)
15083 return;
15085 else
15087 /* We do dump large object security labels in binary-upgrade mode */
15088 if (dopt->schemaOnly && !dopt->binary_upgrade)
15089 return;
15092 /* Search for security labels associated with catalogId, using table */
15093 nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15095 query = createPQExpBuffer();
15097 for (i = 0; i < nlabels; i++)
15100 * Ignore label entries for which the subid doesn't match.
15102 if (labels[i].objsubid != subid)
15103 continue;
15105 appendPQExpBuffer(query,
15106 "SECURITY LABEL FOR %s ON %s ",
15107 fmtId(labels[i].provider), type);
15108 if (namespace && *namespace)
15109 appendPQExpBuffer(query, "%s.", fmtId(namespace));
15110 appendPQExpBuffer(query, "%s IS ", name);
15111 appendStringLiteralAH(query, labels[i].label, fout);
15112 appendPQExpBufferStr(query, ";\n");
15115 if (query->len > 0)
15117 PQExpBuffer tag = createPQExpBuffer();
15119 appendPQExpBuffer(tag, "%s %s", type, name);
15120 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15121 ARCHIVE_OPTS(.tag = tag->data,
15122 .namespace = namespace,
15123 .owner = owner,
15124 .description = "SECURITY LABEL",
15125 .section = SECTION_NONE,
15126 .createStmt = query->data,
15127 .deps = &dumpId,
15128 .nDeps = 1));
15129 destroyPQExpBuffer(tag);
15132 destroyPQExpBuffer(query);
15136 * dumpTableSecLabel
15138 * As above, but dump security label for both the specified table (or view)
15139 * and its columns.
15141 static void
15142 dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15144 DumpOptions *dopt = fout->dopt;
15145 SecLabelItem *labels;
15146 int nlabels;
15147 int i;
15148 PQExpBuffer query;
15149 PQExpBuffer target;
15151 /* do nothing, if --no-security-labels is supplied */
15152 if (dopt->no_security_labels)
15153 return;
15155 /* SecLabel are SCHEMA not data */
15156 if (dopt->dataOnly)
15157 return;
15159 /* Search for comments associated with relation, using table */
15160 nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15161 tbinfo->dobj.catId.oid,
15162 &labels);
15164 /* If security labels exist, build SECURITY LABEL statements */
15165 if (nlabels <= 0)
15166 return;
15168 query = createPQExpBuffer();
15169 target = createPQExpBuffer();
15171 for (i = 0; i < nlabels; i++)
15173 const char *colname;
15174 const char *provider = labels[i].provider;
15175 const char *label = labels[i].label;
15176 int objsubid = labels[i].objsubid;
15178 resetPQExpBuffer(target);
15179 if (objsubid == 0)
15181 appendPQExpBuffer(target, "%s %s", reltypename,
15182 fmtQualifiedDumpable(tbinfo));
15184 else
15186 colname = getAttrName(objsubid, tbinfo);
15187 /* first fmtXXX result must be consumed before calling again */
15188 appendPQExpBuffer(target, "COLUMN %s",
15189 fmtQualifiedDumpable(tbinfo));
15190 appendPQExpBuffer(target, ".%s", fmtId(colname));
15192 appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15193 fmtId(provider), target->data);
15194 appendStringLiteralAH(query, label, fout);
15195 appendPQExpBufferStr(query, ";\n");
15197 if (query->len > 0)
15199 resetPQExpBuffer(target);
15200 appendPQExpBuffer(target, "%s %s", reltypename,
15201 fmtId(tbinfo->dobj.name));
15202 ArchiveEntry(fout, nilCatalogId, createDumpId(),
15203 ARCHIVE_OPTS(.tag = target->data,
15204 .namespace = tbinfo->dobj.namespace->dobj.name,
15205 .owner = tbinfo->rolname,
15206 .description = "SECURITY LABEL",
15207 .section = SECTION_NONE,
15208 .createStmt = query->data,
15209 .deps = &(tbinfo->dobj.dumpId),
15210 .nDeps = 1));
15212 destroyPQExpBuffer(query);
15213 destroyPQExpBuffer(target);
15217 * findSecLabels
15219 * Find the security label(s), if any, associated with the given object.
15220 * All the objsubid values associated with the given classoid/objoid are
15221 * found with one search.
15223 static int
15224 findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
15226 SecLabelItem *middle = NULL;
15227 SecLabelItem *low;
15228 SecLabelItem *high;
15229 int nmatch;
15231 if (nseclabels <= 0) /* no labels, so no match is possible */
15233 *items = NULL;
15234 return 0;
15238 * Do binary search to find some item matching the object.
15240 low = &seclabels[0];
15241 high = &seclabels[nseclabels - 1];
15242 while (low <= high)
15244 middle = low + (high - low) / 2;
15246 if (classoid < middle->classoid)
15247 high = middle - 1;
15248 else if (classoid > middle->classoid)
15249 low = middle + 1;
15250 else if (objoid < middle->objoid)
15251 high = middle - 1;
15252 else if (objoid > middle->objoid)
15253 low = middle + 1;
15254 else
15255 break; /* found a match */
15258 if (low > high) /* no matches */
15260 *items = NULL;
15261 return 0;
15265 * Now determine how many items match the object. The search loop
15266 * invariant still holds: only items between low and high inclusive could
15267 * match.
15269 nmatch = 1;
15270 while (middle > low)
15272 if (classoid != middle[-1].classoid ||
15273 objoid != middle[-1].objoid)
15274 break;
15275 middle--;
15276 nmatch++;
15279 *items = middle;
15281 middle += nmatch;
15282 while (middle <= high)
15284 if (classoid != middle->classoid ||
15285 objoid != middle->objoid)
15286 break;
15287 middle++;
15288 nmatch++;
15291 return nmatch;
15295 * collectSecLabels
15297 * Construct a table of all security labels available for database objects;
15298 * also set the has-seclabel component flag for each relevant object.
15300 * The table is sorted by classoid/objid/objsubid for speed in lookup.
15302 static void
15303 collectSecLabels(Archive *fout)
15305 PGresult *res;
15306 PQExpBuffer query;
15307 int i_label;
15308 int i_provider;
15309 int i_classoid;
15310 int i_objoid;
15311 int i_objsubid;
15312 int ntups;
15313 int i;
15314 DumpableObject *dobj;
15316 query = createPQExpBuffer();
15318 appendPQExpBufferStr(query,
15319 "SELECT label, provider, classoid, objoid, objsubid "
15320 "FROM pg_catalog.pg_seclabel "
15321 "ORDER BY classoid, objoid, objsubid");
15323 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15325 /* Construct lookup table containing OIDs in numeric form */
15326 i_label = PQfnumber(res, "label");
15327 i_provider = PQfnumber(res, "provider");
15328 i_classoid = PQfnumber(res, "classoid");
15329 i_objoid = PQfnumber(res, "objoid");
15330 i_objsubid = PQfnumber(res, "objsubid");
15332 ntups = PQntuples(res);
15334 seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
15335 nseclabels = 0;
15336 dobj = NULL;
15338 for (i = 0; i < ntups; i++)
15340 CatalogId objId;
15341 int subid;
15343 objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
15344 objId.oid = atooid(PQgetvalue(res, i, i_objoid));
15345 subid = atoi(PQgetvalue(res, i, i_objsubid));
15347 /* We needn't remember labels that don't match any dumpable object */
15348 if (dobj == NULL ||
15349 dobj->catId.tableoid != objId.tableoid ||
15350 dobj->catId.oid != objId.oid)
15351 dobj = findObjectByCatalogId(objId);
15352 if (dobj == NULL)
15353 continue;
15356 * Labels on columns of composite types are linked to the type's
15357 * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
15358 * in the type's own DumpableObject.
15360 if (subid != 0 && dobj->objType == DO_TABLE &&
15361 ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
15363 TypeInfo *cTypeInfo;
15365 cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
15366 if (cTypeInfo)
15367 cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
15369 else
15370 dobj->components |= DUMP_COMPONENT_SECLABEL;
15372 seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
15373 seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
15374 seclabels[nseclabels].classoid = objId.tableoid;
15375 seclabels[nseclabels].objoid = objId.oid;
15376 seclabels[nseclabels].objsubid = subid;
15377 nseclabels++;
15380 PQclear(res);
15381 destroyPQExpBuffer(query);
15385 * dumpTable
15386 * write out to fout the declarations (not data) of a user-defined table
15388 static void
15389 dumpTable(Archive *fout, const TableInfo *tbinfo)
15391 DumpOptions *dopt = fout->dopt;
15392 DumpId tableAclDumpId = InvalidDumpId;
15393 char *namecopy;
15395 /* Do nothing in data-only dump */
15396 if (dopt->dataOnly)
15397 return;
15399 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15401 if (tbinfo->relkind == RELKIND_SEQUENCE)
15402 dumpSequence(fout, tbinfo);
15403 else
15404 dumpTableSchema(fout, tbinfo);
15407 /* Handle the ACL here */
15408 namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
15409 if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15411 const char *objtype =
15412 (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
15414 tableAclDumpId =
15415 dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
15416 objtype, namecopy, NULL,
15417 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15418 &tbinfo->dacl);
15422 * Handle column ACLs, if any. Note: we pull these with a separate query
15423 * rather than trying to fetch them during getTableAttrs, so that we won't
15424 * miss ACLs on system columns. Doing it this way also allows us to dump
15425 * ACLs for catalogs that we didn't mark "interesting" back in getTables.
15427 if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
15429 PQExpBuffer query = createPQExpBuffer();
15430 PGresult *res;
15431 int i;
15433 if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
15435 /* Set up query for column ACLs */
15436 appendPQExpBufferStr(query,
15437 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
15439 if (fout->remoteVersion >= 90600)
15442 * In principle we should call acldefault('c', relowner) to
15443 * get the default ACL for a column. However, we don't
15444 * currently store the numeric OID of the relowner in
15445 * TableInfo. We could convert the owner name using regrole,
15446 * but that creates a risk of failure due to concurrent role
15447 * renames. Given that the default ACL for columns is empty
15448 * and is likely to stay that way, it's not worth extra cycles
15449 * and risk to avoid hard-wiring that knowledge here.
15451 appendPQExpBufferStr(query,
15452 "SELECT at.attname, "
15453 "at.attacl, "
15454 "'{}' AS acldefault, "
15455 "pip.privtype, pip.initprivs "
15456 "FROM pg_catalog.pg_attribute at "
15457 "LEFT JOIN pg_catalog.pg_init_privs pip ON "
15458 "(at.attrelid = pip.objoid "
15459 "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
15460 "AND at.attnum = pip.objsubid) "
15461 "WHERE at.attrelid = $1 AND "
15462 "NOT at.attisdropped "
15463 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
15464 "ORDER BY at.attnum");
15466 else
15468 appendPQExpBufferStr(query,
15469 "SELECT attname, attacl, '{}' AS acldefault, "
15470 "NULL AS privtype, NULL AS initprivs "
15471 "FROM pg_catalog.pg_attribute "
15472 "WHERE attrelid = $1 AND NOT attisdropped "
15473 "AND attacl IS NOT NULL "
15474 "ORDER BY attnum");
15477 ExecuteSqlStatement(fout, query->data);
15479 fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
15482 printfPQExpBuffer(query,
15483 "EXECUTE getColumnACLs('%u')",
15484 tbinfo->dobj.catId.oid);
15486 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15488 for (i = 0; i < PQntuples(res); i++)
15490 char *attname = PQgetvalue(res, i, 0);
15491 char *attacl = PQgetvalue(res, i, 1);
15492 char *acldefault = PQgetvalue(res, i, 2);
15493 char privtype = *(PQgetvalue(res, i, 3));
15494 char *initprivs = PQgetvalue(res, i, 4);
15495 DumpableAcl coldacl;
15496 char *attnamecopy;
15498 coldacl.acl = attacl;
15499 coldacl.acldefault = acldefault;
15500 coldacl.privtype = privtype;
15501 coldacl.initprivs = initprivs;
15502 attnamecopy = pg_strdup(fmtId(attname));
15505 * Column's GRANT type is always TABLE. Each column ACL depends
15506 * on the table-level ACL, since we can restore column ACLs in
15507 * parallel but the table-level ACL has to be done first.
15509 dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
15510 "TABLE", namecopy, attnamecopy,
15511 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15512 &coldacl);
15513 free(attnamecopy);
15515 PQclear(res);
15516 destroyPQExpBuffer(query);
15519 free(namecopy);
15523 * Create the AS clause for a view or materialized view. The semicolon is
15524 * stripped because a materialized view must add a WITH NO DATA clause.
15526 * This returns a new buffer which must be freed by the caller.
15528 static PQExpBuffer
15529 createViewAsClause(Archive *fout, const TableInfo *tbinfo)
15531 PQExpBuffer query = createPQExpBuffer();
15532 PQExpBuffer result = createPQExpBuffer();
15533 PGresult *res;
15534 int len;
15536 /* Fetch the view definition */
15537 appendPQExpBuffer(query,
15538 "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
15539 tbinfo->dobj.catId.oid);
15541 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15543 if (PQntuples(res) != 1)
15545 if (PQntuples(res) < 1)
15546 pg_fatal("query to obtain definition of view \"%s\" returned no data",
15547 tbinfo->dobj.name);
15548 else
15549 pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
15550 tbinfo->dobj.name);
15553 len = PQgetlength(res, 0, 0);
15555 if (len == 0)
15556 pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
15557 tbinfo->dobj.name);
15559 /* Strip off the trailing semicolon so that other things may follow. */
15560 Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
15561 appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
15563 PQclear(res);
15564 destroyPQExpBuffer(query);
15566 return result;
15570 * Create a dummy AS clause for a view. This is used when the real view
15571 * definition has to be postponed because of circular dependencies.
15572 * We must duplicate the view's external properties -- column names and types
15573 * (including collation) -- so that it works for subsequent references.
15575 * This returns a new buffer which must be freed by the caller.
15577 static PQExpBuffer
15578 createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
15580 PQExpBuffer result = createPQExpBuffer();
15581 int j;
15583 appendPQExpBufferStr(result, "SELECT");
15585 for (j = 0; j < tbinfo->numatts; j++)
15587 if (j > 0)
15588 appendPQExpBufferChar(result, ',');
15589 appendPQExpBufferStr(result, "\n ");
15591 appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
15594 * Must add collation if not default for the type, because CREATE OR
15595 * REPLACE VIEW won't change it
15597 if (OidIsValid(tbinfo->attcollation[j]))
15599 CollInfo *coll;
15601 coll = findCollationByOid(tbinfo->attcollation[j]);
15602 if (coll)
15603 appendPQExpBuffer(result, " COLLATE %s",
15604 fmtQualifiedDumpable(coll));
15607 appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
15610 return result;
15614 * dumpTableSchema
15615 * write the declaration (not data) of one user-defined table or view
15617 static void
15618 dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
15620 DumpOptions *dopt = fout->dopt;
15621 PQExpBuffer q = createPQExpBuffer();
15622 PQExpBuffer delq = createPQExpBuffer();
15623 char *qrelname;
15624 char *qualrelname;
15625 int numParents;
15626 TableInfo **parents;
15627 int actual_atts; /* number of attrs in this CREATE statement */
15628 const char *reltypename;
15629 char *storage;
15630 int j,
15633 /* We had better have loaded per-column details about this table */
15634 Assert(tbinfo->interesting);
15636 qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
15637 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
15639 if (tbinfo->hasoids)
15640 pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
15641 qrelname);
15643 if (dopt->binary_upgrade)
15644 binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
15646 /* Is it a table or a view? */
15647 if (tbinfo->relkind == RELKIND_VIEW)
15649 PQExpBuffer result;
15652 * Note: keep this code in sync with the is_view case in dumpRule()
15655 reltypename = "VIEW";
15657 appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
15659 if (dopt->binary_upgrade)
15660 binary_upgrade_set_pg_class_oids(fout, q,
15661 tbinfo->dobj.catId.oid, false);
15663 appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
15665 if (tbinfo->dummy_view)
15666 result = createDummyViewAsClause(fout, tbinfo);
15667 else
15669 if (nonemptyReloptions(tbinfo->reloptions))
15671 appendPQExpBufferStr(q, " WITH (");
15672 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15673 appendPQExpBufferChar(q, ')');
15675 result = createViewAsClause(fout, tbinfo);
15677 appendPQExpBuffer(q, " AS\n%s", result->data);
15678 destroyPQExpBuffer(result);
15680 if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
15681 appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
15682 appendPQExpBufferStr(q, ";\n");
15684 else
15686 char *partkeydef = NULL;
15687 char *ftoptions = NULL;
15688 char *srvname = NULL;
15689 char *foreign = "";
15692 * Set reltypename, and collect any relkind-specific data that we
15693 * didn't fetch during getTables().
15695 switch (tbinfo->relkind)
15697 case RELKIND_PARTITIONED_TABLE:
15699 PQExpBuffer query = createPQExpBuffer();
15700 PGresult *res;
15702 reltypename = "TABLE";
15704 /* retrieve partition key definition */
15705 appendPQExpBuffer(query,
15706 "SELECT pg_get_partkeydef('%u')",
15707 tbinfo->dobj.catId.oid);
15708 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15709 partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
15710 PQclear(res);
15711 destroyPQExpBuffer(query);
15712 break;
15714 case RELKIND_FOREIGN_TABLE:
15716 PQExpBuffer query = createPQExpBuffer();
15717 PGresult *res;
15718 int i_srvname;
15719 int i_ftoptions;
15721 reltypename = "FOREIGN TABLE";
15723 /* retrieve name of foreign server and generic options */
15724 appendPQExpBuffer(query,
15725 "SELECT fs.srvname, "
15726 "pg_catalog.array_to_string(ARRAY("
15727 "SELECT pg_catalog.quote_ident(option_name) || "
15728 "' ' || pg_catalog.quote_literal(option_value) "
15729 "FROM pg_catalog.pg_options_to_table(ftoptions) "
15730 "ORDER BY option_name"
15731 "), E',\n ') AS ftoptions "
15732 "FROM pg_catalog.pg_foreign_table ft "
15733 "JOIN pg_catalog.pg_foreign_server fs "
15734 "ON (fs.oid = ft.ftserver) "
15735 "WHERE ft.ftrelid = '%u'",
15736 tbinfo->dobj.catId.oid);
15737 res = ExecuteSqlQueryForSingleRow(fout, query->data);
15738 i_srvname = PQfnumber(res, "srvname");
15739 i_ftoptions = PQfnumber(res, "ftoptions");
15740 srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
15741 ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
15742 PQclear(res);
15743 destroyPQExpBuffer(query);
15745 foreign = "FOREIGN ";
15746 break;
15748 case RELKIND_MATVIEW:
15749 reltypename = "MATERIALIZED VIEW";
15750 break;
15751 default:
15752 reltypename = "TABLE";
15753 break;
15756 numParents = tbinfo->numParents;
15757 parents = tbinfo->parents;
15759 appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
15761 if (dopt->binary_upgrade)
15762 binary_upgrade_set_pg_class_oids(fout, q,
15763 tbinfo->dobj.catId.oid, false);
15765 appendPQExpBuffer(q, "CREATE %s%s %s",
15766 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
15767 "UNLOGGED " : "",
15768 reltypename,
15769 qualrelname);
15772 * Attach to type, if reloftype; except in case of a binary upgrade,
15773 * we dump the table normally and attach it to the type afterward.
15775 if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
15776 appendPQExpBuffer(q, " OF %s",
15777 getFormattedTypeName(fout, tbinfo->reloftype,
15778 zeroIsError));
15780 if (tbinfo->relkind != RELKIND_MATVIEW)
15782 /* Dump the attributes */
15783 actual_atts = 0;
15784 for (j = 0; j < tbinfo->numatts; j++)
15787 * Normally, dump if it's locally defined in this table, and
15788 * not dropped. But for binary upgrade, we'll dump all the
15789 * columns, and then fix up the dropped and nonlocal cases
15790 * below.
15792 if (shouldPrintColumn(dopt, tbinfo, j))
15794 bool print_default;
15795 bool print_notnull;
15798 * Default value --- suppress if to be printed separately
15799 * or not at all.
15801 print_default = (tbinfo->attrdefs[j] != NULL &&
15802 tbinfo->attrdefs[j]->dobj.dump &&
15803 !tbinfo->attrdefs[j]->separate);
15806 * Not Null constraint --- suppress unless it is locally
15807 * defined, except if partition, or in binary-upgrade case
15808 * where that won't work.
15810 print_notnull =
15811 (tbinfo->notnull_constrs[j] != NULL &&
15812 (!tbinfo->notnull_inh[j] || tbinfo->ispartition ||
15813 dopt->binary_upgrade));
15816 * Skip column if fully defined by reloftype, except in
15817 * binary upgrade
15819 if (OidIsValid(tbinfo->reloftype) &&
15820 !print_default && !print_notnull &&
15821 !dopt->binary_upgrade)
15822 continue;
15824 /* Format properly if not first attr */
15825 if (actual_atts == 0)
15826 appendPQExpBufferStr(q, " (");
15827 else
15828 appendPQExpBufferChar(q, ',');
15829 appendPQExpBufferStr(q, "\n ");
15830 actual_atts++;
15832 /* Attribute name */
15833 appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
15835 if (tbinfo->attisdropped[j])
15838 * ALTER TABLE DROP COLUMN clears
15839 * pg_attribute.atttypid, so we will not have gotten a
15840 * valid type name; insert INTEGER as a stopgap. We'll
15841 * clean things up later.
15843 appendPQExpBufferStr(q, " INTEGER /* dummy */");
15844 /* and skip to the next column */
15845 continue;
15849 * Attribute type; print it except when creating a typed
15850 * table ('OF type_name'), but in binary-upgrade mode,
15851 * print it in that case too.
15853 if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
15855 appendPQExpBuffer(q, " %s",
15856 tbinfo->atttypnames[j]);
15859 if (print_default)
15861 if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
15862 appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
15863 tbinfo->attrdefs[j]->adef_expr);
15864 else
15865 appendPQExpBuffer(q, " DEFAULT %s",
15866 tbinfo->attrdefs[j]->adef_expr);
15870 if (print_notnull)
15872 if (tbinfo->notnull_constrs[j][0] == '\0')
15873 appendPQExpBufferStr(q, " NOT NULL");
15874 else
15875 appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
15876 fmtId(tbinfo->notnull_constrs[j]));
15878 if (tbinfo->notnull_noinh[j])
15879 appendPQExpBufferStr(q, " NO INHERIT");
15882 /* Add collation if not default for the type */
15883 if (OidIsValid(tbinfo->attcollation[j]))
15885 CollInfo *coll;
15887 coll = findCollationByOid(tbinfo->attcollation[j]);
15888 if (coll)
15889 appendPQExpBuffer(q, " COLLATE %s",
15890 fmtQualifiedDumpable(coll));
15896 * Add non-inherited CHECK constraints, if any.
15898 * For partitions, we need to include check constraints even if
15899 * they're not defined locally, because the ALTER TABLE ATTACH
15900 * PARTITION that we'll emit later expects the constraint to be
15901 * there. (No need to fix conislocal: ATTACH PARTITION does that)
15903 for (j = 0; j < tbinfo->ncheck; j++)
15905 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
15907 if (constr->separate ||
15908 (!constr->conislocal && !tbinfo->ispartition))
15909 continue;
15911 if (actual_atts == 0)
15912 appendPQExpBufferStr(q, " (\n ");
15913 else
15914 appendPQExpBufferStr(q, ",\n ");
15916 appendPQExpBuffer(q, "CONSTRAINT %s ",
15917 fmtId(constr->dobj.name));
15918 appendPQExpBufferStr(q, constr->condef);
15920 actual_atts++;
15923 if (actual_atts)
15924 appendPQExpBufferStr(q, "\n)");
15925 else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
15928 * No attributes? we must have a parenthesized attribute list,
15929 * even though empty, when not using the OF TYPE syntax.
15931 appendPQExpBufferStr(q, " (\n)");
15935 * Emit the INHERITS clause (not for partitions), except in
15936 * binary-upgrade mode.
15938 if (numParents > 0 && !tbinfo->ispartition &&
15939 !dopt->binary_upgrade)
15941 appendPQExpBufferStr(q, "\nINHERITS (");
15942 for (k = 0; k < numParents; k++)
15944 TableInfo *parentRel = parents[k];
15946 if (k > 0)
15947 appendPQExpBufferStr(q, ", ");
15948 appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
15950 appendPQExpBufferChar(q, ')');
15953 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
15954 appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
15956 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
15957 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
15960 if (nonemptyReloptions(tbinfo->reloptions) ||
15961 nonemptyReloptions(tbinfo->toast_reloptions))
15963 bool addcomma = false;
15965 appendPQExpBufferStr(q, "\nWITH (");
15966 if (nonemptyReloptions(tbinfo->reloptions))
15968 addcomma = true;
15969 appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15971 if (nonemptyReloptions(tbinfo->toast_reloptions))
15973 if (addcomma)
15974 appendPQExpBufferStr(q, ", ");
15975 appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
15976 fout);
15978 appendPQExpBufferChar(q, ')');
15981 /* Dump generic options if any */
15982 if (ftoptions && ftoptions[0])
15983 appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
15986 * For materialized views, create the AS clause just like a view. At
15987 * this point, we always mark the view as not populated.
15989 if (tbinfo->relkind == RELKIND_MATVIEW)
15991 PQExpBuffer result;
15993 result = createViewAsClause(fout, tbinfo);
15994 appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
15995 result->data);
15996 destroyPQExpBuffer(result);
15998 else
15999 appendPQExpBufferStr(q, ";\n");
16001 /* Materialized views can depend on extensions */
16002 if (tbinfo->relkind == RELKIND_MATVIEW)
16003 append_depends_on_extension(fout, q, &tbinfo->dobj,
16004 "pg_catalog.pg_class",
16005 "MATERIALIZED VIEW",
16006 qualrelname);
16009 * in binary upgrade mode, update the catalog with any missing values
16010 * that might be present.
16012 if (dopt->binary_upgrade)
16014 for (j = 0; j < tbinfo->numatts; j++)
16016 if (tbinfo->attmissingval[j][0] != '\0')
16018 appendPQExpBufferStr(q, "\n-- set missing value.\n");
16019 appendPQExpBufferStr(q,
16020 "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16021 appendStringLiteralAH(q, qualrelname, fout);
16022 appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16023 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16024 appendPQExpBufferChar(q, ',');
16025 appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16026 appendPQExpBufferStr(q, ");\n\n");
16032 * To create binary-compatible heap files, we have to ensure the same
16033 * physical column order, including dropped columns, as in the
16034 * original. Therefore, we create dropped columns above and drop them
16035 * here, also updating their attlen/attalign values so that the
16036 * dropped column can be skipped properly. (We do not bother with
16037 * restoring the original attbyval setting.) Also, inheritance
16038 * relationships are set up by doing ALTER TABLE INHERIT rather than
16039 * using an INHERITS clause --- the latter would possibly mess up the
16040 * column order. That also means we have to take care about setting
16041 * attislocal correctly, plus fix up any inherited CHECK constraints.
16042 * Analogously, we set up typed tables using ALTER TABLE / OF here.
16044 * We process foreign and partitioned tables here, even though they
16045 * lack heap storage, because they can participate in inheritance
16046 * relationships and we want this stuff to be consistent across the
16047 * inheritance tree. We can exclude indexes, toast tables, sequences
16048 * and matviews, even though they have storage, because we don't
16049 * support altering or dropping columns in them, nor can they be part
16050 * of inheritance trees.
16052 if (dopt->binary_upgrade &&
16053 (tbinfo->relkind == RELKIND_RELATION ||
16054 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16055 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16057 for (j = 0; j < tbinfo->numatts; j++)
16059 if (tbinfo->attisdropped[j])
16061 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n");
16062 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n"
16063 "SET attlen = %d, "
16064 "attalign = '%c', attbyval = false\n"
16065 "WHERE attname = ",
16066 tbinfo->attlen[j],
16067 tbinfo->attalign[j]);
16068 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16069 appendPQExpBufferStr(q, "\n AND attrelid = ");
16070 appendStringLiteralAH(q, qualrelname, fout);
16071 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16073 if (tbinfo->relkind == RELKIND_RELATION ||
16074 tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16075 appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16076 qualrelname);
16077 else
16078 appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ",
16079 qualrelname);
16080 appendPQExpBuffer(q, "DROP COLUMN %s;\n",
16081 fmtId(tbinfo->attnames[j]));
16083 else if (!tbinfo->attislocal[j])
16085 appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n");
16086 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16087 "SET attislocal = false\n"
16088 "WHERE attname = ");
16089 appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16090 appendPQExpBufferStr(q, "\n AND attrelid = ");
16091 appendStringLiteralAH(q, qualrelname, fout);
16092 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16095 * If a not-null constraint comes from inheritance, reset
16096 * conislocal. The inhcount is fixed later.
16098 if (tbinfo->notnull_constrs[j] != NULL &&
16099 !tbinfo->notnull_throwaway[j] &&
16100 tbinfo->notnull_inh[j] &&
16101 !tbinfo->ispartition)
16103 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16104 "SET conislocal = false\n"
16105 "WHERE contype = 'n' AND conrelid = ");
16106 appendStringLiteralAH(q, qualrelname, fout);
16107 appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
16108 "conname = ");
16109 appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
16110 appendPQExpBufferStr(q, ";\n");
16116 * Add inherited CHECK constraints, if any.
16118 * For partitions, they were already dumped, and conislocal
16119 * doesn't need fixing.
16121 for (k = 0; k < tbinfo->ncheck; k++)
16123 ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
16125 if (constr->separate || constr->conislocal || tbinfo->ispartition)
16126 continue;
16128 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n");
16129 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
16130 foreign, qualrelname,
16131 fmtId(constr->dobj.name),
16132 constr->condef);
16133 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16134 "SET conislocal = false\n"
16135 "WHERE contype = 'c' AND conname = ");
16136 appendStringLiteralAH(q, constr->dobj.name, fout);
16137 appendPQExpBufferStr(q, "\n AND conrelid = ");
16138 appendStringLiteralAH(q, qualrelname, fout);
16139 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16142 if (numParents > 0 && !tbinfo->ispartition)
16144 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
16145 for (k = 0; k < numParents; k++)
16147 TableInfo *parentRel = parents[k];
16149 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
16150 qualrelname,
16151 fmtQualifiedDumpable(parentRel));
16155 if (OidIsValid(tbinfo->reloftype))
16157 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
16158 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
16159 qualrelname,
16160 getFormattedTypeName(fout, tbinfo->reloftype,
16161 zeroIsError));
16166 * In binary_upgrade mode, arrange to restore the old relfrozenxid and
16167 * relminmxid of all vacuumable relations. (While vacuum.c processes
16168 * TOAST tables semi-independently, here we see them only as children
16169 * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
16170 * child toast table is handled below.)
16172 if (dopt->binary_upgrade &&
16173 (tbinfo->relkind == RELKIND_RELATION ||
16174 tbinfo->relkind == RELKIND_MATVIEW))
16176 appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
16177 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16178 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16179 "WHERE oid = ",
16180 tbinfo->frozenxid, tbinfo->minmxid);
16181 appendStringLiteralAH(q, qualrelname, fout);
16182 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16184 if (tbinfo->toast_oid)
16187 * The toast table will have the same OID at restore, so we
16188 * can safely target it by OID.
16190 appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
16191 appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16192 "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16193 "WHERE oid = '%u';\n",
16194 tbinfo->toast_frozenxid,
16195 tbinfo->toast_minmxid, tbinfo->toast_oid);
16200 * In binary_upgrade mode, restore matviews' populated status by
16201 * poking pg_class directly. This is pretty ugly, but we can't use
16202 * REFRESH MATERIALIZED VIEW since it's possible that some underlying
16203 * matview is not populated even though this matview is; in any case,
16204 * we want to transfer the matview's heap storage, not run REFRESH.
16206 if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
16207 tbinfo->relispopulated)
16209 appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
16210 appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
16211 "SET relispopulated = 't'\n"
16212 "WHERE oid = ");
16213 appendStringLiteralAH(q, qualrelname, fout);
16214 appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16218 * Dump additional per-column properties that we can't handle in the
16219 * main CREATE TABLE command.
16221 for (j = 0; j < tbinfo->numatts; j++)
16223 /* None of this applies to dropped columns */
16224 if (tbinfo->attisdropped[j])
16225 continue;
16228 * If we didn't dump the column definition explicitly above, and
16229 * it is not-null and did not inherit that property from a parent,
16230 * we have to mark it separately.
16232 if (!shouldPrintColumn(dopt, tbinfo, j) &&
16233 tbinfo->notnull_constrs[j] != NULL &&
16234 (!tbinfo->notnull_inh[j] && !tbinfo->ispartition && !dopt->binary_upgrade))
16236 /* No constraint name desired? */
16237 if (tbinfo->notnull_constrs[j][0] == '\0')
16238 appendPQExpBuffer(q,
16239 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET NOT NULL;\n",
16240 foreign, qualrelname,
16241 fmtId(tbinfo->attnames[j]));
16242 else
16243 appendPQExpBuffer(q,
16244 "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s NOT NULL %s;\n",
16245 foreign, qualrelname,
16246 tbinfo->notnull_constrs[j],
16247 fmtId(tbinfo->attnames[j]));
16251 * Dump per-column statistics information. We only issue an ALTER
16252 * TABLE statement if the attstattarget entry for this column is
16253 * non-negative (i.e. it's not the default value)
16255 if (tbinfo->attstattarget[j] >= 0)
16256 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
16257 foreign, qualrelname,
16258 fmtId(tbinfo->attnames[j]),
16259 tbinfo->attstattarget[j]);
16262 * Dump per-column storage information. The statement is only
16263 * dumped if the storage has been changed from the type's default.
16265 if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
16267 switch (tbinfo->attstorage[j])
16269 case TYPSTORAGE_PLAIN:
16270 storage = "PLAIN";
16271 break;
16272 case TYPSTORAGE_EXTERNAL:
16273 storage = "EXTERNAL";
16274 break;
16275 case TYPSTORAGE_EXTENDED:
16276 storage = "EXTENDED";
16277 break;
16278 case TYPSTORAGE_MAIN:
16279 storage = "MAIN";
16280 break;
16281 default:
16282 storage = NULL;
16286 * Only dump the statement if it's a storage type we recognize
16288 if (storage != NULL)
16289 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
16290 foreign, qualrelname,
16291 fmtId(tbinfo->attnames[j]),
16292 storage);
16296 * Dump per-column compression, if it's been set.
16298 if (!dopt->no_toast_compression)
16300 const char *cmname;
16302 switch (tbinfo->attcompression[j])
16304 case 'p':
16305 cmname = "pglz";
16306 break;
16307 case 'l':
16308 cmname = "lz4";
16309 break;
16310 default:
16311 cmname = NULL;
16312 break;
16315 if (cmname != NULL)
16316 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
16317 foreign, qualrelname,
16318 fmtId(tbinfo->attnames[j]),
16319 cmname);
16323 * Dump per-column attributes.
16325 if (tbinfo->attoptions[j][0] != '\0')
16326 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
16327 foreign, qualrelname,
16328 fmtId(tbinfo->attnames[j]),
16329 tbinfo->attoptions[j]);
16332 * Dump per-column fdw options.
16334 if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
16335 tbinfo->attfdwoptions[j][0] != '\0')
16336 appendPQExpBuffer(q,
16337 "ALTER FOREIGN TABLE %s ALTER COLUMN %s OPTIONS (\n"
16338 " %s\n"
16339 ");\n",
16340 qualrelname,
16341 fmtId(tbinfo->attnames[j]),
16342 tbinfo->attfdwoptions[j]);
16343 } /* end loop over columns */
16345 free(partkeydef);
16346 free(ftoptions);
16347 free(srvname);
16351 * dump properties we only have ALTER TABLE syntax for
16353 if ((tbinfo->relkind == RELKIND_RELATION ||
16354 tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
16355 tbinfo->relkind == RELKIND_MATVIEW) &&
16356 tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
16358 if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
16360 /* nothing to do, will be set when the index is dumped */
16362 else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
16364 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
16365 qualrelname);
16367 else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
16369 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
16370 qualrelname);
16374 if (tbinfo->forcerowsec)
16375 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
16376 qualrelname);
16378 if (dopt->binary_upgrade)
16379 binary_upgrade_extension_member(q, &tbinfo->dobj,
16380 reltypename, qrelname,
16381 tbinfo->dobj.namespace->dobj.name);
16383 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16385 char *tablespace = NULL;
16386 char *tableam = NULL;
16389 * _selectTablespace() relies on tablespace-enabled objects in the
16390 * default tablespace to have a tablespace of "" (empty string) versus
16391 * non-tablespace-enabled objects to have a tablespace of NULL.
16392 * getTables() sets tbinfo->reltablespace to "" for the default
16393 * tablespace (not NULL).
16395 if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
16396 tablespace = tbinfo->reltablespace;
16398 if (RELKIND_HAS_TABLE_AM(tbinfo->relkind))
16399 tableam = tbinfo->amname;
16401 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16402 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
16403 .namespace = tbinfo->dobj.namespace->dobj.name,
16404 .tablespace = tablespace,
16405 .tableam = tableam,
16406 .owner = tbinfo->rolname,
16407 .description = reltypename,
16408 .section = tbinfo->postponed_def ?
16409 SECTION_POST_DATA : SECTION_PRE_DATA,
16410 .createStmt = q->data,
16411 .dropStmt = delq->data));
16414 /* Dump Table Comments */
16415 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16416 dumpTableComment(fout, tbinfo, reltypename);
16418 /* Dump Table Security Labels */
16419 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16420 dumpTableSecLabel(fout, tbinfo, reltypename);
16422 /* Dump comments on inlined table constraints */
16423 for (j = 0; j < tbinfo->ncheck; j++)
16425 ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16427 if (constr->separate || !constr->conislocal)
16428 continue;
16430 if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
16431 dumpTableConstraintComment(fout, constr);
16434 destroyPQExpBuffer(q);
16435 destroyPQExpBuffer(delq);
16436 free(qrelname);
16437 free(qualrelname);
16441 * dumpTableAttach
16442 * write to fout the commands to attach a child partition
16444 * Child partitions are always made by creating them separately
16445 * and then using ATTACH PARTITION, rather than using
16446 * CREATE TABLE ... PARTITION OF. This is important for preserving
16447 * any possible discrepancy in column layout, to allow assigning the
16448 * correct tablespace if different, and so that it's possible to restore
16449 * a partition without restoring its parent. (You'll get an error from
16450 * the ATTACH PARTITION command, but that can be ignored, or skipped
16451 * using "pg_restore -L" if you prefer.) The last point motivates
16452 * treating ATTACH PARTITION as a completely separate ArchiveEntry
16453 * rather than emitting it within the child partition's ArchiveEntry.
16455 static void
16456 dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
16458 DumpOptions *dopt = fout->dopt;
16459 PQExpBuffer q;
16460 PGresult *res;
16461 char *partbound;
16463 /* Do nothing in data-only dump */
16464 if (dopt->dataOnly)
16465 return;
16467 q = createPQExpBuffer();
16469 if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
16471 /* Set up query for partbound details */
16472 appendPQExpBufferStr(q,
16473 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
16475 appendPQExpBufferStr(q,
16476 "SELECT pg_get_expr(c.relpartbound, c.oid) "
16477 "FROM pg_class c "
16478 "WHERE c.oid = $1");
16480 ExecuteSqlStatement(fout, q->data);
16482 fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
16485 printfPQExpBuffer(q,
16486 "EXECUTE dumpTableAttach('%u')",
16487 attachinfo->partitionTbl->dobj.catId.oid);
16489 res = ExecuteSqlQueryForSingleRow(fout, q->data);
16490 partbound = PQgetvalue(res, 0, 0);
16492 /* Perform ALTER TABLE on the parent */
16493 printfPQExpBuffer(q,
16494 "ALTER TABLE ONLY %s ",
16495 fmtQualifiedDumpable(attachinfo->parentTbl));
16496 appendPQExpBuffer(q,
16497 "ATTACH PARTITION %s %s;\n",
16498 fmtQualifiedDumpable(attachinfo->partitionTbl),
16499 partbound);
16502 * There is no point in creating a drop query as the drop is done by table
16503 * drop. (If you think to change this, see also _printTocEntry().)
16504 * Although this object doesn't really have ownership as such, set the
16505 * owner field anyway to ensure that the command is run by the correct
16506 * role at restore time.
16508 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16509 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16510 .namespace = attachinfo->dobj.namespace->dobj.name,
16511 .owner = attachinfo->partitionTbl->rolname,
16512 .description = "TABLE ATTACH",
16513 .section = SECTION_PRE_DATA,
16514 .createStmt = q->data));
16516 PQclear(res);
16517 destroyPQExpBuffer(q);
16521 * dumpAttrDef --- dump an attribute's default-value declaration
16523 static void
16524 dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
16526 DumpOptions *dopt = fout->dopt;
16527 TableInfo *tbinfo = adinfo->adtable;
16528 int adnum = adinfo->adnum;
16529 PQExpBuffer q;
16530 PQExpBuffer delq;
16531 char *qualrelname;
16532 char *tag;
16533 char *foreign;
16535 /* Do nothing in data-only dump */
16536 if (dopt->dataOnly)
16537 return;
16539 /* Skip if not "separate"; it was dumped in the table's definition */
16540 if (!adinfo->separate)
16541 return;
16543 q = createPQExpBuffer();
16544 delq = createPQExpBuffer();
16546 qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16548 foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
16550 appendPQExpBuffer(q,
16551 "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
16552 foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
16553 adinfo->adef_expr);
16555 appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
16556 foreign, qualrelname,
16557 fmtId(tbinfo->attnames[adnum - 1]));
16559 tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
16561 if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16562 ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
16563 ARCHIVE_OPTS(.tag = tag,
16564 .namespace = tbinfo->dobj.namespace->dobj.name,
16565 .owner = tbinfo->rolname,
16566 .description = "DEFAULT",
16567 .section = SECTION_PRE_DATA,
16568 .createStmt = q->data,
16569 .dropStmt = delq->data));
16571 free(tag);
16572 destroyPQExpBuffer(q);
16573 destroyPQExpBuffer(delq);
16574 free(qualrelname);
16578 * getAttrName: extract the correct name for an attribute
16580 * The array tblInfo->attnames[] only provides names of user attributes;
16581 * if a system attribute number is supplied, we have to fake it.
16582 * We also do a little bit of bounds checking for safety's sake.
16584 static const char *
16585 getAttrName(int attrnum, const TableInfo *tblInfo)
16587 if (attrnum > 0 && attrnum <= tblInfo->numatts)
16588 return tblInfo->attnames[attrnum - 1];
16589 switch (attrnum)
16591 case SelfItemPointerAttributeNumber:
16592 return "ctid";
16593 case MinTransactionIdAttributeNumber:
16594 return "xmin";
16595 case MinCommandIdAttributeNumber:
16596 return "cmin";
16597 case MaxTransactionIdAttributeNumber:
16598 return "xmax";
16599 case MaxCommandIdAttributeNumber:
16600 return "cmax";
16601 case TableOidAttributeNumber:
16602 return "tableoid";
16604 pg_fatal("invalid column number %d for table \"%s\"",
16605 attrnum, tblInfo->dobj.name);
16606 return NULL; /* keep compiler quiet */
16610 * dumpIndex
16611 * write out to fout a user-defined index
16613 static void
16614 dumpIndex(Archive *fout, const IndxInfo *indxinfo)
16616 DumpOptions *dopt = fout->dopt;
16617 TableInfo *tbinfo = indxinfo->indextable;
16618 bool is_constraint = (indxinfo->indexconstraint != 0);
16619 PQExpBuffer q;
16620 PQExpBuffer delq;
16621 char *qindxname;
16622 char *qqindxname;
16624 /* Do nothing in data-only dump */
16625 if (dopt->dataOnly)
16626 return;
16628 q = createPQExpBuffer();
16629 delq = createPQExpBuffer();
16631 qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
16632 qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
16635 * If there's an associated constraint, don't dump the index per se, but
16636 * do dump any comment for it. (This is safe because dependency ordering
16637 * will have ensured the constraint is emitted first.) Note that the
16638 * emitted comment has to be shown as depending on the constraint, not the
16639 * index, in such cases.
16641 if (!is_constraint)
16643 char *indstatcols = indxinfo->indstatcols;
16644 char *indstatvals = indxinfo->indstatvals;
16645 char **indstatcolsarray = NULL;
16646 char **indstatvalsarray = NULL;
16647 int nstatcols = 0;
16648 int nstatvals = 0;
16650 if (dopt->binary_upgrade)
16651 binary_upgrade_set_pg_class_oids(fout, q,
16652 indxinfo->dobj.catId.oid, true);
16654 /* Plain secondary index */
16655 appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
16658 * Append ALTER TABLE commands as needed to set properties that we
16659 * only have ALTER TABLE syntax for. Keep this in sync with the
16660 * similar code in dumpConstraint!
16663 /* If the index is clustered, we need to record that. */
16664 if (indxinfo->indisclustered)
16666 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
16667 fmtQualifiedDumpable(tbinfo));
16668 /* index name is not qualified in this syntax */
16669 appendPQExpBuffer(q, " ON %s;\n",
16670 qindxname);
16674 * If the index has any statistics on some of its columns, generate
16675 * the associated ALTER INDEX queries.
16677 if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
16679 int j;
16681 if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
16682 pg_fatal("could not parse index statistic columns");
16683 if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
16684 pg_fatal("could not parse index statistic values");
16685 if (nstatcols != nstatvals)
16686 pg_fatal("mismatched number of columns and values for index statistics");
16688 for (j = 0; j < nstatcols; j++)
16690 appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
16693 * Note that this is a column number, so no quotes should be
16694 * used.
16696 appendPQExpBuffer(q, "ALTER COLUMN %s ",
16697 indstatcolsarray[j]);
16698 appendPQExpBuffer(q, "SET STATISTICS %s;\n",
16699 indstatvalsarray[j]);
16703 /* Indexes can depend on extensions */
16704 append_depends_on_extension(fout, q, &indxinfo->dobj,
16705 "pg_catalog.pg_class",
16706 "INDEX", qqindxname);
16708 /* If the index defines identity, we need to record that. */
16709 if (indxinfo->indisreplident)
16711 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
16712 fmtQualifiedDumpable(tbinfo));
16713 /* index name is not qualified in this syntax */
16714 appendPQExpBuffer(q, " INDEX %s;\n",
16715 qindxname);
16718 appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
16720 if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16721 ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
16722 ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
16723 .namespace = tbinfo->dobj.namespace->dobj.name,
16724 .tablespace = indxinfo->tablespace,
16725 .owner = tbinfo->rolname,
16726 .description = "INDEX",
16727 .section = SECTION_POST_DATA,
16728 .createStmt = q->data,
16729 .dropStmt = delq->data));
16731 free(indstatcolsarray);
16732 free(indstatvalsarray);
16735 /* Dump Index Comments */
16736 if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16737 dumpComment(fout, "INDEX", qindxname,
16738 tbinfo->dobj.namespace->dobj.name,
16739 tbinfo->rolname,
16740 indxinfo->dobj.catId, 0,
16741 is_constraint ? indxinfo->indexconstraint :
16742 indxinfo->dobj.dumpId);
16744 destroyPQExpBuffer(q);
16745 destroyPQExpBuffer(delq);
16746 free(qindxname);
16747 free(qqindxname);
16751 * dumpIndexAttach
16752 * write out to fout a partitioned-index attachment clause
16754 static void
16755 dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
16757 /* Do nothing in data-only dump */
16758 if (fout->dopt->dataOnly)
16759 return;
16761 if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
16763 PQExpBuffer q = createPQExpBuffer();
16765 appendPQExpBuffer(q, "ALTER INDEX %s ",
16766 fmtQualifiedDumpable(attachinfo->parentIdx));
16767 appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
16768 fmtQualifiedDumpable(attachinfo->partitionIdx));
16771 * There is no point in creating a drop query as the drop is done by
16772 * index drop. (If you think to change this, see also
16773 * _printTocEntry().) Although this object doesn't really have
16774 * ownership as such, set the owner field anyway to ensure that the
16775 * command is run by the correct role at restore time.
16777 ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16778 ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16779 .namespace = attachinfo->dobj.namespace->dobj.name,
16780 .owner = attachinfo->parentIdx->indextable->rolname,
16781 .description = "INDEX ATTACH",
16782 .section = SECTION_POST_DATA,
16783 .createStmt = q->data));
16785 destroyPQExpBuffer(q);
16790 * dumpStatisticsExt
16791 * write out to fout an extended statistics object
16793 static void
16794 dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
16796 DumpOptions *dopt = fout->dopt;
16797 PQExpBuffer q;
16798 PQExpBuffer delq;
16799 PQExpBuffer query;
16800 char *qstatsextname;
16801 PGresult *res;
16802 char *stxdef;
16804 /* Do nothing in data-only dump */
16805 if (dopt->dataOnly)
16806 return;
16808 q = createPQExpBuffer();
16809 delq = createPQExpBuffer();
16810 query = createPQExpBuffer();
16812 qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
16814 appendPQExpBuffer(query, "SELECT "
16815 "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
16816 statsextinfo->dobj.catId.oid);
16818 res = ExecuteSqlQueryForSingleRow(fout, query->data);
16820 stxdef = PQgetvalue(res, 0, 0);
16822 /* Result of pg_get_statisticsobjdef is complete except for semicolon */
16823 appendPQExpBuffer(q, "%s;\n", stxdef);
16826 * We only issue an ALTER STATISTICS statement if the stxstattarget entry
16827 * for this statistics object is non-negative (i.e. it's not the default
16828 * value).
16830 if (statsextinfo->stattarget >= 0)
16832 appendPQExpBuffer(q, "ALTER STATISTICS %s ",
16833 fmtQualifiedDumpable(statsextinfo));
16834 appendPQExpBuffer(q, "SET STATISTICS %d;\n",
16835 statsextinfo->stattarget);
16838 appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
16839 fmtQualifiedDumpable(statsextinfo));
16841 if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16842 ArchiveEntry(fout, statsextinfo->dobj.catId,
16843 statsextinfo->dobj.dumpId,
16844 ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
16845 .namespace = statsextinfo->dobj.namespace->dobj.name,
16846 .owner = statsextinfo->rolname,
16847 .description = "STATISTICS",
16848 .section = SECTION_POST_DATA,
16849 .createStmt = q->data,
16850 .dropStmt = delq->data));
16852 /* Dump Statistics Comments */
16853 if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16854 dumpComment(fout, "STATISTICS", qstatsextname,
16855 statsextinfo->dobj.namespace->dobj.name,
16856 statsextinfo->rolname,
16857 statsextinfo->dobj.catId, 0,
16858 statsextinfo->dobj.dumpId);
16860 PQclear(res);
16861 destroyPQExpBuffer(q);
16862 destroyPQExpBuffer(delq);
16863 destroyPQExpBuffer(query);
16864 free(qstatsextname);
16868 * dumpConstraint
16869 * write out to fout a user-defined constraint
16871 static void
16872 dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
16874 DumpOptions *dopt = fout->dopt;
16875 TableInfo *tbinfo = coninfo->contable;
16876 PQExpBuffer q;
16877 PQExpBuffer delq;
16878 char *tag = NULL;
16879 char *foreign;
16881 /* Do nothing in data-only dump */
16882 if (dopt->dataOnly)
16883 return;
16885 q = createPQExpBuffer();
16886 delq = createPQExpBuffer();
16888 foreign = tbinfo &&
16889 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
16891 if (coninfo->contype == 'p' ||
16892 coninfo->contype == 'u' ||
16893 coninfo->contype == 'x')
16895 /* Index-related constraint */
16896 IndxInfo *indxinfo;
16897 int k;
16899 indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
16901 if (indxinfo == NULL)
16902 pg_fatal("missing index for constraint \"%s\"",
16903 coninfo->dobj.name);
16905 if (dopt->binary_upgrade)
16906 binary_upgrade_set_pg_class_oids(fout, q,
16907 indxinfo->dobj.catId.oid, true);
16909 appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
16910 fmtQualifiedDumpable(tbinfo));
16911 appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
16912 fmtId(coninfo->dobj.name));
16914 if (coninfo->condef)
16916 /* pg_get_constraintdef should have provided everything */
16917 appendPQExpBuffer(q, "%s;\n", coninfo->condef);
16919 else
16921 appendPQExpBufferStr(q,
16922 coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
16925 * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
16926 * indexes. Being able to create this was fixed, but we need to
16927 * make the index distinct in order to be able to restore the
16928 * dump.
16930 if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
16931 appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
16932 appendPQExpBufferStr(q, " (");
16933 for (k = 0; k < indxinfo->indnkeyattrs; k++)
16935 int indkey = (int) indxinfo->indkeys[k];
16936 const char *attname;
16938 if (indkey == InvalidAttrNumber)
16939 break;
16940 attname = getAttrName(indkey, tbinfo);
16942 appendPQExpBuffer(q, "%s%s",
16943 (k == 0) ? "" : ", ",
16944 fmtId(attname));
16947 if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
16948 appendPQExpBufferStr(q, ") INCLUDE (");
16950 for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
16952 int indkey = (int) indxinfo->indkeys[k];
16953 const char *attname;
16955 if (indkey == InvalidAttrNumber)
16956 break;
16957 attname = getAttrName(indkey, tbinfo);
16959 appendPQExpBuffer(q, "%s%s",
16960 (k == indxinfo->indnkeyattrs) ? "" : ", ",
16961 fmtId(attname));
16964 appendPQExpBufferChar(q, ')');
16966 if (nonemptyReloptions(indxinfo->indreloptions))
16968 appendPQExpBufferStr(q, " WITH (");
16969 appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
16970 appendPQExpBufferChar(q, ')');
16973 if (coninfo->condeferrable)
16975 appendPQExpBufferStr(q, " DEFERRABLE");
16976 if (coninfo->condeferred)
16977 appendPQExpBufferStr(q, " INITIALLY DEFERRED");
16980 appendPQExpBufferStr(q, ";\n");
16984 * Append ALTER TABLE commands as needed to set properties that we
16985 * only have ALTER TABLE syntax for. Keep this in sync with the
16986 * similar code in dumpIndex!
16989 /* Drop any not-null constraints that were added to support the PK */
16990 if (coninfo->contype == 'p')
16991 for (int i = 0; i < tbinfo->numatts; i++)
16992 if (tbinfo->notnull_throwaway[i])
16993 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s DROP CONSTRAINT %s;",
16994 fmtQualifiedDumpable(tbinfo),
16995 tbinfo->notnull_constrs[i]);
16997 /* If the index is clustered, we need to record that. */
16998 if (indxinfo->indisclustered)
17000 appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17001 fmtQualifiedDumpable(tbinfo));
17002 /* index name is not qualified in this syntax */
17003 appendPQExpBuffer(q, " ON %s;\n",
17004 fmtId(indxinfo->dobj.name));
17007 /* If the index defines identity, we need to record that. */
17008 if (indxinfo->indisreplident)
17010 appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17011 fmtQualifiedDumpable(tbinfo));
17012 /* index name is not qualified in this syntax */
17013 appendPQExpBuffer(q, " INDEX %s;\n",
17014 fmtId(indxinfo->dobj.name));
17017 /* Indexes can depend on extensions */
17018 append_depends_on_extension(fout, q, &indxinfo->dobj,
17019 "pg_catalog.pg_class", "INDEX",
17020 fmtQualifiedDumpable(indxinfo));
17022 appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17023 fmtQualifiedDumpable(tbinfo));
17024 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17025 fmtId(coninfo->dobj.name));
17027 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17029 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17030 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17031 ARCHIVE_OPTS(.tag = tag,
17032 .namespace = tbinfo->dobj.namespace->dobj.name,
17033 .tablespace = indxinfo->tablespace,
17034 .owner = tbinfo->rolname,
17035 .description = "CONSTRAINT",
17036 .section = SECTION_POST_DATA,
17037 .createStmt = q->data,
17038 .dropStmt = delq->data));
17040 else if (coninfo->contype == 'f')
17042 char *only;
17045 * Foreign keys on partitioned tables are always declared as
17046 * inheriting to partitions; for all other cases, emit them as
17047 * applying ONLY directly to the named table, because that's how they
17048 * work for regular inherited tables.
17050 only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17053 * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17054 * current table data is not processed
17056 appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17057 only, fmtQualifiedDumpable(tbinfo));
17058 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17059 fmtId(coninfo->dobj.name),
17060 coninfo->condef);
17062 appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
17063 only, fmtQualifiedDumpable(tbinfo));
17064 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17065 fmtId(coninfo->dobj.name));
17067 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17069 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17070 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17071 ARCHIVE_OPTS(.tag = tag,
17072 .namespace = tbinfo->dobj.namespace->dobj.name,
17073 .owner = tbinfo->rolname,
17074 .description = "FK CONSTRAINT",
17075 .section = SECTION_POST_DATA,
17076 .createStmt = q->data,
17077 .dropStmt = delq->data));
17079 else if (coninfo->contype == 'c' && tbinfo)
17081 /* CHECK constraint on a table */
17083 /* Ignore if not to be dumped separately, or if it was inherited */
17084 if (coninfo->separate && coninfo->conislocal)
17086 /* not ONLY since we want it to propagate to children */
17087 appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
17088 fmtQualifiedDumpable(tbinfo));
17089 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17090 fmtId(coninfo->dobj.name),
17091 coninfo->condef);
17093 appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
17094 fmtQualifiedDumpable(tbinfo));
17095 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17096 fmtId(coninfo->dobj.name));
17098 tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17100 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17101 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17102 ARCHIVE_OPTS(.tag = tag,
17103 .namespace = tbinfo->dobj.namespace->dobj.name,
17104 .owner = tbinfo->rolname,
17105 .description = "CHECK CONSTRAINT",
17106 .section = SECTION_POST_DATA,
17107 .createStmt = q->data,
17108 .dropStmt = delq->data));
17111 else if (coninfo->contype == 'c' && tbinfo == NULL)
17113 /* CHECK constraint on a domain */
17114 TypeInfo *tyinfo = coninfo->condomain;
17116 /* Ignore if not to be dumped separately */
17117 if (coninfo->separate)
17119 appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
17120 fmtQualifiedDumpable(tyinfo));
17121 appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17122 fmtId(coninfo->dobj.name),
17123 coninfo->condef);
17125 appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
17126 fmtQualifiedDumpable(tyinfo));
17127 appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17128 fmtId(coninfo->dobj.name));
17130 tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
17132 if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17133 ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17134 ARCHIVE_OPTS(.tag = tag,
17135 .namespace = tyinfo->dobj.namespace->dobj.name,
17136 .owner = tyinfo->rolname,
17137 .description = "CHECK CONSTRAINT",
17138 .section = SECTION_POST_DATA,
17139 .createStmt = q->data,
17140 .dropStmt = delq->data));
17143 else
17145 pg_fatal("unrecognized constraint type: %c",
17146 coninfo->contype);
17149 /* Dump Constraint Comments --- only works for table constraints */
17150 if (tbinfo && coninfo->separate &&
17151 coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17152 dumpTableConstraintComment(fout, coninfo);
17154 free(tag);
17155 destroyPQExpBuffer(q);
17156 destroyPQExpBuffer(delq);
17160 * dumpTableConstraintComment --- dump a constraint's comment if any
17162 * This is split out because we need the function in two different places
17163 * depending on whether the constraint is dumped as part of CREATE TABLE
17164 * or as a separate ALTER command.
17166 static void
17167 dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
17169 TableInfo *tbinfo = coninfo->contable;
17170 PQExpBuffer conprefix = createPQExpBuffer();
17171 char *qtabname;
17173 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17175 appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
17176 fmtId(coninfo->dobj.name));
17178 if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17179 dumpComment(fout, conprefix->data, qtabname,
17180 tbinfo->dobj.namespace->dobj.name,
17181 tbinfo->rolname,
17182 coninfo->dobj.catId, 0,
17183 coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
17185 destroyPQExpBuffer(conprefix);
17186 free(qtabname);
17190 * dumpSequence
17191 * write the declaration (not data) of one user-defined sequence
17193 static void
17194 dumpSequence(Archive *fout, const TableInfo *tbinfo)
17196 DumpOptions *dopt = fout->dopt;
17197 PGresult *res;
17198 char *startv,
17199 *incby,
17200 *maxv,
17201 *minv,
17202 *cache,
17203 *seqtype;
17204 bool cycled;
17205 bool is_ascending;
17206 int64 default_minv,
17207 default_maxv;
17208 char bufm[32],
17209 bufx[32];
17210 PQExpBuffer query = createPQExpBuffer();
17211 PQExpBuffer delqry = createPQExpBuffer();
17212 char *qseqname;
17213 TableInfo *owning_tab = NULL;
17215 qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
17217 if (fout->remoteVersion >= 100000)
17219 appendPQExpBuffer(query,
17220 "SELECT format_type(seqtypid, NULL), "
17221 "seqstart, seqincrement, "
17222 "seqmax, seqmin, "
17223 "seqcache, seqcycle "
17224 "FROM pg_catalog.pg_sequence "
17225 "WHERE seqrelid = '%u'::oid",
17226 tbinfo->dobj.catId.oid);
17228 else
17231 * Before PostgreSQL 10, sequence metadata is in the sequence itself.
17233 * Note: it might seem that 'bigint' potentially needs to be
17234 * schema-qualified, but actually that's a keyword.
17236 appendPQExpBuffer(query,
17237 "SELECT 'bigint' AS sequence_type, "
17238 "start_value, increment_by, max_value, min_value, "
17239 "cache_value, is_cycled FROM %s",
17240 fmtQualifiedDumpable(tbinfo));
17243 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17245 if (PQntuples(res) != 1)
17246 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17247 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17248 PQntuples(res)),
17249 tbinfo->dobj.name, PQntuples(res));
17251 seqtype = PQgetvalue(res, 0, 0);
17252 startv = PQgetvalue(res, 0, 1);
17253 incby = PQgetvalue(res, 0, 2);
17254 maxv = PQgetvalue(res, 0, 3);
17255 minv = PQgetvalue(res, 0, 4);
17256 cache = PQgetvalue(res, 0, 5);
17257 cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
17259 /* Calculate default limits for a sequence of this type */
17260 is_ascending = (incby[0] != '-');
17261 if (strcmp(seqtype, "smallint") == 0)
17263 default_minv = is_ascending ? 1 : PG_INT16_MIN;
17264 default_maxv = is_ascending ? PG_INT16_MAX : -1;
17266 else if (strcmp(seqtype, "integer") == 0)
17268 default_minv = is_ascending ? 1 : PG_INT32_MIN;
17269 default_maxv = is_ascending ? PG_INT32_MAX : -1;
17271 else if (strcmp(seqtype, "bigint") == 0)
17273 default_minv = is_ascending ? 1 : PG_INT64_MIN;
17274 default_maxv = is_ascending ? PG_INT64_MAX : -1;
17276 else
17278 pg_fatal("unrecognized sequence type: %s", seqtype);
17279 default_minv = default_maxv = 0; /* keep compiler quiet */
17283 * 64-bit strtol() isn't very portable, so convert the limits to strings
17284 * and compare that way.
17286 snprintf(bufm, sizeof(bufm), INT64_FORMAT, default_minv);
17287 snprintf(bufx, sizeof(bufx), INT64_FORMAT, default_maxv);
17289 /* Don't print minv/maxv if they match the respective default limit */
17290 if (strcmp(minv, bufm) == 0)
17291 minv = NULL;
17292 if (strcmp(maxv, bufx) == 0)
17293 maxv = NULL;
17296 * Identity sequences are not to be dropped separately.
17298 if (!tbinfo->is_identity_sequence)
17300 appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
17301 fmtQualifiedDumpable(tbinfo));
17304 resetPQExpBuffer(query);
17306 if (dopt->binary_upgrade)
17308 binary_upgrade_set_pg_class_oids(fout, query,
17309 tbinfo->dobj.catId.oid, false);
17312 * In older PG versions a sequence will have a pg_type entry, but v14
17313 * and up don't use that, so don't attempt to preserve the type OID.
17317 if (tbinfo->is_identity_sequence)
17319 owning_tab = findTableByOid(tbinfo->owning_tab);
17321 appendPQExpBuffer(query,
17322 "ALTER TABLE %s ",
17323 fmtQualifiedDumpable(owning_tab));
17324 appendPQExpBuffer(query,
17325 "ALTER COLUMN %s ADD GENERATED ",
17326 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17327 if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
17328 appendPQExpBufferStr(query, "ALWAYS");
17329 else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
17330 appendPQExpBufferStr(query, "BY DEFAULT");
17331 appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
17332 fmtQualifiedDumpable(tbinfo));
17334 else
17336 appendPQExpBuffer(query,
17337 "CREATE %sSEQUENCE %s\n",
17338 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17339 "UNLOGGED " : "",
17340 fmtQualifiedDumpable(tbinfo));
17342 if (strcmp(seqtype, "bigint") != 0)
17343 appendPQExpBuffer(query, " AS %s\n", seqtype);
17346 appendPQExpBuffer(query, " START WITH %s\n", startv);
17348 appendPQExpBuffer(query, " INCREMENT BY %s\n", incby);
17350 if (minv)
17351 appendPQExpBuffer(query, " MINVALUE %s\n", minv);
17352 else
17353 appendPQExpBufferStr(query, " NO MINVALUE\n");
17355 if (maxv)
17356 appendPQExpBuffer(query, " MAXVALUE %s\n", maxv);
17357 else
17358 appendPQExpBufferStr(query, " NO MAXVALUE\n");
17360 appendPQExpBuffer(query,
17361 " CACHE %s%s",
17362 cache, (cycled ? "\n CYCLE" : ""));
17364 if (tbinfo->is_identity_sequence)
17366 appendPQExpBufferStr(query, "\n);\n");
17367 if (tbinfo->relpersistence != owning_tab->relpersistence)
17368 appendPQExpBuffer(query,
17369 "ALTER SEQUENCE %s SET %s;\n",
17370 fmtQualifiedDumpable(tbinfo),
17371 tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17372 "UNLOGGED" : "LOGGED");
17374 else
17375 appendPQExpBufferStr(query, ";\n");
17377 /* binary_upgrade: no need to clear TOAST table oid */
17379 if (dopt->binary_upgrade)
17380 binary_upgrade_extension_member(query, &tbinfo->dobj,
17381 "SEQUENCE", qseqname,
17382 tbinfo->dobj.namespace->dobj.name);
17384 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17385 ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17386 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17387 .namespace = tbinfo->dobj.namespace->dobj.name,
17388 .owner = tbinfo->rolname,
17389 .description = "SEQUENCE",
17390 .section = SECTION_PRE_DATA,
17391 .createStmt = query->data,
17392 .dropStmt = delqry->data));
17395 * If the sequence is owned by a table column, emit the ALTER for it as a
17396 * separate TOC entry immediately following the sequence's own entry. It's
17397 * OK to do this rather than using full sorting logic, because the
17398 * dependency that tells us it's owned will have forced the table to be
17399 * created first. We can't just include the ALTER in the TOC entry
17400 * because it will fail if we haven't reassigned the sequence owner to
17401 * match the table's owner.
17403 * We need not schema-qualify the table reference because both sequence
17404 * and table must be in the same schema.
17406 if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
17408 owning_tab = findTableByOid(tbinfo->owning_tab);
17410 if (owning_tab == NULL)
17411 pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
17412 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
17414 if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
17416 resetPQExpBuffer(query);
17417 appendPQExpBuffer(query, "ALTER SEQUENCE %s",
17418 fmtQualifiedDumpable(tbinfo));
17419 appendPQExpBuffer(query, " OWNED BY %s",
17420 fmtQualifiedDumpable(owning_tab));
17421 appendPQExpBuffer(query, ".%s;\n",
17422 fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17424 if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17425 ArchiveEntry(fout, nilCatalogId, createDumpId(),
17426 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17427 .namespace = tbinfo->dobj.namespace->dobj.name,
17428 .owner = tbinfo->rolname,
17429 .description = "SEQUENCE OWNED BY",
17430 .section = SECTION_PRE_DATA,
17431 .createStmt = query->data,
17432 .deps = &(tbinfo->dobj.dumpId),
17433 .nDeps = 1));
17437 /* Dump Sequence Comments and Security Labels */
17438 if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17439 dumpComment(fout, "SEQUENCE", qseqname,
17440 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17441 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17443 if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17444 dumpSecLabel(fout, "SEQUENCE", qseqname,
17445 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17446 tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17448 PQclear(res);
17450 destroyPQExpBuffer(query);
17451 destroyPQExpBuffer(delqry);
17452 free(qseqname);
17456 * dumpSequenceData
17457 * write the data of one user-defined sequence
17459 static void
17460 dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
17462 TableInfo *tbinfo = tdinfo->tdtable;
17463 PGresult *res;
17464 char *last;
17465 bool called;
17466 PQExpBuffer query = createPQExpBuffer();
17468 appendPQExpBuffer(query,
17469 "SELECT last_value, is_called FROM %s",
17470 fmtQualifiedDumpable(tbinfo));
17472 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17474 if (PQntuples(res) != 1)
17475 pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17476 "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17477 PQntuples(res)),
17478 tbinfo->dobj.name, PQntuples(res));
17480 last = PQgetvalue(res, 0, 0);
17481 called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
17483 resetPQExpBuffer(query);
17484 appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
17485 appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
17486 appendPQExpBuffer(query, ", %s, %s);\n",
17487 last, (called ? "true" : "false"));
17489 if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
17490 ArchiveEntry(fout, nilCatalogId, createDumpId(),
17491 ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17492 .namespace = tbinfo->dobj.namespace->dobj.name,
17493 .owner = tbinfo->rolname,
17494 .description = "SEQUENCE SET",
17495 .section = SECTION_DATA,
17496 .createStmt = query->data,
17497 .deps = &(tbinfo->dobj.dumpId),
17498 .nDeps = 1));
17500 PQclear(res);
17502 destroyPQExpBuffer(query);
17506 * dumpTrigger
17507 * write the declaration of one user-defined table trigger
17509 static void
17510 dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
17512 DumpOptions *dopt = fout->dopt;
17513 TableInfo *tbinfo = tginfo->tgtable;
17514 PQExpBuffer query;
17515 PQExpBuffer delqry;
17516 PQExpBuffer trigprefix;
17517 PQExpBuffer trigidentity;
17518 char *qtabname;
17519 char *tgargs;
17520 size_t lentgargs;
17521 const char *p;
17522 int findx;
17523 char *tag;
17525 /* Do nothing in data-only dump */
17526 if (dopt->dataOnly)
17527 return;
17529 query = createPQExpBuffer();
17530 delqry = createPQExpBuffer();
17531 trigprefix = createPQExpBuffer();
17532 trigidentity = createPQExpBuffer();
17534 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17536 appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
17537 appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
17539 appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
17541 if (tginfo->tgdef)
17543 appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
17545 else
17547 if (tginfo->tgisconstraint)
17549 appendPQExpBufferStr(query, "CREATE CONSTRAINT TRIGGER ");
17550 appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
17552 else
17554 appendPQExpBufferStr(query, "CREATE TRIGGER ");
17555 appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
17557 appendPQExpBufferStr(query, "\n ");
17559 /* Trigger type */
17560 if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
17561 appendPQExpBufferStr(query, "BEFORE");
17562 else if (TRIGGER_FOR_AFTER(tginfo->tgtype))
17563 appendPQExpBufferStr(query, "AFTER");
17564 else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
17565 appendPQExpBufferStr(query, "INSTEAD OF");
17566 else
17567 pg_fatal("unexpected tgtype value: %d", tginfo->tgtype);
17569 findx = 0;
17570 if (TRIGGER_FOR_INSERT(tginfo->tgtype))
17572 appendPQExpBufferStr(query, " INSERT");
17573 findx++;
17575 if (TRIGGER_FOR_DELETE(tginfo->tgtype))
17577 if (findx > 0)
17578 appendPQExpBufferStr(query, " OR DELETE");
17579 else
17580 appendPQExpBufferStr(query, " DELETE");
17581 findx++;
17583 if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
17585 if (findx > 0)
17586 appendPQExpBufferStr(query, " OR UPDATE");
17587 else
17588 appendPQExpBufferStr(query, " UPDATE");
17589 findx++;
17591 if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype))
17593 if (findx > 0)
17594 appendPQExpBufferStr(query, " OR TRUNCATE");
17595 else
17596 appendPQExpBufferStr(query, " TRUNCATE");
17597 findx++;
17599 appendPQExpBuffer(query, " ON %s\n",
17600 fmtQualifiedDumpable(tbinfo));
17602 if (tginfo->tgisconstraint)
17604 if (OidIsValid(tginfo->tgconstrrelid))
17606 /* regclass output is already quoted */
17607 appendPQExpBuffer(query, " FROM %s\n ",
17608 tginfo->tgconstrrelname);
17610 if (!tginfo->tgdeferrable)
17611 appendPQExpBufferStr(query, "NOT ");
17612 appendPQExpBufferStr(query, "DEFERRABLE INITIALLY ");
17613 if (tginfo->tginitdeferred)
17614 appendPQExpBufferStr(query, "DEFERRED\n");
17615 else
17616 appendPQExpBufferStr(query, "IMMEDIATE\n");
17619 if (TRIGGER_FOR_ROW(tginfo->tgtype))
17620 appendPQExpBufferStr(query, " FOR EACH ROW\n ");
17621 else
17622 appendPQExpBufferStr(query, " FOR EACH STATEMENT\n ");
17624 /* regproc output is already sufficiently quoted */
17625 appendPQExpBuffer(query, "EXECUTE FUNCTION %s(",
17626 tginfo->tgfname);
17628 tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs,
17629 &lentgargs);
17630 p = tgargs;
17631 for (findx = 0; findx < tginfo->tgnargs; findx++)
17633 /* find the embedded null that terminates this trigger argument */
17634 size_t tlen = strlen(p);
17636 if (p + tlen >= tgargs + lentgargs)
17638 /* hm, not found before end of bytea value... */
17639 pg_fatal("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
17640 tginfo->tgargs,
17641 tginfo->dobj.name,
17642 tbinfo->dobj.name);
17645 if (findx > 0)
17646 appendPQExpBufferStr(query, ", ");
17647 appendStringLiteralAH(query, p, fout);
17648 p += tlen + 1;
17650 free(tgargs);
17651 appendPQExpBufferStr(query, ");\n");
17654 /* Triggers can depend on extensions */
17655 append_depends_on_extension(fout, query, &tginfo->dobj,
17656 "pg_catalog.pg_trigger", "TRIGGER",
17657 trigidentity->data);
17659 if (tginfo->tgispartition)
17661 Assert(tbinfo->ispartition);
17664 * Partition triggers only appear here because their 'tgenabled' flag
17665 * differs from its parent's. The trigger is created already, so
17666 * remove the CREATE and replace it with an ALTER. (Clear out the
17667 * DROP query too, so that pg_dump --create does not cause errors.)
17669 resetPQExpBuffer(query);
17670 resetPQExpBuffer(delqry);
17671 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
17672 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
17673 fmtQualifiedDumpable(tbinfo));
17674 switch (tginfo->tgenabled)
17676 case 'f':
17677 case 'D':
17678 appendPQExpBufferStr(query, "DISABLE");
17679 break;
17680 case 't':
17681 case 'O':
17682 appendPQExpBufferStr(query, "ENABLE");
17683 break;
17684 case 'R':
17685 appendPQExpBufferStr(query, "ENABLE REPLICA");
17686 break;
17687 case 'A':
17688 appendPQExpBufferStr(query, "ENABLE ALWAYS");
17689 break;
17691 appendPQExpBuffer(query, " TRIGGER %s;\n",
17692 fmtId(tginfo->dobj.name));
17694 else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
17696 appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
17697 tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
17698 fmtQualifiedDumpable(tbinfo));
17699 switch (tginfo->tgenabled)
17701 case 'D':
17702 case 'f':
17703 appendPQExpBufferStr(query, "DISABLE");
17704 break;
17705 case 'A':
17706 appendPQExpBufferStr(query, "ENABLE ALWAYS");
17707 break;
17708 case 'R':
17709 appendPQExpBufferStr(query, "ENABLE REPLICA");
17710 break;
17711 default:
17712 appendPQExpBufferStr(query, "ENABLE");
17713 break;
17715 appendPQExpBuffer(query, " TRIGGER %s;\n",
17716 fmtId(tginfo->dobj.name));
17719 appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
17720 fmtId(tginfo->dobj.name));
17722 tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
17724 if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17725 ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
17726 ARCHIVE_OPTS(.tag = tag,
17727 .namespace = tbinfo->dobj.namespace->dobj.name,
17728 .owner = tbinfo->rolname,
17729 .description = "TRIGGER",
17730 .section = SECTION_POST_DATA,
17731 .createStmt = query->data,
17732 .dropStmt = delqry->data));
17734 if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17735 dumpComment(fout, trigprefix->data, qtabname,
17736 tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17737 tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
17739 free(tag);
17740 destroyPQExpBuffer(query);
17741 destroyPQExpBuffer(delqry);
17742 destroyPQExpBuffer(trigprefix);
17743 destroyPQExpBuffer(trigidentity);
17744 free(qtabname);
17748 * dumpEventTrigger
17749 * write the declaration of one user-defined event trigger
17751 static void
17752 dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
17754 DumpOptions *dopt = fout->dopt;
17755 PQExpBuffer query;
17756 PQExpBuffer delqry;
17757 char *qevtname;
17759 /* Do nothing in data-only dump */
17760 if (dopt->dataOnly)
17761 return;
17763 query = createPQExpBuffer();
17764 delqry = createPQExpBuffer();
17766 qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
17768 appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
17769 appendPQExpBufferStr(query, qevtname);
17770 appendPQExpBufferStr(query, " ON ");
17771 appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
17773 if (strcmp("", evtinfo->evttags) != 0)
17775 appendPQExpBufferStr(query, "\n WHEN TAG IN (");
17776 appendPQExpBufferStr(query, evtinfo->evttags);
17777 appendPQExpBufferChar(query, ')');
17780 appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
17781 appendPQExpBufferStr(query, evtinfo->evtfname);
17782 appendPQExpBufferStr(query, "();\n");
17784 if (evtinfo->evtenabled != 'O')
17786 appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
17787 qevtname);
17788 switch (evtinfo->evtenabled)
17790 case 'D':
17791 appendPQExpBufferStr(query, "DISABLE");
17792 break;
17793 case 'A':
17794 appendPQExpBufferStr(query, "ENABLE ALWAYS");
17795 break;
17796 case 'R':
17797 appendPQExpBufferStr(query, "ENABLE REPLICA");
17798 break;
17799 default:
17800 appendPQExpBufferStr(query, "ENABLE");
17801 break;
17803 appendPQExpBufferStr(query, ";\n");
17806 appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
17807 qevtname);
17809 if (dopt->binary_upgrade)
17810 binary_upgrade_extension_member(query, &evtinfo->dobj,
17811 "EVENT TRIGGER", qevtname, NULL);
17813 if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17814 ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
17815 ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
17816 .owner = evtinfo->evtowner,
17817 .description = "EVENT TRIGGER",
17818 .section = SECTION_POST_DATA,
17819 .createStmt = query->data,
17820 .dropStmt = delqry->data));
17822 if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17823 dumpComment(fout, "EVENT TRIGGER", qevtname,
17824 NULL, evtinfo->evtowner,
17825 evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
17827 destroyPQExpBuffer(query);
17828 destroyPQExpBuffer(delqry);
17829 free(qevtname);
17833 * dumpRule
17834 * Dump a rule
17836 static void
17837 dumpRule(Archive *fout, const RuleInfo *rinfo)
17839 DumpOptions *dopt = fout->dopt;
17840 TableInfo *tbinfo = rinfo->ruletable;
17841 bool is_view;
17842 PQExpBuffer query;
17843 PQExpBuffer cmd;
17844 PQExpBuffer delcmd;
17845 PQExpBuffer ruleprefix;
17846 char *qtabname;
17847 PGresult *res;
17848 char *tag;
17850 /* Do nothing in data-only dump */
17851 if (dopt->dataOnly)
17852 return;
17855 * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
17856 * we do not want to dump it as a separate object.
17858 if (!rinfo->separate)
17859 return;
17862 * If it's an ON SELECT rule, we want to print it as a view definition,
17863 * instead of a rule.
17865 is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
17867 query = createPQExpBuffer();
17868 cmd = createPQExpBuffer();
17869 delcmd = createPQExpBuffer();
17870 ruleprefix = createPQExpBuffer();
17872 qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17874 if (is_view)
17876 PQExpBuffer result;
17879 * We need OR REPLACE here because we'll be replacing a dummy view.
17880 * Otherwise this should look largely like the regular view dump code.
17882 appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
17883 fmtQualifiedDumpable(tbinfo));
17884 if (nonemptyReloptions(tbinfo->reloptions))
17886 appendPQExpBufferStr(cmd, " WITH (");
17887 appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
17888 appendPQExpBufferChar(cmd, ')');
17890 result = createViewAsClause(fout, tbinfo);
17891 appendPQExpBuffer(cmd, " AS\n%s", result->data);
17892 destroyPQExpBuffer(result);
17893 if (tbinfo->checkoption != NULL)
17894 appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
17895 tbinfo->checkoption);
17896 appendPQExpBufferStr(cmd, ";\n");
17898 else
17900 /* In the rule case, just print pg_get_ruledef's result verbatim */
17901 appendPQExpBuffer(query,
17902 "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
17903 rinfo->dobj.catId.oid);
17905 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17907 if (PQntuples(res) != 1)
17908 pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
17909 rinfo->dobj.name, tbinfo->dobj.name);
17911 printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
17913 PQclear(res);
17917 * Add the command to alter the rules replication firing semantics if it
17918 * differs from the default.
17920 if (rinfo->ev_enabled != 'O')
17922 appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
17923 switch (rinfo->ev_enabled)
17925 case 'A':
17926 appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
17927 fmtId(rinfo->dobj.name));
17928 break;
17929 case 'R':
17930 appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
17931 fmtId(rinfo->dobj.name));
17932 break;
17933 case 'D':
17934 appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
17935 fmtId(rinfo->dobj.name));
17936 break;
17940 if (is_view)
17943 * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
17944 * REPLACE VIEW to replace the rule with something with minimal
17945 * dependencies.
17947 PQExpBuffer result;
17949 appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
17950 fmtQualifiedDumpable(tbinfo));
17951 result = createDummyViewAsClause(fout, tbinfo);
17952 appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
17953 destroyPQExpBuffer(result);
17955 else
17957 appendPQExpBuffer(delcmd, "DROP RULE %s ",
17958 fmtId(rinfo->dobj.name));
17959 appendPQExpBuffer(delcmd, "ON %s;\n",
17960 fmtQualifiedDumpable(tbinfo));
17963 appendPQExpBuffer(ruleprefix, "RULE %s ON",
17964 fmtId(rinfo->dobj.name));
17966 tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
17968 if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17969 ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
17970 ARCHIVE_OPTS(.tag = tag,
17971 .namespace = tbinfo->dobj.namespace->dobj.name,
17972 .owner = tbinfo->rolname,
17973 .description = "RULE",
17974 .section = SECTION_POST_DATA,
17975 .createStmt = cmd->data,
17976 .dropStmt = delcmd->data));
17978 /* Dump rule comments */
17979 if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17980 dumpComment(fout, ruleprefix->data, qtabname,
17981 tbinfo->dobj.namespace->dobj.name,
17982 tbinfo->rolname,
17983 rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
17985 free(tag);
17986 destroyPQExpBuffer(query);
17987 destroyPQExpBuffer(cmd);
17988 destroyPQExpBuffer(delcmd);
17989 destroyPQExpBuffer(ruleprefix);
17990 free(qtabname);
17994 * getExtensionMembership --- obtain extension membership data
17996 * We need to identify objects that are extension members as soon as they're
17997 * loaded, so that we can correctly determine whether they need to be dumped.
17998 * Generally speaking, extension member objects will get marked as *not* to
17999 * be dumped, as they will be recreated by the single CREATE EXTENSION
18000 * command. However, in binary upgrade mode we still need to dump the members
18001 * individually.
18003 void
18004 getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18005 int numExtensions)
18007 PQExpBuffer query;
18008 PGresult *res;
18009 int ntups,
18011 int i_classid,
18012 i_objid,
18013 i_refobjid;
18014 ExtensionInfo *ext;
18016 /* Nothing to do if no extensions */
18017 if (numExtensions == 0)
18018 return;
18020 query = createPQExpBuffer();
18022 /* refclassid constraint is redundant but may speed the search */
18023 appendPQExpBufferStr(query, "SELECT "
18024 "classid, objid, refobjid "
18025 "FROM pg_depend "
18026 "WHERE refclassid = 'pg_extension'::regclass "
18027 "AND deptype = 'e' "
18028 "ORDER BY 3");
18030 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18032 ntups = PQntuples(res);
18034 i_classid = PQfnumber(res, "classid");
18035 i_objid = PQfnumber(res, "objid");
18036 i_refobjid = PQfnumber(res, "refobjid");
18039 * Since we ordered the SELECT by referenced ID, we can expect that
18040 * multiple entries for the same extension will appear together; this
18041 * saves on searches.
18043 ext = NULL;
18045 for (i = 0; i < ntups; i++)
18047 CatalogId objId;
18048 Oid extId;
18050 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18051 objId.oid = atooid(PQgetvalue(res, i, i_objid));
18052 extId = atooid(PQgetvalue(res, i, i_refobjid));
18054 if (ext == NULL ||
18055 ext->dobj.catId.oid != extId)
18056 ext = findExtensionByOid(extId);
18058 if (ext == NULL)
18060 /* shouldn't happen */
18061 pg_log_warning("could not find referenced extension %u", extId);
18062 continue;
18065 recordExtensionMembership(objId, ext);
18068 PQclear(res);
18070 destroyPQExpBuffer(query);
18074 * processExtensionTables --- deal with extension configuration tables
18076 * There are two parts to this process:
18078 * 1. Identify and create dump records for extension configuration tables.
18080 * Extensions can mark tables as "configuration", which means that the user
18081 * is able and expected to modify those tables after the extension has been
18082 * loaded. For these tables, we dump out only the data- the structure is
18083 * expected to be handled at CREATE EXTENSION time, including any indexes or
18084 * foreign keys, which brings us to-
18086 * 2. Record FK dependencies between configuration tables.
18088 * Due to the FKs being created at CREATE EXTENSION time and therefore before
18089 * the data is loaded, we have to work out what the best order for reloading
18090 * the data is, to avoid FK violations when the tables are restored. This is
18091 * not perfect- we can't handle circular dependencies and if any exist they
18092 * will cause an invalid dump to be produced (though at least all of the data
18093 * is included for a user to manually restore). This is currently documented
18094 * but perhaps we can provide a better solution in the future.
18096 void
18097 processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
18098 int numExtensions)
18100 DumpOptions *dopt = fout->dopt;
18101 PQExpBuffer query;
18102 PGresult *res;
18103 int ntups,
18105 int i_conrelid,
18106 i_confrelid;
18108 /* Nothing to do if no extensions */
18109 if (numExtensions == 0)
18110 return;
18113 * Identify extension configuration tables and create TableDataInfo
18114 * objects for them, ensuring their data will be dumped even though the
18115 * tables themselves won't be.
18117 * Note that we create TableDataInfo objects even in schemaOnly mode, ie,
18118 * user data in a configuration table is treated like schema data. This
18119 * seems appropriate since system data in a config table would get
18120 * reloaded by CREATE EXTENSION. If the extension is not listed in the
18121 * list of extensions to be included, none of its data is dumped.
18123 for (i = 0; i < numExtensions; i++)
18125 ExtensionInfo *curext = &(extinfo[i]);
18126 char *extconfig = curext->extconfig;
18127 char *extcondition = curext->extcondition;
18128 char **extconfigarray = NULL;
18129 char **extconditionarray = NULL;
18130 int nconfigitems = 0;
18131 int nconditionitems = 0;
18134 * Check if this extension is listed as to include in the dump. If
18135 * not, any table data associated with it is discarded.
18137 if (extension_include_oids.head != NULL &&
18138 !simple_oid_list_member(&extension_include_oids,
18139 curext->dobj.catId.oid))
18140 continue;
18142 if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
18144 int j;
18146 if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
18147 pg_fatal("could not parse %s array", "extconfig");
18148 if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
18149 pg_fatal("could not parse %s array", "extcondition");
18150 if (nconfigitems != nconditionitems)
18151 pg_fatal("mismatched number of configurations and conditions for extension");
18153 for (j = 0; j < nconfigitems; j++)
18155 TableInfo *configtbl;
18156 Oid configtbloid = atooid(extconfigarray[j]);
18157 bool dumpobj =
18158 curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
18160 configtbl = findTableByOid(configtbloid);
18161 if (configtbl == NULL)
18162 continue;
18165 * Tables of not-to-be-dumped extensions shouldn't be dumped
18166 * unless the table or its schema is explicitly included
18168 if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
18170 /* check table explicitly requested */
18171 if (table_include_oids.head != NULL &&
18172 simple_oid_list_member(&table_include_oids,
18173 configtbloid))
18174 dumpobj = true;
18176 /* check table's schema explicitly requested */
18177 if (configtbl->dobj.namespace->dobj.dump &
18178 DUMP_COMPONENT_DATA)
18179 dumpobj = true;
18182 /* check table excluded by an exclusion switch */
18183 if (table_exclude_oids.head != NULL &&
18184 simple_oid_list_member(&table_exclude_oids,
18185 configtbloid))
18186 dumpobj = false;
18188 /* check schema excluded by an exclusion switch */
18189 if (simple_oid_list_member(&schema_exclude_oids,
18190 configtbl->dobj.namespace->dobj.catId.oid))
18191 dumpobj = false;
18193 if (dumpobj)
18195 makeTableDataInfo(dopt, configtbl);
18196 if (configtbl->dataObj != NULL)
18198 if (strlen(extconditionarray[j]) > 0)
18199 configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
18204 if (extconfigarray)
18205 free(extconfigarray);
18206 if (extconditionarray)
18207 free(extconditionarray);
18211 * Now that all the TableDataInfo objects have been created for all the
18212 * extensions, check their FK dependencies and register them to try and
18213 * dump the data out in an order that they can be restored in.
18215 * Note that this is not a problem for user tables as their FKs are
18216 * recreated after the data has been loaded.
18219 query = createPQExpBuffer();
18221 printfPQExpBuffer(query,
18222 "SELECT conrelid, confrelid "
18223 "FROM pg_constraint "
18224 "JOIN pg_depend ON (objid = confrelid) "
18225 "WHERE contype = 'f' "
18226 "AND refclassid = 'pg_extension'::regclass "
18227 "AND classid = 'pg_class'::regclass;");
18229 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18230 ntups = PQntuples(res);
18232 i_conrelid = PQfnumber(res, "conrelid");
18233 i_confrelid = PQfnumber(res, "confrelid");
18235 /* Now get the dependencies and register them */
18236 for (i = 0; i < ntups; i++)
18238 Oid conrelid,
18239 confrelid;
18240 TableInfo *reftable,
18241 *contable;
18243 conrelid = atooid(PQgetvalue(res, i, i_conrelid));
18244 confrelid = atooid(PQgetvalue(res, i, i_confrelid));
18245 contable = findTableByOid(conrelid);
18246 reftable = findTableByOid(confrelid);
18248 if (reftable == NULL ||
18249 reftable->dataObj == NULL ||
18250 contable == NULL ||
18251 contable->dataObj == NULL)
18252 continue;
18255 * Make referencing TABLE_DATA object depend on the referenced table's
18256 * TABLE_DATA object.
18258 addObjectDependency(&contable->dataObj->dobj,
18259 reftable->dataObj->dobj.dumpId);
18261 PQclear(res);
18262 destroyPQExpBuffer(query);
18266 * getDependencies --- obtain available dependency data
18268 static void
18269 getDependencies(Archive *fout)
18271 PQExpBuffer query;
18272 PGresult *res;
18273 int ntups,
18275 int i_classid,
18276 i_objid,
18277 i_refclassid,
18278 i_refobjid,
18279 i_deptype;
18280 DumpableObject *dobj,
18281 *refdobj;
18283 pg_log_info("reading dependency data");
18285 query = createPQExpBuffer();
18288 * Messy query to collect the dependency data we need. Note that we
18289 * ignore the sub-object column, so that dependencies of or on a column
18290 * look the same as dependencies of or on a whole table.
18292 * PIN dependencies aren't interesting, and EXTENSION dependencies were
18293 * already processed by getExtensionMembership.
18295 appendPQExpBufferStr(query, "SELECT "
18296 "classid, objid, refclassid, refobjid, deptype "
18297 "FROM pg_depend "
18298 "WHERE deptype != 'p' AND deptype != 'e'\n");
18301 * Since we don't treat pg_amop entries as separate DumpableObjects, we
18302 * have to translate their dependencies into dependencies of their parent
18303 * opfamily. Ignore internal dependencies though, as those will point to
18304 * their parent opclass, which we needn't consider here (and if we did,
18305 * it'd just result in circular dependencies). Also, "loose" opfamily
18306 * entries will have dependencies on their parent opfamily, which we
18307 * should drop since they'd likewise become useless self-dependencies.
18308 * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
18310 appendPQExpBufferStr(query, "UNION ALL\n"
18311 "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
18312 "FROM pg_depend d, pg_amop o "
18313 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18314 "classid = 'pg_amop'::regclass AND objid = o.oid "
18315 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
18317 /* Likewise for pg_amproc entries */
18318 appendPQExpBufferStr(query, "UNION ALL\n"
18319 "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
18320 "FROM pg_depend d, pg_amproc p "
18321 "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18322 "classid = 'pg_amproc'::regclass AND objid = p.oid "
18323 "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
18325 /* Sort the output for efficiency below */
18326 appendPQExpBufferStr(query, "ORDER BY 1,2");
18328 res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18330 ntups = PQntuples(res);
18332 i_classid = PQfnumber(res, "classid");
18333 i_objid = PQfnumber(res, "objid");
18334 i_refclassid = PQfnumber(res, "refclassid");
18335 i_refobjid = PQfnumber(res, "refobjid");
18336 i_deptype = PQfnumber(res, "deptype");
18339 * Since we ordered the SELECT by referencing ID, we can expect that
18340 * multiple entries for the same object will appear together; this saves
18341 * on searches.
18343 dobj = NULL;
18345 for (i = 0; i < ntups; i++)
18347 CatalogId objId;
18348 CatalogId refobjId;
18349 char deptype;
18351 objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18352 objId.oid = atooid(PQgetvalue(res, i, i_objid));
18353 refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
18354 refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
18355 deptype = *(PQgetvalue(res, i, i_deptype));
18357 if (dobj == NULL ||
18358 dobj->catId.tableoid != objId.tableoid ||
18359 dobj->catId.oid != objId.oid)
18360 dobj = findObjectByCatalogId(objId);
18363 * Failure to find objects mentioned in pg_depend is not unexpected,
18364 * since for example we don't collect info about TOAST tables.
18366 if (dobj == NULL)
18368 #ifdef NOT_USED
18369 pg_log_warning("no referencing object %u %u",
18370 objId.tableoid, objId.oid);
18371 #endif
18372 continue;
18375 refdobj = findObjectByCatalogId(refobjId);
18377 if (refdobj == NULL)
18379 #ifdef NOT_USED
18380 pg_log_warning("no referenced object %u %u",
18381 refobjId.tableoid, refobjId.oid);
18382 #endif
18383 continue;
18387 * For 'x' dependencies, mark the object for later; we still add the
18388 * normal dependency, for possible ordering purposes. Currently
18389 * pg_dump_sort.c knows to put extensions ahead of all object types
18390 * that could possibly depend on them, but this is safer.
18392 if (deptype == 'x')
18393 dobj->depends_on_ext = true;
18396 * Ordinarily, table rowtypes have implicit dependencies on their
18397 * tables. However, for a composite type the implicit dependency goes
18398 * the other way in pg_depend; which is the right thing for DROP but
18399 * it doesn't produce the dependency ordering we need. So in that one
18400 * case, we reverse the direction of the dependency.
18402 if (deptype == 'i' &&
18403 dobj->objType == DO_TABLE &&
18404 refdobj->objType == DO_TYPE)
18405 addObjectDependency(refdobj, dobj->dumpId);
18406 else
18407 /* normal case */
18408 addObjectDependency(dobj, refdobj->dumpId);
18411 PQclear(res);
18413 destroyPQExpBuffer(query);
18418 * createBoundaryObjects - create dummy DumpableObjects to represent
18419 * dump section boundaries.
18421 static DumpableObject *
18422 createBoundaryObjects(void)
18424 DumpableObject *dobjs;
18426 dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
18428 dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
18429 dobjs[0].catId = nilCatalogId;
18430 AssignDumpId(dobjs + 0);
18431 dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
18433 dobjs[1].objType = DO_POST_DATA_BOUNDARY;
18434 dobjs[1].catId = nilCatalogId;
18435 AssignDumpId(dobjs + 1);
18436 dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
18438 return dobjs;
18442 * addBoundaryDependencies - add dependencies as needed to enforce the dump
18443 * section boundaries.
18445 static void
18446 addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
18447 DumpableObject *boundaryObjs)
18449 DumpableObject *preDataBound = boundaryObjs + 0;
18450 DumpableObject *postDataBound = boundaryObjs + 1;
18451 int i;
18453 for (i = 0; i < numObjs; i++)
18455 DumpableObject *dobj = dobjs[i];
18458 * The classification of object types here must match the SECTION_xxx
18459 * values assigned during subsequent ArchiveEntry calls!
18461 switch (dobj->objType)
18463 case DO_NAMESPACE:
18464 case DO_EXTENSION:
18465 case DO_TYPE:
18466 case DO_SHELL_TYPE:
18467 case DO_FUNC:
18468 case DO_AGG:
18469 case DO_OPERATOR:
18470 case DO_ACCESS_METHOD:
18471 case DO_OPCLASS:
18472 case DO_OPFAMILY:
18473 case DO_COLLATION:
18474 case DO_CONVERSION:
18475 case DO_TABLE:
18476 case DO_TABLE_ATTACH:
18477 case DO_ATTRDEF:
18478 case DO_PROCLANG:
18479 case DO_CAST:
18480 case DO_DUMMY_TYPE:
18481 case DO_TSPARSER:
18482 case DO_TSDICT:
18483 case DO_TSTEMPLATE:
18484 case DO_TSCONFIG:
18485 case DO_FDW:
18486 case DO_FOREIGN_SERVER:
18487 case DO_TRANSFORM:
18488 case DO_LARGE_OBJECT:
18489 /* Pre-data objects: must come before the pre-data boundary */
18490 addObjectDependency(preDataBound, dobj->dumpId);
18491 break;
18492 case DO_TABLE_DATA:
18493 case DO_SEQUENCE_SET:
18494 case DO_LARGE_OBJECT_DATA:
18495 /* Data objects: must come between the boundaries */
18496 addObjectDependency(dobj, preDataBound->dumpId);
18497 addObjectDependency(postDataBound, dobj->dumpId);
18498 break;
18499 case DO_INDEX:
18500 case DO_INDEX_ATTACH:
18501 case DO_STATSEXT:
18502 case DO_REFRESH_MATVIEW:
18503 case DO_TRIGGER:
18504 case DO_EVENT_TRIGGER:
18505 case DO_DEFAULT_ACL:
18506 case DO_POLICY:
18507 case DO_PUBLICATION:
18508 case DO_PUBLICATION_REL:
18509 case DO_PUBLICATION_TABLE_IN_SCHEMA:
18510 case DO_SUBSCRIPTION:
18511 /* Post-data objects: must come after the post-data boundary */
18512 addObjectDependency(dobj, postDataBound->dumpId);
18513 break;
18514 case DO_RULE:
18515 /* Rules are post-data, but only if dumped separately */
18516 if (((RuleInfo *) dobj)->separate)
18517 addObjectDependency(dobj, postDataBound->dumpId);
18518 break;
18519 case DO_CONSTRAINT:
18520 case DO_FK_CONSTRAINT:
18521 /* Constraints are post-data, but only if dumped separately */
18522 if (((ConstraintInfo *) dobj)->separate)
18523 addObjectDependency(dobj, postDataBound->dumpId);
18524 break;
18525 case DO_PRE_DATA_BOUNDARY:
18526 /* nothing to do */
18527 break;
18528 case DO_POST_DATA_BOUNDARY:
18529 /* must come after the pre-data boundary */
18530 addObjectDependency(dobj, preDataBound->dumpId);
18531 break;
18538 * BuildArchiveDependencies - create dependency data for archive TOC entries
18540 * The raw dependency data obtained by getDependencies() is not terribly
18541 * useful in an archive dump, because in many cases there are dependency
18542 * chains linking through objects that don't appear explicitly in the dump.
18543 * For example, a view will depend on its _RETURN rule while the _RETURN rule
18544 * will depend on other objects --- but the rule will not appear as a separate
18545 * object in the dump. We need to adjust the view's dependencies to include
18546 * whatever the rule depends on that is included in the dump.
18548 * Just to make things more complicated, there are also "special" dependencies
18549 * such as the dependency of a TABLE DATA item on its TABLE, which we must
18550 * not rearrange because pg_restore knows that TABLE DATA only depends on
18551 * its table. In these cases we must leave the dependencies strictly as-is
18552 * even if they refer to not-to-be-dumped objects.
18554 * To handle this, the convention is that "special" dependencies are created
18555 * during ArchiveEntry calls, and an archive TOC item that has any such
18556 * entries will not be touched here. Otherwise, we recursively search the
18557 * DumpableObject data structures to build the correct dependencies for each
18558 * archive TOC item.
18560 static void
18561 BuildArchiveDependencies(Archive *fout)
18563 ArchiveHandle *AH = (ArchiveHandle *) fout;
18564 TocEntry *te;
18566 /* Scan all TOC entries in the archive */
18567 for (te = AH->toc->next; te != AH->toc; te = te->next)
18569 DumpableObject *dobj;
18570 DumpId *dependencies;
18571 int nDeps;
18572 int allocDeps;
18574 /* No need to process entries that will not be dumped */
18575 if (te->reqs == 0)
18576 continue;
18577 /* Ignore entries that already have "special" dependencies */
18578 if (te->nDeps > 0)
18579 continue;
18580 /* Otherwise, look up the item's original DumpableObject, if any */
18581 dobj = findObjectByDumpId(te->dumpId);
18582 if (dobj == NULL)
18583 continue;
18584 /* No work if it has no dependencies */
18585 if (dobj->nDeps <= 0)
18586 continue;
18587 /* Set up work array */
18588 allocDeps = 64;
18589 dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
18590 nDeps = 0;
18591 /* Recursively find all dumpable dependencies */
18592 findDumpableDependencies(AH, dobj,
18593 &dependencies, &nDeps, &allocDeps);
18594 /* And save 'em ... */
18595 if (nDeps > 0)
18597 dependencies = (DumpId *) pg_realloc(dependencies,
18598 nDeps * sizeof(DumpId));
18599 te->dependencies = dependencies;
18600 te->nDeps = nDeps;
18602 else
18603 free(dependencies);
18607 /* Recursive search subroutine for BuildArchiveDependencies */
18608 static void
18609 findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
18610 DumpId **dependencies, int *nDeps, int *allocDeps)
18612 int i;
18615 * Ignore section boundary objects: if we search through them, we'll
18616 * report lots of bogus dependencies.
18618 if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
18619 dobj->objType == DO_POST_DATA_BOUNDARY)
18620 return;
18622 for (i = 0; i < dobj->nDeps; i++)
18624 DumpId depid = dobj->dependencies[i];
18626 if (TocIDRequired(AH, depid) != 0)
18628 /* Object will be dumped, so just reference it as a dependency */
18629 if (*nDeps >= *allocDeps)
18631 *allocDeps *= 2;
18632 *dependencies = (DumpId *) pg_realloc(*dependencies,
18633 *allocDeps * sizeof(DumpId));
18635 (*dependencies)[*nDeps] = depid;
18636 (*nDeps)++;
18638 else
18641 * Object will not be dumped, so recursively consider its deps. We
18642 * rely on the assumption that sortDumpableObjects already broke
18643 * any dependency loops, else we might recurse infinitely.
18645 DumpableObject *otherdobj = findObjectByDumpId(depid);
18647 if (otherdobj)
18648 findDumpableDependencies(AH, otherdobj,
18649 dependencies, nDeps, allocDeps);
18656 * getFormattedTypeName - retrieve a nicely-formatted type name for the
18657 * given type OID.
18659 * This does not guarantee to schema-qualify the output, so it should not
18660 * be used to create the target object name for CREATE or ALTER commands.
18662 * Note that the result is cached and must not be freed by the caller.
18664 static const char *
18665 getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
18667 TypeInfo *typeInfo;
18668 char *result;
18669 PQExpBuffer query;
18670 PGresult *res;
18672 if (oid == 0)
18674 if ((opts & zeroAsStar) != 0)
18675 return "*";
18676 else if ((opts & zeroAsNone) != 0)
18677 return "NONE";
18680 /* see if we have the result cached in the type's TypeInfo record */
18681 typeInfo = findTypeByOid(oid);
18682 if (typeInfo && typeInfo->ftypname)
18683 return typeInfo->ftypname;
18685 query = createPQExpBuffer();
18686 appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
18687 oid);
18689 res = ExecuteSqlQueryForSingleRow(fout, query->data);
18691 /* result of format_type is already quoted */
18692 result = pg_strdup(PQgetvalue(res, 0, 0));
18694 PQclear(res);
18695 destroyPQExpBuffer(query);
18698 * Cache the result for re-use in later requests, if possible. If we
18699 * don't have a TypeInfo for the type, the string will be leaked once the
18700 * caller is done with it ... but that case really should not happen, so
18701 * leaking if it does seems acceptable.
18703 if (typeInfo)
18704 typeInfo->ftypname = result;
18706 return result;
18710 * Return a column list clause for the given relation.
18712 * Special case: if there are no undropped columns in the relation, return
18713 * "", not an invalid "()" column list.
18715 static const char *
18716 fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
18718 int numatts = ti->numatts;
18719 char **attnames = ti->attnames;
18720 bool *attisdropped = ti->attisdropped;
18721 char *attgenerated = ti->attgenerated;
18722 bool needComma;
18723 int i;
18725 appendPQExpBufferChar(buffer, '(');
18726 needComma = false;
18727 for (i = 0; i < numatts; i++)
18729 if (attisdropped[i])
18730 continue;
18731 if (attgenerated[i])
18732 continue;
18733 if (needComma)
18734 appendPQExpBufferStr(buffer, ", ");
18735 appendPQExpBufferStr(buffer, fmtId(attnames[i]));
18736 needComma = true;
18739 if (!needComma)
18740 return ""; /* no undropped columns */
18742 appendPQExpBufferChar(buffer, ')');
18743 return buffer->data;
18747 * Check if a reloptions array is nonempty.
18749 static bool
18750 nonemptyReloptions(const char *reloptions)
18752 /* Don't want to print it if it's just "{}" */
18753 return (reloptions != NULL && strlen(reloptions) > 2);
18757 * Format a reloptions array and append it to the given buffer.
18759 * "prefix" is prepended to the option names; typically it's "" or "toast.".
18761 static void
18762 appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
18763 const char *prefix, Archive *fout)
18765 bool res;
18767 res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
18768 fout->std_strings);
18769 if (!res)
18770 pg_log_warning("could not parse %s array", "reloptions");