Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_dump.c
4 : * pg_dump is a utility for dumping out a postgres database
5 : * into a script file.
6 : *
7 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_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 "filter.h"
64 : #include "getopt_long.h"
65 : #include "libpq/libpq-fs.h"
66 : #include "parallel.h"
67 : #include "pg_backup_db.h"
68 : #include "pg_backup_utils.h"
69 : #include "pg_dump.h"
70 : #include "storage/block.h"
71 :
72 : typedef struct
73 : {
74 : Oid roleoid; /* role's OID */
75 : const char *rolename; /* role's name */
76 : } RoleNameItem;
77 :
78 : typedef struct
79 : {
80 : const char *descr; /* comment for an object */
81 : Oid classoid; /* object class (catalog OID) */
82 : Oid objoid; /* object OID */
83 : int objsubid; /* subobject (table column #) */
84 : } CommentItem;
85 :
86 : typedef struct
87 : {
88 : const char *provider; /* label provider of this security label */
89 : const char *label; /* security label for an object */
90 : Oid classoid; /* object class (catalog OID) */
91 : Oid objoid; /* object OID */
92 : int objsubid; /* subobject (table column #) */
93 : } SecLabelItem;
94 :
95 : typedef enum OidOptions
96 : {
97 : zeroIsError = 1,
98 : zeroAsStar = 2,
99 : zeroAsNone = 4,
100 : } OidOptions;
101 :
102 : /* global decls */
103 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
104 :
105 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
106 :
107 : /* The specified names/patterns should to match at least one entity */
108 : static int strict_names = 0;
109 :
110 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
111 :
112 : /*
113 : * Object inclusion/exclusion lists
114 : *
115 : * The string lists record the patterns given by command-line switches,
116 : * which we then convert to lists of OIDs of matching objects.
117 : */
118 : static SimpleStringList schema_include_patterns = {NULL, NULL};
119 : static SimpleOidList schema_include_oids = {NULL, NULL};
120 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
121 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
122 :
123 : static SimpleStringList table_include_patterns = {NULL, NULL};
124 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
125 : static SimpleOidList table_include_oids = {NULL, NULL};
126 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
127 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
128 : static SimpleOidList table_exclude_oids = {NULL, NULL};
129 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
130 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
131 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
132 :
133 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
134 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
135 :
136 : static SimpleStringList extension_include_patterns = {NULL, NULL};
137 : static SimpleOidList extension_include_oids = {NULL, NULL};
138 :
139 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
140 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
141 :
142 : static const CatalogId nilCatalogId = {0, 0};
143 :
144 : /* override for standard extra_float_digits setting */
145 : static bool have_extra_float_digits = false;
146 : static int extra_float_digits;
147 :
148 : /* sorted table of role names */
149 : static RoleNameItem *rolenames = NULL;
150 : static int nrolenames = 0;
151 :
152 : /* sorted table of comments */
153 : static CommentItem *comments = NULL;
154 : static int ncomments = 0;
155 :
156 : /* sorted table of security labels */
157 : static SecLabelItem *seclabels = NULL;
158 : static int nseclabels = 0;
159 :
160 : /*
161 : * The default number of rows per INSERT when
162 : * --inserts is specified without --rows-per-insert
163 : */
164 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
165 :
166 : /*
167 : * Macro for producing quoted, schema-qualified name of a dumpable object.
168 : */
169 : #define fmtQualifiedDumpable(obj) \
170 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
171 : (obj)->dobj.name)
172 :
173 : static void help(const char *progname);
174 : static void setup_connection(Archive *AH,
175 : const char *dumpencoding, const char *dumpsnapshot,
176 : char *use_role);
177 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
178 : static void expand_schema_name_patterns(Archive *fout,
179 : SimpleStringList *patterns,
180 : SimpleOidList *oids,
181 : bool strict_names);
182 : static void expand_extension_name_patterns(Archive *fout,
183 : SimpleStringList *patterns,
184 : SimpleOidList *oids,
185 : bool strict_names);
186 : static void expand_foreign_server_name_patterns(Archive *fout,
187 : SimpleStringList *patterns,
188 : SimpleOidList *oids);
189 : static void expand_table_name_patterns(Archive *fout,
190 : SimpleStringList *patterns,
191 : SimpleOidList *oids,
192 : bool strict_names,
193 : bool with_child_tables);
194 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
195 : const char *pattern);
196 :
197 : static NamespaceInfo *findNamespace(Oid nsoid);
198 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
199 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
200 : static const char *getRoleName(const char *roleoid_str);
201 : static void collectRoleNames(Archive *fout);
202 : static void getAdditionalACLs(Archive *fout);
203 : static void dumpCommentExtended(Archive *fout, const char *type,
204 : const char *name, const char *namespace,
205 : const char *owner, CatalogId catalogId,
206 : int subid, DumpId dumpId,
207 : const char *initdb_comment);
208 : static inline void dumpComment(Archive *fout, const char *type,
209 : const char *name, const char *namespace,
210 : const char *owner, CatalogId catalogId,
211 : int subid, DumpId dumpId);
212 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
213 : static void collectComments(Archive *fout);
214 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
215 : const char *namespace, const char *owner,
216 : CatalogId catalogId, int subid, DumpId dumpId);
217 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
218 : static void collectSecLabels(Archive *fout);
219 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
220 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
221 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
222 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
223 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
224 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
225 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
226 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
227 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
228 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
229 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
230 : PGresult *res);
231 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
232 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
233 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
234 : static void dumpCast(Archive *fout, const CastInfo *cast);
235 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
236 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
237 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
238 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
239 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
240 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
241 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
242 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
243 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
244 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
245 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
246 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
247 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
248 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
249 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
250 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
251 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
252 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
253 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
254 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
255 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
256 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
257 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
258 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
259 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
260 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
261 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
262 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
263 : static void dumpUserMappings(Archive *fout,
264 : const char *servername, const char *namespace,
265 : const char *owner, CatalogId catalogId, DumpId dumpId);
266 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
267 :
268 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
269 : const char *type, const char *name, const char *subname,
270 : const char *nspname, const char *owner,
271 : const DumpableAcl *dacl);
272 :
273 : static void getDependencies(Archive *fout);
274 : static void BuildArchiveDependencies(Archive *fout);
275 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
276 : DumpId **dependencies, int *nDeps, int *allocDeps);
277 :
278 : static DumpableObject *createBoundaryObjects(void);
279 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
280 : DumpableObject *boundaryObjs);
281 :
282 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
283 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
284 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
285 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
286 : static void buildMatViewRefreshDependencies(Archive *fout);
287 : static void getTableDataFKConstraints(void);
288 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
289 : bool is_agg);
290 : static char *format_function_signature(Archive *fout,
291 : const FuncInfo *finfo, bool honor_quotes);
292 : static char *convertRegProcReference(const char *proc);
293 : static char *getFormattedOperatorName(const char *oproid);
294 : static char *convertTSFunction(Archive *fout, Oid funcOid);
295 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
296 : static void getLOs(Archive *fout);
297 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
298 : static int dumpLOs(Archive *fout, const void *arg);
299 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
300 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
301 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
302 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
303 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
304 : static void dumpDatabase(Archive *fout);
305 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
306 : const char *dbname, Oid dboid);
307 : static void dumpEncoding(Archive *AH);
308 : static void dumpStdStrings(Archive *AH);
309 : static void dumpSearchPath(Archive *AH);
310 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
311 : PQExpBuffer upgrade_buffer,
312 : Oid pg_type_oid,
313 : bool force_array_type,
314 : bool include_multirange_type);
315 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
316 : PQExpBuffer upgrade_buffer,
317 : const TableInfo *tbinfo);
318 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
319 : PQExpBuffer upgrade_buffer,
320 : Oid pg_class_oid, bool is_index);
321 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
322 : const DumpableObject *dobj,
323 : const char *objtype,
324 : const char *objname,
325 : const char *objnamespace);
326 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
327 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
328 : static bool nonemptyReloptions(const char *reloptions);
329 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
330 : const char *prefix, Archive *fout);
331 : static char *get_synchronized_snapshot(Archive *fout);
332 : static void setupDumpWorker(Archive *AH);
333 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
334 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
335 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
336 :
337 :
338 : int
339 482 : main(int argc, char **argv)
340 : {
341 : int c;
342 482 : const char *filename = NULL;
343 482 : const char *format = "p";
344 : TableInfo *tblinfo;
345 : int numTables;
346 : DumpableObject **dobjs;
347 : int numObjs;
348 : DumpableObject *boundaryObjs;
349 : int i;
350 : int optindex;
351 : RestoreOptions *ropt;
352 : Archive *fout; /* the script file */
353 482 : bool g_verbose = false;
354 482 : const char *dumpencoding = NULL;
355 482 : const char *dumpsnapshot = NULL;
356 482 : char *use_role = NULL;
357 482 : int numWorkers = 1;
358 482 : int plainText = 0;
359 482 : ArchiveFormat archiveFormat = archUnknown;
360 : ArchiveMode archiveMode;
361 482 : pg_compress_specification compression_spec = {0};
362 482 : char *compression_detail = NULL;
363 482 : char *compression_algorithm_str = "none";
364 482 : char *error_detail = NULL;
365 482 : bool user_compression_defined = false;
366 482 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
367 :
368 : static DumpOptions dopt;
369 :
370 : static struct option long_options[] = {
371 : {"data-only", no_argument, NULL, 'a'},
372 : {"blobs", no_argument, NULL, 'b'},
373 : {"large-objects", no_argument, NULL, 'b'},
374 : {"no-blobs", no_argument, NULL, 'B'},
375 : {"no-large-objects", no_argument, NULL, 'B'},
376 : {"clean", no_argument, NULL, 'c'},
377 : {"create", no_argument, NULL, 'C'},
378 : {"dbname", required_argument, NULL, 'd'},
379 : {"extension", required_argument, NULL, 'e'},
380 : {"file", required_argument, NULL, 'f'},
381 : {"format", required_argument, NULL, 'F'},
382 : {"host", required_argument, NULL, 'h'},
383 : {"jobs", 1, NULL, 'j'},
384 : {"no-reconnect", no_argument, NULL, 'R'},
385 : {"no-owner", no_argument, NULL, 'O'},
386 : {"port", required_argument, NULL, 'p'},
387 : {"schema", required_argument, NULL, 'n'},
388 : {"exclude-schema", required_argument, NULL, 'N'},
389 : {"schema-only", no_argument, NULL, 's'},
390 : {"superuser", required_argument, NULL, 'S'},
391 : {"table", required_argument, NULL, 't'},
392 : {"exclude-table", required_argument, NULL, 'T'},
393 : {"no-password", no_argument, NULL, 'w'},
394 : {"password", no_argument, NULL, 'W'},
395 : {"username", required_argument, NULL, 'U'},
396 : {"verbose", no_argument, NULL, 'v'},
397 : {"no-privileges", no_argument, NULL, 'x'},
398 : {"no-acl", no_argument, NULL, 'x'},
399 : {"compress", required_argument, NULL, 'Z'},
400 : {"encoding", required_argument, NULL, 'E'},
401 : {"help", no_argument, NULL, '?'},
402 : {"version", no_argument, NULL, 'V'},
403 :
404 : /*
405 : * the following options don't have an equivalent short option letter
406 : */
407 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
408 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
409 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
410 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
411 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
412 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
413 : {"exclude-table-data", required_argument, NULL, 4},
414 : {"extra-float-digits", required_argument, NULL, 8},
415 : {"if-exists", no_argument, &dopt.if_exists, 1},
416 : {"inserts", no_argument, NULL, 9},
417 : {"lock-wait-timeout", required_argument, NULL, 2},
418 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
419 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
420 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
421 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
422 : {"role", required_argument, NULL, 3},
423 : {"section", required_argument, NULL, 5},
424 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
425 : {"snapshot", required_argument, NULL, 6},
426 : {"strict-names", no_argument, &strict_names, 1},
427 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
428 : {"no-comments", no_argument, &dopt.no_comments, 1},
429 : {"no-publications", no_argument, &dopt.no_publications, 1},
430 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
431 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
432 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
433 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
434 : {"no-sync", no_argument, NULL, 7},
435 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
436 : {"rows-per-insert", required_argument, NULL, 10},
437 : {"include-foreign-data", required_argument, NULL, 11},
438 : {"table-and-children", required_argument, NULL, 12},
439 : {"exclude-table-and-children", required_argument, NULL, 13},
440 : {"exclude-table-data-and-children", required_argument, NULL, 14},
441 : {"sync-method", required_argument, NULL, 15},
442 : {"filter", required_argument, NULL, 16},
443 : {"exclude-extension", required_argument, NULL, 17},
444 :
445 : {NULL, 0, NULL, 0}
446 : };
447 :
448 482 : pg_logging_init(argv[0]);
449 482 : pg_logging_set_level(PG_LOG_WARNING);
450 482 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
451 :
452 : /*
453 : * Initialize what we need for parallel execution, especially for thread
454 : * support on Windows.
455 : */
456 482 : init_parallel_dump_utils();
457 :
458 482 : progname = get_progname(argv[0]);
459 :
460 482 : if (argc > 1)
461 : {
462 482 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
463 : {
464 2 : help(progname);
465 2 : exit_nicely(0);
466 : }
467 480 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
468 : {
469 94 : puts("pg_dump (PostgreSQL) " PG_VERSION);
470 94 : exit_nicely(0);
471 : }
472 : }
473 :
474 386 : InitDumpOptions(&dopt);
475 :
476 1666 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
477 : long_options, &optindex)) != -1)
478 : {
479 1296 : switch (c)
480 : {
481 16 : case 'a': /* Dump data only */
482 16 : dopt.dataOnly = true;
483 16 : break;
484 :
485 2 : case 'b': /* Dump LOs */
486 2 : dopt.outputLOs = true;
487 2 : break;
488 :
489 4 : case 'B': /* Don't dump LOs */
490 4 : dopt.dontOutputLOs = true;
491 4 : break;
492 :
493 12 : case 'c': /* clean (i.e., drop) schema prior to create */
494 12 : dopt.outputClean = 1;
495 12 : break;
496 :
497 58 : case 'C': /* Create DB */
498 58 : dopt.outputCreateDB = 1;
499 58 : break;
500 :
501 10 : case 'd': /* database name */
502 10 : dopt.cparams.dbname = pg_strdup(optarg);
503 10 : break;
504 :
505 8 : case 'e': /* include extension(s) */
506 8 : simple_string_list_append(&extension_include_patterns, optarg);
507 8 : dopt.include_everything = false;
508 8 : break;
509 :
510 4 : case 'E': /* Dump encoding */
511 4 : dumpencoding = pg_strdup(optarg);
512 4 : break;
513 :
514 302 : case 'f':
515 302 : filename = pg_strdup(optarg);
516 302 : break;
517 :
518 170 : case 'F':
519 170 : format = pg_strdup(optarg);
520 170 : break;
521 :
522 24 : case 'h': /* server host */
523 24 : dopt.cparams.pghost = pg_strdup(optarg);
524 24 : break;
525 :
526 22 : case 'j': /* number of dump jobs */
527 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
528 : PG_MAX_JOBS,
529 : &numWorkers))
530 2 : exit_nicely(1);
531 20 : break;
532 :
533 30 : case 'n': /* include schema(s) */
534 30 : simple_string_list_append(&schema_include_patterns, optarg);
535 30 : dopt.include_everything = false;
536 30 : break;
537 :
538 2 : case 'N': /* exclude schema(s) */
539 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
540 2 : break;
541 :
542 4 : case 'O': /* Don't reconnect to match owner */
543 4 : dopt.outputNoOwner = 1;
544 4 : break;
545 :
546 96 : case 'p': /* server port */
547 96 : dopt.cparams.pgport = pg_strdup(optarg);
548 96 : break;
549 :
550 4 : case 'R':
551 : /* no-op, still accepted for backwards compatibility */
552 4 : break;
553 :
554 36 : case 's': /* dump schema only */
555 36 : dopt.schemaOnly = true;
556 36 : break;
557 :
558 2 : case 'S': /* Username for superuser in plain text output */
559 2 : dopt.outputSuperuser = pg_strdup(optarg);
560 2 : break;
561 :
562 16 : case 't': /* include table(s) */
563 16 : simple_string_list_append(&table_include_patterns, optarg);
564 16 : dopt.include_everything = false;
565 16 : break;
566 :
567 8 : case 'T': /* exclude table(s) */
568 8 : simple_string_list_append(&table_exclude_patterns, optarg);
569 8 : break;
570 :
571 28 : case 'U':
572 28 : dopt.cparams.username = pg_strdup(optarg);
573 28 : break;
574 :
575 12 : case 'v': /* verbose */
576 12 : g_verbose = true;
577 12 : pg_logging_increase_verbosity();
578 12 : break;
579 :
580 2 : case 'w':
581 2 : dopt.cparams.promptPassword = TRI_NO;
582 2 : break;
583 :
584 0 : case 'W':
585 0 : dopt.cparams.promptPassword = TRI_YES;
586 0 : break;
587 :
588 4 : case 'x': /* skip ACL dump */
589 4 : dopt.aclsSkip = true;
590 4 : break;
591 :
592 24 : case 'Z': /* Compression */
593 24 : parse_compress_options(optarg, &compression_algorithm_str,
594 : &compression_detail);
595 24 : user_compression_defined = true;
596 24 : break;
597 :
598 98 : case 0:
599 : /* This covers the long options. */
600 98 : break;
601 :
602 4 : case 2: /* lock-wait-timeout */
603 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
604 4 : break;
605 :
606 6 : case 3: /* SET ROLE */
607 6 : use_role = pg_strdup(optarg);
608 6 : break;
609 :
610 2 : case 4: /* exclude table(s) data */
611 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
612 2 : break;
613 :
614 12 : case 5: /* section */
615 12 : set_dump_section(optarg, &dopt.dumpSections);
616 12 : break;
617 :
618 0 : case 6: /* snapshot */
619 0 : dumpsnapshot = pg_strdup(optarg);
620 0 : break;
621 :
622 194 : case 7: /* no-sync */
623 194 : dosync = false;
624 194 : break;
625 :
626 2 : case 8:
627 2 : have_extra_float_digits = true;
628 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
629 : &extra_float_digits))
630 2 : exit_nicely(1);
631 0 : break;
632 :
633 4 : case 9: /* inserts */
634 :
635 : /*
636 : * dump_inserts also stores --rows-per-insert, careful not to
637 : * overwrite that.
638 : */
639 4 : if (dopt.dump_inserts == 0)
640 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
641 4 : break;
642 :
643 4 : case 10: /* rows per insert */
644 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
645 : &dopt.dump_inserts))
646 2 : exit_nicely(1);
647 2 : break;
648 :
649 8 : case 11: /* include foreign data */
650 8 : simple_string_list_append(&foreign_servers_include_patterns,
651 : optarg);
652 8 : break;
653 :
654 2 : case 12: /* include table(s) and their children */
655 2 : simple_string_list_append(&table_include_patterns_and_children,
656 : optarg);
657 2 : dopt.include_everything = false;
658 2 : break;
659 :
660 2 : case 13: /* exclude table(s) and their children */
661 2 : simple_string_list_append(&table_exclude_patterns_and_children,
662 : optarg);
663 2 : break;
664 :
665 2 : case 14: /* exclude data of table(s) and children */
666 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
667 : optarg);
668 2 : break;
669 :
670 0 : case 15:
671 0 : if (!parse_sync_method(optarg, &sync_method))
672 0 : exit_nicely(1);
673 0 : break;
674 :
675 52 : case 16: /* read object filters from file */
676 52 : read_dump_filters(optarg, &dopt);
677 44 : break;
678 :
679 2 : case 17: /* exclude extension(s) */
680 2 : simple_string_list_append(&extension_exclude_patterns,
681 : optarg);
682 2 : break;
683 :
684 2 : default:
685 : /* getopt_long already emitted a complaint */
686 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
687 2 : exit_nicely(1);
688 : }
689 : }
690 :
691 : /*
692 : * Non-option argument specifies database name as long as it wasn't
693 : * already specified with -d / --dbname
694 : */
695 370 : if (optind < argc && dopt.cparams.dbname == NULL)
696 306 : dopt.cparams.dbname = argv[optind++];
697 :
698 : /* Complain if any arguments remain */
699 370 : if (optind < argc)
700 : {
701 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
702 : argv[optind]);
703 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
704 2 : exit_nicely(1);
705 : }
706 :
707 : /* --column-inserts implies --inserts */
708 368 : if (dopt.column_inserts && dopt.dump_inserts == 0)
709 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
710 :
711 : /*
712 : * Binary upgrade mode implies dumping sequence data even in schema-only
713 : * mode. This is not exposed as a separate option, but kept separate
714 : * internally for clarity.
715 : */
716 368 : if (dopt.binary_upgrade)
717 28 : dopt.sequence_data = 1;
718 :
719 368 : if (dopt.dataOnly && dopt.schemaOnly)
720 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
721 :
722 366 : if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
723 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
724 :
725 364 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
726 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
727 :
728 362 : if (dopt.dataOnly && dopt.outputClean)
729 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
730 :
731 360 : if (dopt.if_exists && !dopt.outputClean)
732 2 : pg_fatal("option --if-exists requires option -c/--clean");
733 :
734 : /*
735 : * --inserts are already implied above if --column-inserts or
736 : * --rows-per-insert were specified.
737 : */
738 358 : if (dopt.do_nothing && dopt.dump_inserts == 0)
739 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
740 :
741 : /* Identify archive format to emit */
742 356 : archiveFormat = parseArchiveFormat(format, &archiveMode);
743 :
744 : /* archiveFormat specific setup */
745 354 : if (archiveFormat == archNull)
746 290 : plainText = 1;
747 :
748 : /*
749 : * Custom and directory formats are compressed by default with gzip when
750 : * available, not the others. If gzip is not available, no compression is
751 : * done by default.
752 : */
753 354 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
754 58 : !user_compression_defined)
755 : {
756 : #ifdef HAVE_LIBZ
757 48 : compression_algorithm_str = "gzip";
758 : #else
759 : compression_algorithm_str = "none";
760 : #endif
761 : }
762 :
763 : /*
764 : * Compression options
765 : */
766 354 : if (!parse_compress_algorithm(compression_algorithm_str,
767 : &compression_algorithm))
768 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
769 : compression_algorithm_str);
770 :
771 352 : parse_compress_specification(compression_algorithm, compression_detail,
772 : &compression_spec);
773 352 : error_detail = validate_compress_specification(&compression_spec);
774 352 : if (error_detail != NULL)
775 6 : pg_fatal("invalid compression specification: %s",
776 : error_detail);
777 :
778 346 : error_detail = supports_compression(compression_spec);
779 346 : if (error_detail != NULL)
780 0 : pg_fatal("%s", error_detail);
781 :
782 : /*
783 : * Disable support for zstd workers for now - these are based on
784 : * threading, and it's unclear how it interacts with parallel dumps on
785 : * platforms where that relies on threads too (e.g. Windows).
786 : */
787 346 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
788 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
789 : "workers");
790 :
791 : /*
792 : * If emitting an archive format, we always want to emit a DATABASE item,
793 : * in case --create is specified at pg_restore time.
794 : */
795 346 : if (!plainText)
796 64 : dopt.outputCreateDB = 1;
797 :
798 : /* Parallel backup only in the directory archive format so far */
799 346 : if (archiveFormat != archDirectory && numWorkers > 1)
800 2 : pg_fatal("parallel backup only supported by the directory format");
801 :
802 : /* Open the output file */
803 344 : fout = CreateArchive(filename, archiveFormat, compression_spec,
804 : dosync, archiveMode, setupDumpWorker, sync_method);
805 :
806 : /* Make dump options accessible right away */
807 342 : SetArchiveOptions(fout, &dopt, NULL);
808 :
809 : /* Register the cleanup hook */
810 342 : on_exit_close_archive(fout);
811 :
812 : /* Let the archiver know how noisy to be */
813 342 : fout->verbose = g_verbose;
814 :
815 :
816 : /*
817 : * We allow the server to be back to 9.2, and up to any minor release of
818 : * our own major version. (See also version check in pg_dumpall.c.)
819 : */
820 342 : fout->minRemoteVersion = 90200;
821 342 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
822 :
823 342 : fout->numWorkers = numWorkers;
824 :
825 : /*
826 : * Open the database using the Archiver, so it knows about it. Errors mean
827 : * death.
828 : */
829 342 : ConnectDatabase(fout, &dopt.cparams, false);
830 338 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
831 :
832 : /*
833 : * On hot standbys, never try to dump unlogged table data, since it will
834 : * just throw an error.
835 : */
836 338 : if (fout->isStandby)
837 6 : dopt.no_unlogged_table_data = true;
838 :
839 : /*
840 : * Find the last built-in OID, if needed (prior to 8.1)
841 : *
842 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
843 : */
844 338 : g_last_builtin_oid = FirstNormalObjectId - 1;
845 :
846 338 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
847 :
848 : /* Expand schema selection patterns into OID lists */
849 338 : if (schema_include_patterns.head != NULL)
850 : {
851 32 : expand_schema_name_patterns(fout, &schema_include_patterns,
852 : &schema_include_oids,
853 : strict_names);
854 20 : if (schema_include_oids.head == NULL)
855 2 : pg_fatal("no matching schemas were found");
856 : }
857 324 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
858 : &schema_exclude_oids,
859 : false);
860 : /* non-matching exclusion patterns aren't an error */
861 :
862 : /* Expand table selection patterns into OID lists */
863 324 : expand_table_name_patterns(fout, &table_include_patterns,
864 : &table_include_oids,
865 : strict_names, false);
866 314 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
867 : &table_include_oids,
868 : strict_names, true);
869 314 : if ((table_include_patterns.head != NULL ||
870 292 : table_include_patterns_and_children.head != NULL) &&
871 26 : table_include_oids.head == NULL)
872 4 : pg_fatal("no matching tables were found");
873 :
874 310 : expand_table_name_patterns(fout, &table_exclude_patterns,
875 : &table_exclude_oids,
876 : false, false);
877 310 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
878 : &table_exclude_oids,
879 : false, true);
880 :
881 310 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
882 : &tabledata_exclude_oids,
883 : false, false);
884 310 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
885 : &tabledata_exclude_oids,
886 : false, true);
887 :
888 310 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
889 : &foreign_servers_include_oids);
890 :
891 : /* non-matching exclusion patterns aren't an error */
892 :
893 : /* Expand extension selection patterns into OID lists */
894 308 : if (extension_include_patterns.head != NULL)
895 : {
896 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
897 : &extension_include_oids,
898 : strict_names);
899 10 : if (extension_include_oids.head == NULL)
900 2 : pg_fatal("no matching extensions were found");
901 : }
902 306 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
903 : &extension_exclude_oids,
904 : false);
905 : /* non-matching exclusion patterns aren't an error */
906 :
907 : /*
908 : * Dumping LOs is the default for dumps where an inclusion switch is not
909 : * used (an "include everything" dump). -B can be used to exclude LOs
910 : * from those dumps. -b can be used to include LOs even when an inclusion
911 : * switch is used.
912 : *
913 : * -s means "schema only" and LOs are data, not schema, so we never
914 : * include LOs when -s is used.
915 : */
916 306 : if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputLOs)
917 228 : dopt.outputLOs = true;
918 :
919 : /*
920 : * Collect role names so we can map object owner OIDs to names.
921 : */
922 306 : collectRoleNames(fout);
923 :
924 : /*
925 : * Now scan the database and create DumpableObject structs for all the
926 : * objects we intend to dump.
927 : */
928 306 : tblinfo = getSchemaData(fout, &numTables);
929 :
930 304 : if (!dopt.schemaOnly)
931 : {
932 272 : getTableData(&dopt, tblinfo, numTables, 0);
933 272 : buildMatViewRefreshDependencies(fout);
934 272 : if (dopt.dataOnly)
935 12 : getTableDataFKConstraints();
936 : }
937 :
938 304 : if (dopt.schemaOnly && dopt.sequence_data)
939 28 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
940 :
941 : /*
942 : * In binary-upgrade mode, we do not have to worry about the actual LO
943 : * data or the associated metadata that resides in the pg_largeobject and
944 : * pg_largeobject_metadata tables, respectively.
945 : *
946 : * However, we do need to collect LO information as there may be comments
947 : * or other information on LOs that we do need to dump out.
948 : */
949 304 : if (dopt.outputLOs || dopt.binary_upgrade)
950 256 : getLOs(fout);
951 :
952 : /*
953 : * Collect dependency data to assist in ordering the objects.
954 : */
955 304 : getDependencies(fout);
956 :
957 : /*
958 : * Collect ACLs, comments, and security labels, if wanted.
959 : */
960 304 : if (!dopt.aclsSkip)
961 300 : getAdditionalACLs(fout);
962 304 : if (!dopt.no_comments)
963 304 : collectComments(fout);
964 304 : if (!dopt.no_security_labels)
965 304 : collectSecLabels(fout);
966 :
967 : /* Lastly, create dummy objects to represent the section boundaries */
968 304 : boundaryObjs = createBoundaryObjects();
969 :
970 : /* Get pointers to all the known DumpableObjects */
971 304 : getDumpableObjects(&dobjs, &numObjs);
972 :
973 : /*
974 : * Add dummy dependencies to enforce the dump section ordering.
975 : */
976 304 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
977 :
978 : /*
979 : * Sort the objects into a safe dump order (no forward references).
980 : *
981 : * We rely on dependency information to help us determine a safe order, so
982 : * the initial sort is mostly for cosmetic purposes: we sort by name to
983 : * ensure that logically identical schemas will dump identically.
984 : */
985 304 : sortDumpableObjectsByTypeName(dobjs, numObjs);
986 :
987 304 : sortDumpableObjects(dobjs, numObjs,
988 304 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
989 :
990 : /*
991 : * Create archive TOC entries for all the objects to be dumped, in a safe
992 : * order.
993 : */
994 :
995 : /*
996 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
997 : */
998 304 : dumpEncoding(fout);
999 304 : dumpStdStrings(fout);
1000 304 : dumpSearchPath(fout);
1001 :
1002 : /* The database items are always next, unless we don't want them at all */
1003 304 : if (dopt.outputCreateDB)
1004 120 : dumpDatabase(fout);
1005 :
1006 : /* Now the rearrangeable objects. */
1007 1098928 : for (i = 0; i < numObjs; i++)
1008 1098624 : dumpDumpableObject(fout, dobjs[i]);
1009 :
1010 : /*
1011 : * Set up options info to ensure we dump what we want.
1012 : */
1013 304 : ropt = NewRestoreOptions();
1014 304 : ropt->filename = filename;
1015 :
1016 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1017 304 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1018 304 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1019 304 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1020 304 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1021 304 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1022 304 : ropt->dropSchema = dopt.outputClean;
1023 304 : ropt->dataOnly = dopt.dataOnly;
1024 304 : ropt->schemaOnly = dopt.schemaOnly;
1025 304 : ropt->if_exists = dopt.if_exists;
1026 304 : ropt->column_inserts = dopt.column_inserts;
1027 304 : ropt->dumpSections = dopt.dumpSections;
1028 304 : ropt->aclsSkip = dopt.aclsSkip;
1029 304 : ropt->superuser = dopt.outputSuperuser;
1030 304 : ropt->createDB = dopt.outputCreateDB;
1031 304 : ropt->noOwner = dopt.outputNoOwner;
1032 304 : ropt->noTableAm = dopt.outputNoTableAm;
1033 304 : ropt->noTablespace = dopt.outputNoTablespaces;
1034 304 : ropt->disable_triggers = dopt.disable_triggers;
1035 304 : ropt->use_setsessauth = dopt.use_setsessauth;
1036 304 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1037 304 : ropt->dump_inserts = dopt.dump_inserts;
1038 304 : ropt->no_comments = dopt.no_comments;
1039 304 : ropt->no_publications = dopt.no_publications;
1040 304 : ropt->no_security_labels = dopt.no_security_labels;
1041 304 : ropt->no_subscriptions = dopt.no_subscriptions;
1042 304 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1043 304 : ropt->include_everything = dopt.include_everything;
1044 304 : ropt->enable_row_security = dopt.enable_row_security;
1045 304 : ropt->sequence_data = dopt.sequence_data;
1046 304 : ropt->binary_upgrade = dopt.binary_upgrade;
1047 :
1048 304 : ropt->compression_spec = compression_spec;
1049 :
1050 304 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1051 :
1052 304 : SetArchiveOptions(fout, &dopt, ropt);
1053 :
1054 : /* Mark which entries should be output */
1055 304 : ProcessArchiveRestoreOptions(fout);
1056 :
1057 : /*
1058 : * The archive's TOC entries are now marked as to which ones will actually
1059 : * be output, so we can set up their dependency lists properly. This isn't
1060 : * necessary for plain-text output, though.
1061 : */
1062 304 : if (!plainText)
1063 62 : BuildArchiveDependencies(fout);
1064 :
1065 : /*
1066 : * And finally we can do the actual output.
1067 : *
1068 : * Note: for non-plain-text output formats, the output file is written
1069 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1070 : * right now.
1071 : */
1072 304 : if (plainText)
1073 242 : RestoreArchive(fout);
1074 :
1075 302 : CloseArchive(fout);
1076 :
1077 302 : exit_nicely(0);
1078 : }
1079 :
1080 :
1081 : static void
1082 2 : help(const char *progname)
1083 : {
1084 2 : printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1085 2 : printf(_("Usage:\n"));
1086 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1087 :
1088 2 : printf(_("\nGeneral options:\n"));
1089 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1090 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1091 : " plain text (default))\n"));
1092 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1093 2 : printf(_(" -v, --verbose verbose mode\n"));
1094 2 : printf(_(" -V, --version output version information, then exit\n"));
1095 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1096 : " compress as specified\n"));
1097 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1098 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1099 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1100 2 : printf(_(" -?, --help show this help, then exit\n"));
1101 :
1102 2 : printf(_("\nOptions controlling the output content:\n"));
1103 2 : printf(_(" -a, --data-only dump only the data, not the schema\n"));
1104 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1105 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1106 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1107 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1108 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1109 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1110 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1111 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1112 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1113 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1114 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1115 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1116 : " plain-text format\n"));
1117 2 : printf(_(" -s, --schema-only dump only the schema, no data\n"));
1118 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1119 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1120 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1121 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1122 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1123 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1124 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1125 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1126 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1127 : " access to)\n"));
1128 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1129 : " do NOT dump the specified table(s), including\n"
1130 : " child and partition tables\n"));
1131 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1132 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1133 : " do NOT dump data for the specified table(s),\n"
1134 : " including child and partition tables\n"));
1135 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1136 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1137 : " based on expressions in FILENAME\n"));
1138 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1139 2 : printf(_(" --include-foreign-data=PATTERN\n"
1140 : " include data of foreign tables on foreign\n"
1141 : " servers matching PATTERN\n"));
1142 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1143 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1144 2 : printf(_(" --no-comments do not dump comments\n"));
1145 2 : printf(_(" --no-publications do not dump publications\n"));
1146 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1147 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1148 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1149 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1150 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1151 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1152 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1153 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1154 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1155 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1156 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1157 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1158 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1159 : " match at least one entity each\n"));
1160 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1161 : " child and partition tables\n"));
1162 2 : printf(_(" --use-set-session-authorization\n"
1163 : " use SET SESSION AUTHORIZATION commands instead of\n"
1164 : " ALTER OWNER commands to set ownership\n"));
1165 :
1166 2 : printf(_("\nConnection options:\n"));
1167 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1168 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1169 2 : printf(_(" -p, --port=PORT database server port number\n"));
1170 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1171 2 : printf(_(" -w, --no-password never prompt for password\n"));
1172 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1173 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1174 :
1175 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1176 : "variable value is used.\n\n"));
1177 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1178 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1179 2 : }
1180 :
1181 : static void
1182 370 : setup_connection(Archive *AH, const char *dumpencoding,
1183 : const char *dumpsnapshot, char *use_role)
1184 : {
1185 370 : DumpOptions *dopt = AH->dopt;
1186 370 : PGconn *conn = GetConnection(AH);
1187 : const char *std_strings;
1188 :
1189 370 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1190 :
1191 : /*
1192 : * Set the client encoding if requested.
1193 : */
1194 370 : if (dumpencoding)
1195 : {
1196 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1197 0 : pg_fatal("invalid client encoding \"%s\" specified",
1198 : dumpencoding);
1199 : }
1200 :
1201 : /*
1202 : * Get the active encoding and the standard_conforming_strings setting, so
1203 : * we know how to escape strings.
1204 : */
1205 370 : AH->encoding = PQclientEncoding(conn);
1206 :
1207 370 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1208 370 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1209 :
1210 : /*
1211 : * Set the role if requested. In a parallel dump worker, we'll be passed
1212 : * use_role == NULL, but AH->use_role is already set (if user specified it
1213 : * originally) and we should use that.
1214 : */
1215 370 : if (!use_role && AH->use_role)
1216 4 : use_role = AH->use_role;
1217 :
1218 : /* Set the role if requested */
1219 370 : if (use_role)
1220 : {
1221 10 : PQExpBuffer query = createPQExpBuffer();
1222 :
1223 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1224 10 : ExecuteSqlStatement(AH, query->data);
1225 10 : destroyPQExpBuffer(query);
1226 :
1227 : /* save it for possible later use by parallel workers */
1228 10 : if (!AH->use_role)
1229 6 : AH->use_role = pg_strdup(use_role);
1230 : }
1231 :
1232 : /* Set the datestyle to ISO to ensure the dump's portability */
1233 370 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1234 :
1235 : /* Likewise, avoid using sql_standard intervalstyle */
1236 370 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1237 :
1238 : /*
1239 : * Use an explicitly specified extra_float_digits if it has been provided.
1240 : * Otherwise, set extra_float_digits so that we can dump float data
1241 : * exactly (given correctly implemented float I/O code, anyway).
1242 : */
1243 370 : if (have_extra_float_digits)
1244 : {
1245 0 : PQExpBuffer q = createPQExpBuffer();
1246 :
1247 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1248 : extra_float_digits);
1249 0 : ExecuteSqlStatement(AH, q->data);
1250 0 : destroyPQExpBuffer(q);
1251 : }
1252 : else
1253 370 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1254 :
1255 : /*
1256 : * Disable synchronized scanning, to prevent unpredictable changes in row
1257 : * ordering across a dump and reload.
1258 : */
1259 370 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1260 :
1261 : /*
1262 : * Disable timeouts if supported.
1263 : */
1264 370 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1265 370 : if (AH->remoteVersion >= 90300)
1266 370 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1267 370 : if (AH->remoteVersion >= 90600)
1268 370 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1269 370 : if (AH->remoteVersion >= 170000)
1270 370 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1271 :
1272 : /*
1273 : * Quote all identifiers, if requested.
1274 : */
1275 370 : if (quote_all_identifiers)
1276 24 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1277 :
1278 : /*
1279 : * Adjust row-security mode, if supported.
1280 : */
1281 370 : if (AH->remoteVersion >= 90500)
1282 : {
1283 370 : if (dopt->enable_row_security)
1284 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1285 : else
1286 370 : ExecuteSqlStatement(AH, "SET row_security = off");
1287 : }
1288 :
1289 : /*
1290 : * Initialize prepared-query state to "nothing prepared". We do this here
1291 : * so that a parallel dump worker will have its own state.
1292 : */
1293 370 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1294 :
1295 : /*
1296 : * Start transaction-snapshot mode transaction to dump consistent data.
1297 : */
1298 370 : ExecuteSqlStatement(AH, "BEGIN");
1299 :
1300 : /*
1301 : * To support the combination of serializable_deferrable with the jobs
1302 : * option we use REPEATABLE READ for the worker connections that are
1303 : * passed a snapshot. As long as the snapshot is acquired in a
1304 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1305 : * REPEATABLE READ transaction provides the appropriate integrity
1306 : * guarantees. This is a kluge, but safe for back-patching.
1307 : */
1308 370 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1309 0 : ExecuteSqlStatement(AH,
1310 : "SET TRANSACTION ISOLATION LEVEL "
1311 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1312 : else
1313 370 : ExecuteSqlStatement(AH,
1314 : "SET TRANSACTION ISOLATION LEVEL "
1315 : "REPEATABLE READ, READ ONLY");
1316 :
1317 : /*
1318 : * If user specified a snapshot to use, select that. In a parallel dump
1319 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1320 : * is already set (if the server can handle it) and we should use that.
1321 : */
1322 370 : if (dumpsnapshot)
1323 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1324 :
1325 370 : if (AH->sync_snapshot_id)
1326 : {
1327 32 : PQExpBuffer query = createPQExpBuffer();
1328 :
1329 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1330 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1331 32 : ExecuteSqlStatement(AH, query->data);
1332 32 : destroyPQExpBuffer(query);
1333 : }
1334 338 : else if (AH->numWorkers > 1)
1335 : {
1336 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1337 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1338 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1339 : }
1340 370 : }
1341 :
1342 : /* Set up connection for a parallel worker process */
1343 : static void
1344 32 : setupDumpWorker(Archive *AH)
1345 : {
1346 : /*
1347 : * We want to re-select all the same values the leader connection is
1348 : * using. We'll have inherited directly-usable values in
1349 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1350 : * inherited encoding value back to a string to pass to setup_connection.
1351 : */
1352 32 : setup_connection(AH,
1353 : pg_encoding_to_char(AH->encoding),
1354 : NULL,
1355 : NULL);
1356 32 : }
1357 :
1358 : static char *
1359 16 : get_synchronized_snapshot(Archive *fout)
1360 : {
1361 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1362 : char *result;
1363 : PGresult *res;
1364 :
1365 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1366 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1367 16 : PQclear(res);
1368 :
1369 16 : return result;
1370 : }
1371 :
1372 : static ArchiveFormat
1373 356 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1374 : {
1375 : ArchiveFormat archiveFormat;
1376 :
1377 356 : *mode = archModeWrite;
1378 :
1379 356 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1380 : {
1381 : /* This is used by pg_dumpall, and is not documented */
1382 86 : archiveFormat = archNull;
1383 86 : *mode = archModeAppend;
1384 : }
1385 270 : else if (pg_strcasecmp(format, "c") == 0)
1386 8 : archiveFormat = archCustom;
1387 262 : else if (pg_strcasecmp(format, "custom") == 0)
1388 30 : archiveFormat = archCustom;
1389 232 : else if (pg_strcasecmp(format, "d") == 0)
1390 14 : archiveFormat = archDirectory;
1391 218 : else if (pg_strcasecmp(format, "directory") == 0)
1392 6 : archiveFormat = archDirectory;
1393 212 : else if (pg_strcasecmp(format, "p") == 0)
1394 198 : archiveFormat = archNull;
1395 14 : else if (pg_strcasecmp(format, "plain") == 0)
1396 6 : archiveFormat = archNull;
1397 8 : else if (pg_strcasecmp(format, "t") == 0)
1398 4 : archiveFormat = archTar;
1399 4 : else if (pg_strcasecmp(format, "tar") == 0)
1400 2 : archiveFormat = archTar;
1401 : else
1402 2 : pg_fatal("invalid output format \"%s\" specified", format);
1403 354 : return archiveFormat;
1404 : }
1405 :
1406 : /*
1407 : * Find the OIDs of all schemas matching the given list of patterns,
1408 : * and append them to the given OID list.
1409 : */
1410 : static void
1411 356 : expand_schema_name_patterns(Archive *fout,
1412 : SimpleStringList *patterns,
1413 : SimpleOidList *oids,
1414 : bool strict_names)
1415 : {
1416 : PQExpBuffer query;
1417 : PGresult *res;
1418 : SimpleStringListCell *cell;
1419 : int i;
1420 :
1421 356 : if (patterns->head == NULL)
1422 318 : return; /* nothing to do */
1423 :
1424 38 : query = createPQExpBuffer();
1425 :
1426 : /*
1427 : * The loop below runs multiple SELECTs might sometimes result in
1428 : * duplicate entries in the OID list, but we don't care.
1429 : */
1430 :
1431 64 : for (cell = patterns->head; cell; cell = cell->next)
1432 : {
1433 : PQExpBufferData dbbuf;
1434 : int dotcnt;
1435 :
1436 38 : appendPQExpBufferStr(query,
1437 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1438 38 : initPQExpBuffer(&dbbuf);
1439 38 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1440 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1441 : &dotcnt);
1442 38 : if (dotcnt > 1)
1443 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1444 : cell->val);
1445 34 : else if (dotcnt == 1)
1446 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1447 28 : termPQExpBuffer(&dbbuf);
1448 :
1449 28 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1450 28 : if (strict_names && PQntuples(res) == 0)
1451 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1452 :
1453 50 : for (i = 0; i < PQntuples(res); i++)
1454 : {
1455 24 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1456 : }
1457 :
1458 26 : PQclear(res);
1459 26 : resetPQExpBuffer(query);
1460 : }
1461 :
1462 26 : destroyPQExpBuffer(query);
1463 : }
1464 :
1465 : /*
1466 : * Find the OIDs of all extensions matching the given list of patterns,
1467 : * and append them to the given OID list.
1468 : */
1469 : static void
1470 316 : expand_extension_name_patterns(Archive *fout,
1471 : SimpleStringList *patterns,
1472 : SimpleOidList *oids,
1473 : bool strict_names)
1474 : {
1475 : PQExpBuffer query;
1476 : PGresult *res;
1477 : SimpleStringListCell *cell;
1478 : int i;
1479 :
1480 316 : if (patterns->head == NULL)
1481 302 : return; /* nothing to do */
1482 :
1483 14 : query = createPQExpBuffer();
1484 :
1485 : /*
1486 : * The loop below runs multiple SELECTs might sometimes result in
1487 : * duplicate entries in the OID list, but we don't care.
1488 : */
1489 28 : for (cell = patterns->head; cell; cell = cell->next)
1490 : {
1491 : int dotcnt;
1492 :
1493 14 : appendPQExpBufferStr(query,
1494 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1495 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1496 : false, NULL, "e.extname", NULL, NULL, NULL,
1497 : &dotcnt);
1498 14 : if (dotcnt > 0)
1499 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1500 : cell->val);
1501 :
1502 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1503 14 : if (strict_names && PQntuples(res) == 0)
1504 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1505 :
1506 26 : for (i = 0; i < PQntuples(res); i++)
1507 : {
1508 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1509 : }
1510 :
1511 14 : PQclear(res);
1512 14 : resetPQExpBuffer(query);
1513 : }
1514 :
1515 14 : destroyPQExpBuffer(query);
1516 : }
1517 :
1518 : /*
1519 : * Find the OIDs of all foreign servers matching the given list of patterns,
1520 : * and append them to the given OID list.
1521 : */
1522 : static void
1523 310 : expand_foreign_server_name_patterns(Archive *fout,
1524 : SimpleStringList *patterns,
1525 : SimpleOidList *oids)
1526 : {
1527 : PQExpBuffer query;
1528 : PGresult *res;
1529 : SimpleStringListCell *cell;
1530 : int i;
1531 :
1532 310 : if (patterns->head == NULL)
1533 304 : return; /* nothing to do */
1534 :
1535 6 : query = createPQExpBuffer();
1536 :
1537 : /*
1538 : * The loop below runs multiple SELECTs might sometimes result in
1539 : * duplicate entries in the OID list, but we don't care.
1540 : */
1541 :
1542 10 : for (cell = patterns->head; cell; cell = cell->next)
1543 : {
1544 : int dotcnt;
1545 :
1546 6 : appendPQExpBufferStr(query,
1547 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1548 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1549 : false, NULL, "s.srvname", NULL, NULL, NULL,
1550 : &dotcnt);
1551 6 : if (dotcnt > 0)
1552 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1553 : cell->val);
1554 :
1555 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1556 6 : if (PQntuples(res) == 0)
1557 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1558 :
1559 8 : for (i = 0; i < PQntuples(res); i++)
1560 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1561 :
1562 4 : PQclear(res);
1563 4 : resetPQExpBuffer(query);
1564 : }
1565 :
1566 4 : destroyPQExpBuffer(query);
1567 : }
1568 :
1569 : /*
1570 : * Find the OIDs of all tables matching the given list of patterns,
1571 : * and append them to the given OID list. See also expand_dbname_patterns()
1572 : * in pg_dumpall.c
1573 : */
1574 : static void
1575 1878 : expand_table_name_patterns(Archive *fout,
1576 : SimpleStringList *patterns, SimpleOidList *oids,
1577 : bool strict_names, bool with_child_tables)
1578 : {
1579 : PQExpBuffer query;
1580 : PGresult *res;
1581 : SimpleStringListCell *cell;
1582 : int i;
1583 :
1584 1878 : if (patterns->head == NULL)
1585 1820 : return; /* nothing to do */
1586 :
1587 58 : query = createPQExpBuffer();
1588 :
1589 : /*
1590 : * this might sometimes result in duplicate entries in the OID list, but
1591 : * we don't care.
1592 : */
1593 :
1594 118 : for (cell = patterns->head; cell; cell = cell->next)
1595 : {
1596 : PQExpBufferData dbbuf;
1597 : int dotcnt;
1598 :
1599 : /*
1600 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1601 : * would be unnecessary given a pg_table_is_visible() variant taking a
1602 : * search_path argument.
1603 : *
1604 : * For with_child_tables, we start with the basic query's results and
1605 : * recursively search the inheritance tree to add child tables.
1606 : */
1607 70 : if (with_child_tables)
1608 : {
1609 12 : appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1610 : }
1611 :
1612 70 : appendPQExpBuffer(query,
1613 : "SELECT c.oid"
1614 : "\nFROM pg_catalog.pg_class c"
1615 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1616 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1617 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1618 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1619 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1620 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1621 : RELKIND_PARTITIONED_TABLE);
1622 70 : initPQExpBuffer(&dbbuf);
1623 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1624 : false, "n.nspname", "c.relname", NULL,
1625 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1626 : &dotcnt);
1627 70 : if (dotcnt > 2)
1628 2 : pg_fatal("improper relation name (too many dotted names): %s",
1629 : cell->val);
1630 68 : else if (dotcnt == 2)
1631 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1632 64 : termPQExpBuffer(&dbbuf);
1633 :
1634 64 : if (with_child_tables)
1635 : {
1636 12 : appendPQExpBuffer(query, "UNION"
1637 : "\nSELECT i.inhrelid"
1638 : "\nFROM partition_tree p"
1639 : "\n JOIN pg_catalog.pg_inherits i"
1640 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1641 : "\n)"
1642 : "\nSELECT relid FROM partition_tree");
1643 : }
1644 :
1645 64 : ExecuteSqlStatement(fout, "RESET search_path");
1646 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1647 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1648 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1649 64 : if (strict_names && PQntuples(res) == 0)
1650 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1651 :
1652 148 : for (i = 0; i < PQntuples(res); i++)
1653 : {
1654 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1655 : }
1656 :
1657 60 : PQclear(res);
1658 60 : resetPQExpBuffer(query);
1659 : }
1660 :
1661 48 : destroyPQExpBuffer(query);
1662 : }
1663 :
1664 : /*
1665 : * Verifies that the connected database name matches the given database name,
1666 : * and if not, dies with an error about the given pattern.
1667 : *
1668 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1669 : */
1670 : static void
1671 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1672 : {
1673 : const char *db;
1674 :
1675 10 : db = PQdb(conn);
1676 10 : if (db == NULL)
1677 0 : pg_fatal("You are currently not connected to a database.");
1678 :
1679 10 : if (strcmp(db, dbname) != 0)
1680 10 : pg_fatal("cross-database references are not implemented: %s",
1681 : pattern);
1682 0 : }
1683 :
1684 : /*
1685 : * checkExtensionMembership
1686 : * Determine whether object is an extension member, and if so,
1687 : * record an appropriate dependency and set the object's dump flag.
1688 : *
1689 : * It's important to call this for each object that could be an extension
1690 : * member. Generally, we integrate this with determining the object's
1691 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1692 : *
1693 : * Returns true if object is an extension member, else false.
1694 : */
1695 : static bool
1696 939468 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1697 : {
1698 939468 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1699 :
1700 939468 : if (ext == NULL)
1701 938080 : return false;
1702 :
1703 1388 : dobj->ext_member = true;
1704 :
1705 : /* Record dependency so that getDependencies needn't deal with that */
1706 1388 : addObjectDependency(dobj, ext->dobj.dumpId);
1707 :
1708 : /*
1709 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1710 : * dumped. (Any initial ACLs will be removed later, using data from
1711 : * pg_init_privs, so that we'll dump only the delta from the extension's
1712 : * initial setup.)
1713 : *
1714 : * Prior to 9.6, we do not include any extension member components.
1715 : *
1716 : * In binary upgrades, we still dump all components of the members
1717 : * individually, since the idea is to exactly reproduce the database
1718 : * contents rather than replace the extension contents with something
1719 : * different.
1720 : *
1721 : * Note: it might be interesting someday to implement storage and delta
1722 : * dumping of extension members' RLS policies and/or security labels.
1723 : * However there is a pitfall for RLS policies: trying to dump them
1724 : * requires getting a lock on their tables, and the calling user might not
1725 : * have privileges for that. We need no lock to examine a table's ACLs,
1726 : * so the current feature doesn't have a problem of that sort.
1727 : */
1728 1388 : if (fout->dopt->binary_upgrade)
1729 152 : dobj->dump = ext->dobj.dump;
1730 : else
1731 : {
1732 1236 : if (fout->remoteVersion < 90600)
1733 0 : dobj->dump = DUMP_COMPONENT_NONE;
1734 : else
1735 1236 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1736 : }
1737 :
1738 1388 : return true;
1739 : }
1740 :
1741 : /*
1742 : * selectDumpableNamespace: policy-setting subroutine
1743 : * Mark a namespace as to be dumped or not
1744 : */
1745 : static void
1746 2244 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1747 : {
1748 : /*
1749 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1750 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1751 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1752 : */
1753 2244 : nsinfo->create = true;
1754 :
1755 : /*
1756 : * If specific tables are being dumped, do not dump any complete
1757 : * namespaces. If specific namespaces are being dumped, dump just those
1758 : * namespaces. Otherwise, dump all non-system namespaces.
1759 : */
1760 2244 : if (table_include_oids.head != NULL)
1761 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1762 2144 : else if (schema_include_oids.head != NULL)
1763 98 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1764 98 : simple_oid_list_member(&schema_include_oids,
1765 : nsinfo->dobj.catId.oid) ?
1766 98 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1767 2046 : else if (fout->remoteVersion >= 90600 &&
1768 2046 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1769 : {
1770 : /*
1771 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1772 : * they are interesting (and not the original ACLs which were set at
1773 : * initdb time, see pg_init_privs).
1774 : */
1775 266 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1776 : }
1777 1780 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1778 810 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1779 : {
1780 : /* Other system schemas don't get dumped */
1781 1236 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1782 : }
1783 544 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1784 : {
1785 : /*
1786 : * The public schema is a strange beast that sits in a sort of
1787 : * no-mans-land between being a system object and a user object.
1788 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1789 : * a comment and an indication of ownership. If the owner is the
1790 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1791 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1792 : */
1793 258 : nsinfo->create = false;
1794 258 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1795 258 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1796 178 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1797 258 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1798 :
1799 : /*
1800 : * Also, make like it has a comment even if it doesn't; this is so
1801 : * that we'll emit a command to drop the comment, if appropriate.
1802 : * (Without this, we'd not call dumpCommentExtended for it.)
1803 : */
1804 258 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1805 : }
1806 : else
1807 286 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1808 :
1809 : /*
1810 : * In any case, a namespace can be excluded by an exclusion switch
1811 : */
1812 3072 : if (nsinfo->dobj.dump_contains &&
1813 828 : simple_oid_list_member(&schema_exclude_oids,
1814 : nsinfo->dobj.catId.oid))
1815 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1816 :
1817 : /*
1818 : * If the schema belongs to an extension, allow extension membership to
1819 : * override the dump decision for the schema itself. However, this does
1820 : * not change dump_contains, so this won't change what we do with objects
1821 : * within the schema. (If they belong to the extension, they'll get
1822 : * suppressed by it, otherwise not.)
1823 : */
1824 2244 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1825 2244 : }
1826 :
1827 : /*
1828 : * selectDumpableTable: policy-setting subroutine
1829 : * Mark a table as to be dumped or not
1830 : */
1831 : static void
1832 77194 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1833 : {
1834 77194 : if (checkExtensionMembership(&tbinfo->dobj, fout))
1835 450 : return; /* extension membership overrides all else */
1836 :
1837 : /*
1838 : * If specific tables are being dumped, dump just those tables; else, dump
1839 : * according to the parent namespace's dump flag.
1840 : */
1841 76744 : if (table_include_oids.head != NULL)
1842 10104 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1843 : tbinfo->dobj.catId.oid) ?
1844 5052 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1845 : else
1846 71692 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1847 :
1848 : /*
1849 : * In any case, a table can be excluded by an exclusion switch
1850 : */
1851 126202 : if (tbinfo->dobj.dump &&
1852 49458 : simple_oid_list_member(&table_exclude_oids,
1853 : tbinfo->dobj.catId.oid))
1854 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1855 : }
1856 :
1857 : /*
1858 : * selectDumpableType: policy-setting subroutine
1859 : * Mark a type as to be dumped or not
1860 : *
1861 : * If it's a table's rowtype or an autogenerated array type, we also apply a
1862 : * special type code to facilitate sorting into the desired order. (We don't
1863 : * want to consider those to be ordinary types because that would bring tables
1864 : * up into the datatype part of the dump order.) We still set the object's
1865 : * dump flag; that's not going to cause the dummy type to be dumped, but we
1866 : * need it so that casts involving such types will be dumped correctly -- see
1867 : * dumpCast. This means the flag should be set the same as for the underlying
1868 : * object (the table or base type).
1869 : */
1870 : static void
1871 213272 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1872 : {
1873 : /* skip complex types, except for standalone composite types */
1874 213272 : if (OidIsValid(tyinfo->typrelid) &&
1875 75962 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1876 : {
1877 75666 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1878 :
1879 75666 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
1880 75666 : if (tytable != NULL)
1881 75666 : tyinfo->dobj.dump = tytable->dobj.dump;
1882 : else
1883 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1884 75666 : return;
1885 : }
1886 :
1887 : /* skip auto-generated array and multirange types */
1888 137606 : if (tyinfo->isArray || tyinfo->isMultirange)
1889 : {
1890 104216 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
1891 :
1892 : /*
1893 : * Fall through to set the dump flag; we assume that the subsequent
1894 : * rules will do the same thing as they would for the array's base
1895 : * type or multirange's range type. (We cannot reliably look up the
1896 : * base type here, since getTypes may not have processed it yet.)
1897 : */
1898 : }
1899 :
1900 137606 : if (checkExtensionMembership(&tyinfo->dobj, fout))
1901 300 : return; /* extension membership overrides all else */
1902 :
1903 : /* Dump based on if the contents of the namespace are being dumped */
1904 137306 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1905 : }
1906 :
1907 : /*
1908 : * selectDumpableDefaultACL: policy-setting subroutine
1909 : * Mark a default ACL as to be dumped or not
1910 : *
1911 : * For per-schema default ACLs, dump if the schema is to be dumped.
1912 : * Otherwise dump if we are dumping "everything". Note that dataOnly
1913 : * and aclsSkip are checked separately.
1914 : */
1915 : static void
1916 344 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
1917 : {
1918 : /* Default ACLs can't be extension members */
1919 :
1920 344 : if (dinfo->dobj.namespace)
1921 : /* default ACLs are considered part of the namespace */
1922 172 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
1923 : else
1924 172 : dinfo->dobj.dump = dopt->include_everything ?
1925 172 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1926 344 : }
1927 :
1928 : /*
1929 : * selectDumpableCast: policy-setting subroutine
1930 : * Mark a cast as to be dumped or not
1931 : *
1932 : * Casts do not belong to any particular namespace (since they haven't got
1933 : * names), nor do they have identifiable owners. To distinguish user-defined
1934 : * casts from built-in ones, we must resort to checking whether the cast's
1935 : * OID is in the range reserved for initdb.
1936 : */
1937 : static void
1938 67938 : selectDumpableCast(CastInfo *cast, Archive *fout)
1939 : {
1940 67938 : if (checkExtensionMembership(&cast->dobj, fout))
1941 0 : return; /* extension membership overrides all else */
1942 :
1943 : /*
1944 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
1945 : * support ACLs currently.
1946 : */
1947 67938 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1948 67792 : cast->dobj.dump = DUMP_COMPONENT_NONE;
1949 : else
1950 146 : cast->dobj.dump = fout->dopt->include_everything ?
1951 146 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1952 : }
1953 :
1954 : /*
1955 : * selectDumpableProcLang: policy-setting subroutine
1956 : * Mark a procedural language as to be dumped or not
1957 : *
1958 : * Procedural languages do not belong to any particular namespace. To
1959 : * identify built-in languages, we must resort to checking whether the
1960 : * language's OID is in the range reserved for initdb.
1961 : */
1962 : static void
1963 390 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
1964 : {
1965 390 : if (checkExtensionMembership(&plang->dobj, fout))
1966 304 : return; /* extension membership overrides all else */
1967 :
1968 : /*
1969 : * Only include procedural languages when we are dumping everything.
1970 : *
1971 : * For from-initdb procedural languages, only include ACLs, as we do for
1972 : * the pg_catalog namespace. We need this because procedural languages do
1973 : * not live in any namespace.
1974 : */
1975 86 : if (!fout->dopt->include_everything)
1976 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
1977 : else
1978 : {
1979 70 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1980 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
1981 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
1982 : else
1983 70 : plang->dobj.dump = DUMP_COMPONENT_ALL;
1984 : }
1985 : }
1986 :
1987 : /*
1988 : * selectDumpableAccessMethod: policy-setting subroutine
1989 : * Mark an access method as to be dumped or not
1990 : *
1991 : * Access methods do not belong to any particular namespace. To identify
1992 : * built-in access methods, we must resort to checking whether the
1993 : * method's OID is in the range reserved for initdb.
1994 : */
1995 : static void
1996 2360 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
1997 : {
1998 2360 : if (checkExtensionMembership(&method->dobj, fout))
1999 50 : return; /* extension membership overrides all else */
2000 :
2001 : /*
2002 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2003 : * they do not support ACLs currently.
2004 : */
2005 2310 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2006 2128 : method->dobj.dump = DUMP_COMPONENT_NONE;
2007 : else
2008 182 : method->dobj.dump = fout->dopt->include_everything ?
2009 182 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2010 : }
2011 :
2012 : /*
2013 : * selectDumpableExtension: policy-setting subroutine
2014 : * Mark an extension as to be dumped or not
2015 : *
2016 : * Built-in extensions should be skipped except for checking ACLs, since we
2017 : * assume those will already be installed in the target database. We identify
2018 : * such extensions by their having OIDs in the range reserved for initdb.
2019 : * We dump all user-added extensions by default. No extensions are dumped
2020 : * if include_everything is false (i.e., a --schema or --table switch was
2021 : * given), except if --extension specifies a list of extensions to dump.
2022 : */
2023 : static void
2024 356 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2025 : {
2026 : /*
2027 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2028 : * change permissions on their member objects, if they wish to, and have
2029 : * those changes preserved.
2030 : */
2031 356 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2032 306 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2033 : else
2034 : {
2035 : /* check if there is a list of extensions to dump */
2036 50 : if (extension_include_oids.head != NULL)
2037 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2038 8 : simple_oid_list_member(&extension_include_oids,
2039 : extinfo->dobj.catId.oid) ?
2040 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2041 : else
2042 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2043 42 : dopt->include_everything ?
2044 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2045 :
2046 : /* check that the extension is not explicitly excluded */
2047 92 : if (extinfo->dobj.dump &&
2048 42 : simple_oid_list_member(&extension_exclude_oids,
2049 : extinfo->dobj.catId.oid))
2050 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2051 : }
2052 356 : }
2053 :
2054 : /*
2055 : * selectDumpablePublicationObject: policy-setting subroutine
2056 : * Mark a publication object as to be dumped or not
2057 : *
2058 : * A publication can have schemas and tables which have schemas, but those are
2059 : * ignored in decision making, because publications are only dumped when we are
2060 : * dumping everything.
2061 : */
2062 : static void
2063 652 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2064 : {
2065 652 : if (checkExtensionMembership(dobj, fout))
2066 0 : return; /* extension membership overrides all else */
2067 :
2068 652 : dobj->dump = fout->dopt->include_everything ?
2069 652 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2070 : }
2071 :
2072 : /*
2073 : * selectDumpableStatisticsObject: policy-setting subroutine
2074 : * Mark an extended statistics object as to be dumped or not
2075 : *
2076 : * We dump an extended statistics object if the schema it's in and the table
2077 : * it's for are being dumped. (This'll need more thought if statistics
2078 : * objects ever support cross-table stats.)
2079 : */
2080 : static void
2081 298 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2082 : {
2083 298 : if (checkExtensionMembership(&sobj->dobj, fout))
2084 0 : return; /* extension membership overrides all else */
2085 :
2086 298 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2087 298 : if (sobj->stattable == NULL ||
2088 298 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2089 40 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2090 : }
2091 :
2092 : /*
2093 : * selectDumpableObject: policy-setting subroutine
2094 : * Mark a generic dumpable object as to be dumped or not
2095 : *
2096 : * Use this only for object types without a special-case routine above.
2097 : */
2098 : static void
2099 650786 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2100 : {
2101 650786 : if (checkExtensionMembership(dobj, fout))
2102 234 : return; /* extension membership overrides all else */
2103 :
2104 : /*
2105 : * Default policy is to dump if parent namespace is dumpable, or for
2106 : * non-namespace-associated items, dump if we're dumping "everything".
2107 : */
2108 650552 : if (dobj->namespace)
2109 649484 : dobj->dump = dobj->namespace->dobj.dump_contains;
2110 : else
2111 1068 : dobj->dump = fout->dopt->include_everything ?
2112 1068 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2113 : }
2114 :
2115 : /*
2116 : * Dump a table's contents for loading using the COPY command
2117 : * - this routine is called by the Archiver when it wants the table
2118 : * to be dumped.
2119 : */
2120 : static int
2121 6778 : dumpTableData_copy(Archive *fout, const void *dcontext)
2122 : {
2123 6778 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2124 6778 : TableInfo *tbinfo = tdinfo->tdtable;
2125 6778 : const char *classname = tbinfo->dobj.name;
2126 6778 : PQExpBuffer q = createPQExpBuffer();
2127 :
2128 : /*
2129 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2130 : * which uses it already.
2131 : */
2132 6778 : PQExpBuffer clistBuf = createPQExpBuffer();
2133 6778 : PGconn *conn = GetConnection(fout);
2134 : PGresult *res;
2135 : int ret;
2136 : char *copybuf;
2137 : const char *column_list;
2138 :
2139 6778 : pg_log_info("dumping contents of table \"%s.%s\"",
2140 : tbinfo->dobj.namespace->dobj.name, classname);
2141 :
2142 : /*
2143 : * Specify the column list explicitly so that we have no possibility of
2144 : * retrieving data in the wrong column order. (The default column
2145 : * ordering of COPY will not be what we want in certain corner cases
2146 : * involving ADD COLUMN and inheritance.)
2147 : */
2148 6778 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2149 :
2150 : /*
2151 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2152 : * a filter condition was specified. For other cases a simple COPY
2153 : * suffices.
2154 : */
2155 6778 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2156 : {
2157 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2158 : /* klugery to get rid of parens in column list */
2159 2 : if (strlen(column_list) > 2)
2160 : {
2161 2 : appendPQExpBufferStr(q, column_list + 1);
2162 2 : q->data[q->len - 1] = ' ';
2163 : }
2164 : else
2165 0 : appendPQExpBufferStr(q, "* ");
2166 :
2167 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2168 2 : fmtQualifiedDumpable(tbinfo),
2169 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2170 : }
2171 : else
2172 : {
2173 6776 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2174 6776 : fmtQualifiedDumpable(tbinfo),
2175 : column_list);
2176 : }
2177 6778 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2178 6776 : PQclear(res);
2179 6776 : destroyPQExpBuffer(clistBuf);
2180 :
2181 : for (;;)
2182 : {
2183 3425970 : ret = PQgetCopyData(conn, ©buf, 0);
2184 :
2185 3425970 : if (ret < 0)
2186 6776 : break; /* done or error */
2187 :
2188 3419194 : if (copybuf)
2189 : {
2190 3419194 : WriteData(fout, copybuf, ret);
2191 3419194 : PQfreemem(copybuf);
2192 : }
2193 :
2194 : /* ----------
2195 : * THROTTLE:
2196 : *
2197 : * There was considerable discussion in late July, 2000 regarding
2198 : * slowing down pg_dump when backing up large tables. Users with both
2199 : * slow & fast (multi-processor) machines experienced performance
2200 : * degradation when doing a backup.
2201 : *
2202 : * Initial attempts based on sleeping for a number of ms for each ms
2203 : * of work were deemed too complex, then a simple 'sleep in each loop'
2204 : * implementation was suggested. The latter failed because the loop
2205 : * was too tight. Finally, the following was implemented:
2206 : *
2207 : * If throttle is non-zero, then
2208 : * See how long since the last sleep.
2209 : * Work out how long to sleep (based on ratio).
2210 : * If sleep is more than 100ms, then
2211 : * sleep
2212 : * reset timer
2213 : * EndIf
2214 : * EndIf
2215 : *
2216 : * where the throttle value was the number of ms to sleep per ms of
2217 : * work. The calculation was done in each loop.
2218 : *
2219 : * Most of the hard work is done in the backend, and this solution
2220 : * still did not work particularly well: on slow machines, the ratio
2221 : * was 50:1, and on medium paced machines, 1:1, and on fast
2222 : * multi-processor machines, it had little or no effect, for reasons
2223 : * that were unclear.
2224 : *
2225 : * Further discussion ensued, and the proposal was dropped.
2226 : *
2227 : * For those people who want this feature, it can be implemented using
2228 : * gettimeofday in each loop, calculating the time since last sleep,
2229 : * multiplying that by the sleep ratio, then if the result is more
2230 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2231 : * function to sleep for a subsecond period ie.
2232 : *
2233 : * select(0, NULL, NULL, NULL, &tvi);
2234 : *
2235 : * This will return after the interval specified in the structure tvi.
2236 : * Finally, call gettimeofday again to save the 'last sleep time'.
2237 : * ----------
2238 : */
2239 : }
2240 6776 : archprintf(fout, "\\.\n\n\n");
2241 :
2242 6776 : if (ret == -2)
2243 : {
2244 : /* copy data transfer failed */
2245 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2246 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2247 0 : pg_log_error_detail("Command was: %s", q->data);
2248 0 : exit_nicely(1);
2249 : }
2250 :
2251 : /* Check command status and return to normal libpq state */
2252 6776 : res = PQgetResult(conn);
2253 6776 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2254 : {
2255 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2256 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2257 0 : pg_log_error_detail("Command was: %s", q->data);
2258 0 : exit_nicely(1);
2259 : }
2260 6776 : PQclear(res);
2261 :
2262 : /* Do this to ensure we've pumped libpq back to idle state */
2263 6776 : if (PQgetResult(conn) != NULL)
2264 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2265 : classname);
2266 :
2267 6776 : destroyPQExpBuffer(q);
2268 6776 : return 1;
2269 : }
2270 :
2271 : /*
2272 : * Dump table data using INSERT commands.
2273 : *
2274 : * Caution: when we restore from an archive file direct to database, the
2275 : * INSERT commands emitted by this function have to be parsed by
2276 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2277 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2278 : */
2279 : static int
2280 138 : dumpTableData_insert(Archive *fout, const void *dcontext)
2281 : {
2282 138 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2283 138 : TableInfo *tbinfo = tdinfo->tdtable;
2284 138 : DumpOptions *dopt = fout->dopt;
2285 138 : PQExpBuffer q = createPQExpBuffer();
2286 138 : PQExpBuffer insertStmt = NULL;
2287 : char *attgenerated;
2288 : PGresult *res;
2289 : int nfields,
2290 : i;
2291 138 : int rows_per_statement = dopt->dump_inserts;
2292 138 : int rows_this_statement = 0;
2293 :
2294 : /*
2295 : * If we're going to emit INSERTs with column names, the most efficient
2296 : * way to deal with generated columns is to exclude them entirely. For
2297 : * INSERTs without column names, we have to emit DEFAULT rather than the
2298 : * actual column value --- but we can save a few cycles by fetching nulls
2299 : * rather than the uninteresting-to-us value.
2300 : */
2301 138 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2302 138 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2303 138 : nfields = 0;
2304 442 : for (i = 0; i < tbinfo->numatts; i++)
2305 : {
2306 304 : if (tbinfo->attisdropped[i])
2307 4 : continue;
2308 300 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2309 10 : continue;
2310 290 : if (nfields > 0)
2311 166 : appendPQExpBufferStr(q, ", ");
2312 290 : if (tbinfo->attgenerated[i])
2313 10 : appendPQExpBufferStr(q, "NULL");
2314 : else
2315 280 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2316 290 : attgenerated[nfields] = tbinfo->attgenerated[i];
2317 290 : nfields++;
2318 : }
2319 : /* Servers before 9.4 will complain about zero-column SELECT */
2320 138 : if (nfields == 0)
2321 14 : appendPQExpBufferStr(q, "NULL");
2322 138 : appendPQExpBuffer(q, " FROM ONLY %s",
2323 138 : fmtQualifiedDumpable(tbinfo));
2324 138 : if (tdinfo->filtercond)
2325 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2326 :
2327 138 : ExecuteSqlStatement(fout, q->data);
2328 :
2329 : while (1)
2330 : {
2331 238 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2332 : PGRES_TUPLES_OK);
2333 :
2334 : /* cross-check field count, allowing for dummy NULL if any */
2335 238 : if (nfields != PQnfields(res) &&
2336 20 : !(nfields == 0 && PQnfields(res) == 1))
2337 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2338 : tbinfo->dobj.name);
2339 :
2340 : /*
2341 : * First time through, we build as much of the INSERT statement as
2342 : * possible in "insertStmt", which we can then just print for each
2343 : * statement. If the table happens to have zero dumpable columns then
2344 : * this will be a complete statement, otherwise it will end in
2345 : * "VALUES" and be ready to have the row's column values printed.
2346 : */
2347 238 : if (insertStmt == NULL)
2348 : {
2349 : TableInfo *targettab;
2350 :
2351 138 : insertStmt = createPQExpBuffer();
2352 :
2353 : /*
2354 : * When load-via-partition-root is set or forced, get the root
2355 : * table name for the partition table, so that we can reload data
2356 : * through the root table.
2357 : */
2358 138 : if (tbinfo->ispartition &&
2359 80 : (dopt->load_via_partition_root ||
2360 40 : forcePartitionRootLoad(tbinfo)))
2361 6 : targettab = getRootTableInfo(tbinfo);
2362 : else
2363 132 : targettab = tbinfo;
2364 :
2365 138 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2366 138 : fmtQualifiedDumpable(targettab));
2367 :
2368 : /* corner case for zero-column table */
2369 138 : if (nfields == 0)
2370 : {
2371 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2372 : }
2373 : else
2374 : {
2375 : /* append the list of column names if required */
2376 124 : if (dopt->column_inserts)
2377 : {
2378 54 : appendPQExpBufferChar(insertStmt, '(');
2379 176 : for (int field = 0; field < nfields; field++)
2380 : {
2381 122 : if (field > 0)
2382 68 : appendPQExpBufferStr(insertStmt, ", ");
2383 122 : appendPQExpBufferStr(insertStmt,
2384 122 : fmtId(PQfname(res, field)));
2385 : }
2386 54 : appendPQExpBufferStr(insertStmt, ") ");
2387 : }
2388 :
2389 124 : if (tbinfo->needs_override)
2390 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2391 :
2392 124 : appendPQExpBufferStr(insertStmt, "VALUES");
2393 : }
2394 : }
2395 :
2396 6380 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2397 : {
2398 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2399 6142 : if (rows_this_statement == 0)
2400 6130 : archputs(insertStmt->data, fout);
2401 :
2402 : /*
2403 : * If it is zero-column table then we've already written the
2404 : * complete statement, which will mean we've disobeyed
2405 : * --rows-per-insert when it's set greater than 1. We do support
2406 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2407 : * UNION ALL ... but that's non-standard so we should avoid it
2408 : * given that using INSERTs is mostly only ever needed for
2409 : * cross-database exports.
2410 : */
2411 6142 : if (nfields == 0)
2412 12 : continue;
2413 :
2414 : /* Emit a row heading */
2415 6130 : if (rows_per_statement == 1)
2416 6112 : archputs(" (", fout);
2417 18 : else if (rows_this_statement > 0)
2418 12 : archputs(",\n\t(", fout);
2419 : else
2420 6 : archputs("\n\t(", fout);
2421 :
2422 18498 : for (int field = 0; field < nfields; field++)
2423 : {
2424 12368 : if (field > 0)
2425 6238 : archputs(", ", fout);
2426 12368 : if (attgenerated[field])
2427 : {
2428 4 : archputs("DEFAULT", fout);
2429 4 : continue;
2430 : }
2431 12364 : if (PQgetisnull(res, tuple, field))
2432 : {
2433 166 : archputs("NULL", fout);
2434 166 : continue;
2435 : }
2436 :
2437 : /* XXX This code is partially duplicated in ruleutils.c */
2438 12198 : switch (PQftype(res, field))
2439 : {
2440 8138 : case INT2OID:
2441 : case INT4OID:
2442 : case INT8OID:
2443 : case OIDOID:
2444 : case FLOAT4OID:
2445 : case FLOAT8OID:
2446 : case NUMERICOID:
2447 : {
2448 : /*
2449 : * These types are printed without quotes unless
2450 : * they contain values that aren't accepted by the
2451 : * scanner unquoted (e.g., 'NaN'). Note that
2452 : * strtod() and friends might accept NaN, so we
2453 : * can't use that to test.
2454 : *
2455 : * In reality we only need to defend against
2456 : * infinity and NaN, so we need not get too crazy
2457 : * about pattern matching here.
2458 : */
2459 8138 : const char *s = PQgetvalue(res, tuple, field);
2460 :
2461 8138 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2462 8134 : archputs(s, fout);
2463 : else
2464 4 : archprintf(fout, "'%s'", s);
2465 : }
2466 8138 : break;
2467 :
2468 4 : case BITOID:
2469 : case VARBITOID:
2470 4 : archprintf(fout, "B'%s'",
2471 : PQgetvalue(res, tuple, field));
2472 4 : break;
2473 :
2474 8 : case BOOLOID:
2475 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2476 4 : archputs("true", fout);
2477 : else
2478 4 : archputs("false", fout);
2479 8 : break;
2480 :
2481 4048 : default:
2482 : /* All other types are printed as string literals. */
2483 4048 : resetPQExpBuffer(q);
2484 4048 : appendStringLiteralAH(q,
2485 : PQgetvalue(res, tuple, field),
2486 : fout);
2487 4048 : archputs(q->data, fout);
2488 4048 : break;
2489 : }
2490 : }
2491 :
2492 : /* Terminate the row ... */
2493 6130 : archputs(")", fout);
2494 :
2495 : /* ... and the statement, if the target no. of rows is reached */
2496 6130 : if (++rows_this_statement >= rows_per_statement)
2497 : {
2498 6116 : if (dopt->do_nothing)
2499 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2500 : else
2501 6116 : archputs(";\n", fout);
2502 : /* Reset the row counter */
2503 6116 : rows_this_statement = 0;
2504 : }
2505 : }
2506 :
2507 238 : if (PQntuples(res) <= 0)
2508 : {
2509 138 : PQclear(res);
2510 138 : break;
2511 : }
2512 100 : PQclear(res);
2513 : }
2514 :
2515 : /* Terminate any statements that didn't make the row count. */
2516 138 : if (rows_this_statement > 0)
2517 : {
2518 2 : if (dopt->do_nothing)
2519 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2520 : else
2521 2 : archputs(";\n", fout);
2522 : }
2523 :
2524 138 : archputs("\n\n", fout);
2525 :
2526 138 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2527 :
2528 138 : destroyPQExpBuffer(q);
2529 138 : if (insertStmt != NULL)
2530 138 : destroyPQExpBuffer(insertStmt);
2531 138 : free(attgenerated);
2532 :
2533 138 : return 1;
2534 : }
2535 :
2536 : /*
2537 : * getRootTableInfo:
2538 : * get the root TableInfo for the given partition table.
2539 : */
2540 : static TableInfo *
2541 18 : getRootTableInfo(const TableInfo *tbinfo)
2542 : {
2543 : TableInfo *parentTbinfo;
2544 :
2545 : Assert(tbinfo->ispartition);
2546 : Assert(tbinfo->numParents == 1);
2547 :
2548 18 : parentTbinfo = tbinfo->parents[0];
2549 18 : while (parentTbinfo->ispartition)
2550 : {
2551 : Assert(parentTbinfo->numParents == 1);
2552 0 : parentTbinfo = parentTbinfo->parents[0];
2553 : }
2554 :
2555 18 : return parentTbinfo;
2556 : }
2557 :
2558 : /*
2559 : * forcePartitionRootLoad
2560 : * Check if we must force load_via_partition_root for this partition.
2561 : *
2562 : * This is required if any level of ancestral partitioned table has an
2563 : * unsafe partitioning scheme.
2564 : */
2565 : static bool
2566 1868 : forcePartitionRootLoad(const TableInfo *tbinfo)
2567 : {
2568 : TableInfo *parentTbinfo;
2569 :
2570 : Assert(tbinfo->ispartition);
2571 : Assert(tbinfo->numParents == 1);
2572 :
2573 1868 : parentTbinfo = tbinfo->parents[0];
2574 1868 : if (parentTbinfo->unsafe_partitions)
2575 18 : return true;
2576 2282 : while (parentTbinfo->ispartition)
2577 : {
2578 : Assert(parentTbinfo->numParents == 1);
2579 432 : parentTbinfo = parentTbinfo->parents[0];
2580 432 : if (parentTbinfo->unsafe_partitions)
2581 0 : return true;
2582 : }
2583 :
2584 1850 : return false;
2585 : }
2586 :
2587 : /*
2588 : * dumpTableData -
2589 : * dump the contents of a single table
2590 : *
2591 : * Actually, this just makes an ArchiveEntry for the table contents.
2592 : */
2593 : static void
2594 7052 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2595 : {
2596 7052 : DumpOptions *dopt = fout->dopt;
2597 7052 : TableInfo *tbinfo = tdinfo->tdtable;
2598 7052 : PQExpBuffer copyBuf = createPQExpBuffer();
2599 7052 : PQExpBuffer clistBuf = createPQExpBuffer();
2600 : DataDumperPtr dumpFn;
2601 7052 : char *tdDefn = NULL;
2602 : char *copyStmt;
2603 : const char *copyFrom;
2604 :
2605 : /* We had better have loaded per-column details about this table */
2606 : Assert(tbinfo->interesting);
2607 :
2608 : /*
2609 : * When load-via-partition-root is set or forced, get the root table name
2610 : * for the partition table, so that we can reload data through the root
2611 : * table. Then construct a comment to be inserted into the TOC entry's
2612 : * defn field, so that such cases can be identified reliably.
2613 : */
2614 7052 : if (tbinfo->ispartition &&
2615 3656 : (dopt->load_via_partition_root ||
2616 1828 : forcePartitionRootLoad(tbinfo)))
2617 12 : {
2618 : TableInfo *parentTbinfo;
2619 :
2620 12 : parentTbinfo = getRootTableInfo(tbinfo);
2621 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2622 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2623 : copyFrom);
2624 12 : tdDefn = pg_strdup(copyBuf->data);
2625 : }
2626 : else
2627 7040 : copyFrom = fmtQualifiedDumpable(tbinfo);
2628 :
2629 7052 : if (dopt->dump_inserts == 0)
2630 : {
2631 : /* Dump/restore using COPY */
2632 6914 : dumpFn = dumpTableData_copy;
2633 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2634 6914 : printfPQExpBuffer(copyBuf, "COPY %s ",
2635 : copyFrom);
2636 6914 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2637 : fmtCopyColumnList(tbinfo, clistBuf));
2638 6914 : copyStmt = copyBuf->data;
2639 : }
2640 : else
2641 : {
2642 : /* Restore using INSERT */
2643 138 : dumpFn = dumpTableData_insert;
2644 138 : copyStmt = NULL;
2645 : }
2646 :
2647 : /*
2648 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2649 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2650 : * See comments for BuildArchiveDependencies.
2651 : */
2652 7052 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2653 : {
2654 : TocEntry *te;
2655 :
2656 7052 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2657 7052 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2658 : .namespace = tbinfo->dobj.namespace->dobj.name,
2659 : .owner = tbinfo->rolname,
2660 : .description = "TABLE DATA",
2661 : .section = SECTION_DATA,
2662 : .createStmt = tdDefn,
2663 : .copyStmt = copyStmt,
2664 : .deps = &(tbinfo->dobj.dumpId),
2665 : .nDeps = 1,
2666 : .dumpFn = dumpFn,
2667 : .dumpArg = tdinfo));
2668 :
2669 : /*
2670 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2671 : * and want to order dump jobs by table size. We choose to measure
2672 : * dataLength in table pages (including TOAST pages) during dump, so
2673 : * no scaling is needed.
2674 : *
2675 : * However, relpages is declared as "integer" in pg_class, and hence
2676 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2677 : * Cast so that we get the right interpretation of table sizes
2678 : * exceeding INT_MAX pages.
2679 : */
2680 7052 : te->dataLength = (BlockNumber) tbinfo->relpages;
2681 7052 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2682 :
2683 : /*
2684 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2685 : * and instead we'd better worry about integer overflow. Clamp to
2686 : * INT_MAX if the correct result exceeds that.
2687 : */
2688 : if (sizeof(te->dataLength) == 4 &&
2689 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2690 : te->dataLength < 0))
2691 : te->dataLength = INT_MAX;
2692 : }
2693 :
2694 7052 : destroyPQExpBuffer(copyBuf);
2695 7052 : destroyPQExpBuffer(clistBuf);
2696 7052 : }
2697 :
2698 : /*
2699 : * refreshMatViewData -
2700 : * load or refresh the contents of a single materialized view
2701 : *
2702 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2703 : * statement.
2704 : */
2705 : static void
2706 676 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2707 : {
2708 676 : TableInfo *tbinfo = tdinfo->tdtable;
2709 : PQExpBuffer q;
2710 :
2711 : /* If the materialized view is not flagged as populated, skip this. */
2712 676 : if (!tbinfo->relispopulated)
2713 136 : return;
2714 :
2715 540 : q = createPQExpBuffer();
2716 :
2717 540 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2718 540 : fmtQualifiedDumpable(tbinfo));
2719 :
2720 540 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2721 540 : ArchiveEntry(fout,
2722 : tdinfo->dobj.catId, /* catalog ID */
2723 : tdinfo->dobj.dumpId, /* dump ID */
2724 540 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2725 : .namespace = tbinfo->dobj.namespace->dobj.name,
2726 : .owner = tbinfo->rolname,
2727 : .description = "MATERIALIZED VIEW DATA",
2728 : .section = SECTION_POST_DATA,
2729 : .createStmt = q->data,
2730 : .deps = tdinfo->dobj.dependencies,
2731 : .nDeps = tdinfo->dobj.nDeps));
2732 :
2733 540 : destroyPQExpBuffer(q);
2734 : }
2735 :
2736 : /*
2737 : * getTableData -
2738 : * set up dumpable objects representing the contents of tables
2739 : */
2740 : static void
2741 300 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2742 : {
2743 : int i;
2744 :
2745 76310 : for (i = 0; i < numTables; i++)
2746 : {
2747 76010 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2748 1636 : (!relkind || tblinfo[i].relkind == relkind))
2749 9974 : makeTableDataInfo(dopt, &(tblinfo[i]));
2750 : }
2751 300 : }
2752 :
2753 : /*
2754 : * Make a dumpable object for the data of this specific table
2755 : *
2756 : * Note: we make a TableDataInfo if and only if we are going to dump the
2757 : * table data; the "dump" field in such objects isn't very interesting.
2758 : */
2759 : static void
2760 10052 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2761 : {
2762 : TableDataInfo *tdinfo;
2763 :
2764 : /*
2765 : * Nothing to do if we already decided to dump the table. This will
2766 : * happen for "config" tables.
2767 : */
2768 10052 : if (tbinfo->dataObj != NULL)
2769 2 : return;
2770 :
2771 : /* Skip VIEWs (no data to dump) */
2772 10050 : if (tbinfo->relkind == RELKIND_VIEW)
2773 600 : return;
2774 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2775 9450 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2776 76 : (foreign_servers_include_oids.head == NULL ||
2777 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2778 : tbinfo->foreign_server)))
2779 74 : return;
2780 : /* Skip partitioned tables (data in partitions) */
2781 9376 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2782 852 : return;
2783 :
2784 : /* Don't dump data in unlogged tables, if so requested */
2785 8524 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2786 64 : dopt->no_unlogged_table_data)
2787 28 : return;
2788 :
2789 : /* Check that the data is not explicitly excluded */
2790 8496 : if (simple_oid_list_member(&tabledata_exclude_oids,
2791 : tbinfo->dobj.catId.oid))
2792 16 : return;
2793 :
2794 : /* OK, let's dump it */
2795 8480 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2796 :
2797 8480 : if (tbinfo->relkind == RELKIND_MATVIEW)
2798 676 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2799 7804 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2800 752 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2801 : else
2802 7052 : tdinfo->dobj.objType = DO_TABLE_DATA;
2803 :
2804 : /*
2805 : * Note: use tableoid 0 so that this object won't be mistaken for
2806 : * something that pg_depend entries apply to.
2807 : */
2808 8480 : tdinfo->dobj.catId.tableoid = 0;
2809 8480 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2810 8480 : AssignDumpId(&tdinfo->dobj);
2811 8480 : tdinfo->dobj.name = tbinfo->dobj.name;
2812 8480 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2813 8480 : tdinfo->tdtable = tbinfo;
2814 8480 : tdinfo->filtercond = NULL; /* might get set later */
2815 8480 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2816 :
2817 : /* A TableDataInfo contains data, of course */
2818 8480 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
2819 :
2820 8480 : tbinfo->dataObj = tdinfo;
2821 :
2822 : /* Make sure that we'll collect per-column info for this table. */
2823 8480 : tbinfo->interesting = true;
2824 : }
2825 :
2826 : /*
2827 : * The refresh for a materialized view must be dependent on the refresh for
2828 : * any materialized view that this one is dependent on.
2829 : *
2830 : * This must be called after all the objects are created, but before they are
2831 : * sorted.
2832 : */
2833 : static void
2834 272 : buildMatViewRefreshDependencies(Archive *fout)
2835 : {
2836 : PQExpBuffer query;
2837 : PGresult *res;
2838 : int ntups,
2839 : i;
2840 : int i_classid,
2841 : i_objid,
2842 : i_refobjid;
2843 :
2844 : /* No Mat Views before 9.3. */
2845 272 : if (fout->remoteVersion < 90300)
2846 0 : return;
2847 :
2848 272 : query = createPQExpBuffer();
2849 :
2850 272 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2851 : "( "
2852 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2853 : "FROM pg_depend d1 "
2854 : "JOIN pg_class c1 ON c1.oid = d1.objid "
2855 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2856 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2857 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2858 : "AND d2.objid = r1.oid "
2859 : "AND d2.refobjid <> d1.objid "
2860 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2861 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2862 : CppAsString2(RELKIND_VIEW) ") "
2863 : "WHERE d1.classid = 'pg_class'::regclass "
2864 : "UNION "
2865 : "SELECT w.objid, d3.refobjid, c3.relkind "
2866 : "FROM w "
2867 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2868 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2869 : "AND d3.objid = r3.oid "
2870 : "AND d3.refobjid <> w.refobjid "
2871 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2872 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2873 : CppAsString2(RELKIND_VIEW) ") "
2874 : ") "
2875 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2876 : "FROM w "
2877 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2878 :
2879 272 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2880 :
2881 272 : ntups = PQntuples(res);
2882 :
2883 272 : i_classid = PQfnumber(res, "classid");
2884 272 : i_objid = PQfnumber(res, "objid");
2885 272 : i_refobjid = PQfnumber(res, "refobjid");
2886 :
2887 788 : for (i = 0; i < ntups; i++)
2888 : {
2889 : CatalogId objId;
2890 : CatalogId refobjId;
2891 : DumpableObject *dobj;
2892 : DumpableObject *refdobj;
2893 : TableInfo *tbinfo;
2894 : TableInfo *reftbinfo;
2895 :
2896 516 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2897 516 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
2898 516 : refobjId.tableoid = objId.tableoid;
2899 516 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
2900 :
2901 516 : dobj = findObjectByCatalogId(objId);
2902 516 : if (dobj == NULL)
2903 84 : continue;
2904 :
2905 : Assert(dobj->objType == DO_TABLE);
2906 516 : tbinfo = (TableInfo *) dobj;
2907 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
2908 516 : dobj = (DumpableObject *) tbinfo->dataObj;
2909 516 : if (dobj == NULL)
2910 84 : continue;
2911 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
2912 :
2913 432 : refdobj = findObjectByCatalogId(refobjId);
2914 432 : if (refdobj == NULL)
2915 0 : continue;
2916 :
2917 : Assert(refdobj->objType == DO_TABLE);
2918 432 : reftbinfo = (TableInfo *) refdobj;
2919 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
2920 432 : refdobj = (DumpableObject *) reftbinfo->dataObj;
2921 432 : if (refdobj == NULL)
2922 0 : continue;
2923 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
2924 :
2925 432 : addObjectDependency(dobj, refdobj->dumpId);
2926 :
2927 432 : if (!reftbinfo->relispopulated)
2928 68 : tbinfo->relispopulated = false;
2929 : }
2930 :
2931 272 : PQclear(res);
2932 :
2933 272 : destroyPQExpBuffer(query);
2934 : }
2935 :
2936 : /*
2937 : * getTableDataFKConstraints -
2938 : * add dump-order dependencies reflecting foreign key constraints
2939 : *
2940 : * This code is executed only in a data-only dump --- in schema+data dumps
2941 : * we handle foreign key issues by not creating the FK constraints until
2942 : * after the data is loaded. In a data-only dump, however, we want to
2943 : * order the table data objects in such a way that a table's referenced
2944 : * tables are restored first. (In the presence of circular references or
2945 : * self-references this may be impossible; we'll detect and complain about
2946 : * that during the dependency sorting step.)
2947 : */
2948 : static void
2949 12 : getTableDataFKConstraints(void)
2950 : {
2951 : DumpableObject **dobjs;
2952 : int numObjs;
2953 : int i;
2954 :
2955 : /* Search through all the dumpable objects for FK constraints */
2956 12 : getDumpableObjects(&dobjs, &numObjs);
2957 42770 : for (i = 0; i < numObjs; i++)
2958 : {
2959 42758 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
2960 : {
2961 12 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
2962 : TableInfo *ftable;
2963 :
2964 : /* Not interesting unless both tables are to be dumped */
2965 12 : if (cinfo->contable == NULL ||
2966 12 : cinfo->contable->dataObj == NULL)
2967 6 : continue;
2968 6 : ftable = findTableByOid(cinfo->confrelid);
2969 6 : if (ftable == NULL ||
2970 6 : ftable->dataObj == NULL)
2971 0 : continue;
2972 :
2973 : /*
2974 : * Okay, make referencing table's TABLE_DATA object depend on the
2975 : * referenced table's TABLE_DATA object.
2976 : */
2977 6 : addObjectDependency(&cinfo->contable->dataObj->dobj,
2978 6 : ftable->dataObj->dobj.dumpId);
2979 : }
2980 : }
2981 12 : free(dobjs);
2982 12 : }
2983 :
2984 :
2985 : /*
2986 : * dumpDatabase:
2987 : * dump the database definition
2988 : */
2989 : static void
2990 120 : dumpDatabase(Archive *fout)
2991 : {
2992 120 : DumpOptions *dopt = fout->dopt;
2993 120 : PQExpBuffer dbQry = createPQExpBuffer();
2994 120 : PQExpBuffer delQry = createPQExpBuffer();
2995 120 : PQExpBuffer creaQry = createPQExpBuffer();
2996 120 : PQExpBuffer labelq = createPQExpBuffer();
2997 120 : PGconn *conn = GetConnection(fout);
2998 : PGresult *res;
2999 : int i_tableoid,
3000 : i_oid,
3001 : i_datname,
3002 : i_datdba,
3003 : i_encoding,
3004 : i_datlocprovider,
3005 : i_collate,
3006 : i_ctype,
3007 : i_datlocale,
3008 : i_daticurules,
3009 : i_frozenxid,
3010 : i_minmxid,
3011 : i_datacl,
3012 : i_acldefault,
3013 : i_datistemplate,
3014 : i_datconnlimit,
3015 : i_datcollversion,
3016 : i_tablespace;
3017 : CatalogId dbCatId;
3018 : DumpId dbDumpId;
3019 : DumpableAcl dbdacl;
3020 : const char *datname,
3021 : *dba,
3022 : *encoding,
3023 : *datlocprovider,
3024 : *collate,
3025 : *ctype,
3026 : *locale,
3027 : *icurules,
3028 : *datistemplate,
3029 : *datconnlimit,
3030 : *tablespace;
3031 : uint32 frozenxid,
3032 : minmxid;
3033 : char *qdatname;
3034 :
3035 120 : pg_log_info("saving database definition");
3036 :
3037 : /*
3038 : * Fetch the database-level properties for this database.
3039 : */
3040 120 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3041 : "datdba, "
3042 : "pg_encoding_to_char(encoding) AS encoding, "
3043 : "datcollate, datctype, datfrozenxid, "
3044 : "datacl, acldefault('d', datdba) AS acldefault, "
3045 : "datistemplate, datconnlimit, ");
3046 120 : if (fout->remoteVersion >= 90300)
3047 120 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3048 : else
3049 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3050 120 : if (fout->remoteVersion >= 170000)
3051 120 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3052 0 : else if (fout->remoteVersion >= 150000)
3053 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3054 : else
3055 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3056 120 : if (fout->remoteVersion >= 160000)
3057 120 : appendPQExpBufferStr(dbQry, "daticurules, ");
3058 : else
3059 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3060 120 : appendPQExpBufferStr(dbQry,
3061 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3062 : "shobj_description(oid, 'pg_database') AS description "
3063 : "FROM pg_database "
3064 : "WHERE datname = current_database()");
3065 :
3066 120 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3067 :
3068 120 : i_tableoid = PQfnumber(res, "tableoid");
3069 120 : i_oid = PQfnumber(res, "oid");
3070 120 : i_datname = PQfnumber(res, "datname");
3071 120 : i_datdba = PQfnumber(res, "datdba");
3072 120 : i_encoding = PQfnumber(res, "encoding");
3073 120 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3074 120 : i_collate = PQfnumber(res, "datcollate");
3075 120 : i_ctype = PQfnumber(res, "datctype");
3076 120 : i_datlocale = PQfnumber(res, "datlocale");
3077 120 : i_daticurules = PQfnumber(res, "daticurules");
3078 120 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3079 120 : i_minmxid = PQfnumber(res, "datminmxid");
3080 120 : i_datacl = PQfnumber(res, "datacl");
3081 120 : i_acldefault = PQfnumber(res, "acldefault");
3082 120 : i_datistemplate = PQfnumber(res, "datistemplate");
3083 120 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3084 120 : i_datcollversion = PQfnumber(res, "datcollversion");
3085 120 : i_tablespace = PQfnumber(res, "tablespace");
3086 :
3087 120 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3088 120 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3089 120 : datname = PQgetvalue(res, 0, i_datname);
3090 120 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3091 120 : encoding = PQgetvalue(res, 0, i_encoding);
3092 120 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3093 120 : collate = PQgetvalue(res, 0, i_collate);
3094 120 : ctype = PQgetvalue(res, 0, i_ctype);
3095 120 : if (!PQgetisnull(res, 0, i_datlocale))
3096 28 : locale = PQgetvalue(res, 0, i_datlocale);
3097 : else
3098 92 : locale = NULL;
3099 120 : if (!PQgetisnull(res, 0, i_daticurules))
3100 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3101 : else
3102 120 : icurules = NULL;
3103 120 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3104 120 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3105 120 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3106 120 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3107 120 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3108 120 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3109 120 : tablespace = PQgetvalue(res, 0, i_tablespace);
3110 :
3111 120 : qdatname = pg_strdup(fmtId(datname));
3112 :
3113 : /*
3114 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3115 : * to preserve that), as well as the encoding, locale, and tablespace
3116 : * since those can't be altered later. Other DB properties are left to
3117 : * the DATABASE PROPERTIES entry, so that they can be applied after
3118 : * reconnecting to the target DB.
3119 : */
3120 120 : if (dopt->binary_upgrade)
3121 : {
3122 26 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0 OID = %u",
3123 : qdatname, dbCatId.oid);
3124 : }
3125 : else
3126 : {
3127 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3128 : qdatname);
3129 : }
3130 120 : if (strlen(encoding) > 0)
3131 : {
3132 120 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3133 120 : appendStringLiteralAH(creaQry, encoding, fout);
3134 : }
3135 :
3136 120 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3137 120 : if (datlocprovider[0] == 'b')
3138 28 : appendPQExpBufferStr(creaQry, "builtin");
3139 92 : else if (datlocprovider[0] == 'c')
3140 92 : appendPQExpBufferStr(creaQry, "libc");
3141 0 : else if (datlocprovider[0] == 'i')
3142 0 : appendPQExpBufferStr(creaQry, "icu");
3143 : else
3144 0 : pg_fatal("unrecognized locale provider: %s",
3145 : datlocprovider);
3146 :
3147 120 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3148 : {
3149 120 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3150 120 : appendStringLiteralAH(creaQry, collate, fout);
3151 : }
3152 : else
3153 : {
3154 0 : if (strlen(collate) > 0)
3155 : {
3156 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3157 0 : appendStringLiteralAH(creaQry, collate, fout);
3158 : }
3159 0 : if (strlen(ctype) > 0)
3160 : {
3161 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3162 0 : appendStringLiteralAH(creaQry, ctype, fout);
3163 : }
3164 : }
3165 120 : if (locale)
3166 : {
3167 28 : if (datlocprovider[0] == 'b')
3168 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3169 : else
3170 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3171 :
3172 28 : appendStringLiteralAH(creaQry, locale, fout);
3173 : }
3174 :
3175 120 : if (icurules)
3176 : {
3177 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3178 0 : appendStringLiteralAH(creaQry, icurules, fout);
3179 : }
3180 :
3181 : /*
3182 : * For binary upgrade, carry over the collation version. For normal
3183 : * dump/restore, omit the version, so that it is computed upon restore.
3184 : */
3185 120 : if (dopt->binary_upgrade)
3186 : {
3187 26 : if (!PQgetisnull(res, 0, i_datcollversion))
3188 : {
3189 14 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3190 14 : appendStringLiteralAH(creaQry,
3191 : PQgetvalue(res, 0, i_datcollversion),
3192 : fout);
3193 : }
3194 : }
3195 :
3196 : /*
3197 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3198 : * thing; the decision whether to specify a tablespace should be left till
3199 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3200 : * label the DATABASE entry with the tablespace and let the normal
3201 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3202 : * attention to default_tablespace, so that won't work.
3203 : */
3204 120 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3205 0 : !dopt->outputNoTablespaces)
3206 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3207 : fmtId(tablespace));
3208 120 : appendPQExpBufferStr(creaQry, ";\n");
3209 :
3210 120 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3211 : qdatname);
3212 :
3213 120 : dbDumpId = createDumpId();
3214 :
3215 120 : ArchiveEntry(fout,
3216 : dbCatId, /* catalog ID */
3217 : dbDumpId, /* dump ID */
3218 120 : ARCHIVE_OPTS(.tag = datname,
3219 : .owner = dba,
3220 : .description = "DATABASE",
3221 : .section = SECTION_PRE_DATA,
3222 : .createStmt = creaQry->data,
3223 : .dropStmt = delQry->data));
3224 :
3225 : /* Compute correct tag for archive entry */
3226 120 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3227 :
3228 : /* Dump DB comment if any */
3229 : {
3230 : /*
3231 : * 8.2 and up keep comments on shared objects in a shared table, so we
3232 : * cannot use the dumpComment() code used for other database objects.
3233 : * Be careful that the ArchiveEntry parameters match that function.
3234 : */
3235 120 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3236 :
3237 120 : if (comment && *comment && !dopt->no_comments)
3238 : {
3239 50 : resetPQExpBuffer(dbQry);
3240 :
3241 : /*
3242 : * Generates warning when loaded into a differently-named
3243 : * database.
3244 : */
3245 50 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3246 50 : appendStringLiteralAH(dbQry, comment, fout);
3247 50 : appendPQExpBufferStr(dbQry, ";\n");
3248 :
3249 50 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3250 50 : ARCHIVE_OPTS(.tag = labelq->data,
3251 : .owner = dba,
3252 : .description = "COMMENT",
3253 : .section = SECTION_NONE,
3254 : .createStmt = dbQry->data,
3255 : .deps = &dbDumpId,
3256 : .nDeps = 1));
3257 : }
3258 : }
3259 :
3260 : /* Dump DB security label, if enabled */
3261 120 : if (!dopt->no_security_labels)
3262 : {
3263 : PGresult *shres;
3264 : PQExpBuffer seclabelQry;
3265 :
3266 120 : seclabelQry = createPQExpBuffer();
3267 :
3268 120 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3269 120 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3270 120 : resetPQExpBuffer(seclabelQry);
3271 120 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3272 120 : if (seclabelQry->len > 0)
3273 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3274 0 : ARCHIVE_OPTS(.tag = labelq->data,
3275 : .owner = dba,
3276 : .description = "SECURITY LABEL",
3277 : .section = SECTION_NONE,
3278 : .createStmt = seclabelQry->data,
3279 : .deps = &dbDumpId,
3280 : .nDeps = 1));
3281 120 : destroyPQExpBuffer(seclabelQry);
3282 120 : PQclear(shres);
3283 : }
3284 :
3285 : /*
3286 : * Dump ACL if any. Note that we do not support initial privileges
3287 : * (pg_init_privs) on databases.
3288 : */
3289 120 : dbdacl.privtype = 0;
3290 120 : dbdacl.initprivs = NULL;
3291 :
3292 120 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3293 : qdatname, NULL, NULL,
3294 : dba, &dbdacl);
3295 :
3296 : /*
3297 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3298 : * non-default database-level properties. (The reason this must be
3299 : * separate is that we cannot put any additional commands into the TOC
3300 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3301 : * in an implicit transaction block, and the backend won't allow CREATE
3302 : * DATABASE in that context.)
3303 : */
3304 120 : resetPQExpBuffer(creaQry);
3305 120 : resetPQExpBuffer(delQry);
3306 :
3307 120 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3308 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3309 : qdatname, datconnlimit);
3310 :
3311 120 : if (strcmp(datistemplate, "t") == 0)
3312 : {
3313 8 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3314 : qdatname);
3315 :
3316 : /*
3317 : * The backend won't accept DROP DATABASE on a template database. We
3318 : * can deal with that by removing the template marking before the DROP
3319 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3320 : * since no such command is currently supported, fake it with a direct
3321 : * UPDATE on pg_database.
3322 : */
3323 8 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3324 : "SET datistemplate = false WHERE datname = ");
3325 8 : appendStringLiteralAH(delQry, datname, fout);
3326 8 : appendPQExpBufferStr(delQry, ";\n");
3327 : }
3328 :
3329 : /*
3330 : * We do not restore pg_database.dathasloginevt because it is set
3331 : * automatically on login event trigger creation.
3332 : */
3333 :
3334 : /* Add database-specific SET options */
3335 120 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3336 :
3337 : /*
3338 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3339 : * entry, too, for lack of a better place.
3340 : */
3341 120 : if (dopt->binary_upgrade)
3342 : {
3343 26 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3344 26 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3345 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3346 : "WHERE datname = ",
3347 : frozenxid, minmxid);
3348 26 : appendStringLiteralAH(creaQry, datname, fout);
3349 26 : appendPQExpBufferStr(creaQry, ";\n");
3350 : }
3351 :
3352 120 : if (creaQry->len > 0)
3353 34 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3354 34 : ARCHIVE_OPTS(.tag = datname,
3355 : .owner = dba,
3356 : .description = "DATABASE PROPERTIES",
3357 : .section = SECTION_PRE_DATA,
3358 : .createStmt = creaQry->data,
3359 : .dropStmt = delQry->data,
3360 : .deps = &dbDumpId));
3361 :
3362 : /*
3363 : * pg_largeobject comes from the old system intact, so set its
3364 : * relfrozenxids, relminmxids and relfilenode.
3365 : */
3366 120 : if (dopt->binary_upgrade)
3367 : {
3368 : PGresult *lo_res;
3369 26 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3370 26 : PQExpBuffer loOutQry = createPQExpBuffer();
3371 26 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3372 : int ii_relfrozenxid,
3373 : ii_relfilenode,
3374 : ii_oid,
3375 : ii_relminmxid;
3376 :
3377 : /*
3378 : * pg_largeobject
3379 : */
3380 26 : if (fout->remoteVersion >= 90300)
3381 26 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3382 : "FROM pg_catalog.pg_class\n"
3383 : "WHERE oid IN (%u, %u);\n",
3384 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3385 : else
3386 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3387 : "FROM pg_catalog.pg_class\n"
3388 : "WHERE oid IN (%u, %u);\n",
3389 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3390 :
3391 26 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3392 :
3393 26 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3394 26 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3395 26 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3396 26 : ii_oid = PQfnumber(lo_res, "oid");
3397 :
3398 26 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3399 26 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3400 78 : for (int i = 0; i < PQntuples(lo_res); ++i)
3401 : {
3402 : Oid oid;
3403 : RelFileNumber relfilenumber;
3404 :
3405 52 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3406 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3407 : "WHERE oid = %u;\n",
3408 52 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3409 52 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3410 52 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3411 :
3412 52 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3413 52 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3414 :
3415 52 : if (oid == LargeObjectRelationId)
3416 26 : appendPQExpBuffer(loOutQry,
3417 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3418 : relfilenumber);
3419 26 : else if (oid == LargeObjectLOidPNIndexId)
3420 26 : appendPQExpBuffer(loOutQry,
3421 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3422 : relfilenumber);
3423 : }
3424 :
3425 26 : appendPQExpBufferStr(loOutQry,
3426 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3427 26 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3428 :
3429 26 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3430 26 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3431 : .description = "pg_largeobject",
3432 : .section = SECTION_PRE_DATA,
3433 : .createStmt = loOutQry->data));
3434 :
3435 26 : PQclear(lo_res);
3436 :
3437 26 : destroyPQExpBuffer(loFrozenQry);
3438 26 : destroyPQExpBuffer(loHorizonQry);
3439 26 : destroyPQExpBuffer(loOutQry);
3440 : }
3441 :
3442 120 : PQclear(res);
3443 :
3444 120 : free(qdatname);
3445 120 : destroyPQExpBuffer(dbQry);
3446 120 : destroyPQExpBuffer(delQry);
3447 120 : destroyPQExpBuffer(creaQry);
3448 120 : destroyPQExpBuffer(labelq);
3449 120 : }
3450 :
3451 : /*
3452 : * Collect any database-specific or role-and-database-specific SET options
3453 : * for this database, and append them to outbuf.
3454 : */
3455 : static void
3456 120 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3457 : const char *dbname, Oid dboid)
3458 : {
3459 120 : PGconn *conn = GetConnection(AH);
3460 120 : PQExpBuffer buf = createPQExpBuffer();
3461 : PGresult *res;
3462 :
3463 : /* First collect database-specific options */
3464 120 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3465 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3466 : dboid);
3467 :
3468 120 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3469 :
3470 180 : for (int i = 0; i < PQntuples(res); i++)
3471 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3472 : "DATABASE", dbname, NULL, NULL,
3473 : outbuf);
3474 :
3475 120 : PQclear(res);
3476 :
3477 : /* Now look for role-and-database-specific options */
3478 120 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3479 : "FROM pg_db_role_setting s, pg_roles r "
3480 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3481 : dboid);
3482 :
3483 120 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3484 :
3485 120 : for (int i = 0; i < PQntuples(res); i++)
3486 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3487 0 : "ROLE", PQgetvalue(res, i, 0),
3488 : "DATABASE", dbname,
3489 : outbuf);
3490 :
3491 120 : PQclear(res);
3492 :
3493 120 : destroyPQExpBuffer(buf);
3494 120 : }
3495 :
3496 : /*
3497 : * dumpEncoding: put the correct encoding into the archive
3498 : */
3499 : static void
3500 304 : dumpEncoding(Archive *AH)
3501 : {
3502 304 : const char *encname = pg_encoding_to_char(AH->encoding);
3503 304 : PQExpBuffer qry = createPQExpBuffer();
3504 :
3505 304 : pg_log_info("saving encoding = %s", encname);
3506 :
3507 304 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3508 304 : appendStringLiteralAH(qry, encname, AH);
3509 304 : appendPQExpBufferStr(qry, ";\n");
3510 :
3511 304 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3512 304 : ARCHIVE_OPTS(.tag = "ENCODING",
3513 : .description = "ENCODING",
3514 : .section = SECTION_PRE_DATA,
3515 : .createStmt = qry->data));
3516 :
3517 304 : destroyPQExpBuffer(qry);
3518 304 : }
3519 :
3520 :
3521 : /*
3522 : * dumpStdStrings: put the correct escape string behavior into the archive
3523 : */
3524 : static void
3525 304 : dumpStdStrings(Archive *AH)
3526 : {
3527 304 : const char *stdstrings = AH->std_strings ? "on" : "off";
3528 304 : PQExpBuffer qry = createPQExpBuffer();
3529 :
3530 304 : pg_log_info("saving standard_conforming_strings = %s",
3531 : stdstrings);
3532 :
3533 304 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3534 : stdstrings);
3535 :
3536 304 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3537 304 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3538 : .description = "STDSTRINGS",
3539 : .section = SECTION_PRE_DATA,
3540 : .createStmt = qry->data));
3541 :
3542 304 : destroyPQExpBuffer(qry);
3543 304 : }
3544 :
3545 : /*
3546 : * dumpSearchPath: record the active search_path in the archive
3547 : */
3548 : static void
3549 304 : dumpSearchPath(Archive *AH)
3550 : {
3551 304 : PQExpBuffer qry = createPQExpBuffer();
3552 304 : PQExpBuffer path = createPQExpBuffer();
3553 : PGresult *res;
3554 304 : char **schemanames = NULL;
3555 304 : int nschemanames = 0;
3556 : int i;
3557 :
3558 : /*
3559 : * We use the result of current_schemas(), not the search_path GUC,
3560 : * because that might contain wildcards such as "$user", which won't
3561 : * necessarily have the same value during restore. Also, this way avoids
3562 : * listing schemas that may appear in search_path but not actually exist,
3563 : * which seems like a prudent exclusion.
3564 : */
3565 304 : res = ExecuteSqlQueryForSingleRow(AH,
3566 : "SELECT pg_catalog.current_schemas(false)");
3567 :
3568 304 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3569 0 : pg_fatal("could not parse result of current_schemas()");
3570 :
3571 : /*
3572 : * We use set_config(), not a simple "SET search_path" command, because
3573 : * the latter has less-clean behavior if the search path is empty. While
3574 : * that's likely to get fixed at some point, it seems like a good idea to
3575 : * be as backwards-compatible as possible in what we put into archives.
3576 : */
3577 304 : for (i = 0; i < nschemanames; i++)
3578 : {
3579 0 : if (i > 0)
3580 0 : appendPQExpBufferStr(path, ", ");
3581 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3582 : }
3583 :
3584 304 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3585 304 : appendStringLiteralAH(qry, path->data, AH);
3586 304 : appendPQExpBufferStr(qry, ", false);\n");
3587 :
3588 304 : pg_log_info("saving search_path = %s", path->data);
3589 :
3590 304 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3591 304 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3592 : .description = "SEARCHPATH",
3593 : .section = SECTION_PRE_DATA,
3594 : .createStmt = qry->data));
3595 :
3596 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3597 304 : AH->searchpath = pg_strdup(qry->data);
3598 :
3599 304 : free(schemanames);
3600 304 : PQclear(res);
3601 304 : destroyPQExpBuffer(qry);
3602 304 : destroyPQExpBuffer(path);
3603 304 : }
3604 :
3605 :
3606 : /*
3607 : * getLOs:
3608 : * Collect schema-level data about large objects
3609 : */
3610 : static void
3611 256 : getLOs(Archive *fout)
3612 : {
3613 256 : DumpOptions *dopt = fout->dopt;
3614 256 : PQExpBuffer loQry = createPQExpBuffer();
3615 : LoInfo *loinfo;
3616 : DumpableObject *lodata;
3617 : PGresult *res;
3618 : int ntups;
3619 : int i;
3620 : int i_oid;
3621 : int i_lomowner;
3622 : int i_lomacl;
3623 : int i_acldefault;
3624 :
3625 256 : pg_log_info("reading large objects");
3626 :
3627 : /* Fetch LO OIDs, and owner/ACL data */
3628 256 : appendPQExpBufferStr(loQry,
3629 : "SELECT oid, lomowner, lomacl, "
3630 : "acldefault('L', lomowner) AS acldefault "
3631 : "FROM pg_largeobject_metadata");
3632 :
3633 256 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3634 :
3635 256 : i_oid = PQfnumber(res, "oid");
3636 256 : i_lomowner = PQfnumber(res, "lomowner");
3637 256 : i_lomacl = PQfnumber(res, "lomacl");
3638 256 : i_acldefault = PQfnumber(res, "acldefault");
3639 :
3640 256 : ntups = PQntuples(res);
3641 :
3642 : /*
3643 : * Each large object has its own "BLOB" archive entry.
3644 : */
3645 256 : loinfo = (LoInfo *) pg_malloc(ntups * sizeof(LoInfo));
3646 :
3647 422 : for (i = 0; i < ntups; i++)
3648 : {
3649 166 : loinfo[i].dobj.objType = DO_LARGE_OBJECT;
3650 166 : loinfo[i].dobj.catId.tableoid = LargeObjectRelationId;
3651 166 : loinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
3652 166 : AssignDumpId(&loinfo[i].dobj);
3653 :
3654 166 : loinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid));
3655 166 : loinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lomacl));
3656 166 : loinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3657 166 : loinfo[i].dacl.privtype = 0;
3658 166 : loinfo[i].dacl.initprivs = NULL;
3659 166 : loinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_lomowner));
3660 :
3661 : /* LOs have data */
3662 166 : loinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
3663 :
3664 : /* Mark whether LO has an ACL */
3665 166 : if (!PQgetisnull(res, i, i_lomacl))
3666 68 : loinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
3667 :
3668 : /*
3669 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3670 : * as it will be copied by pg_upgrade, which simply copies the
3671 : * pg_largeobject table. We *do* however dump out anything but the
3672 : * data, as pg_upgrade copies just pg_largeobject, but not
3673 : * pg_largeobject_metadata, after the dump is restored.
3674 : */
3675 166 : if (dopt->binary_upgrade)
3676 10 : loinfo[i].dobj.dump &= ~DUMP_COMPONENT_DATA;
3677 : }
3678 :
3679 : /*
3680 : * If we have any large objects, a "BLOBS" archive entry is needed. This
3681 : * is just a placeholder for sorting; it carries no data now.
3682 : */
3683 256 : if (ntups > 0)
3684 : {
3685 78 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3686 78 : lodata->objType = DO_LARGE_OBJECT_DATA;
3687 78 : lodata->catId = nilCatalogId;
3688 78 : AssignDumpId(lodata);
3689 78 : lodata->name = pg_strdup("BLOBS");
3690 78 : lodata->components |= DUMP_COMPONENT_DATA;
3691 : }
3692 :
3693 256 : PQclear(res);
3694 256 : destroyPQExpBuffer(loQry);
3695 256 : }
3696 :
3697 : /*
3698 : * dumpLO
3699 : *
3700 : * dump the definition (metadata) of the given large object
3701 : */
3702 : static void
3703 166 : dumpLO(Archive *fout, const LoInfo *loinfo)
3704 : {
3705 166 : PQExpBuffer cquery = createPQExpBuffer();
3706 166 : PQExpBuffer dquery = createPQExpBuffer();
3707 :
3708 166 : appendPQExpBuffer(cquery,
3709 : "SELECT pg_catalog.lo_create('%s');\n",
3710 : loinfo->dobj.name);
3711 :
3712 166 : appendPQExpBuffer(dquery,
3713 : "SELECT pg_catalog.lo_unlink('%s');\n",
3714 : loinfo->dobj.name);
3715 :
3716 166 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3717 166 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3718 166 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3719 : .owner = loinfo->rolname,
3720 : .description = "BLOB",
3721 : .section = SECTION_PRE_DATA,
3722 : .createStmt = cquery->data,
3723 : .dropStmt = dquery->data));
3724 :
3725 : /* Dump comment if any */
3726 166 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3727 88 : dumpComment(fout, "LARGE OBJECT", loinfo->dobj.name,
3728 : NULL, loinfo->rolname,
3729 : loinfo->dobj.catId, 0, loinfo->dobj.dumpId);
3730 :
3731 : /* Dump security label if any */
3732 166 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3733 0 : dumpSecLabel(fout, "LARGE OBJECT", loinfo->dobj.name,
3734 : NULL, loinfo->rolname,
3735 : loinfo->dobj.catId, 0, loinfo->dobj.dumpId);
3736 :
3737 : /* Dump ACL if any */
3738 166 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
3739 68 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT",
3740 68 : loinfo->dobj.name, NULL,
3741 : NULL, loinfo->rolname, &loinfo->dacl);
3742 :
3743 166 : destroyPQExpBuffer(cquery);
3744 166 : destroyPQExpBuffer(dquery);
3745 166 : }
3746 :
3747 : /*
3748 : * dumpLOs:
3749 : * dump the data contents of all large objects
3750 : */
3751 : static int
3752 70 : dumpLOs(Archive *fout, const void *arg)
3753 : {
3754 : const char *loQry;
3755 : const char *loFetchQry;
3756 70 : PGconn *conn = GetConnection(fout);
3757 : PGresult *res;
3758 : char buf[LOBBUFSIZE];
3759 : int ntups;
3760 : int i;
3761 : int cnt;
3762 :
3763 70 : pg_log_info("saving large objects");
3764 :
3765 : /*
3766 : * Currently, we re-fetch all LO OIDs using a cursor. Consider scanning
3767 : * the already-in-memory dumpable objects instead...
3768 : */
3769 70 : loQry =
3770 : "DECLARE looid CURSOR FOR "
3771 : "SELECT oid FROM pg_largeobject_metadata ORDER BY 1";
3772 :
3773 70 : ExecuteSqlStatement(fout, loQry);
3774 :
3775 : /* Command to fetch from cursor */
3776 70 : loFetchQry = "FETCH 1000 IN looid";
3777 :
3778 : do
3779 : {
3780 : /* Do a fetch */
3781 140 : res = ExecuteSqlQuery(fout, loFetchQry, PGRES_TUPLES_OK);
3782 :
3783 : /* Process the tuples, if any */
3784 140 : ntups = PQntuples(res);
3785 288 : for (i = 0; i < ntups; i++)
3786 : {
3787 : Oid loOid;
3788 : int loFd;
3789 :
3790 148 : loOid = atooid(PQgetvalue(res, i, 0));
3791 : /* Open the LO */
3792 148 : loFd = lo_open(conn, loOid, INV_READ);
3793 148 : if (loFd == -1)
3794 0 : pg_fatal("could not open large object %u: %s",
3795 : loOid, PQerrorMessage(conn));
3796 :
3797 148 : StartLO(fout, loOid);
3798 :
3799 : /* Now read it in chunks, sending data to archive */
3800 : do
3801 : {
3802 226 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3803 226 : if (cnt < 0)
3804 0 : pg_fatal("error reading large object %u: %s",
3805 : loOid, PQerrorMessage(conn));
3806 :
3807 226 : WriteData(fout, buf, cnt);
3808 226 : } while (cnt > 0);
3809 :
3810 148 : lo_close(conn, loFd);
3811 :
3812 148 : EndLO(fout, loOid);
3813 : }
3814 :
3815 140 : PQclear(res);
3816 140 : } while (ntups > 0);
3817 :
3818 70 : return 1;
3819 : }
3820 :
3821 : /*
3822 : * getPolicies
3823 : * get information about all RLS policies on dumpable tables.
3824 : */
3825 : void
3826 304 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3827 : {
3828 : PQExpBuffer query;
3829 : PQExpBuffer tbloids;
3830 : PGresult *res;
3831 : PolicyInfo *polinfo;
3832 : int i_oid;
3833 : int i_tableoid;
3834 : int i_polrelid;
3835 : int i_polname;
3836 : int i_polcmd;
3837 : int i_polpermissive;
3838 : int i_polroles;
3839 : int i_polqual;
3840 : int i_polwithcheck;
3841 : int i,
3842 : j,
3843 : ntups;
3844 :
3845 : /* No policies before 9.5 */
3846 304 : if (fout->remoteVersion < 90500)
3847 0 : return;
3848 :
3849 304 : query = createPQExpBuffer();
3850 304 : tbloids = createPQExpBuffer();
3851 :
3852 : /*
3853 : * Identify tables of interest, and check which ones have RLS enabled.
3854 : */
3855 304 : appendPQExpBufferChar(tbloids, '{');
3856 77278 : for (i = 0; i < numTables; i++)
3857 : {
3858 76974 : TableInfo *tbinfo = &tblinfo[i];
3859 :
3860 : /* Ignore row security on tables not to be dumped */
3861 76974 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
3862 65382 : continue;
3863 :
3864 : /* It can't have RLS or policies if it's not a table */
3865 11592 : if (tbinfo->relkind != RELKIND_RELATION &&
3866 3266 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
3867 2242 : continue;
3868 :
3869 : /* Add it to the list of table OIDs to be probed below */
3870 9350 : if (tbloids->len > 1) /* do we have more than the '{'? */
3871 9158 : appendPQExpBufferChar(tbloids, ',');
3872 9350 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
3873 :
3874 : /* Is RLS enabled? (That's separate from whether it has policies) */
3875 9350 : if (tbinfo->rowsec)
3876 : {
3877 104 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
3878 :
3879 : /*
3880 : * We represent RLS being enabled on a table by creating a
3881 : * PolicyInfo object with null polname.
3882 : *
3883 : * Note: use tableoid 0 so that this object won't be mistaken for
3884 : * something that pg_depend entries apply to.
3885 : */
3886 104 : polinfo = pg_malloc(sizeof(PolicyInfo));
3887 104 : polinfo->dobj.objType = DO_POLICY;
3888 104 : polinfo->dobj.catId.tableoid = 0;
3889 104 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3890 104 : AssignDumpId(&polinfo->dobj);
3891 104 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
3892 104 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
3893 104 : polinfo->poltable = tbinfo;
3894 104 : polinfo->polname = NULL;
3895 104 : polinfo->polcmd = '\0';
3896 104 : polinfo->polpermissive = 0;
3897 104 : polinfo->polroles = NULL;
3898 104 : polinfo->polqual = NULL;
3899 104 : polinfo->polwithcheck = NULL;
3900 : }
3901 : }
3902 304 : appendPQExpBufferChar(tbloids, '}');
3903 :
3904 : /*
3905 : * Now, read all RLS policies belonging to the tables of interest, and
3906 : * create PolicyInfo objects for them. (Note that we must filter the
3907 : * results server-side not locally, because we dare not apply pg_get_expr
3908 : * to tables we don't have lock on.)
3909 : */
3910 304 : pg_log_info("reading row-level security policies");
3911 :
3912 304 : printfPQExpBuffer(query,
3913 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
3914 304 : if (fout->remoteVersion >= 100000)
3915 304 : appendPQExpBufferStr(query, "pol.polpermissive, ");
3916 : else
3917 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
3918 304 : appendPQExpBuffer(query,
3919 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
3920 : " 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, "
3921 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
3922 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
3923 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
3924 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
3925 : tbloids->data);
3926 :
3927 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3928 :
3929 304 : ntups = PQntuples(res);
3930 304 : if (ntups > 0)
3931 : {
3932 84 : i_oid = PQfnumber(res, "oid");
3933 84 : i_tableoid = PQfnumber(res, "tableoid");
3934 84 : i_polrelid = PQfnumber(res, "polrelid");
3935 84 : i_polname = PQfnumber(res, "polname");
3936 84 : i_polcmd = PQfnumber(res, "polcmd");
3937 84 : i_polpermissive = PQfnumber(res, "polpermissive");
3938 84 : i_polroles = PQfnumber(res, "polroles");
3939 84 : i_polqual = PQfnumber(res, "polqual");
3940 84 : i_polwithcheck = PQfnumber(res, "polwithcheck");
3941 :
3942 84 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
3943 :
3944 618 : for (j = 0; j < ntups; j++)
3945 : {
3946 534 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
3947 534 : TableInfo *tbinfo = findTableByOid(polrelid);
3948 :
3949 534 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
3950 :
3951 534 : polinfo[j].dobj.objType = DO_POLICY;
3952 534 : polinfo[j].dobj.catId.tableoid =
3953 534 : atooid(PQgetvalue(res, j, i_tableoid));
3954 534 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
3955 534 : AssignDumpId(&polinfo[j].dobj);
3956 534 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
3957 534 : polinfo[j].poltable = tbinfo;
3958 534 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
3959 534 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
3960 :
3961 534 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
3962 534 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
3963 :
3964 534 : if (PQgetisnull(res, j, i_polroles))
3965 238 : polinfo[j].polroles = NULL;
3966 : else
3967 296 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
3968 :
3969 534 : if (PQgetisnull(res, j, i_polqual))
3970 74 : polinfo[j].polqual = NULL;
3971 : else
3972 460 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
3973 :
3974 534 : if (PQgetisnull(res, j, i_polwithcheck))
3975 282 : polinfo[j].polwithcheck = NULL;
3976 : else
3977 252 : polinfo[j].polwithcheck
3978 252 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
3979 : }
3980 : }
3981 :
3982 304 : PQclear(res);
3983 :
3984 304 : destroyPQExpBuffer(query);
3985 304 : destroyPQExpBuffer(tbloids);
3986 : }
3987 :
3988 : /*
3989 : * dumpPolicy
3990 : * dump the definition of the given policy
3991 : */
3992 : static void
3993 638 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
3994 : {
3995 638 : DumpOptions *dopt = fout->dopt;
3996 638 : TableInfo *tbinfo = polinfo->poltable;
3997 : PQExpBuffer query;
3998 : PQExpBuffer delqry;
3999 : PQExpBuffer polprefix;
4000 : char *qtabname;
4001 : const char *cmd;
4002 : char *tag;
4003 :
4004 : /* Do nothing in data-only dump */
4005 638 : if (dopt->dataOnly)
4006 56 : return;
4007 :
4008 : /*
4009 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4010 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4011 : * ROW LEVEL SECURITY.
4012 : */
4013 582 : if (polinfo->polname == NULL)
4014 : {
4015 96 : query = createPQExpBuffer();
4016 :
4017 96 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4018 96 : fmtQualifiedDumpable(tbinfo));
4019 :
4020 : /*
4021 : * We must emit the ROW SECURITY object's dependency on its table
4022 : * explicitly, because it will not match anything in pg_depend (unlike
4023 : * the case for other PolicyInfo objects).
4024 : */
4025 96 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4026 96 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4027 96 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4028 : .namespace = polinfo->dobj.namespace->dobj.name,
4029 : .owner = tbinfo->rolname,
4030 : .description = "ROW SECURITY",
4031 : .section = SECTION_POST_DATA,
4032 : .createStmt = query->data,
4033 : .deps = &(tbinfo->dobj.dumpId),
4034 : .nDeps = 1));
4035 :
4036 96 : destroyPQExpBuffer(query);
4037 96 : return;
4038 : }
4039 :
4040 486 : if (polinfo->polcmd == '*')
4041 162 : cmd = "";
4042 324 : else if (polinfo->polcmd == 'r')
4043 86 : cmd = " FOR SELECT";
4044 238 : else if (polinfo->polcmd == 'a')
4045 66 : cmd = " FOR INSERT";
4046 172 : else if (polinfo->polcmd == 'w')
4047 86 : cmd = " FOR UPDATE";
4048 86 : else if (polinfo->polcmd == 'd')
4049 86 : cmd = " FOR DELETE";
4050 : else
4051 0 : pg_fatal("unexpected policy command type: %c",
4052 : polinfo->polcmd);
4053 :
4054 486 : query = createPQExpBuffer();
4055 486 : delqry = createPQExpBuffer();
4056 486 : polprefix = createPQExpBuffer();
4057 :
4058 486 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4059 :
4060 486 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4061 :
4062 486 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4063 486 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4064 :
4065 486 : if (polinfo->polroles != NULL)
4066 264 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4067 :
4068 486 : if (polinfo->polqual != NULL)
4069 420 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4070 :
4071 486 : if (polinfo->polwithcheck != NULL)
4072 228 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4073 :
4074 486 : appendPQExpBufferStr(query, ";\n");
4075 :
4076 486 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4077 486 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4078 :
4079 486 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4080 486 : fmtId(polinfo->polname));
4081 :
4082 486 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4083 :
4084 486 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4085 486 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4086 486 : ARCHIVE_OPTS(.tag = tag,
4087 : .namespace = polinfo->dobj.namespace->dobj.name,
4088 : .owner = tbinfo->rolname,
4089 : .description = "POLICY",
4090 : .section = SECTION_POST_DATA,
4091 : .createStmt = query->data,
4092 : .dropStmt = delqry->data));
4093 :
4094 486 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4095 0 : dumpComment(fout, polprefix->data, qtabname,
4096 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4097 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4098 :
4099 486 : free(tag);
4100 486 : destroyPQExpBuffer(query);
4101 486 : destroyPQExpBuffer(delqry);
4102 486 : destroyPQExpBuffer(polprefix);
4103 486 : free(qtabname);
4104 : }
4105 :
4106 : /*
4107 : * getPublications
4108 : * get information about publications
4109 : */
4110 : PublicationInfo *
4111 304 : getPublications(Archive *fout, int *numPublications)
4112 : {
4113 304 : DumpOptions *dopt = fout->dopt;
4114 : PQExpBuffer query;
4115 : PGresult *res;
4116 : PublicationInfo *pubinfo;
4117 : int i_tableoid;
4118 : int i_oid;
4119 : int i_pubname;
4120 : int i_pubowner;
4121 : int i_puballtables;
4122 : int i_pubinsert;
4123 : int i_pubupdate;
4124 : int i_pubdelete;
4125 : int i_pubtruncate;
4126 : int i_pubviaroot;
4127 : int i,
4128 : ntups;
4129 :
4130 304 : if (dopt->no_publications || fout->remoteVersion < 100000)
4131 : {
4132 0 : *numPublications = 0;
4133 0 : return NULL;
4134 : }
4135 :
4136 304 : query = createPQExpBuffer();
4137 :
4138 304 : resetPQExpBuffer(query);
4139 :
4140 : /* Get the publications. */
4141 304 : if (fout->remoteVersion >= 130000)
4142 304 : appendPQExpBufferStr(query,
4143 : "SELECT p.tableoid, p.oid, p.pubname, "
4144 : "p.pubowner, "
4145 : "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
4146 : "FROM pg_publication p");
4147 0 : else if (fout->remoteVersion >= 110000)
4148 0 : appendPQExpBufferStr(query,
4149 : "SELECT p.tableoid, p.oid, p.pubname, "
4150 : "p.pubowner, "
4151 : "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
4152 : "FROM pg_publication p");
4153 : else
4154 0 : appendPQExpBufferStr(query,
4155 : "SELECT p.tableoid, p.oid, p.pubname, "
4156 : "p.pubowner, "
4157 : "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
4158 : "FROM pg_publication p");
4159 :
4160 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4161 :
4162 304 : ntups = PQntuples(res);
4163 :
4164 304 : i_tableoid = PQfnumber(res, "tableoid");
4165 304 : i_oid = PQfnumber(res, "oid");
4166 304 : i_pubname = PQfnumber(res, "pubname");
4167 304 : i_pubowner = PQfnumber(res, "pubowner");
4168 304 : i_puballtables = PQfnumber(res, "puballtables");
4169 304 : i_pubinsert = PQfnumber(res, "pubinsert");
4170 304 : i_pubupdate = PQfnumber(res, "pubupdate");
4171 304 : i_pubdelete = PQfnumber(res, "pubdelete");
4172 304 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4173 304 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4174 :
4175 304 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4176 :
4177 650 : for (i = 0; i < ntups; i++)
4178 : {
4179 346 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4180 346 : pubinfo[i].dobj.catId.tableoid =
4181 346 : atooid(PQgetvalue(res, i, i_tableoid));
4182 346 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4183 346 : AssignDumpId(&pubinfo[i].dobj);
4184 346 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4185 346 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4186 346 : pubinfo[i].puballtables =
4187 346 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4188 346 : pubinfo[i].pubinsert =
4189 346 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4190 346 : pubinfo[i].pubupdate =
4191 346 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4192 346 : pubinfo[i].pubdelete =
4193 346 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4194 346 : pubinfo[i].pubtruncate =
4195 346 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4196 346 : pubinfo[i].pubviaroot =
4197 346 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4198 :
4199 : /* Decide whether we want to dump it */
4200 346 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4201 : }
4202 304 : PQclear(res);
4203 :
4204 304 : destroyPQExpBuffer(query);
4205 :
4206 304 : *numPublications = ntups;
4207 304 : return pubinfo;
4208 : }
4209 :
4210 : /*
4211 : * dumpPublication
4212 : * dump the definition of the given publication
4213 : */
4214 : static void
4215 282 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4216 : {
4217 282 : DumpOptions *dopt = fout->dopt;
4218 : PQExpBuffer delq;
4219 : PQExpBuffer query;
4220 : char *qpubname;
4221 282 : bool first = true;
4222 :
4223 : /* Do nothing in data-only dump */
4224 282 : if (dopt->dataOnly)
4225 24 : return;
4226 :
4227 258 : delq = createPQExpBuffer();
4228 258 : query = createPQExpBuffer();
4229 :
4230 258 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4231 :
4232 258 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4233 : qpubname);
4234 :
4235 258 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4236 : qpubname);
4237 :
4238 258 : if (pubinfo->puballtables)
4239 66 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4240 :
4241 258 : appendPQExpBufferStr(query, " WITH (publish = '");
4242 258 : if (pubinfo->pubinsert)
4243 : {
4244 194 : appendPQExpBufferStr(query, "insert");
4245 194 : first = false;
4246 : }
4247 :
4248 258 : if (pubinfo->pubupdate)
4249 : {
4250 194 : if (!first)
4251 194 : appendPQExpBufferStr(query, ", ");
4252 :
4253 194 : appendPQExpBufferStr(query, "update");
4254 194 : first = false;
4255 : }
4256 :
4257 258 : if (pubinfo->pubdelete)
4258 : {
4259 194 : if (!first)
4260 194 : appendPQExpBufferStr(query, ", ");
4261 :
4262 194 : appendPQExpBufferStr(query, "delete");
4263 194 : first = false;
4264 : }
4265 :
4266 258 : if (pubinfo->pubtruncate)
4267 : {
4268 194 : if (!first)
4269 194 : appendPQExpBufferStr(query, ", ");
4270 :
4271 194 : appendPQExpBufferStr(query, "truncate");
4272 194 : first = false;
4273 : }
4274 :
4275 258 : appendPQExpBufferChar(query, '\'');
4276 :
4277 258 : if (pubinfo->pubviaroot)
4278 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4279 :
4280 258 : appendPQExpBufferStr(query, ");\n");
4281 :
4282 258 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4283 258 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4284 258 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4285 : .owner = pubinfo->rolname,
4286 : .description = "PUBLICATION",
4287 : .section = SECTION_POST_DATA,
4288 : .createStmt = query->data,
4289 : .dropStmt = delq->data));
4290 :
4291 258 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4292 64 : dumpComment(fout, "PUBLICATION", qpubname,
4293 : NULL, pubinfo->rolname,
4294 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4295 :
4296 258 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4297 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4298 : NULL, pubinfo->rolname,
4299 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4300 :
4301 258 : destroyPQExpBuffer(delq);
4302 258 : destroyPQExpBuffer(query);
4303 258 : free(qpubname);
4304 : }
4305 :
4306 : /*
4307 : * getPublicationNamespaces
4308 : * get information about publication membership for dumpable schemas.
4309 : */
4310 : void
4311 304 : getPublicationNamespaces(Archive *fout)
4312 : {
4313 : PQExpBuffer query;
4314 : PGresult *res;
4315 : PublicationSchemaInfo *pubsinfo;
4316 304 : DumpOptions *dopt = fout->dopt;
4317 : int i_tableoid;
4318 : int i_oid;
4319 : int i_pnpubid;
4320 : int i_pnnspid;
4321 : int i,
4322 : j,
4323 : ntups;
4324 :
4325 304 : if (dopt->no_publications || fout->remoteVersion < 150000)
4326 0 : return;
4327 :
4328 304 : query = createPQExpBuffer();
4329 :
4330 : /* Collect all publication membership info. */
4331 304 : appendPQExpBufferStr(query,
4332 : "SELECT tableoid, oid, pnpubid, pnnspid "
4333 : "FROM pg_catalog.pg_publication_namespace");
4334 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4335 :
4336 304 : ntups = PQntuples(res);
4337 :
4338 304 : i_tableoid = PQfnumber(res, "tableoid");
4339 304 : i_oid = PQfnumber(res, "oid");
4340 304 : i_pnpubid = PQfnumber(res, "pnpubid");
4341 304 : i_pnnspid = PQfnumber(res, "pnnspid");
4342 :
4343 : /* this allocation may be more than we need */
4344 304 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4345 304 : j = 0;
4346 :
4347 476 : for (i = 0; i < ntups; i++)
4348 : {
4349 172 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4350 172 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4351 : PublicationInfo *pubinfo;
4352 : NamespaceInfo *nspinfo;
4353 :
4354 : /*
4355 : * Ignore any entries for which we aren't interested in either the
4356 : * publication or the rel.
4357 : */
4358 172 : pubinfo = findPublicationByOid(pnpubid);
4359 172 : if (pubinfo == NULL)
4360 0 : continue;
4361 172 : nspinfo = findNamespaceByOid(pnnspid);
4362 172 : if (nspinfo == NULL)
4363 0 : continue;
4364 :
4365 : /*
4366 : * We always dump publication namespaces unless the corresponding
4367 : * namespace is excluded from the dump.
4368 : */
4369 172 : if (nspinfo->dobj.dump == DUMP_COMPONENT_NONE)
4370 30 : continue;
4371 :
4372 : /* OK, make a DumpableObject for this relationship */
4373 142 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4374 142 : pubsinfo[j].dobj.catId.tableoid =
4375 142 : atooid(PQgetvalue(res, i, i_tableoid));
4376 142 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4377 142 : AssignDumpId(&pubsinfo[j].dobj);
4378 142 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4379 142 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4380 142 : pubsinfo[j].publication = pubinfo;
4381 142 : pubsinfo[j].pubschema = nspinfo;
4382 :
4383 : /* Decide whether we want to dump it */
4384 142 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4385 :
4386 142 : j++;
4387 : }
4388 :
4389 304 : PQclear(res);
4390 304 : destroyPQExpBuffer(query);
4391 : }
4392 :
4393 : /*
4394 : * getPublicationTables
4395 : * get information about publication membership for dumpable tables.
4396 : */
4397 : void
4398 304 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4399 : {
4400 : PQExpBuffer query;
4401 : PGresult *res;
4402 : PublicationRelInfo *pubrinfo;
4403 304 : DumpOptions *dopt = fout->dopt;
4404 : int i_tableoid;
4405 : int i_oid;
4406 : int i_prpubid;
4407 : int i_prrelid;
4408 : int i_prrelqual;
4409 : int i_prattrs;
4410 : int i,
4411 : j,
4412 : ntups;
4413 :
4414 304 : if (dopt->no_publications || fout->remoteVersion < 100000)
4415 0 : return;
4416 :
4417 304 : query = createPQExpBuffer();
4418 :
4419 : /* Collect all publication membership info. */
4420 304 : if (fout->remoteVersion >= 150000)
4421 304 : appendPQExpBufferStr(query,
4422 : "SELECT tableoid, oid, prpubid, prrelid, "
4423 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4424 : "(CASE\n"
4425 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4426 : " (SELECT array_agg(attname)\n"
4427 : " FROM\n"
4428 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4429 : " pg_catalog.pg_attribute\n"
4430 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4431 : " ELSE NULL END) prattrs "
4432 : "FROM pg_catalog.pg_publication_rel pr");
4433 : else
4434 0 : appendPQExpBufferStr(query,
4435 : "SELECT tableoid, oid, prpubid, prrelid, "
4436 : "NULL AS prrelqual, NULL AS prattrs "
4437 : "FROM pg_catalog.pg_publication_rel");
4438 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4439 :
4440 304 : ntups = PQntuples(res);
4441 :
4442 304 : i_tableoid = PQfnumber(res, "tableoid");
4443 304 : i_oid = PQfnumber(res, "oid");
4444 304 : i_prpubid = PQfnumber(res, "prpubid");
4445 304 : i_prrelid = PQfnumber(res, "prrelid");
4446 304 : i_prrelqual = PQfnumber(res, "prrelqual");
4447 304 : i_prattrs = PQfnumber(res, "prattrs");
4448 :
4449 : /* this allocation may be more than we need */
4450 304 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4451 304 : j = 0;
4452 :
4453 906 : for (i = 0; i < ntups; i++)
4454 : {
4455 602 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4456 602 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4457 : PublicationInfo *pubinfo;
4458 : TableInfo *tbinfo;
4459 :
4460 : /*
4461 : * Ignore any entries for which we aren't interested in either the
4462 : * publication or the rel.
4463 : */
4464 602 : pubinfo = findPublicationByOid(prpubid);
4465 602 : if (pubinfo == NULL)
4466 0 : continue;
4467 602 : tbinfo = findTableByOid(prrelid);
4468 602 : if (tbinfo == NULL)
4469 0 : continue;
4470 :
4471 : /*
4472 : * Ignore publication membership of tables whose definitions are not
4473 : * to be dumped.
4474 : */
4475 602 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4476 92 : continue;
4477 :
4478 : /* OK, make a DumpableObject for this relationship */
4479 510 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4480 510 : pubrinfo[j].dobj.catId.tableoid =
4481 510 : atooid(PQgetvalue(res, i, i_tableoid));
4482 510 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4483 510 : AssignDumpId(&pubrinfo[j].dobj);
4484 510 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4485 510 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4486 510 : pubrinfo[j].publication = pubinfo;
4487 510 : pubrinfo[j].pubtable = tbinfo;
4488 510 : if (PQgetisnull(res, i, i_prrelqual))
4489 292 : pubrinfo[j].pubrelqual = NULL;
4490 : else
4491 218 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4492 :
4493 510 : if (!PQgetisnull(res, i, i_prattrs))
4494 : {
4495 : char **attnames;
4496 : int nattnames;
4497 : PQExpBuffer attribs;
4498 :
4499 144 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4500 : &attnames, &nattnames))
4501 0 : pg_fatal("could not parse %s array", "prattrs");
4502 144 : attribs = createPQExpBuffer();
4503 432 : for (int k = 0; k < nattnames; k++)
4504 : {
4505 288 : if (k > 0)
4506 144 : appendPQExpBufferStr(attribs, ", ");
4507 :
4508 288 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4509 : }
4510 144 : pubrinfo[j].pubrattrs = attribs->data;
4511 : }
4512 : else
4513 366 : pubrinfo[j].pubrattrs = NULL;
4514 :
4515 : /* Decide whether we want to dump it */
4516 510 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4517 :
4518 510 : j++;
4519 : }
4520 :
4521 304 : PQclear(res);
4522 304 : destroyPQExpBuffer(query);
4523 : }
4524 :
4525 : /*
4526 : * dumpPublicationNamespace
4527 : * dump the definition of the given publication schema mapping.
4528 : */
4529 : static void
4530 138 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4531 : {
4532 138 : DumpOptions *dopt = fout->dopt;
4533 138 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4534 138 : PublicationInfo *pubinfo = pubsinfo->publication;
4535 : PQExpBuffer query;
4536 : char *tag;
4537 :
4538 : /* Do nothing in data-only dump */
4539 138 : if (dopt->dataOnly)
4540 12 : return;
4541 :
4542 126 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4543 :
4544 126 : query = createPQExpBuffer();
4545 :
4546 126 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4547 126 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4548 :
4549 : /*
4550 : * There is no point in creating drop query as the drop is done by schema
4551 : * drop.
4552 : */
4553 126 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4554 126 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4555 126 : ARCHIVE_OPTS(.tag = tag,
4556 : .namespace = schemainfo->dobj.name,
4557 : .owner = pubinfo->rolname,
4558 : .description = "PUBLICATION TABLES IN SCHEMA",
4559 : .section = SECTION_POST_DATA,
4560 : .createStmt = query->data));
4561 :
4562 : /* These objects can't currently have comments or seclabels */
4563 :
4564 126 : free(tag);
4565 126 : destroyPQExpBuffer(query);
4566 : }
4567 :
4568 : /*
4569 : * dumpPublicationTable
4570 : * dump the definition of the given publication table mapping
4571 : */
4572 : static void
4573 470 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4574 : {
4575 470 : DumpOptions *dopt = fout->dopt;
4576 470 : PublicationInfo *pubinfo = pubrinfo->publication;
4577 470 : TableInfo *tbinfo = pubrinfo->pubtable;
4578 : PQExpBuffer query;
4579 : char *tag;
4580 :
4581 : /* Do nothing in data-only dump */
4582 470 : if (dopt->dataOnly)
4583 42 : return;
4584 :
4585 428 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4586 :
4587 428 : query = createPQExpBuffer();
4588 :
4589 428 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4590 428 : fmtId(pubinfo->dobj.name));
4591 428 : appendPQExpBuffer(query, " %s",
4592 428 : fmtQualifiedDumpable(tbinfo));
4593 :
4594 428 : if (pubrinfo->pubrattrs)
4595 124 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4596 :
4597 428 : if (pubrinfo->pubrelqual)
4598 : {
4599 : /*
4600 : * It's necessary to add parentheses around the expression because
4601 : * pg_get_expr won't supply the parentheses for things like WHERE
4602 : * TRUE.
4603 : */
4604 184 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4605 : }
4606 428 : appendPQExpBufferStr(query, ";\n");
4607 :
4608 : /*
4609 : * There is no point in creating a drop query as the drop is done by table
4610 : * drop. (If you think to change this, see also _printTocEntry().)
4611 : * Although this object doesn't really have ownership as such, set the
4612 : * owner field anyway to ensure that the command is run by the correct
4613 : * role at restore time.
4614 : */
4615 428 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4616 428 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4617 428 : ARCHIVE_OPTS(.tag = tag,
4618 : .namespace = tbinfo->dobj.namespace->dobj.name,
4619 : .owner = pubinfo->rolname,
4620 : .description = "PUBLICATION TABLE",
4621 : .section = SECTION_POST_DATA,
4622 : .createStmt = query->data));
4623 :
4624 : /* These objects can't currently have comments or seclabels */
4625 :
4626 428 : free(tag);
4627 428 : destroyPQExpBuffer(query);
4628 : }
4629 :
4630 : /*
4631 : * Is the currently connected user a superuser?
4632 : */
4633 : static bool
4634 304 : is_superuser(Archive *fout)
4635 : {
4636 304 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4637 : const char *val;
4638 :
4639 304 : val = PQparameterStatus(AH->connection, "is_superuser");
4640 :
4641 304 : if (val && strcmp(val, "on") == 0)
4642 298 : return true;
4643 :
4644 6 : return false;
4645 : }
4646 :
4647 : /*
4648 : * getSubscriptions
4649 : * get information about subscriptions
4650 : */
4651 : void
4652 304 : getSubscriptions(Archive *fout)
4653 : {
4654 304 : DumpOptions *dopt = fout->dopt;
4655 : PQExpBuffer query;
4656 : PGresult *res;
4657 : SubscriptionInfo *subinfo;
4658 : int i_tableoid;
4659 : int i_oid;
4660 : int i_subname;
4661 : int i_subowner;
4662 : int i_subbinary;
4663 : int i_substream;
4664 : int i_subtwophasestate;
4665 : int i_subdisableonerr;
4666 : int i_subpasswordrequired;
4667 : int i_subrunasowner;
4668 : int i_subconninfo;
4669 : int i_subslotname;
4670 : int i_subsynccommit;
4671 : int i_subpublications;
4672 : int i_suborigin;
4673 : int i_suboriginremotelsn;
4674 : int i_subenabled;
4675 : int i_subfailover;
4676 : int i,
4677 : ntups;
4678 :
4679 304 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4680 0 : return;
4681 :
4682 304 : if (!is_superuser(fout))
4683 : {
4684 : int n;
4685 :
4686 6 : res = ExecuteSqlQuery(fout,
4687 : "SELECT count(*) FROM pg_subscription "
4688 : "WHERE subdbid = (SELECT oid FROM pg_database"
4689 : " WHERE datname = current_database())",
4690 : PGRES_TUPLES_OK);
4691 6 : n = atoi(PQgetvalue(res, 0, 0));
4692 6 : if (n > 0)
4693 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4694 6 : PQclear(res);
4695 6 : return;
4696 : }
4697 :
4698 298 : query = createPQExpBuffer();
4699 :
4700 : /* Get the subscriptions in current database. */
4701 298 : appendPQExpBufferStr(query,
4702 : "SELECT s.tableoid, s.oid, s.subname,\n"
4703 : " s.subowner,\n"
4704 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4705 : " s.subpublications,\n");
4706 :
4707 298 : if (fout->remoteVersion >= 140000)
4708 298 : appendPQExpBufferStr(query, " s.subbinary,\n");
4709 : else
4710 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
4711 :
4712 298 : if (fout->remoteVersion >= 140000)
4713 298 : appendPQExpBufferStr(query, " s.substream,\n");
4714 : else
4715 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
4716 :
4717 298 : if (fout->remoteVersion >= 150000)
4718 298 : appendPQExpBufferStr(query,
4719 : " s.subtwophasestate,\n"
4720 : " s.subdisableonerr,\n");
4721 : else
4722 0 : appendPQExpBuffer(query,
4723 : " '%c' AS subtwophasestate,\n"
4724 : " false AS subdisableonerr,\n",
4725 : LOGICALREP_TWOPHASE_STATE_DISABLED);
4726 :
4727 298 : if (fout->remoteVersion >= 160000)
4728 298 : appendPQExpBufferStr(query,
4729 : " s.subpasswordrequired,\n"
4730 : " s.subrunasowner,\n"
4731 : " s.suborigin,\n");
4732 : else
4733 0 : appendPQExpBuffer(query,
4734 : " 't' AS subpasswordrequired,\n"
4735 : " 't' AS subrunasowner,\n"
4736 : " '%s' AS suborigin,\n",
4737 : LOGICALREP_ORIGIN_ANY);
4738 :
4739 298 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4740 28 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
4741 : " s.subenabled,\n"
4742 : " s.subfailover\n");
4743 : else
4744 270 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
4745 : " false AS subenabled,\n"
4746 : " false AS subfailover\n");
4747 :
4748 298 : appendPQExpBufferStr(query,
4749 : "FROM pg_subscription s\n");
4750 :
4751 298 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4752 28 : appendPQExpBufferStr(query,
4753 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
4754 : " ON o.external_id = 'pg_' || s.oid::text \n");
4755 :
4756 298 : appendPQExpBufferStr(query,
4757 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
4758 : " WHERE datname = current_database())");
4759 :
4760 298 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4761 :
4762 298 : ntups = PQntuples(res);
4763 :
4764 : /*
4765 : * Get subscription fields. We don't include subskiplsn in the dump as
4766 : * after restoring the dump this value may no longer be relevant.
4767 : */
4768 298 : i_tableoid = PQfnumber(res, "tableoid");
4769 298 : i_oid = PQfnumber(res, "oid");
4770 298 : i_subname = PQfnumber(res, "subname");
4771 298 : i_subowner = PQfnumber(res, "subowner");
4772 298 : i_subbinary = PQfnumber(res, "subbinary");
4773 298 : i_substream = PQfnumber(res, "substream");
4774 298 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
4775 298 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
4776 298 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
4777 298 : i_subrunasowner = PQfnumber(res, "subrunasowner");
4778 298 : i_subconninfo = PQfnumber(res, "subconninfo");
4779 298 : i_subslotname = PQfnumber(res, "subslotname");
4780 298 : i_subsynccommit = PQfnumber(res, "subsynccommit");
4781 298 : i_subpublications = PQfnumber(res, "subpublications");
4782 298 : i_suborigin = PQfnumber(res, "suborigin");
4783 298 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
4784 298 : i_subenabled = PQfnumber(res, "subenabled");
4785 298 : i_subfailover = PQfnumber(res, "subfailover");
4786 :
4787 298 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
4788 :
4789 548 : for (i = 0; i < ntups; i++)
4790 : {
4791 250 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
4792 250 : subinfo[i].dobj.catId.tableoid =
4793 250 : atooid(PQgetvalue(res, i, i_tableoid));
4794 250 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4795 250 : AssignDumpId(&subinfo[i].dobj);
4796 250 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
4797 250 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
4798 :
4799 500 : subinfo[i].subbinary =
4800 250 : pg_strdup(PQgetvalue(res, i, i_subbinary));
4801 500 : subinfo[i].substream =
4802 250 : pg_strdup(PQgetvalue(res, i, i_substream));
4803 500 : subinfo[i].subtwophasestate =
4804 250 : pg_strdup(PQgetvalue(res, i, i_subtwophasestate));
4805 500 : subinfo[i].subdisableonerr =
4806 250 : pg_strdup(PQgetvalue(res, i, i_subdisableonerr));
4807 500 : subinfo[i].subpasswordrequired =
4808 250 : pg_strdup(PQgetvalue(res, i, i_subpasswordrequired));
4809 500 : subinfo[i].subrunasowner =
4810 250 : pg_strdup(PQgetvalue(res, i, i_subrunasowner));
4811 500 : subinfo[i].subconninfo =
4812 250 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
4813 250 : if (PQgetisnull(res, i, i_subslotname))
4814 0 : subinfo[i].subslotname = NULL;
4815 : else
4816 250 : subinfo[i].subslotname =
4817 250 : pg_strdup(PQgetvalue(res, i, i_subslotname));
4818 500 : subinfo[i].subsynccommit =
4819 250 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
4820 500 : subinfo[i].subpublications =
4821 250 : pg_strdup(PQgetvalue(res, i, i_subpublications));
4822 250 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
4823 250 : if (PQgetisnull(res, i, i_suboriginremotelsn))
4824 248 : subinfo[i].suboriginremotelsn = NULL;
4825 : else
4826 2 : subinfo[i].suboriginremotelsn =
4827 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
4828 500 : subinfo[i].subenabled =
4829 250 : pg_strdup(PQgetvalue(res, i, i_subenabled));
4830 500 : subinfo[i].subfailover =
4831 250 : pg_strdup(PQgetvalue(res, i, i_subfailover));
4832 :
4833 : /* Decide whether we want to dump it */
4834 250 : selectDumpableObject(&(subinfo[i].dobj), fout);
4835 : }
4836 298 : PQclear(res);
4837 :
4838 298 : destroyPQExpBuffer(query);
4839 : }
4840 :
4841 : /*
4842 : * getSubscriptionTables
4843 : * Get information about subscription membership for dumpable tables. This
4844 : * will be used only in binary-upgrade mode for PG17 or later versions.
4845 : */
4846 : void
4847 304 : getSubscriptionTables(Archive *fout)
4848 : {
4849 304 : DumpOptions *dopt = fout->dopt;
4850 304 : SubscriptionInfo *subinfo = NULL;
4851 : SubRelInfo *subrinfo;
4852 : PGresult *res;
4853 : int i_srsubid;
4854 : int i_srrelid;
4855 : int i_srsubstate;
4856 : int i_srsublsn;
4857 : int ntups;
4858 304 : Oid last_srsubid = InvalidOid;
4859 :
4860 304 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
4861 28 : fout->remoteVersion < 170000)
4862 276 : return;
4863 :
4864 28 : res = ExecuteSqlQuery(fout,
4865 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
4866 : "FROM pg_catalog.pg_subscription_rel "
4867 : "ORDER BY srsubid",
4868 : PGRES_TUPLES_OK);
4869 28 : ntups = PQntuples(res);
4870 28 : if (ntups == 0)
4871 26 : goto cleanup;
4872 :
4873 : /* Get pg_subscription_rel attributes */
4874 2 : i_srsubid = PQfnumber(res, "srsubid");
4875 2 : i_srrelid = PQfnumber(res, "srrelid");
4876 2 : i_srsubstate = PQfnumber(res, "srsubstate");
4877 2 : i_srsublsn = PQfnumber(res, "srsublsn");
4878 :
4879 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
4880 6 : for (int i = 0; i < ntups; i++)
4881 : {
4882 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
4883 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
4884 : TableInfo *tblinfo;
4885 :
4886 : /*
4887 : * If we switched to a new subscription, check if the subscription
4888 : * exists.
4889 : */
4890 4 : if (cur_srsubid != last_srsubid)
4891 : {
4892 4 : subinfo = findSubscriptionByOid(cur_srsubid);
4893 4 : if (subinfo == NULL)
4894 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
4895 :
4896 4 : last_srsubid = cur_srsubid;
4897 : }
4898 :
4899 4 : tblinfo = findTableByOid(relid);
4900 4 : if (tblinfo == NULL)
4901 0 : pg_fatal("failed sanity check, table with OID %u not found",
4902 : relid);
4903 :
4904 : /* OK, make a DumpableObject for this relationship */
4905 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
4906 4 : subrinfo[i].dobj.catId.tableoid = relid;
4907 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
4908 4 : AssignDumpId(&subrinfo[i].dobj);
4909 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
4910 4 : subrinfo[i].tblinfo = tblinfo;
4911 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
4912 4 : if (PQgetisnull(res, i, i_srsublsn))
4913 2 : subrinfo[i].srsublsn = NULL;
4914 : else
4915 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
4916 :
4917 4 : subrinfo[i].subinfo = subinfo;
4918 :
4919 : /* Decide whether we want to dump it */
4920 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
4921 : }
4922 :
4923 2 : cleanup:
4924 28 : PQclear(res);
4925 : }
4926 :
4927 : /*
4928 : * dumpSubscriptionTable
4929 : * Dump the definition of the given subscription table mapping. This will be
4930 : * used only in binary-upgrade mode for PG17 or later versions.
4931 : */
4932 : static void
4933 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
4934 : {
4935 4 : DumpOptions *dopt = fout->dopt;
4936 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
4937 : PQExpBuffer query;
4938 : char *tag;
4939 :
4940 : /* Do nothing in data-only dump */
4941 4 : if (dopt->dataOnly)
4942 0 : return;
4943 :
4944 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
4945 :
4946 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
4947 :
4948 4 : query = createPQExpBuffer();
4949 :
4950 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4951 : {
4952 : /*
4953 : * binary_upgrade_add_sub_rel_state will add the subscription relation
4954 : * to pg_subscription_rel table. This will be used only in
4955 : * binary-upgrade mode.
4956 : */
4957 4 : appendPQExpBufferStr(query,
4958 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
4959 4 : appendPQExpBufferStr(query,
4960 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
4961 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
4962 4 : appendPQExpBuffer(query,
4963 : ", %u, '%c'",
4964 4 : subrinfo->tblinfo->dobj.catId.oid,
4965 4 : subrinfo->srsubstate);
4966 :
4967 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
4968 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
4969 : else
4970 2 : appendPQExpBuffer(query, ", NULL");
4971 :
4972 4 : appendPQExpBufferStr(query, ");\n");
4973 : }
4974 :
4975 : /*
4976 : * There is no point in creating a drop query as the drop is done by table
4977 : * drop. (If you think to change this, see also _printTocEntry().)
4978 : * Although this object doesn't really have ownership as such, set the
4979 : * owner field anyway to ensure that the command is run by the correct
4980 : * role at restore time.
4981 : */
4982 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4983 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
4984 4 : ARCHIVE_OPTS(.tag = tag,
4985 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
4986 : .owner = subinfo->rolname,
4987 : .description = "SUBSCRIPTION TABLE",
4988 : .section = SECTION_POST_DATA,
4989 : .createStmt = query->data));
4990 :
4991 : /* These objects can't currently have comments or seclabels */
4992 :
4993 4 : free(tag);
4994 4 : destroyPQExpBuffer(query);
4995 : }
4996 :
4997 : /*
4998 : * dumpSubscription
4999 : * dump the definition of the given subscription
5000 : */
5001 : static void
5002 214 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5003 : {
5004 214 : DumpOptions *dopt = fout->dopt;
5005 : PQExpBuffer delq;
5006 : PQExpBuffer query;
5007 : PQExpBuffer publications;
5008 : char *qsubname;
5009 214 : char **pubnames = NULL;
5010 214 : int npubnames = 0;
5011 : int i;
5012 214 : char two_phase_disabled[] = {LOGICALREP_TWOPHASE_STATE_DISABLED, '\0'};
5013 :
5014 : /* Do nothing in data-only dump */
5015 214 : if (dopt->dataOnly)
5016 18 : return;
5017 :
5018 196 : delq = createPQExpBuffer();
5019 196 : query = createPQExpBuffer();
5020 :
5021 196 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5022 :
5023 196 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5024 : qsubname);
5025 :
5026 196 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5027 : qsubname);
5028 196 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5029 :
5030 : /* Build list of quoted publications and append them to query. */
5031 196 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5032 0 : pg_fatal("could not parse %s array", "subpublications");
5033 :
5034 196 : publications = createPQExpBuffer();
5035 392 : for (i = 0; i < npubnames; i++)
5036 : {
5037 196 : if (i > 0)
5038 0 : appendPQExpBufferStr(publications, ", ");
5039 :
5040 196 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5041 : }
5042 :
5043 196 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5044 196 : if (subinfo->subslotname)
5045 196 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5046 : else
5047 0 : appendPQExpBufferStr(query, "NONE");
5048 :
5049 196 : if (strcmp(subinfo->subbinary, "t") == 0)
5050 0 : appendPQExpBufferStr(query, ", binary = true");
5051 :
5052 196 : if (strcmp(subinfo->substream, "t") == 0)
5053 0 : appendPQExpBufferStr(query, ", streaming = on");
5054 196 : else if (strcmp(subinfo->substream, "p") == 0)
5055 0 : appendPQExpBufferStr(query, ", streaming = parallel");
5056 :
5057 196 : if (strcmp(subinfo->subtwophasestate, two_phase_disabled) != 0)
5058 0 : appendPQExpBufferStr(query, ", two_phase = on");
5059 :
5060 196 : if (strcmp(subinfo->subdisableonerr, "t") == 0)
5061 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5062 :
5063 196 : if (strcmp(subinfo->subpasswordrequired, "t") != 0)
5064 0 : appendPQExpBuffer(query, ", password_required = false");
5065 :
5066 196 : if (strcmp(subinfo->subrunasowner, "t") == 0)
5067 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5068 :
5069 196 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5070 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5071 :
5072 196 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5073 64 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5074 :
5075 196 : appendPQExpBufferStr(query, ");\n");
5076 :
5077 : /*
5078 : * In binary-upgrade mode, we allow the replication to continue after the
5079 : * upgrade.
5080 : */
5081 196 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5082 : {
5083 10 : if (subinfo->suboriginremotelsn)
5084 : {
5085 : /*
5086 : * Preserve the remote_lsn for the subscriber's replication
5087 : * origin. This value is required to start the replication from
5088 : * the position before the upgrade. This value will be stale if
5089 : * the publisher gets upgraded before the subscriber node.
5090 : * However, this shouldn't be a problem as the upgrade of the
5091 : * publisher ensures that all the transactions were replicated
5092 : * before upgrading it.
5093 : */
5094 2 : appendPQExpBufferStr(query,
5095 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5096 2 : appendPQExpBufferStr(query,
5097 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5098 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5099 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5100 : }
5101 :
5102 10 : if (strcmp(subinfo->subfailover, "t") == 0)
5103 : {
5104 : /*
5105 : * Enable the failover to allow the subscription's slot to be
5106 : * synced to the standbys after the upgrade.
5107 : */
5108 2 : appendPQExpBufferStr(query,
5109 : "\n-- For binary upgrade, must preserve the subscriber's failover option.\n");
5110 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s SET(failover = true);\n", qsubname);
5111 : }
5112 :
5113 10 : if (strcmp(subinfo->subenabled, "t") == 0)
5114 : {
5115 : /*
5116 : * Enable the subscription to allow the replication to continue
5117 : * after the upgrade.
5118 : */
5119 2 : appendPQExpBufferStr(query,
5120 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5121 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5122 : }
5123 : }
5124 :
5125 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5126 196 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5127 196 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5128 : .owner = subinfo->rolname,
5129 : .description = "SUBSCRIPTION",
5130 : .section = SECTION_POST_DATA,
5131 : .createStmt = query->data,
5132 : .dropStmt = delq->data));
5133 :
5134 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5135 64 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5136 : NULL, subinfo->rolname,
5137 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5138 :
5139 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5140 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5141 : NULL, subinfo->rolname,
5142 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5143 :
5144 196 : destroyPQExpBuffer(publications);
5145 196 : free(pubnames);
5146 :
5147 196 : destroyPQExpBuffer(delq);
5148 196 : destroyPQExpBuffer(query);
5149 196 : free(qsubname);
5150 : }
5151 :
5152 : /*
5153 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5154 : * the object needs.
5155 : */
5156 : static void
5157 9292 : append_depends_on_extension(Archive *fout,
5158 : PQExpBuffer create,
5159 : const DumpableObject *dobj,
5160 : const char *catalog,
5161 : const char *keyword,
5162 : const char *objname)
5163 : {
5164 9292 : if (dobj->depends_on_ext)
5165 : {
5166 : char *nm;
5167 : PGresult *res;
5168 : PQExpBuffer query;
5169 : int ntups;
5170 : int i_extname;
5171 : int i;
5172 :
5173 : /* dodge fmtId() non-reentrancy */
5174 84 : nm = pg_strdup(objname);
5175 :
5176 84 : query = createPQExpBuffer();
5177 84 : appendPQExpBuffer(query,
5178 : "SELECT e.extname "
5179 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5180 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5181 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5182 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5183 : catalog,
5184 : dobj->catId.oid);
5185 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5186 84 : ntups = PQntuples(res);
5187 84 : i_extname = PQfnumber(res, "extname");
5188 168 : for (i = 0; i < ntups; i++)
5189 : {
5190 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5191 : keyword, nm,
5192 84 : fmtId(PQgetvalue(res, i, i_extname)));
5193 : }
5194 :
5195 84 : PQclear(res);
5196 84 : destroyPQExpBuffer(query);
5197 84 : pg_free(nm);
5198 : }
5199 9292 : }
5200 :
5201 : static Oid
5202 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5203 : {
5204 : /*
5205 : * If the old version didn't assign an array type, but the new version
5206 : * does, we must select an unused type OID to assign. This currently only
5207 : * happens for domains, when upgrading pre-v11 to v11 and up.
5208 : *
5209 : * Note: local state here is kind of ugly, but we must have some, since we
5210 : * mustn't choose the same unused OID more than once.
5211 : */
5212 : static Oid next_possible_free_oid = FirstNormalObjectId;
5213 : PGresult *res;
5214 : bool is_dup;
5215 :
5216 : do
5217 : {
5218 0 : ++next_possible_free_oid;
5219 0 : printfPQExpBuffer(upgrade_query,
5220 : "SELECT EXISTS(SELECT 1 "
5221 : "FROM pg_catalog.pg_type "
5222 : "WHERE oid = '%u'::pg_catalog.oid);",
5223 : next_possible_free_oid);
5224 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5225 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5226 0 : PQclear(res);
5227 0 : } while (is_dup);
5228 :
5229 0 : return next_possible_free_oid;
5230 : }
5231 :
5232 : static void
5233 1654 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5234 : PQExpBuffer upgrade_buffer,
5235 : Oid pg_type_oid,
5236 : bool force_array_type,
5237 : bool include_multirange_type)
5238 : {
5239 1654 : PQExpBuffer upgrade_query = createPQExpBuffer();
5240 : PGresult *res;
5241 : Oid pg_type_array_oid;
5242 : Oid pg_type_multirange_oid;
5243 : Oid pg_type_multirange_array_oid;
5244 :
5245 1654 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5246 1654 : appendPQExpBuffer(upgrade_buffer,
5247 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5248 : pg_type_oid);
5249 :
5250 1654 : appendPQExpBuffer(upgrade_query,
5251 : "SELECT typarray "
5252 : "FROM pg_catalog.pg_type "
5253 : "WHERE oid = '%u'::pg_catalog.oid;",
5254 : pg_type_oid);
5255 :
5256 1654 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5257 :
5258 1654 : pg_type_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5259 :
5260 1654 : PQclear(res);
5261 :
5262 1654 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5263 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5264 :
5265 1654 : if (OidIsValid(pg_type_array_oid))
5266 : {
5267 1650 : appendPQExpBufferStr(upgrade_buffer,
5268 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5269 1650 : appendPQExpBuffer(upgrade_buffer,
5270 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5271 : pg_type_array_oid);
5272 : }
5273 :
5274 : /*
5275 : * Pre-set the multirange type oid and its own array type oid.
5276 : */
5277 1654 : if (include_multirange_type)
5278 : {
5279 12 : if (fout->remoteVersion >= 140000)
5280 : {
5281 12 : printfPQExpBuffer(upgrade_query,
5282 : "SELECT t.oid, t.typarray "
5283 : "FROM pg_catalog.pg_type t "
5284 : "JOIN pg_catalog.pg_range r "
5285 : "ON t.oid = r.rngmultitypid "
5286 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5287 : pg_type_oid);
5288 :
5289 12 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5290 :
5291 12 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5292 12 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5293 :
5294 12 : PQclear(res);
5295 : }
5296 : else
5297 : {
5298 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5299 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5300 : }
5301 :
5302 12 : appendPQExpBufferStr(upgrade_buffer,
5303 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5304 12 : appendPQExpBuffer(upgrade_buffer,
5305 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5306 : pg_type_multirange_oid);
5307 12 : appendPQExpBufferStr(upgrade_buffer,
5308 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5309 12 : appendPQExpBuffer(upgrade_buffer,
5310 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5311 : pg_type_multirange_array_oid);
5312 : }
5313 :
5314 1654 : destroyPQExpBuffer(upgrade_query);
5315 1654 : }
5316 :
5317 : static void
5318 1520 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5319 : PQExpBuffer upgrade_buffer,
5320 : const TableInfo *tbinfo)
5321 : {
5322 1520 : Oid pg_type_oid = tbinfo->reltype;
5323 :
5324 1520 : if (OidIsValid(pg_type_oid))
5325 1520 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5326 : pg_type_oid, false, false);
5327 1520 : }
5328 :
5329 : static void
5330 2238 : binary_upgrade_set_pg_class_oids(Archive *fout,
5331 : PQExpBuffer upgrade_buffer, Oid pg_class_oid,
5332 : bool is_index)
5333 : {
5334 2238 : PQExpBuffer upgrade_query = createPQExpBuffer();
5335 : PGresult *upgrade_res;
5336 : RelFileNumber relfilenumber;
5337 : Oid toast_oid;
5338 : RelFileNumber toast_relfilenumber;
5339 : char relkind;
5340 : Oid toast_index_oid;
5341 : RelFileNumber toast_index_relfilenumber;
5342 :
5343 : /*
5344 : * Preserve the OID and relfilenumber of the table, table's index, table's
5345 : * toast table and toast table's index if any.
5346 : *
5347 : * One complexity is that the current table definition might not require
5348 : * the creation of a TOAST table, but the old database might have a TOAST
5349 : * table that was created earlier, before some wide columns were dropped.
5350 : * By setting the TOAST oid we force creation of the TOAST heap and index
5351 : * by the new backend, so we can copy the files during binary upgrade
5352 : * without worrying about this case.
5353 : */
5354 2238 : appendPQExpBuffer(upgrade_query,
5355 : "SELECT c.relkind, c.relfilenode, c.reltoastrelid, ct.relfilenode AS toast_relfilenode, i.indexrelid, cti.relfilenode AS toast_index_relfilenode "
5356 : "FROM pg_catalog.pg_class c LEFT JOIN "
5357 : "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5358 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5359 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5360 : "WHERE c.oid = '%u'::pg_catalog.oid;",
5361 : pg_class_oid);
5362 :
5363 2238 : upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5364 :
5365 2238 : relkind = *PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "relkind"));
5366 :
5367 2238 : relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5368 : PQfnumber(upgrade_res, "relfilenode")));
5369 2238 : toast_oid = atooid(PQgetvalue(upgrade_res, 0,
5370 : PQfnumber(upgrade_res, "reltoastrelid")));
5371 2238 : toast_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5372 : PQfnumber(upgrade_res, "toast_relfilenode")));
5373 2238 : toast_index_oid = atooid(PQgetvalue(upgrade_res, 0,
5374 : PQfnumber(upgrade_res, "indexrelid")));
5375 2238 : toast_index_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5376 : PQfnumber(upgrade_res, "toast_index_relfilenode")));
5377 :
5378 2238 : appendPQExpBufferStr(upgrade_buffer,
5379 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5380 :
5381 2238 : if (!is_index)
5382 : {
5383 1672 : appendPQExpBuffer(upgrade_buffer,
5384 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5385 : pg_class_oid);
5386 :
5387 : /*
5388 : * Not every relation has storage. Also, in a pre-v12 database,
5389 : * partitioned tables have a relfilenumber, which should not be
5390 : * preserved when upgrading.
5391 : */
5392 1672 : if (RelFileNumberIsValid(relfilenumber) && relkind != RELKIND_PARTITIONED_TABLE)
5393 1372 : appendPQExpBuffer(upgrade_buffer,
5394 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5395 : relfilenumber);
5396 :
5397 : /*
5398 : * In a pre-v12 database, partitioned tables might be marked as having
5399 : * toast tables, but we should ignore them if so.
5400 : */
5401 1672 : if (OidIsValid(toast_oid) &&
5402 : relkind != RELKIND_PARTITIONED_TABLE)
5403 : {
5404 544 : appendPQExpBuffer(upgrade_buffer,
5405 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5406 : toast_oid);
5407 544 : appendPQExpBuffer(upgrade_buffer,
5408 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5409 : toast_relfilenumber);
5410 :
5411 : /* every toast table has an index */
5412 544 : appendPQExpBuffer(upgrade_buffer,
5413 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5414 : toast_index_oid);
5415 544 : appendPQExpBuffer(upgrade_buffer,
5416 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5417 : toast_index_relfilenumber);
5418 : }
5419 :
5420 1672 : PQclear(upgrade_res);
5421 : }
5422 : else
5423 : {
5424 : /* Preserve the OID and relfilenumber of the index */
5425 566 : appendPQExpBuffer(upgrade_buffer,
5426 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5427 : pg_class_oid);
5428 566 : appendPQExpBuffer(upgrade_buffer,
5429 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5430 : relfilenumber);
5431 : }
5432 :
5433 2238 : appendPQExpBufferChar(upgrade_buffer, '\n');
5434 :
5435 2238 : destroyPQExpBuffer(upgrade_query);
5436 2238 : }
5437 :
5438 : /*
5439 : * If the DumpableObject is a member of an extension, add a suitable
5440 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5441 : *
5442 : * For somewhat historical reasons, objname should already be quoted,
5443 : * but not objnamespace (if any).
5444 : */
5445 : static void
5446 2636 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5447 : const DumpableObject *dobj,
5448 : const char *objtype,
5449 : const char *objname,
5450 : const char *objnamespace)
5451 : {
5452 2636 : DumpableObject *extobj = NULL;
5453 : int i;
5454 :
5455 2636 : if (!dobj->ext_member)
5456 2604 : return;
5457 :
5458 : /*
5459 : * Find the parent extension. We could avoid this search if we wanted to
5460 : * add a link field to DumpableObject, but the space costs of that would
5461 : * be considerable. We assume that member objects could only have a
5462 : * direct dependency on their own extension, not any others.
5463 : */
5464 32 : for (i = 0; i < dobj->nDeps; i++)
5465 : {
5466 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5467 32 : if (extobj && extobj->objType == DO_EXTENSION)
5468 32 : break;
5469 0 : extobj = NULL;
5470 : }
5471 32 : if (extobj == NULL)
5472 0 : pg_fatal("could not find parent extension for %s %s",
5473 : objtype, objname);
5474 :
5475 32 : appendPQExpBufferStr(upgrade_buffer,
5476 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5477 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5478 32 : fmtId(extobj->name),
5479 : objtype);
5480 32 : if (objnamespace && *objnamespace)
5481 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5482 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5483 : }
5484 :
5485 : /*
5486 : * getNamespaces:
5487 : * read all namespaces in the system catalogs and return them in the
5488 : * NamespaceInfo* structure
5489 : *
5490 : * numNamespaces is set to the number of namespaces read in
5491 : */
5492 : NamespaceInfo *
5493 306 : getNamespaces(Archive *fout, int *numNamespaces)
5494 : {
5495 : PGresult *res;
5496 : int ntups;
5497 : int i;
5498 : PQExpBuffer query;
5499 : NamespaceInfo *nsinfo;
5500 : int i_tableoid;
5501 : int i_oid;
5502 : int i_nspname;
5503 : int i_nspowner;
5504 : int i_nspacl;
5505 : int i_acldefault;
5506 :
5507 306 : query = createPQExpBuffer();
5508 :
5509 : /*
5510 : * we fetch all namespaces including system ones, so that every object we
5511 : * read in can be linked to a containing namespace.
5512 : */
5513 306 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5514 : "n.nspowner, "
5515 : "n.nspacl, "
5516 : "acldefault('n', n.nspowner) AS acldefault "
5517 : "FROM pg_namespace n");
5518 :
5519 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5520 :
5521 306 : ntups = PQntuples(res);
5522 :
5523 306 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5524 :
5525 306 : i_tableoid = PQfnumber(res, "tableoid");
5526 306 : i_oid = PQfnumber(res, "oid");
5527 306 : i_nspname = PQfnumber(res, "nspname");
5528 306 : i_nspowner = PQfnumber(res, "nspowner");
5529 306 : i_nspacl = PQfnumber(res, "nspacl");
5530 306 : i_acldefault = PQfnumber(res, "acldefault");
5531 :
5532 2550 : for (i = 0; i < ntups; i++)
5533 : {
5534 : const char *nspowner;
5535 :
5536 2244 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5537 2244 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5538 2244 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5539 2244 : AssignDumpId(&nsinfo[i].dobj);
5540 2244 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5541 2244 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5542 2244 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5543 2244 : nsinfo[i].dacl.privtype = 0;
5544 2244 : nsinfo[i].dacl.initprivs = NULL;
5545 2244 : nspowner = PQgetvalue(res, i, i_nspowner);
5546 2244 : nsinfo[i].nspowner = atooid(nspowner);
5547 2244 : nsinfo[i].rolname = getRoleName(nspowner);
5548 :
5549 : /* Decide whether to dump this namespace */
5550 2244 : selectDumpableNamespace(&nsinfo[i], fout);
5551 :
5552 : /* Mark whether namespace has an ACL */
5553 2244 : if (!PQgetisnull(res, i, i_nspacl))
5554 1006 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5555 :
5556 : /*
5557 : * We ignore any pg_init_privs.initprivs entry for the public schema
5558 : * and assume a predetermined default, for several reasons. First,
5559 : * dropping and recreating the schema removes its pg_init_privs entry,
5560 : * but an empty destination database starts with this ACL nonetheless.
5561 : * Second, we support dump/reload of public schema ownership changes.
5562 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5563 : * initprivs continues to reflect the initial owner. Hence,
5564 : * synthesize the value that nspacl will have after the restore's
5565 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5566 : * match the source's ACL, even if the latter was an initdb-default
5567 : * ACL, which changed in v15. An upgrade pulls in changes to most
5568 : * system object ACLs that the DBA had not customized. We've made the
5569 : * public schema depart from that, because changing its ACL so easily
5570 : * breaks applications.
5571 : */
5572 2244 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5573 : {
5574 298 : PQExpBuffer aclarray = createPQExpBuffer();
5575 298 : PQExpBuffer aclitem = createPQExpBuffer();
5576 :
5577 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5578 298 : appendPQExpBufferChar(aclarray, '{');
5579 298 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5580 298 : appendPQExpBufferStr(aclitem, "=UC/");
5581 298 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5582 298 : appendPGArray(aclarray, aclitem->data);
5583 298 : resetPQExpBuffer(aclitem);
5584 298 : appendPQExpBufferStr(aclitem, "=U/");
5585 298 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5586 298 : appendPGArray(aclarray, aclitem->data);
5587 298 : appendPQExpBufferChar(aclarray, '}');
5588 :
5589 298 : nsinfo[i].dacl.privtype = 'i';
5590 298 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5591 298 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5592 :
5593 298 : destroyPQExpBuffer(aclarray);
5594 298 : destroyPQExpBuffer(aclitem);
5595 : }
5596 : }
5597 :
5598 306 : PQclear(res);
5599 306 : destroyPQExpBuffer(query);
5600 :
5601 306 : *numNamespaces = ntups;
5602 :
5603 306 : return nsinfo;
5604 : }
5605 :
5606 : /*
5607 : * findNamespace:
5608 : * given a namespace OID, look up the info read by getNamespaces
5609 : */
5610 : static NamespaceInfo *
5611 940952 : findNamespace(Oid nsoid)
5612 : {
5613 : NamespaceInfo *nsinfo;
5614 :
5615 940952 : nsinfo = findNamespaceByOid(nsoid);
5616 940952 : if (nsinfo == NULL)
5617 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5618 940952 : return nsinfo;
5619 : }
5620 :
5621 : /*
5622 : * getExtensions:
5623 : * read all extensions in the system catalogs and return them in the
5624 : * ExtensionInfo* structure
5625 : *
5626 : * numExtensions is set to the number of extensions read in
5627 : */
5628 : ExtensionInfo *
5629 306 : getExtensions(Archive *fout, int *numExtensions)
5630 : {
5631 306 : DumpOptions *dopt = fout->dopt;
5632 : PGresult *res;
5633 : int ntups;
5634 : int i;
5635 : PQExpBuffer query;
5636 : ExtensionInfo *extinfo;
5637 : int i_tableoid;
5638 : int i_oid;
5639 : int i_extname;
5640 : int i_nspname;
5641 : int i_extrelocatable;
5642 : int i_extversion;
5643 : int i_extconfig;
5644 : int i_extcondition;
5645 :
5646 306 : query = createPQExpBuffer();
5647 :
5648 306 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5649 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5650 : "FROM pg_extension x "
5651 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5652 :
5653 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5654 :
5655 306 : ntups = PQntuples(res);
5656 :
5657 306 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5658 :
5659 306 : i_tableoid = PQfnumber(res, "tableoid");
5660 306 : i_oid = PQfnumber(res, "oid");
5661 306 : i_extname = PQfnumber(res, "extname");
5662 306 : i_nspname = PQfnumber(res, "nspname");
5663 306 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5664 306 : i_extversion = PQfnumber(res, "extversion");
5665 306 : i_extconfig = PQfnumber(res, "extconfig");
5666 306 : i_extcondition = PQfnumber(res, "extcondition");
5667 :
5668 662 : for (i = 0; i < ntups; i++)
5669 : {
5670 356 : extinfo[i].dobj.objType = DO_EXTENSION;
5671 356 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5672 356 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5673 356 : AssignDumpId(&extinfo[i].dobj);
5674 356 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5675 356 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5676 356 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5677 356 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5678 356 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5679 356 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5680 :
5681 : /* Decide whether we want to dump it */
5682 356 : selectDumpableExtension(&(extinfo[i]), dopt);
5683 : }
5684 :
5685 306 : PQclear(res);
5686 306 : destroyPQExpBuffer(query);
5687 :
5688 306 : *numExtensions = ntups;
5689 :
5690 306 : return extinfo;
5691 : }
5692 :
5693 : /*
5694 : * getTypes:
5695 : * read all types in the system catalogs and return them in the
5696 : * TypeInfo* structure
5697 : *
5698 : * numTypes is set to the number of types read in
5699 : *
5700 : * NB: this must run after getFuncs() because we assume we can do
5701 : * findFuncByOid().
5702 : */
5703 : TypeInfo *
5704 304 : getTypes(Archive *fout, int *numTypes)
5705 : {
5706 : PGresult *res;
5707 : int ntups;
5708 : int i;
5709 304 : PQExpBuffer query = createPQExpBuffer();
5710 : TypeInfo *tyinfo;
5711 : ShellTypeInfo *stinfo;
5712 : int i_tableoid;
5713 : int i_oid;
5714 : int i_typname;
5715 : int i_typnamespace;
5716 : int i_typacl;
5717 : int i_acldefault;
5718 : int i_typowner;
5719 : int i_typelem;
5720 : int i_typrelid;
5721 : int i_typrelkind;
5722 : int i_typtype;
5723 : int i_typisdefined;
5724 : int i_isarray;
5725 :
5726 : /*
5727 : * we include even the built-in types because those may be used as array
5728 : * elements by user-defined types
5729 : *
5730 : * we filter out the built-in types when we dump out the types
5731 : *
5732 : * same approach for undefined (shell) types and array types
5733 : *
5734 : * Note: as of 8.3 we can reliably detect whether a type is an
5735 : * auto-generated array type by checking the element type's typarray.
5736 : * (Before that the test is capable of generating false positives.) We
5737 : * still check for name beginning with '_', though, so as to avoid the
5738 : * cost of the subselect probe for all standard types. This would have to
5739 : * be revisited if the backend ever allows renaming of array types.
5740 : */
5741 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
5742 : "typnamespace, typacl, "
5743 : "acldefault('T', typowner) AS acldefault, "
5744 : "typowner, "
5745 : "typelem, typrelid, "
5746 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
5747 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
5748 : "typtype, typisdefined, "
5749 : "typname[0] = '_' AND typelem != 0 AND "
5750 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
5751 : "FROM pg_type");
5752 :
5753 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5754 :
5755 304 : ntups = PQntuples(res);
5756 :
5757 304 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
5758 :
5759 304 : i_tableoid = PQfnumber(res, "tableoid");
5760 304 : i_oid = PQfnumber(res, "oid");
5761 304 : i_typname = PQfnumber(res, "typname");
5762 304 : i_typnamespace = PQfnumber(res, "typnamespace");
5763 304 : i_typacl = PQfnumber(res, "typacl");
5764 304 : i_acldefault = PQfnumber(res, "acldefault");
5765 304 : i_typowner = PQfnumber(res, "typowner");
5766 304 : i_typelem = PQfnumber(res, "typelem");
5767 304 : i_typrelid = PQfnumber(res, "typrelid");
5768 304 : i_typrelkind = PQfnumber(res, "typrelkind");
5769 304 : i_typtype = PQfnumber(res, "typtype");
5770 304 : i_typisdefined = PQfnumber(res, "typisdefined");
5771 304 : i_isarray = PQfnumber(res, "isarray");
5772 :
5773 213576 : for (i = 0; i < ntups; i++)
5774 : {
5775 213272 : tyinfo[i].dobj.objType = DO_TYPE;
5776 213272 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5777 213272 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5778 213272 : AssignDumpId(&tyinfo[i].dobj);
5779 213272 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
5780 426544 : tyinfo[i].dobj.namespace =
5781 213272 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
5782 213272 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
5783 213272 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5784 213272 : tyinfo[i].dacl.privtype = 0;
5785 213272 : tyinfo[i].dacl.initprivs = NULL;
5786 213272 : tyinfo[i].ftypname = NULL; /* may get filled later */
5787 213272 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
5788 213272 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
5789 213272 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
5790 213272 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
5791 213272 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
5792 213272 : tyinfo[i].shellType = NULL;
5793 :
5794 213272 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
5795 213176 : tyinfo[i].isDefined = true;
5796 : else
5797 96 : tyinfo[i].isDefined = false;
5798 :
5799 213272 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
5800 102180 : tyinfo[i].isArray = true;
5801 : else
5802 111092 : tyinfo[i].isArray = false;
5803 :
5804 213272 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
5805 2036 : tyinfo[i].isMultirange = true;
5806 : else
5807 211236 : tyinfo[i].isMultirange = false;
5808 :
5809 : /* Decide whether we want to dump it */
5810 213272 : selectDumpableType(&tyinfo[i], fout);
5811 :
5812 : /* Mark whether type has an ACL */
5813 213272 : if (!PQgetisnull(res, i, i_typacl))
5814 394 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5815 :
5816 : /*
5817 : * If it's a domain, fetch info about its constraints, if any
5818 : */
5819 213272 : tyinfo[i].nDomChecks = 0;
5820 213272 : tyinfo[i].domChecks = NULL;
5821 213272 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
5822 24390 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
5823 262 : getDomainConstraints(fout, &(tyinfo[i]));
5824 :
5825 : /*
5826 : * If it's a base type, make a DumpableObject representing a shell
5827 : * definition of the type. We will need to dump that ahead of the I/O
5828 : * functions for the type. Similarly, range types need a shell
5829 : * definition in case they have a canonicalize function.
5830 : *
5831 : * Note: the shell type doesn't have a catId. You might think it
5832 : * should copy the base type's catId, but then it might capture the
5833 : * pg_depend entries for the type, which we don't want.
5834 : */
5835 213272 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
5836 24390 : (tyinfo[i].typtype == TYPTYPE_BASE ||
5837 11994 : tyinfo[i].typtype == TYPTYPE_RANGE))
5838 : {
5839 12592 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
5840 12592 : stinfo->dobj.objType = DO_SHELL_TYPE;
5841 12592 : stinfo->dobj.catId = nilCatalogId;
5842 12592 : AssignDumpId(&stinfo->dobj);
5843 12592 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
5844 12592 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
5845 12592 : stinfo->baseType = &(tyinfo[i]);
5846 12592 : tyinfo[i].shellType = stinfo;
5847 :
5848 : /*
5849 : * Initially mark the shell type as not to be dumped. We'll only
5850 : * dump it if the I/O or canonicalize functions need to be dumped;
5851 : * this is taken care of while sorting dependencies.
5852 : */
5853 12592 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
5854 : }
5855 : }
5856 :
5857 304 : *numTypes = ntups;
5858 :
5859 304 : PQclear(res);
5860 :
5861 304 : destroyPQExpBuffer(query);
5862 :
5863 304 : return tyinfo;
5864 : }
5865 :
5866 : /*
5867 : * getOperators:
5868 : * read all operators in the system catalogs and return them in the
5869 : * OprInfo* structure
5870 : *
5871 : * numOprs is set to the number of operators read in
5872 : */
5873 : OprInfo *
5874 304 : getOperators(Archive *fout, int *numOprs)
5875 : {
5876 : PGresult *res;
5877 : int ntups;
5878 : int i;
5879 304 : PQExpBuffer query = createPQExpBuffer();
5880 : OprInfo *oprinfo;
5881 : int i_tableoid;
5882 : int i_oid;
5883 : int i_oprname;
5884 : int i_oprnamespace;
5885 : int i_oprowner;
5886 : int i_oprkind;
5887 : int i_oprcode;
5888 :
5889 : /*
5890 : * find all operators, including builtin operators; we filter out
5891 : * system-defined operators at dump-out time.
5892 : */
5893 :
5894 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
5895 : "oprnamespace, "
5896 : "oprowner, "
5897 : "oprkind, "
5898 : "oprcode::oid AS oprcode "
5899 : "FROM pg_operator");
5900 :
5901 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5902 :
5903 304 : ntups = PQntuples(res);
5904 304 : *numOprs = ntups;
5905 :
5906 304 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
5907 :
5908 304 : i_tableoid = PQfnumber(res, "tableoid");
5909 304 : i_oid = PQfnumber(res, "oid");
5910 304 : i_oprname = PQfnumber(res, "oprname");
5911 304 : i_oprnamespace = PQfnumber(res, "oprnamespace");
5912 304 : i_oprowner = PQfnumber(res, "oprowner");
5913 304 : i_oprkind = PQfnumber(res, "oprkind");
5914 304 : i_oprcode = PQfnumber(res, "oprcode");
5915 :
5916 243424 : for (i = 0; i < ntups; i++)
5917 : {
5918 243120 : oprinfo[i].dobj.objType = DO_OPERATOR;
5919 243120 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5920 243120 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5921 243120 : AssignDumpId(&oprinfo[i].dobj);
5922 243120 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
5923 486240 : oprinfo[i].dobj.namespace =
5924 243120 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
5925 243120 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
5926 243120 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
5927 243120 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
5928 :
5929 : /* Decide whether we want to dump it */
5930 243120 : selectDumpableObject(&(oprinfo[i].dobj), fout);
5931 : }
5932 :
5933 304 : PQclear(res);
5934 :
5935 304 : destroyPQExpBuffer(query);
5936 :
5937 304 : return oprinfo;
5938 : }
5939 :
5940 : /*
5941 : * getCollations:
5942 : * read all collations in the system catalogs and return them in the
5943 : * CollInfo* structure
5944 : *
5945 : * numCollations is set to the number of collations read in
5946 : */
5947 : CollInfo *
5948 304 : getCollations(Archive *fout, int *numCollations)
5949 : {
5950 : PGresult *res;
5951 : int ntups;
5952 : int i;
5953 : PQExpBuffer query;
5954 : CollInfo *collinfo;
5955 : int i_tableoid;
5956 : int i_oid;
5957 : int i_collname;
5958 : int i_collnamespace;
5959 : int i_collowner;
5960 :
5961 304 : query = createPQExpBuffer();
5962 :
5963 : /*
5964 : * find all collations, including builtin collations; we filter out
5965 : * system-defined collations at dump-out time.
5966 : */
5967 :
5968 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
5969 : "collnamespace, "
5970 : "collowner "
5971 : "FROM pg_collation");
5972 :
5973 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5974 :
5975 304 : ntups = PQntuples(res);
5976 304 : *numCollations = ntups;
5977 :
5978 304 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
5979 :
5980 304 : i_tableoid = PQfnumber(res, "tableoid");
5981 304 : i_oid = PQfnumber(res, "oid");
5982 304 : i_collname = PQfnumber(res, "collname");
5983 304 : i_collnamespace = PQfnumber(res, "collnamespace");
5984 304 : i_collowner = PQfnumber(res, "collowner");
5985 :
5986 241264 : for (i = 0; i < ntups; i++)
5987 : {
5988 240960 : collinfo[i].dobj.objType = DO_COLLATION;
5989 240960 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5990 240960 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5991 240960 : AssignDumpId(&collinfo[i].dobj);
5992 240960 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
5993 481920 : collinfo[i].dobj.namespace =
5994 240960 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
5995 240960 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
5996 :
5997 : /* Decide whether we want to dump it */
5998 240960 : selectDumpableObject(&(collinfo[i].dobj), fout);
5999 : }
6000 :
6001 304 : PQclear(res);
6002 :
6003 304 : destroyPQExpBuffer(query);
6004 :
6005 304 : return collinfo;
6006 : }
6007 :
6008 : /*
6009 : * getConversions:
6010 : * read all conversions in the system catalogs and return them in the
6011 : * ConvInfo* structure
6012 : *
6013 : * numConversions is set to the number of conversions read in
6014 : */
6015 : ConvInfo *
6016 304 : getConversions(Archive *fout, int *numConversions)
6017 : {
6018 : PGresult *res;
6019 : int ntups;
6020 : int i;
6021 : PQExpBuffer query;
6022 : ConvInfo *convinfo;
6023 : int i_tableoid;
6024 : int i_oid;
6025 : int i_conname;
6026 : int i_connamespace;
6027 : int i_conowner;
6028 :
6029 304 : query = createPQExpBuffer();
6030 :
6031 : /*
6032 : * find all conversions, including builtin conversions; we filter out
6033 : * system-defined conversions at dump-out time.
6034 : */
6035 :
6036 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6037 : "connamespace, "
6038 : "conowner "
6039 : "FROM pg_conversion");
6040 :
6041 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6042 :
6043 304 : ntups = PQntuples(res);
6044 304 : *numConversions = ntups;
6045 :
6046 304 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6047 :
6048 304 : i_tableoid = PQfnumber(res, "tableoid");
6049 304 : i_oid = PQfnumber(res, "oid");
6050 304 : i_conname = PQfnumber(res, "conname");
6051 304 : i_connamespace = PQfnumber(res, "connamespace");
6052 304 : i_conowner = PQfnumber(res, "conowner");
6053 :
6054 39302 : for (i = 0; i < ntups; i++)
6055 : {
6056 38998 : convinfo[i].dobj.objType = DO_CONVERSION;
6057 38998 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6058 38998 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6059 38998 : AssignDumpId(&convinfo[i].dobj);
6060 38998 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6061 77996 : convinfo[i].dobj.namespace =
6062 38998 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6063 38998 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6064 :
6065 : /* Decide whether we want to dump it */
6066 38998 : selectDumpableObject(&(convinfo[i].dobj), fout);
6067 : }
6068 :
6069 304 : PQclear(res);
6070 :
6071 304 : destroyPQExpBuffer(query);
6072 :
6073 304 : return convinfo;
6074 : }
6075 :
6076 : /*
6077 : * getAccessMethods:
6078 : * read all user-defined access methods in the system catalogs and return
6079 : * them in the AccessMethodInfo* structure
6080 : *
6081 : * numAccessMethods is set to the number of access methods read in
6082 : */
6083 : AccessMethodInfo *
6084 304 : getAccessMethods(Archive *fout, int *numAccessMethods)
6085 : {
6086 : PGresult *res;
6087 : int ntups;
6088 : int i;
6089 : PQExpBuffer query;
6090 : AccessMethodInfo *aminfo;
6091 : int i_tableoid;
6092 : int i_oid;
6093 : int i_amname;
6094 : int i_amhandler;
6095 : int i_amtype;
6096 :
6097 : /* Before 9.6, there are no user-defined access methods */
6098 304 : if (fout->remoteVersion < 90600)
6099 : {
6100 0 : *numAccessMethods = 0;
6101 0 : return NULL;
6102 : }
6103 :
6104 304 : query = createPQExpBuffer();
6105 :
6106 : /* Select all access methods from pg_am table */
6107 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6108 : "amhandler::pg_catalog.regproc AS amhandler "
6109 : "FROM pg_am");
6110 :
6111 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6112 :
6113 304 : ntups = PQntuples(res);
6114 304 : *numAccessMethods = ntups;
6115 :
6116 304 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6117 :
6118 304 : i_tableoid = PQfnumber(res, "tableoid");
6119 304 : i_oid = PQfnumber(res, "oid");
6120 304 : i_amname = PQfnumber(res, "amname");
6121 304 : i_amhandler = PQfnumber(res, "amhandler");
6122 304 : i_amtype = PQfnumber(res, "amtype");
6123 :
6124 2664 : for (i = 0; i < ntups; i++)
6125 : {
6126 2360 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6127 2360 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6128 2360 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6129 2360 : AssignDumpId(&aminfo[i].dobj);
6130 2360 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6131 2360 : aminfo[i].dobj.namespace = NULL;
6132 2360 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6133 2360 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6134 :
6135 : /* Decide whether we want to dump it */
6136 2360 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6137 : }
6138 :
6139 304 : PQclear(res);
6140 :
6141 304 : destroyPQExpBuffer(query);
6142 :
6143 304 : return aminfo;
6144 : }
6145 :
6146 :
6147 : /*
6148 : * getOpclasses:
6149 : * read all opclasses in the system catalogs and return them in the
6150 : * OpclassInfo* structure
6151 : *
6152 : * numOpclasses is set to the number of opclasses read in
6153 : */
6154 : OpclassInfo *
6155 304 : getOpclasses(Archive *fout, int *numOpclasses)
6156 : {
6157 : PGresult *res;
6158 : int ntups;
6159 : int i;
6160 304 : PQExpBuffer query = createPQExpBuffer();
6161 : OpclassInfo *opcinfo;
6162 : int i_tableoid;
6163 : int i_oid;
6164 : int i_opcname;
6165 : int i_opcnamespace;
6166 : int i_opcowner;
6167 :
6168 : /*
6169 : * find all opclasses, including builtin opclasses; we filter out
6170 : * system-defined opclasses at dump-out time.
6171 : */
6172 :
6173 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6174 : "opcnamespace, "
6175 : "opcowner "
6176 : "FROM pg_opclass");
6177 :
6178 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6179 :
6180 304 : ntups = PQntuples(res);
6181 304 : *numOpclasses = ntups;
6182 :
6183 304 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6184 :
6185 304 : i_tableoid = PQfnumber(res, "tableoid");
6186 304 : i_oid = PQfnumber(res, "oid");
6187 304 : i_opcname = PQfnumber(res, "opcname");
6188 304 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6189 304 : i_opcowner = PQfnumber(res, "opcowner");
6190 :
6191 54400 : for (i = 0; i < ntups; i++)
6192 : {
6193 54096 : opcinfo[i].dobj.objType = DO_OPCLASS;
6194 54096 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6195 54096 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6196 54096 : AssignDumpId(&opcinfo[i].dobj);
6197 54096 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6198 108192 : opcinfo[i].dobj.namespace =
6199 54096 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6200 54096 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6201 :
6202 : /* Decide whether we want to dump it */
6203 54096 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6204 : }
6205 :
6206 304 : PQclear(res);
6207 :
6208 304 : destroyPQExpBuffer(query);
6209 :
6210 304 : return opcinfo;
6211 : }
6212 :
6213 : /*
6214 : * getOpfamilies:
6215 : * read all opfamilies in the system catalogs and return them in the
6216 : * OpfamilyInfo* structure
6217 : *
6218 : * numOpfamilies is set to the number of opfamilies read in
6219 : */
6220 : OpfamilyInfo *
6221 304 : getOpfamilies(Archive *fout, int *numOpfamilies)
6222 : {
6223 : PGresult *res;
6224 : int ntups;
6225 : int i;
6226 : PQExpBuffer query;
6227 : OpfamilyInfo *opfinfo;
6228 : int i_tableoid;
6229 : int i_oid;
6230 : int i_opfname;
6231 : int i_opfnamespace;
6232 : int i_opfowner;
6233 :
6234 304 : query = createPQExpBuffer();
6235 :
6236 : /*
6237 : * find all opfamilies, including builtin opfamilies; we filter out
6238 : * system-defined opfamilies at dump-out time.
6239 : */
6240 :
6241 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6242 : "opfnamespace, "
6243 : "opfowner "
6244 : "FROM pg_opfamily");
6245 :
6246 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6247 :
6248 304 : ntups = PQntuples(res);
6249 304 : *numOpfamilies = ntups;
6250 :
6251 304 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6252 :
6253 304 : i_tableoid = PQfnumber(res, "tableoid");
6254 304 : i_oid = PQfnumber(res, "oid");
6255 304 : i_opfname = PQfnumber(res, "opfname");
6256 304 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6257 304 : i_opfowner = PQfnumber(res, "opfowner");
6258 :
6259 44930 : for (i = 0; i < ntups; i++)
6260 : {
6261 44626 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6262 44626 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6263 44626 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6264 44626 : AssignDumpId(&opfinfo[i].dobj);
6265 44626 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6266 89252 : opfinfo[i].dobj.namespace =
6267 44626 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6268 44626 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6269 :
6270 : /* Decide whether we want to dump it */
6271 44626 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6272 : }
6273 :
6274 304 : PQclear(res);
6275 :
6276 304 : destroyPQExpBuffer(query);
6277 :
6278 304 : return opfinfo;
6279 : }
6280 :
6281 : /*
6282 : * getAggregates:
6283 : * read all the user-defined aggregates in the system catalogs and
6284 : * return them in the AggInfo* structure
6285 : *
6286 : * numAggs is set to the number of aggregates read in
6287 : */
6288 : AggInfo *
6289 304 : getAggregates(Archive *fout, int *numAggs)
6290 : {
6291 304 : DumpOptions *dopt = fout->dopt;
6292 : PGresult *res;
6293 : int ntups;
6294 : int i;
6295 304 : PQExpBuffer query = createPQExpBuffer();
6296 : AggInfo *agginfo;
6297 : int i_tableoid;
6298 : int i_oid;
6299 : int i_aggname;
6300 : int i_aggnamespace;
6301 : int i_pronargs;
6302 : int i_proargtypes;
6303 : int i_proowner;
6304 : int i_aggacl;
6305 : int i_acldefault;
6306 :
6307 : /*
6308 : * Find all interesting aggregates. See comment in getFuncs() for the
6309 : * rationale behind the filtering logic.
6310 : */
6311 304 : if (fout->remoteVersion >= 90600)
6312 : {
6313 : const char *agg_check;
6314 :
6315 608 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6316 304 : : "p.proisagg");
6317 :
6318 304 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6319 : "p.proname AS aggname, "
6320 : "p.pronamespace AS aggnamespace, "
6321 : "p.pronargs, p.proargtypes, "
6322 : "p.proowner, "
6323 : "p.proacl AS aggacl, "
6324 : "acldefault('f', p.proowner) AS acldefault "
6325 : "FROM pg_proc p "
6326 : "LEFT JOIN pg_init_privs pip ON "
6327 : "(p.oid = pip.objoid "
6328 : "AND pip.classoid = 'pg_proc'::regclass "
6329 : "AND pip.objsubid = 0) "
6330 : "WHERE %s AND ("
6331 : "p.pronamespace != "
6332 : "(SELECT oid FROM pg_namespace "
6333 : "WHERE nspname = 'pg_catalog') OR "
6334 : "p.proacl IS DISTINCT FROM pip.initprivs",
6335 : agg_check);
6336 304 : if (dopt->binary_upgrade)
6337 28 : appendPQExpBufferStr(query,
6338 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6339 : "classid = 'pg_proc'::regclass AND "
6340 : "objid = p.oid AND "
6341 : "refclassid = 'pg_extension'::regclass AND "
6342 : "deptype = 'e')");
6343 304 : appendPQExpBufferChar(query, ')');
6344 : }
6345 : else
6346 : {
6347 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6348 : "pronamespace AS aggnamespace, "
6349 : "pronargs, proargtypes, "
6350 : "proowner, "
6351 : "proacl AS aggacl, "
6352 : "acldefault('f', proowner) AS acldefault "
6353 : "FROM pg_proc p "
6354 : "WHERE proisagg AND ("
6355 : "pronamespace != "
6356 : "(SELECT oid FROM pg_namespace "
6357 : "WHERE nspname = 'pg_catalog')");
6358 0 : if (dopt->binary_upgrade)
6359 0 : appendPQExpBufferStr(query,
6360 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6361 : "classid = 'pg_proc'::regclass AND "
6362 : "objid = p.oid AND "
6363 : "refclassid = 'pg_extension'::regclass AND "
6364 : "deptype = 'e')");
6365 0 : appendPQExpBufferChar(query, ')');
6366 : }
6367 :
6368 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6369 :
6370 304 : ntups = PQntuples(res);
6371 304 : *numAggs = ntups;
6372 :
6373 304 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6374 :
6375 304 : i_tableoid = PQfnumber(res, "tableoid");
6376 304 : i_oid = PQfnumber(res, "oid");
6377 304 : i_aggname = PQfnumber(res, "aggname");
6378 304 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6379 304 : i_pronargs = PQfnumber(res, "pronargs");
6380 304 : i_proargtypes = PQfnumber(res, "proargtypes");
6381 304 : i_proowner = PQfnumber(res, "proowner");
6382 304 : i_aggacl = PQfnumber(res, "aggacl");
6383 304 : i_acldefault = PQfnumber(res, "acldefault");
6384 :
6385 910 : for (i = 0; i < ntups; i++)
6386 : {
6387 606 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6388 606 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6389 606 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6390 606 : AssignDumpId(&agginfo[i].aggfn.dobj);
6391 606 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6392 1212 : agginfo[i].aggfn.dobj.namespace =
6393 606 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6394 606 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6395 606 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6396 606 : agginfo[i].aggfn.dacl.privtype = 0;
6397 606 : agginfo[i].aggfn.dacl.initprivs = NULL;
6398 606 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6399 606 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6400 606 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6401 606 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6402 606 : if (agginfo[i].aggfn.nargs == 0)
6403 80 : agginfo[i].aggfn.argtypes = NULL;
6404 : else
6405 : {
6406 526 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6407 526 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6408 526 : agginfo[i].aggfn.argtypes,
6409 526 : agginfo[i].aggfn.nargs);
6410 : }
6411 606 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6412 :
6413 : /* Decide whether we want to dump it */
6414 606 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6415 :
6416 : /* Mark whether aggregate has an ACL */
6417 606 : if (!PQgetisnull(res, i, i_aggacl))
6418 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6419 : }
6420 :
6421 304 : PQclear(res);
6422 :
6423 304 : destroyPQExpBuffer(query);
6424 :
6425 304 : return agginfo;
6426 : }
6427 :
6428 : /*
6429 : * getFuncs:
6430 : * read all the user-defined functions in the system catalogs and
6431 : * return them in the FuncInfo* structure
6432 : *
6433 : * numFuncs is set to the number of functions read in
6434 : */
6435 : FuncInfo *
6436 304 : getFuncs(Archive *fout, int *numFuncs)
6437 : {
6438 304 : DumpOptions *dopt = fout->dopt;
6439 : PGresult *res;
6440 : int ntups;
6441 : int i;
6442 304 : PQExpBuffer query = createPQExpBuffer();
6443 : FuncInfo *finfo;
6444 : int i_tableoid;
6445 : int i_oid;
6446 : int i_proname;
6447 : int i_pronamespace;
6448 : int i_proowner;
6449 : int i_prolang;
6450 : int i_pronargs;
6451 : int i_proargtypes;
6452 : int i_prorettype;
6453 : int i_proacl;
6454 : int i_acldefault;
6455 :
6456 : /*
6457 : * Find all interesting functions. This is a bit complicated:
6458 : *
6459 : * 1. Always exclude aggregates; those are handled elsewhere.
6460 : *
6461 : * 2. Always exclude functions that are internally dependent on something
6462 : * else, since presumably those will be created as a result of creating
6463 : * the something else. This currently acts only to suppress constructor
6464 : * functions for range types. Note this is OK only because the
6465 : * constructors don't have any dependencies the range type doesn't have;
6466 : * otherwise we might not get creation ordering correct.
6467 : *
6468 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6469 : * they're members of extensions and we are in binary-upgrade mode then
6470 : * include them, since we want to dump extension members individually in
6471 : * that mode. Also, if they are used by casts or transforms then we need
6472 : * to gather the information about them, though they won't be dumped if
6473 : * they are built-in. Also, in 9.6 and up, include functions in
6474 : * pg_catalog if they have an ACL different from what's shown in
6475 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6476 : */
6477 304 : if (fout->remoteVersion >= 90600)
6478 : {
6479 : const char *not_agg_check;
6480 :
6481 608 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6482 304 : : "NOT p.proisagg");
6483 :
6484 304 : appendPQExpBuffer(query,
6485 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6486 : "p.pronargs, p.proargtypes, p.prorettype, "
6487 : "p.proacl, "
6488 : "acldefault('f', p.proowner) AS acldefault, "
6489 : "p.pronamespace, "
6490 : "p.proowner "
6491 : "FROM pg_proc p "
6492 : "LEFT JOIN pg_init_privs pip ON "
6493 : "(p.oid = pip.objoid "
6494 : "AND pip.classoid = 'pg_proc'::regclass "
6495 : "AND pip.objsubid = 0) "
6496 : "WHERE %s"
6497 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6498 : "WHERE classid = 'pg_proc'::regclass AND "
6499 : "objid = p.oid AND deptype = 'i')"
6500 : "\n AND ("
6501 : "\n pronamespace != "
6502 : "(SELECT oid FROM pg_namespace "
6503 : "WHERE nspname = 'pg_catalog')"
6504 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6505 : "\n WHERE pg_cast.oid > %u "
6506 : "\n AND p.oid = pg_cast.castfunc)"
6507 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6508 : "\n WHERE pg_transform.oid > %u AND "
6509 : "\n (p.oid = pg_transform.trffromsql"
6510 : "\n OR p.oid = pg_transform.trftosql))",
6511 : not_agg_check,
6512 : g_last_builtin_oid,
6513 : g_last_builtin_oid);
6514 304 : if (dopt->binary_upgrade)
6515 28 : appendPQExpBufferStr(query,
6516 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6517 : "classid = 'pg_proc'::regclass AND "
6518 : "objid = p.oid AND "
6519 : "refclassid = 'pg_extension'::regclass AND "
6520 : "deptype = 'e')");
6521 304 : appendPQExpBufferStr(query,
6522 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6523 304 : appendPQExpBufferChar(query, ')');
6524 : }
6525 : else
6526 : {
6527 0 : appendPQExpBuffer(query,
6528 : "SELECT tableoid, oid, proname, prolang, "
6529 : "pronargs, proargtypes, prorettype, proacl, "
6530 : "acldefault('f', proowner) AS acldefault, "
6531 : "pronamespace, "
6532 : "proowner "
6533 : "FROM pg_proc p "
6534 : "WHERE NOT proisagg"
6535 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6536 : "WHERE classid = 'pg_proc'::regclass AND "
6537 : "objid = p.oid AND deptype = 'i')"
6538 : "\n AND ("
6539 : "\n pronamespace != "
6540 : "(SELECT oid FROM pg_namespace "
6541 : "WHERE nspname = 'pg_catalog')"
6542 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6543 : "\n WHERE pg_cast.oid > '%u'::oid"
6544 : "\n AND p.oid = pg_cast.castfunc)",
6545 : g_last_builtin_oid);
6546 :
6547 0 : if (fout->remoteVersion >= 90500)
6548 0 : appendPQExpBuffer(query,
6549 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6550 : "\n WHERE pg_transform.oid > '%u'::oid"
6551 : "\n AND (p.oid = pg_transform.trffromsql"
6552 : "\n OR p.oid = pg_transform.trftosql))",
6553 : g_last_builtin_oid);
6554 :
6555 0 : if (dopt->binary_upgrade)
6556 0 : appendPQExpBufferStr(query,
6557 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6558 : "classid = 'pg_proc'::regclass AND "
6559 : "objid = p.oid AND "
6560 : "refclassid = 'pg_extension'::regclass AND "
6561 : "deptype = 'e')");
6562 0 : appendPQExpBufferChar(query, ')');
6563 : }
6564 :
6565 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6566 :
6567 304 : ntups = PQntuples(res);
6568 :
6569 304 : *numFuncs = ntups;
6570 :
6571 304 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6572 :
6573 304 : i_tableoid = PQfnumber(res, "tableoid");
6574 304 : i_oid = PQfnumber(res, "oid");
6575 304 : i_proname = PQfnumber(res, "proname");
6576 304 : i_pronamespace = PQfnumber(res, "pronamespace");
6577 304 : i_proowner = PQfnumber(res, "proowner");
6578 304 : i_prolang = PQfnumber(res, "prolang");
6579 304 : i_pronargs = PQfnumber(res, "pronargs");
6580 304 : i_proargtypes = PQfnumber(res, "proargtypes");
6581 304 : i_prorettype = PQfnumber(res, "prorettype");
6582 304 : i_proacl = PQfnumber(res, "proacl");
6583 304 : i_acldefault = PQfnumber(res, "acldefault");
6584 :
6585 7686 : for (i = 0; i < ntups; i++)
6586 : {
6587 7382 : finfo[i].dobj.objType = DO_FUNC;
6588 7382 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6589 7382 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6590 7382 : AssignDumpId(&finfo[i].dobj);
6591 7382 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6592 14764 : finfo[i].dobj.namespace =
6593 7382 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6594 7382 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6595 7382 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6596 7382 : finfo[i].dacl.privtype = 0;
6597 7382 : finfo[i].dacl.initprivs = NULL;
6598 7382 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6599 7382 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6600 7382 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6601 7382 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6602 7382 : if (finfo[i].nargs == 0)
6603 1618 : finfo[i].argtypes = NULL;
6604 : else
6605 : {
6606 5764 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6607 5764 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6608 5764 : finfo[i].argtypes, finfo[i].nargs);
6609 : }
6610 7382 : finfo[i].postponed_def = false; /* might get set during sort */
6611 :
6612 : /* Decide whether we want to dump it */
6613 7382 : selectDumpableObject(&(finfo[i].dobj), fout);
6614 :
6615 : /* Mark whether function has an ACL */
6616 7382 : if (!PQgetisnull(res, i, i_proacl))
6617 272 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6618 : }
6619 :
6620 304 : PQclear(res);
6621 :
6622 304 : destroyPQExpBuffer(query);
6623 :
6624 304 : return finfo;
6625 : }
6626 :
6627 : /*
6628 : * getTables
6629 : * read all the tables (no indexes) in the system catalogs,
6630 : * and return them as an array of TableInfo structures
6631 : *
6632 : * *numTables is set to the number of tables read in
6633 : */
6634 : TableInfo *
6635 306 : getTables(Archive *fout, int *numTables)
6636 : {
6637 306 : DumpOptions *dopt = fout->dopt;
6638 : PGresult *res;
6639 : int ntups;
6640 : int i;
6641 306 : PQExpBuffer query = createPQExpBuffer();
6642 : TableInfo *tblinfo;
6643 : int i_reltableoid;
6644 : int i_reloid;
6645 : int i_relname;
6646 : int i_relnamespace;
6647 : int i_relkind;
6648 : int i_reltype;
6649 : int i_relowner;
6650 : int i_relchecks;
6651 : int i_relhasindex;
6652 : int i_relhasrules;
6653 : int i_relpages;
6654 : int i_toastpages;
6655 : int i_owning_tab;
6656 : int i_owning_col;
6657 : int i_reltablespace;
6658 : int i_relhasoids;
6659 : int i_relhastriggers;
6660 : int i_relpersistence;
6661 : int i_relispopulated;
6662 : int i_relreplident;
6663 : int i_relrowsec;
6664 : int i_relforcerowsec;
6665 : int i_relfrozenxid;
6666 : int i_toastfrozenxid;
6667 : int i_toastoid;
6668 : int i_relminmxid;
6669 : int i_toastminmxid;
6670 : int i_reloptions;
6671 : int i_checkoption;
6672 : int i_toastreloptions;
6673 : int i_reloftype;
6674 : int i_foreignserver;
6675 : int i_amname;
6676 : int i_is_identity_sequence;
6677 : int i_relacl;
6678 : int i_acldefault;
6679 : int i_ispartition;
6680 :
6681 : /*
6682 : * Find all the tables and table-like objects.
6683 : *
6684 : * We must fetch all tables in this phase because otherwise we cannot
6685 : * correctly identify inherited columns, owned sequences, etc.
6686 : *
6687 : * We include system catalogs, so that we can work if a user table is
6688 : * defined to inherit from a system catalog (pretty weird, but...)
6689 : *
6690 : * Note: in this phase we should collect only a minimal amount of
6691 : * information about each table, basically just enough to decide if it is
6692 : * interesting. In particular, since we do not yet have lock on any user
6693 : * table, we MUST NOT invoke any server-side data collection functions
6694 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
6695 : * wrong answers if any concurrent DDL is happening.
6696 : */
6697 :
6698 306 : appendPQExpBufferStr(query,
6699 : "SELECT c.tableoid, c.oid, c.relname, "
6700 : "c.relnamespace, c.relkind, c.reltype, "
6701 : "c.relowner, "
6702 : "c.relchecks, "
6703 : "c.relhasindex, c.relhasrules, c.relpages, "
6704 : "c.relhastriggers, "
6705 : "c.relpersistence, "
6706 : "c.reloftype, "
6707 : "c.relacl, "
6708 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6709 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
6710 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
6711 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6712 : "ELSE 0 END AS foreignserver, "
6713 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
6714 : "tc.oid AS toid, "
6715 : "tc.relpages AS toastpages, "
6716 : "tc.reloptions AS toast_reloptions, "
6717 : "d.refobjid AS owning_tab, "
6718 : "d.refobjsubid AS owning_col, "
6719 : "tsp.spcname AS reltablespace, ");
6720 :
6721 306 : if (fout->remoteVersion >= 120000)
6722 306 : appendPQExpBufferStr(query,
6723 : "false AS relhasoids, ");
6724 : else
6725 0 : appendPQExpBufferStr(query,
6726 : "c.relhasoids, ");
6727 :
6728 306 : if (fout->remoteVersion >= 90300)
6729 306 : appendPQExpBufferStr(query,
6730 : "c.relispopulated, ");
6731 : else
6732 0 : appendPQExpBufferStr(query,
6733 : "'t' as relispopulated, ");
6734 :
6735 306 : if (fout->remoteVersion >= 90400)
6736 306 : appendPQExpBufferStr(query,
6737 : "c.relreplident, ");
6738 : else
6739 0 : appendPQExpBufferStr(query,
6740 : "'d' AS relreplident, ");
6741 :
6742 306 : if (fout->remoteVersion >= 90500)
6743 306 : appendPQExpBufferStr(query,
6744 : "c.relrowsecurity, c.relforcerowsecurity, ");
6745 : else
6746 0 : appendPQExpBufferStr(query,
6747 : "false AS relrowsecurity, "
6748 : "false AS relforcerowsecurity, ");
6749 :
6750 306 : if (fout->remoteVersion >= 90300)
6751 306 : appendPQExpBufferStr(query,
6752 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
6753 : else
6754 0 : appendPQExpBufferStr(query,
6755 : "0 AS relminmxid, 0 AS tminmxid, ");
6756 :
6757 306 : if (fout->remoteVersion >= 90300)
6758 306 : appendPQExpBufferStr(query,
6759 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6760 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6761 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
6762 : else
6763 0 : appendPQExpBufferStr(query,
6764 : "c.reloptions, NULL AS checkoption, ");
6765 :
6766 306 : if (fout->remoteVersion >= 90600)
6767 306 : appendPQExpBufferStr(query,
6768 : "am.amname, ");
6769 : else
6770 0 : appendPQExpBufferStr(query,
6771 : "NULL AS amname, ");
6772 :
6773 306 : if (fout->remoteVersion >= 90600)
6774 306 : appendPQExpBufferStr(query,
6775 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
6776 : else
6777 0 : appendPQExpBufferStr(query,
6778 : "false AS is_identity_sequence, ");
6779 :
6780 306 : if (fout->remoteVersion >= 100000)
6781 306 : appendPQExpBufferStr(query,
6782 : "c.relispartition AS ispartition ");
6783 : else
6784 0 : appendPQExpBufferStr(query,
6785 : "false AS ispartition ");
6786 :
6787 : /*
6788 : * Left join to pg_depend to pick up dependency info linking sequences to
6789 : * their owning column, if any (note this dependency is AUTO except for
6790 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
6791 : * collect the spcname.
6792 : */
6793 306 : appendPQExpBufferStr(query,
6794 : "\nFROM pg_class c\n"
6795 : "LEFT JOIN pg_depend d ON "
6796 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
6797 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
6798 : "d.objsubid = 0 AND "
6799 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
6800 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
6801 :
6802 : /*
6803 : * In 9.6 and up, left join to pg_am to pick up the amname.
6804 : */
6805 306 : if (fout->remoteVersion >= 90600)
6806 306 : appendPQExpBufferStr(query,
6807 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
6808 :
6809 : /*
6810 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
6811 : * that versions 10 and 11 have them, but later versions do not, so
6812 : * emitting them causes the upgrade to fail.
6813 : */
6814 306 : appendPQExpBufferStr(query,
6815 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
6816 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
6817 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
6818 :
6819 : /*
6820 : * Restrict to interesting relkinds (in particular, not indexes). Not all
6821 : * relkinds are possible in older servers, but it's not worth the trouble
6822 : * to emit a version-dependent list.
6823 : *
6824 : * Composite-type table entries won't be dumped as such, but we have to
6825 : * make a DumpableObject for them so that we can track dependencies of the
6826 : * composite type (pg_depend entries for columns of the composite type
6827 : * link to the pg_class entry not the pg_type entry).
6828 : */
6829 306 : appendPQExpBufferStr(query,
6830 : "WHERE c.relkind IN ("
6831 : CppAsString2(RELKIND_RELATION) ", "
6832 : CppAsString2(RELKIND_SEQUENCE) ", "
6833 : CppAsString2(RELKIND_VIEW) ", "
6834 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
6835 : CppAsString2(RELKIND_MATVIEW) ", "
6836 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
6837 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
6838 : "ORDER BY c.oid");
6839 :
6840 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6841 :
6842 306 : ntups = PQntuples(res);
6843 :
6844 306 : *numTables = ntups;
6845 :
6846 : /*
6847 : * Extract data from result and lock dumpable tables. We do the locking
6848 : * before anything else, to minimize the window wherein a table could
6849 : * disappear under us.
6850 : *
6851 : * Note that we have to save info about all tables here, even when dumping
6852 : * only one, because we don't yet know which tables might be inheritance
6853 : * ancestors of the target table.
6854 : */
6855 306 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
6856 :
6857 306 : i_reltableoid = PQfnumber(res, "tableoid");
6858 306 : i_reloid = PQfnumber(res, "oid");
6859 306 : i_relname = PQfnumber(res, "relname");
6860 306 : i_relnamespace = PQfnumber(res, "relnamespace");
6861 306 : i_relkind = PQfnumber(res, "relkind");
6862 306 : i_reltype = PQfnumber(res, "reltype");
6863 306 : i_relowner = PQfnumber(res, "relowner");
6864 306 : i_relchecks = PQfnumber(res, "relchecks");
6865 306 : i_relhasindex = PQfnumber(res, "relhasindex");
6866 306 : i_relhasrules = PQfnumber(res, "relhasrules");
6867 306 : i_relpages = PQfnumber(res, "relpages");
6868 306 : i_toastpages = PQfnumber(res, "toastpages");
6869 306 : i_owning_tab = PQfnumber(res, "owning_tab");
6870 306 : i_owning_col = PQfnumber(res, "owning_col");
6871 306 : i_reltablespace = PQfnumber(res, "reltablespace");
6872 306 : i_relhasoids = PQfnumber(res, "relhasoids");
6873 306 : i_relhastriggers = PQfnumber(res, "relhastriggers");
6874 306 : i_relpersistence = PQfnumber(res, "relpersistence");
6875 306 : i_relispopulated = PQfnumber(res, "relispopulated");
6876 306 : i_relreplident = PQfnumber(res, "relreplident");
6877 306 : i_relrowsec = PQfnumber(res, "relrowsecurity");
6878 306 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
6879 306 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
6880 306 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
6881 306 : i_toastoid = PQfnumber(res, "toid");
6882 306 : i_relminmxid = PQfnumber(res, "relminmxid");
6883 306 : i_toastminmxid = PQfnumber(res, "tminmxid");
6884 306 : i_reloptions = PQfnumber(res, "reloptions");
6885 306 : i_checkoption = PQfnumber(res, "checkoption");
6886 306 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
6887 306 : i_reloftype = PQfnumber(res, "reloftype");
6888 306 : i_foreignserver = PQfnumber(res, "foreignserver");
6889 306 : i_amname = PQfnumber(res, "amname");
6890 306 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
6891 306 : i_relacl = PQfnumber(res, "relacl");
6892 306 : i_acldefault = PQfnumber(res, "acldefault");
6893 306 : i_ispartition = PQfnumber(res, "ispartition");
6894 :
6895 306 : if (dopt->lockWaitTimeout)
6896 : {
6897 : /*
6898 : * Arrange to fail instead of waiting forever for a table lock.
6899 : *
6900 : * NB: this coding assumes that the only queries issued within the
6901 : * following loop are LOCK TABLEs; else the timeout may be undesirably
6902 : * applied to other things too.
6903 : */
6904 4 : resetPQExpBuffer(query);
6905 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
6906 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
6907 4 : ExecuteSqlStatement(fout, query->data);
6908 : }
6909 :
6910 306 : resetPQExpBuffer(query);
6911 :
6912 77798 : for (i = 0; i < ntups; i++)
6913 : {
6914 77492 : tblinfo[i].dobj.objType = DO_TABLE;
6915 77492 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
6916 77492 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
6917 77492 : AssignDumpId(&tblinfo[i].dobj);
6918 77492 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
6919 154984 : tblinfo[i].dobj.namespace =
6920 77492 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
6921 77492 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
6922 77492 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6923 77492 : tblinfo[i].dacl.privtype = 0;
6924 77492 : tblinfo[i].dacl.initprivs = NULL;
6925 77492 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
6926 77492 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
6927 77492 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
6928 77492 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
6929 77492 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
6930 77492 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
6931 77492 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
6932 77492 : if (PQgetisnull(res, i, i_toastpages))
6933 61490 : tblinfo[i].toastpages = 0;
6934 : else
6935 16002 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
6936 77492 : if (PQgetisnull(res, i, i_owning_tab))
6937 : {
6938 76848 : tblinfo[i].owning_tab = InvalidOid;
6939 76848 : tblinfo[i].owning_col = 0;
6940 : }
6941 : else
6942 : {
6943 644 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
6944 644 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
6945 : }
6946 77492 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
6947 77492 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
6948 77492 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
6949 77492 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
6950 77492 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
6951 77492 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
6952 77492 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
6953 77492 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
6954 77492 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
6955 77492 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
6956 77492 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
6957 77492 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
6958 77492 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
6959 77492 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
6960 77492 : if (PQgetisnull(res, i, i_checkoption))
6961 77404 : tblinfo[i].checkoption = NULL;
6962 : else
6963 88 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
6964 77492 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
6965 77492 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
6966 77492 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
6967 77492 : if (PQgetisnull(res, i, i_amname))
6968 46712 : tblinfo[i].amname = NULL;
6969 : else
6970 30780 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
6971 77492 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
6972 77492 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
6973 :
6974 : /* other fields were zeroed above */
6975 :
6976 : /*
6977 : * Decide whether we want to dump this table.
6978 : */
6979 77492 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
6980 298 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
6981 : else
6982 77194 : selectDumpableTable(&tblinfo[i], fout);
6983 :
6984 : /*
6985 : * Now, consider the table "interesting" if we need to dump its
6986 : * definition or its data. Later on, we'll skip a lot of data
6987 : * collection for uninteresting tables.
6988 : *
6989 : * Note: the "interesting" flag will also be set by flagInhTables for
6990 : * parents of interesting tables, so that we collect necessary
6991 : * inheritance info even when the parents are not themselves being
6992 : * dumped. This is the main reason why we need an "interesting" flag
6993 : * that's separate from the components-to-dump bitmask.
6994 : */
6995 77492 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
6996 : (DUMP_COMPONENT_DEFINITION |
6997 77492 : DUMP_COMPONENT_DATA)) != 0;
6998 :
6999 77492 : tblinfo[i].dummy_view = false; /* might get set during sort */
7000 77492 : tblinfo[i].postponed_def = false; /* might get set during sort */
7001 :
7002 : /* Tables have data */
7003 77492 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7004 :
7005 : /* Mark whether table has an ACL */
7006 77492 : if (!PQgetisnull(res, i, i_relacl))
7007 63496 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7008 77492 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7009 :
7010 : /*
7011 : * Read-lock target tables to make sure they aren't DROPPED or altered
7012 : * in schema before we get around to dumping them.
7013 : *
7014 : * Note that we don't explicitly lock parents of the target tables; we
7015 : * assume our lock on the child is enough to prevent schema
7016 : * alterations to parent tables.
7017 : *
7018 : * NOTE: it'd be kinda nice to lock other relations too, not only
7019 : * plain or partitioned tables, but the backend doesn't presently
7020 : * allow that.
7021 : *
7022 : * We only need to lock the table for certain components; see
7023 : * pg_dump.h
7024 : */
7025 77492 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7026 11680 : (tblinfo[i].relkind == RELKIND_RELATION ||
7027 3294 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7028 : {
7029 : /*
7030 : * Tables are locked in batches. When dumping from a remote
7031 : * server this can save a significant amount of time by reducing
7032 : * the number of round trips.
7033 : */
7034 9416 : if (query->len == 0)
7035 194 : appendPQExpBuffer(query, "LOCK TABLE %s",
7036 194 : fmtQualifiedDumpable(&tblinfo[i]));
7037 : else
7038 : {
7039 9222 : appendPQExpBuffer(query, ", %s",
7040 9222 : fmtQualifiedDumpable(&tblinfo[i]));
7041 :
7042 : /* Arbitrarily end a batch when query length reaches 100K. */
7043 9222 : if (query->len >= 100000)
7044 : {
7045 : /* Lock another batch of tables. */
7046 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7047 0 : ExecuteSqlStatement(fout, query->data);
7048 0 : resetPQExpBuffer(query);
7049 : }
7050 : }
7051 : }
7052 : }
7053 :
7054 306 : if (query->len != 0)
7055 : {
7056 : /* Lock the tables in the last batch. */
7057 194 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7058 194 : ExecuteSqlStatement(fout, query->data);
7059 : }
7060 :
7061 304 : if (dopt->lockWaitTimeout)
7062 : {
7063 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7064 : }
7065 :
7066 304 : PQclear(res);
7067 :
7068 304 : destroyPQExpBuffer(query);
7069 :
7070 304 : return tblinfo;
7071 : }
7072 :
7073 : /*
7074 : * getOwnedSeqs
7075 : * identify owned sequences and mark them as dumpable if owning table is
7076 : *
7077 : * We used to do this in getTables(), but it's better to do it after the
7078 : * index used by findTableByOid() has been set up.
7079 : */
7080 : void
7081 304 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7082 : {
7083 : int i;
7084 :
7085 : /*
7086 : * Force sequences that are "owned" by table columns to be dumped whenever
7087 : * their owning table is being dumped.
7088 : */
7089 77278 : for (i = 0; i < numTables; i++)
7090 : {
7091 76974 : TableInfo *seqinfo = &tblinfo[i];
7092 : TableInfo *owning_tab;
7093 :
7094 76974 : if (!OidIsValid(seqinfo->owning_tab))
7095 76336 : continue; /* not an owned sequence */
7096 :
7097 638 : owning_tab = findTableByOid(seqinfo->owning_tab);
7098 638 : if (owning_tab == NULL)
7099 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7100 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7101 :
7102 : /*
7103 : * Only dump identity sequences if we're going to dump the table that
7104 : * it belongs to.
7105 : */
7106 638 : if (owning_tab->dobj.dump == DUMP_COMPONENT_NONE &&
7107 52 : seqinfo->is_identity_sequence)
7108 : {
7109 14 : seqinfo->dobj.dump = DUMP_COMPONENT_NONE;
7110 14 : continue;
7111 : }
7112 :
7113 : /*
7114 : * Otherwise we need to dump the components that are being dumped for
7115 : * the table and any components which the sequence is explicitly
7116 : * marked with.
7117 : *
7118 : * We can't simply use the set of components which are being dumped
7119 : * for the table as the table might be in an extension (and only the
7120 : * non-extension components, eg: ACLs if changed, security labels, and
7121 : * policies, are being dumped) while the sequence is not (and
7122 : * therefore the definition and other components should also be
7123 : * dumped).
7124 : *
7125 : * If the sequence is part of the extension then it should be properly
7126 : * marked by checkExtensionMembership() and this will be a no-op as
7127 : * the table will be equivalently marked.
7128 : */
7129 624 : seqinfo->dobj.dump = seqinfo->dobj.dump | owning_tab->dobj.dump;
7130 :
7131 624 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7132 590 : seqinfo->interesting = true;
7133 : }
7134 304 : }
7135 :
7136 : /*
7137 : * getInherits
7138 : * read all the inheritance information
7139 : * from the system catalogs return them in the InhInfo* structure
7140 : *
7141 : * numInherits is set to the number of pairs read in
7142 : */
7143 : InhInfo *
7144 304 : getInherits(Archive *fout, int *numInherits)
7145 : {
7146 : PGresult *res;
7147 : int ntups;
7148 : int i;
7149 304 : PQExpBuffer query = createPQExpBuffer();
7150 : InhInfo *inhinfo;
7151 :
7152 : int i_inhrelid;
7153 : int i_inhparent;
7154 :
7155 : /* find all the inheritance information */
7156 304 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7157 :
7158 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7159 :
7160 304 : ntups = PQntuples(res);
7161 :
7162 304 : *numInherits = ntups;
7163 :
7164 304 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7165 :
7166 304 : i_inhrelid = PQfnumber(res, "inhrelid");
7167 304 : i_inhparent = PQfnumber(res, "inhparent");
7168 :
7169 4894 : for (i = 0; i < ntups; i++)
7170 : {
7171 4590 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7172 4590 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7173 : }
7174 :
7175 304 : PQclear(res);
7176 :
7177 304 : destroyPQExpBuffer(query);
7178 :
7179 304 : return inhinfo;
7180 : }
7181 :
7182 : /*
7183 : * getPartitioningInfo
7184 : * get information about partitioning
7185 : *
7186 : * For the most part, we only collect partitioning info about tables we
7187 : * intend to dump. However, this function has to consider all partitioned
7188 : * tables in the database, because we need to know about parents of partitions
7189 : * we are going to dump even if the parents themselves won't be dumped.
7190 : *
7191 : * Specifically, what we need to know is whether each partitioned table
7192 : * has an "unsafe" partitioning scheme that requires us to force
7193 : * load-via-partition-root mode for its children. Currently the only case
7194 : * for which we force that is hash partitioning on enum columns, since the
7195 : * hash codes depend on enum value OIDs which won't be replicated across
7196 : * dump-and-reload. There are other cases in which load-via-partition-root
7197 : * might be necessary, but we expect users to cope with them.
7198 : */
7199 : void
7200 304 : getPartitioningInfo(Archive *fout)
7201 : {
7202 : PQExpBuffer query;
7203 : PGresult *res;
7204 : int ntups;
7205 :
7206 : /* hash partitioning didn't exist before v11 */
7207 304 : if (fout->remoteVersion < 110000)
7208 0 : return;
7209 : /* needn't bother if schema-only dump */
7210 304 : if (fout->dopt->schemaOnly)
7211 32 : return;
7212 :
7213 272 : query = createPQExpBuffer();
7214 :
7215 : /*
7216 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7217 : * appears among the partition opclasses. We needn't check partstrat.
7218 : *
7219 : * Note that this query may well retrieve info about tables we aren't
7220 : * going to dump and hence have no lock on. That's okay since we need not
7221 : * invoke any unsafe server-side functions.
7222 : */
7223 272 : appendPQExpBufferStr(query,
7224 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7225 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7226 : "ON c.opcmethod = a.oid\n"
7227 : "WHERE opcname = 'enum_ops' "
7228 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7229 : "AND amname = 'hash') = ANY(partclass)");
7230 :
7231 272 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7232 :
7233 272 : ntups = PQntuples(res);
7234 :
7235 276 : for (int i = 0; i < ntups; i++)
7236 : {
7237 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7238 : TableInfo *tbinfo;
7239 :
7240 4 : tbinfo = findTableByOid(tabrelid);
7241 4 : if (tbinfo == NULL)
7242 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7243 : tabrelid);
7244 4 : tbinfo->unsafe_partitions = true;
7245 : }
7246 :
7247 272 : PQclear(res);
7248 :
7249 272 : destroyPQExpBuffer(query);
7250 : }
7251 :
7252 : /*
7253 : * getIndexes
7254 : * get information about every index on a dumpable table
7255 : *
7256 : * Note: index data is not returned directly to the caller, but it
7257 : * does get entered into the DumpableObject tables.
7258 : */
7259 : void
7260 304 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7261 : {
7262 304 : PQExpBuffer query = createPQExpBuffer();
7263 304 : PQExpBuffer tbloids = createPQExpBuffer();
7264 : PGresult *res;
7265 : int ntups;
7266 : int curtblindx;
7267 : IndxInfo *indxinfo;
7268 : int i_tableoid,
7269 : i_oid,
7270 : i_indrelid,
7271 : i_indexname,
7272 : i_parentidx,
7273 : i_indexdef,
7274 : i_indnkeyatts,
7275 : i_indnatts,
7276 : i_indkey,
7277 : i_indisclustered,
7278 : i_indisreplident,
7279 : i_indnullsnotdistinct,
7280 : i_contype,
7281 : i_conname,
7282 : i_condeferrable,
7283 : i_condeferred,
7284 : i_conperiod,
7285 : i_contableoid,
7286 : i_conoid,
7287 : i_condef,
7288 : i_tablespace,
7289 : i_indreloptions,
7290 : i_indstatcols,
7291 : i_indstatvals;
7292 :
7293 : /*
7294 : * We want to perform just one query against pg_index. However, we
7295 : * mustn't try to select every row of the catalog and then sort it out on
7296 : * the client side, because some of the server-side functions we need
7297 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7298 : * build an array of the OIDs of tables we care about (and now have lock
7299 : * on!), and use a WHERE clause to constrain which rows are selected.
7300 : */
7301 304 : appendPQExpBufferChar(tbloids, '{');
7302 77278 : for (int i = 0; i < numTables; i++)
7303 : {
7304 76974 : TableInfo *tbinfo = &tblinfo[i];
7305 :
7306 76974 : if (!tbinfo->hasindex)
7307 54092 : continue;
7308 :
7309 : /*
7310 : * We can ignore indexes of uninteresting tables.
7311 : */
7312 22882 : if (!tbinfo->interesting)
7313 19500 : continue;
7314 :
7315 : /* OK, we need info for this table */
7316 3382 : if (tbloids->len > 1) /* do we have more than the '{'? */
7317 3234 : appendPQExpBufferChar(tbloids, ',');
7318 3382 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7319 : }
7320 304 : appendPQExpBufferChar(tbloids, '}');
7321 :
7322 304 : appendPQExpBufferStr(query,
7323 : "SELECT t.tableoid, t.oid, i.indrelid, "
7324 : "t.relname AS indexname, "
7325 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7326 : "i.indkey, i.indisclustered, "
7327 : "c.contype, c.conname, "
7328 : "c.condeferrable, c.condeferred, "
7329 : "c.tableoid AS contableoid, "
7330 : "c.oid AS conoid, "
7331 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7332 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7333 : "t.reloptions AS indreloptions, ");
7334 :
7335 :
7336 304 : if (fout->remoteVersion >= 90400)
7337 304 : appendPQExpBufferStr(query,
7338 : "i.indisreplident, ");
7339 : else
7340 0 : appendPQExpBufferStr(query,
7341 : "false AS indisreplident, ");
7342 :
7343 304 : if (fout->remoteVersion >= 110000)
7344 304 : appendPQExpBufferStr(query,
7345 : "inh.inhparent AS parentidx, "
7346 : "i.indnkeyatts AS indnkeyatts, "
7347 : "i.indnatts AS indnatts, "
7348 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7349 : " FROM pg_catalog.pg_attribute "
7350 : " WHERE attrelid = i.indexrelid AND "
7351 : " attstattarget >= 0) AS indstatcols, "
7352 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7353 : " FROM pg_catalog.pg_attribute "
7354 : " WHERE attrelid = i.indexrelid AND "
7355 : " attstattarget >= 0) AS indstatvals, ");
7356 : else
7357 0 : appendPQExpBufferStr(query,
7358 : "0 AS parentidx, "
7359 : "i.indnatts AS indnkeyatts, "
7360 : "i.indnatts AS indnatts, "
7361 : "'' AS indstatcols, "
7362 : "'' AS indstatvals, ");
7363 :
7364 304 : if (fout->remoteVersion >= 150000)
7365 304 : appendPQExpBufferStr(query,
7366 : "i.indnullsnotdistinct, ");
7367 : else
7368 0 : appendPQExpBufferStr(query,
7369 : "false AS indnullsnotdistinct, ");
7370 :
7371 304 : if (fout->remoteVersion >= 170000)
7372 304 : appendPQExpBufferStr(query,
7373 : "c.conperiod ");
7374 : else
7375 0 : appendPQExpBufferStr(query,
7376 : "NULL AS conperiod ");
7377 :
7378 : /*
7379 : * The point of the messy-looking outer join is to find a constraint that
7380 : * is related by an internal dependency link to the index. If we find one,
7381 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7382 : * index won't have more than one internal dependency.
7383 : *
7384 : * Note: the check on conrelid is redundant, but useful because that
7385 : * column is indexed while conindid is not.
7386 : */
7387 304 : if (fout->remoteVersion >= 110000)
7388 : {
7389 304 : appendPQExpBuffer(query,
7390 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7391 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7392 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7393 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7394 : "LEFT JOIN pg_catalog.pg_constraint c "
7395 : "ON (i.indrelid = c.conrelid AND "
7396 : "i.indexrelid = c.conindid AND "
7397 : "c.contype IN ('p','u','x')) "
7398 : "LEFT JOIN pg_catalog.pg_inherits inh "
7399 : "ON (inh.inhrelid = indexrelid) "
7400 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7401 : "AND i.indisready "
7402 : "ORDER BY i.indrelid, indexname",
7403 : tbloids->data);
7404 : }
7405 : else
7406 : {
7407 : /*
7408 : * the test on indisready is necessary in 9.2, and harmless in
7409 : * earlier/later versions
7410 : */
7411 0 : appendPQExpBuffer(query,
7412 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7413 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7414 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7415 : "LEFT JOIN pg_catalog.pg_constraint c "
7416 : "ON (i.indrelid = c.conrelid AND "
7417 : "i.indexrelid = c.conindid AND "
7418 : "c.contype IN ('p','u','x')) "
7419 : "WHERE i.indisvalid AND i.indisready "
7420 : "ORDER BY i.indrelid, indexname",
7421 : tbloids->data);
7422 : }
7423 :
7424 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7425 :
7426 304 : ntups = PQntuples(res);
7427 :
7428 304 : i_tableoid = PQfnumber(res, "tableoid");
7429 304 : i_oid = PQfnumber(res, "oid");
7430 304 : i_indrelid = PQfnumber(res, "indrelid");
7431 304 : i_indexname = PQfnumber(res, "indexname");
7432 304 : i_parentidx = PQfnumber(res, "parentidx");
7433 304 : i_indexdef = PQfnumber(res, "indexdef");
7434 304 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7435 304 : i_indnatts = PQfnumber(res, "indnatts");
7436 304 : i_indkey = PQfnumber(res, "indkey");
7437 304 : i_indisclustered = PQfnumber(res, "indisclustered");
7438 304 : i_indisreplident = PQfnumber(res, "indisreplident");
7439 304 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7440 304 : i_contype = PQfnumber(res, "contype");
7441 304 : i_conname = PQfnumber(res, "conname");
7442 304 : i_condeferrable = PQfnumber(res, "condeferrable");
7443 304 : i_condeferred = PQfnumber(res, "condeferred");
7444 304 : i_conperiod = PQfnumber(res, "conperiod");
7445 304 : i_contableoid = PQfnumber(res, "contableoid");
7446 304 : i_conoid = PQfnumber(res, "conoid");
7447 304 : i_condef = PQfnumber(res, "condef");
7448 304 : i_tablespace = PQfnumber(res, "tablespace");
7449 304 : i_indreloptions = PQfnumber(res, "indreloptions");
7450 304 : i_indstatcols = PQfnumber(res, "indstatcols");
7451 304 : i_indstatvals = PQfnumber(res, "indstatvals");
7452 :
7453 304 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7454 :
7455 : /*
7456 : * Outer loop iterates once per table, not once per row. Incrementing of
7457 : * j is handled by the inner loop.
7458 : */
7459 304 : curtblindx = -1;
7460 3678 : for (int j = 0; j < ntups;)
7461 : {
7462 3374 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7463 3374 : TableInfo *tbinfo = NULL;
7464 : int numinds;
7465 :
7466 : /* Count rows for this table */
7467 4336 : for (numinds = 1; numinds < ntups - j; numinds++)
7468 4188 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7469 3226 : break;
7470 :
7471 : /*
7472 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7473 : * order.
7474 : */
7475 42864 : while (++curtblindx < numTables)
7476 : {
7477 42864 : tbinfo = &tblinfo[curtblindx];
7478 42864 : if (tbinfo->dobj.catId.oid == indrelid)
7479 3374 : break;
7480 : }
7481 3374 : if (curtblindx >= numTables)
7482 0 : pg_fatal("unrecognized table OID %u", indrelid);
7483 : /* cross-check that we only got requested tables */
7484 3374 : if (!tbinfo->hasindex ||
7485 3374 : !tbinfo->interesting)
7486 0 : pg_fatal("unexpected index data for table \"%s\"",
7487 : tbinfo->dobj.name);
7488 :
7489 : /* Save data for this table */
7490 3374 : tbinfo->indexes = indxinfo + j;
7491 3374 : tbinfo->numIndexes = numinds;
7492 :
7493 7710 : for (int c = 0; c < numinds; c++, j++)
7494 : {
7495 : char contype;
7496 :
7497 4336 : indxinfo[j].dobj.objType = DO_INDEX;
7498 4336 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7499 4336 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7500 4336 : AssignDumpId(&indxinfo[j].dobj);
7501 4336 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7502 4336 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7503 4336 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7504 4336 : indxinfo[j].indextable = tbinfo;
7505 4336 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7506 4336 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7507 4336 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7508 4336 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7509 4336 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7510 4336 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7511 4336 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7512 4336 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7513 4336 : parseOidArray(PQgetvalue(res, j, i_indkey),
7514 4336 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7515 4336 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7516 4336 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7517 4336 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7518 4336 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7519 4336 : indxinfo[j].partattaches = (SimplePtrList)
7520 : {
7521 : NULL, NULL
7522 : };
7523 4336 : contype = *(PQgetvalue(res, j, i_contype));
7524 :
7525 4336 : if (contype == 'p' || contype == 'u' || contype == 'x')
7526 2350 : {
7527 : /*
7528 : * If we found a constraint matching the index, create an
7529 : * entry for it.
7530 : */
7531 : ConstraintInfo *constrinfo;
7532 :
7533 2350 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7534 2350 : constrinfo->dobj.objType = DO_CONSTRAINT;
7535 2350 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7536 2350 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7537 2350 : AssignDumpId(&constrinfo->dobj);
7538 2350 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7539 2350 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7540 2350 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7541 2350 : constrinfo->contable = tbinfo;
7542 2350 : constrinfo->condomain = NULL;
7543 2350 : constrinfo->contype = contype;
7544 2350 : if (contype == 'x')
7545 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7546 : else
7547 2330 : constrinfo->condef = NULL;
7548 2350 : constrinfo->confrelid = InvalidOid;
7549 2350 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7550 2350 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7551 2350 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7552 2350 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7553 2350 : constrinfo->conislocal = true;
7554 2350 : constrinfo->separate = true;
7555 :
7556 2350 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7557 : }
7558 : else
7559 : {
7560 : /* Plain secondary index */
7561 1986 : indxinfo[j].indexconstraint = 0;
7562 : }
7563 : }
7564 : }
7565 :
7566 304 : PQclear(res);
7567 :
7568 304 : destroyPQExpBuffer(query);
7569 304 : destroyPQExpBuffer(tbloids);
7570 304 : }
7571 :
7572 : /*
7573 : * getExtendedStatistics
7574 : * get information about extended-statistics objects.
7575 : *
7576 : * Note: extended statistics data is not returned directly to the caller, but
7577 : * it does get entered into the DumpableObject tables.
7578 : */
7579 : void
7580 304 : getExtendedStatistics(Archive *fout)
7581 : {
7582 : PQExpBuffer query;
7583 : PGresult *res;
7584 : StatsExtInfo *statsextinfo;
7585 : int ntups;
7586 : int i_tableoid;
7587 : int i_oid;
7588 : int i_stxname;
7589 : int i_stxnamespace;
7590 : int i_stxowner;
7591 : int i_stxrelid;
7592 : int i_stattarget;
7593 : int i;
7594 :
7595 : /* Extended statistics were new in v10 */
7596 304 : if (fout->remoteVersion < 100000)
7597 0 : return;
7598 :
7599 304 : query = createPQExpBuffer();
7600 :
7601 304 : if (fout->remoteVersion < 130000)
7602 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7603 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
7604 : "FROM pg_catalog.pg_statistic_ext");
7605 : else
7606 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7607 : "stxnamespace, stxowner, stxrelid, stxstattarget "
7608 : "FROM pg_catalog.pg_statistic_ext");
7609 :
7610 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7611 :
7612 304 : ntups = PQntuples(res);
7613 :
7614 304 : i_tableoid = PQfnumber(res, "tableoid");
7615 304 : i_oid = PQfnumber(res, "oid");
7616 304 : i_stxname = PQfnumber(res, "stxname");
7617 304 : i_stxnamespace = PQfnumber(res, "stxnamespace");
7618 304 : i_stxowner = PQfnumber(res, "stxowner");
7619 304 : i_stxrelid = PQfnumber(res, "stxrelid");
7620 304 : i_stattarget = PQfnumber(res, "stxstattarget");
7621 :
7622 304 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7623 :
7624 602 : for (i = 0; i < ntups; i++)
7625 : {
7626 298 : statsextinfo[i].dobj.objType = DO_STATSEXT;
7627 298 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7628 298 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7629 298 : AssignDumpId(&statsextinfo[i].dobj);
7630 298 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7631 596 : statsextinfo[i].dobj.namespace =
7632 298 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
7633 298 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
7634 596 : statsextinfo[i].stattable =
7635 298 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
7636 298 : if (PQgetisnull(res, i, i_stattarget))
7637 212 : statsextinfo[i].stattarget = -1;
7638 : else
7639 86 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
7640 :
7641 : /* Decide whether we want to dump it */
7642 298 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
7643 : }
7644 :
7645 304 : PQclear(res);
7646 304 : destroyPQExpBuffer(query);
7647 : }
7648 :
7649 : /*
7650 : * getConstraints
7651 : *
7652 : * Get info about constraints on dumpable tables.
7653 : *
7654 : * Currently handles foreign keys only.
7655 : * Unique and primary key constraints are handled with indexes,
7656 : * while check constraints are processed in getTableAttrs().
7657 : */
7658 : void
7659 304 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7660 : {
7661 304 : PQExpBuffer query = createPQExpBuffer();
7662 304 : PQExpBuffer tbloids = createPQExpBuffer();
7663 : PGresult *res;
7664 : int ntups;
7665 : int curtblindx;
7666 304 : TableInfo *tbinfo = NULL;
7667 : ConstraintInfo *constrinfo;
7668 : int i_contableoid,
7669 : i_conoid,
7670 : i_conrelid,
7671 : i_conname,
7672 : i_confrelid,
7673 : i_conindid,
7674 : i_condef;
7675 :
7676 : /*
7677 : * We want to perform just one query against pg_constraint. However, we
7678 : * mustn't try to select every row of the catalog and then sort it out on
7679 : * the client side, because some of the server-side functions we need
7680 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7681 : * build an array of the OIDs of tables we care about (and now have lock
7682 : * on!), and use a WHERE clause to constrain which rows are selected.
7683 : */
7684 304 : appendPQExpBufferChar(tbloids, '{');
7685 77278 : for (int i = 0; i < numTables; i++)
7686 : {
7687 76974 : TableInfo *tinfo = &tblinfo[i];
7688 :
7689 : /*
7690 : * For partitioned tables, foreign keys have no triggers so they must
7691 : * be included anyway in case some foreign keys are defined.
7692 : */
7693 76974 : if ((!tinfo->hastriggers &&
7694 75196 : tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7695 2456 : !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7696 74648 : continue;
7697 :
7698 : /* OK, we need info for this table */
7699 2326 : if (tbloids->len > 1) /* do we have more than the '{'? */
7700 2224 : appendPQExpBufferChar(tbloids, ',');
7701 2326 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
7702 : }
7703 304 : appendPQExpBufferChar(tbloids, '}');
7704 :
7705 304 : appendPQExpBufferStr(query,
7706 : "SELECT c.tableoid, c.oid, "
7707 : "conrelid, conname, confrelid, ");
7708 304 : if (fout->remoteVersion >= 110000)
7709 304 : appendPQExpBufferStr(query, "conindid, ");
7710 : else
7711 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
7712 304 : appendPQExpBuffer(query,
7713 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
7714 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7715 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
7716 : "WHERE contype = 'f' ",
7717 : tbloids->data);
7718 304 : if (fout->remoteVersion >= 110000)
7719 304 : appendPQExpBufferStr(query,
7720 : "AND conparentid = 0 ");
7721 304 : appendPQExpBufferStr(query,
7722 : "ORDER BY conrelid, conname");
7723 :
7724 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7725 :
7726 304 : ntups = PQntuples(res);
7727 :
7728 304 : i_contableoid = PQfnumber(res, "tableoid");
7729 304 : i_conoid = PQfnumber(res, "oid");
7730 304 : i_conrelid = PQfnumber(res, "conrelid");
7731 304 : i_conname = PQfnumber(res, "conname");
7732 304 : i_confrelid = PQfnumber(res, "confrelid");
7733 304 : i_conindid = PQfnumber(res, "conindid");
7734 304 : i_condef = PQfnumber(res, "condef");
7735 :
7736 304 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7737 :
7738 304 : curtblindx = -1;
7739 648 : for (int j = 0; j < ntups; j++)
7740 : {
7741 344 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
7742 : TableInfo *reftable;
7743 :
7744 : /*
7745 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7746 : * order.
7747 : */
7748 344 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
7749 : {
7750 25076 : while (++curtblindx < numTables)
7751 : {
7752 25076 : tbinfo = &tblinfo[curtblindx];
7753 25076 : if (tbinfo->dobj.catId.oid == conrelid)
7754 324 : break;
7755 : }
7756 324 : if (curtblindx >= numTables)
7757 0 : pg_fatal("unrecognized table OID %u", conrelid);
7758 : }
7759 :
7760 344 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
7761 344 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7762 344 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7763 344 : AssignDumpId(&constrinfo[j].dobj);
7764 344 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7765 344 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7766 344 : constrinfo[j].contable = tbinfo;
7767 344 : constrinfo[j].condomain = NULL;
7768 344 : constrinfo[j].contype = 'f';
7769 344 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7770 344 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
7771 344 : constrinfo[j].conindex = 0;
7772 344 : constrinfo[j].condeferrable = false;
7773 344 : constrinfo[j].condeferred = false;
7774 344 : constrinfo[j].conislocal = true;
7775 344 : constrinfo[j].separate = true;
7776 :
7777 : /*
7778 : * Restoring an FK that points to a partitioned table requires that
7779 : * all partition indexes have been attached beforehand. Ensure that
7780 : * happens by making the constraint depend on each index partition
7781 : * attach object.
7782 : */
7783 344 : reftable = findTableByOid(constrinfo[j].confrelid);
7784 344 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
7785 : {
7786 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
7787 :
7788 40 : if (indexOid != InvalidOid)
7789 : {
7790 40 : for (int k = 0; k < reftable->numIndexes; k++)
7791 : {
7792 : IndxInfo *refidx;
7793 :
7794 : /* not our index? */
7795 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
7796 0 : continue;
7797 :
7798 40 : refidx = &reftable->indexes[k];
7799 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
7800 40 : break;
7801 : }
7802 : }
7803 : }
7804 : }
7805 :
7806 304 : PQclear(res);
7807 :
7808 304 : destroyPQExpBuffer(query);
7809 304 : destroyPQExpBuffer(tbloids);
7810 304 : }
7811 :
7812 : /*
7813 : * addConstrChildIdxDeps
7814 : *
7815 : * Recursive subroutine for getConstraints
7816 : *
7817 : * Given an object representing a foreign key constraint and an index on the
7818 : * partitioned table it references, mark the constraint object as dependent
7819 : * on the DO_INDEX_ATTACH object of each index partition, recursively
7820 : * drilling down to their partitions if any. This ensures that the FK is not
7821 : * restored until the index is fully marked valid.
7822 : */
7823 : static void
7824 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
7825 : {
7826 : SimplePtrListCell *cell;
7827 :
7828 : Assert(dobj->objType == DO_FK_CONSTRAINT);
7829 :
7830 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
7831 : {
7832 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
7833 :
7834 220 : addObjectDependency(dobj, attach->dobj.dumpId);
7835 :
7836 220 : if (attach->partitionIdx->partattaches.head != NULL)
7837 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
7838 : }
7839 90 : }
7840 :
7841 : /*
7842 : * getDomainConstraints
7843 : *
7844 : * Get info about constraints on a domain.
7845 : */
7846 : static void
7847 262 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
7848 : {
7849 : int i;
7850 : ConstraintInfo *constrinfo;
7851 262 : PQExpBuffer query = createPQExpBuffer();
7852 : PGresult *res;
7853 : int i_tableoid,
7854 : i_oid,
7855 : i_conname,
7856 : i_consrc;
7857 : int ntups;
7858 :
7859 262 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
7860 : {
7861 : /* Set up query for constraint-specific details */
7862 82 : appendPQExpBufferStr(query,
7863 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
7864 : "SELECT tableoid, oid, conname, "
7865 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
7866 : "convalidated "
7867 : "FROM pg_catalog.pg_constraint "
7868 : "WHERE contypid = $1 AND contype = 'c' "
7869 : "ORDER BY conname");
7870 :
7871 82 : ExecuteSqlStatement(fout, query->data);
7872 :
7873 82 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
7874 : }
7875 :
7876 262 : printfPQExpBuffer(query,
7877 : "EXECUTE getDomainConstraints('%u')",
7878 : tyinfo->dobj.catId.oid);
7879 :
7880 262 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7881 :
7882 262 : ntups = PQntuples(res);
7883 :
7884 262 : i_tableoid = PQfnumber(res, "tableoid");
7885 262 : i_oid = PQfnumber(res, "oid");
7886 262 : i_conname = PQfnumber(res, "conname");
7887 262 : i_consrc = PQfnumber(res, "consrc");
7888 :
7889 262 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7890 :
7891 262 : tyinfo->nDomChecks = ntups;
7892 262 : tyinfo->domChecks = constrinfo;
7893 :
7894 434 : for (i = 0; i < ntups; i++)
7895 : {
7896 172 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
7897 :
7898 172 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
7899 172 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7900 172 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7901 172 : AssignDumpId(&constrinfo[i].dobj);
7902 172 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
7903 172 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
7904 172 : constrinfo[i].contable = NULL;
7905 172 : constrinfo[i].condomain = tyinfo;
7906 172 : constrinfo[i].contype = 'c';
7907 172 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
7908 172 : constrinfo[i].confrelid = InvalidOid;
7909 172 : constrinfo[i].conindex = 0;
7910 172 : constrinfo[i].condeferrable = false;
7911 172 : constrinfo[i].condeferred = false;
7912 172 : constrinfo[i].conislocal = true;
7913 :
7914 172 : constrinfo[i].separate = !validated;
7915 :
7916 : /*
7917 : * Make the domain depend on the constraint, ensuring it won't be
7918 : * output till any constraint dependencies are OK. If the constraint
7919 : * has not been validated, it's going to be dumped after the domain
7920 : * anyway, so this doesn't matter.
7921 : */
7922 172 : if (validated)
7923 172 : addObjectDependency(&tyinfo->dobj,
7924 172 : constrinfo[i].dobj.dumpId);
7925 : }
7926 :
7927 262 : PQclear(res);
7928 :
7929 262 : destroyPQExpBuffer(query);
7930 262 : }
7931 :
7932 : /*
7933 : * getRules
7934 : * get basic information about every rule in the system
7935 : *
7936 : * numRules is set to the number of rules read in
7937 : */
7938 : RuleInfo *
7939 304 : getRules(Archive *fout, int *numRules)
7940 : {
7941 : PGresult *res;
7942 : int ntups;
7943 : int i;
7944 304 : PQExpBuffer query = createPQExpBuffer();
7945 : RuleInfo *ruleinfo;
7946 : int i_tableoid;
7947 : int i_oid;
7948 : int i_rulename;
7949 : int i_ruletable;
7950 : int i_ev_type;
7951 : int i_is_instead;
7952 : int i_ev_enabled;
7953 :
7954 304 : appendPQExpBufferStr(query, "SELECT "
7955 : "tableoid, oid, rulename, "
7956 : "ev_class AS ruletable, ev_type, is_instead, "
7957 : "ev_enabled "
7958 : "FROM pg_rewrite "
7959 : "ORDER BY oid");
7960 :
7961 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7962 :
7963 304 : ntups = PQntuples(res);
7964 :
7965 304 : *numRules = ntups;
7966 :
7967 304 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
7968 :
7969 304 : i_tableoid = PQfnumber(res, "tableoid");
7970 304 : i_oid = PQfnumber(res, "oid");
7971 304 : i_rulename = PQfnumber(res, "rulename");
7972 304 : i_ruletable = PQfnumber(res, "ruletable");
7973 304 : i_ev_type = PQfnumber(res, "ev_type");
7974 304 : i_is_instead = PQfnumber(res, "is_instead");
7975 304 : i_ev_enabled = PQfnumber(res, "ev_enabled");
7976 :
7977 46184 : for (i = 0; i < ntups; i++)
7978 : {
7979 : Oid ruletableoid;
7980 :
7981 45880 : ruleinfo[i].dobj.objType = DO_RULE;
7982 45880 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7983 45880 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7984 45880 : AssignDumpId(&ruleinfo[i].dobj);
7985 45880 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
7986 45880 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
7987 45880 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
7988 45880 : if (ruleinfo[i].ruletable == NULL)
7989 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
7990 : ruletableoid, ruleinfo[i].dobj.catId.oid);
7991 45880 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
7992 45880 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
7993 45880 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
7994 45880 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
7995 45880 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
7996 45880 : if (ruleinfo[i].ruletable)
7997 : {
7998 : /*
7999 : * If the table is a view or materialized view, force its ON
8000 : * SELECT rule to be sorted before the view itself --- this
8001 : * ensures that any dependencies for the rule affect the table's
8002 : * positioning. Other rules are forced to appear after their
8003 : * table.
8004 : */
8005 45880 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8006 1184 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8007 45550 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8008 : {
8009 44882 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8010 44882 : ruleinfo[i].dobj.dumpId);
8011 : /* We'll merge the rule into CREATE VIEW, if possible */
8012 44882 : ruleinfo[i].separate = false;
8013 : }
8014 : else
8015 : {
8016 998 : addObjectDependency(&ruleinfo[i].dobj,
8017 998 : ruleinfo[i].ruletable->dobj.dumpId);
8018 998 : ruleinfo[i].separate = true;
8019 : }
8020 : }
8021 : else
8022 0 : ruleinfo[i].separate = true;
8023 : }
8024 :
8025 304 : PQclear(res);
8026 :
8027 304 : destroyPQExpBuffer(query);
8028 :
8029 304 : return ruleinfo;
8030 : }
8031 :
8032 : /*
8033 : * getTriggers
8034 : * get information about every trigger on a dumpable table
8035 : *
8036 : * Note: trigger data is not returned directly to the caller, but it
8037 : * does get entered into the DumpableObject tables.
8038 : */
8039 : void
8040 304 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8041 : {
8042 304 : PQExpBuffer query = createPQExpBuffer();
8043 304 : PQExpBuffer tbloids = createPQExpBuffer();
8044 : PGresult *res;
8045 : int ntups;
8046 : int curtblindx;
8047 : TriggerInfo *tginfo;
8048 : int i_tableoid,
8049 : i_oid,
8050 : i_tgrelid,
8051 : i_tgname,
8052 : i_tgenabled,
8053 : i_tgispartition,
8054 : i_tgdef;
8055 :
8056 : /*
8057 : * We want to perform just one query against pg_trigger. However, we
8058 : * mustn't try to select every row of the catalog and then sort it out on
8059 : * the client side, because some of the server-side functions we need
8060 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8061 : * build an array of the OIDs of tables we care about (and now have lock
8062 : * on!), and use a WHERE clause to constrain which rows are selected.
8063 : */
8064 304 : appendPQExpBufferChar(tbloids, '{');
8065 77278 : for (int i = 0; i < numTables; i++)
8066 : {
8067 76974 : TableInfo *tbinfo = &tblinfo[i];
8068 :
8069 76974 : if (!tbinfo->hastriggers ||
8070 1778 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8071 75312 : continue;
8072 :
8073 : /* OK, we need info for this table */
8074 1662 : if (tbloids->len > 1) /* do we have more than the '{'? */
8075 1564 : appendPQExpBufferChar(tbloids, ',');
8076 1662 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8077 : }
8078 304 : appendPQExpBufferChar(tbloids, '}');
8079 :
8080 304 : if (fout->remoteVersion >= 150000)
8081 : {
8082 : /*
8083 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8084 : * result in non-forward-compatible dumps of WHEN clauses due to
8085 : * under-parenthesization.
8086 : *
8087 : * NB: We need to see partition triggers in case the tgenabled flag
8088 : * has been changed from the parent.
8089 : */
8090 304 : appendPQExpBuffer(query,
8091 : "SELECT t.tgrelid, t.tgname, "
8092 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8093 : "t.tgenabled, t.tableoid, t.oid, "
8094 : "t.tgparentid <> 0 AS tgispartition\n"
8095 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8096 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8097 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8098 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8099 : "OR t.tgenabled != u.tgenabled) "
8100 : "ORDER BY t.tgrelid, t.tgname",
8101 : tbloids->data);
8102 : }
8103 0 : else if (fout->remoteVersion >= 130000)
8104 : {
8105 : /*
8106 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8107 : * result in non-forward-compatible dumps of WHEN clauses due to
8108 : * under-parenthesization.
8109 : *
8110 : * NB: We need to see tgisinternal triggers in partitions, in case the
8111 : * tgenabled flag has been changed from the parent.
8112 : */
8113 0 : appendPQExpBuffer(query,
8114 : "SELECT t.tgrelid, t.tgname, "
8115 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8116 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8117 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8118 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8119 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8120 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8121 : "ORDER BY t.tgrelid, t.tgname",
8122 : tbloids->data);
8123 : }
8124 0 : else if (fout->remoteVersion >= 110000)
8125 : {
8126 : /*
8127 : * NB: We need to see tgisinternal triggers in partitions, in case the
8128 : * tgenabled flag has been changed from the parent. No tgparentid in
8129 : * version 11-12, so we have to match them via pg_depend.
8130 : *
8131 : * See above about pretty=true in pg_get_triggerdef.
8132 : */
8133 0 : appendPQExpBuffer(query,
8134 : "SELECT t.tgrelid, t.tgname, "
8135 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8136 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8137 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8138 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8139 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8140 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8141 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8142 : " d.objid = t.oid "
8143 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8144 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8145 : "ORDER BY t.tgrelid, t.tgname",
8146 : tbloids->data);
8147 : }
8148 : else
8149 : {
8150 : /* See above about pretty=true in pg_get_triggerdef */
8151 0 : appendPQExpBuffer(query,
8152 : "SELECT t.tgrelid, t.tgname, "
8153 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8154 : "t.tgenabled, false as tgispartition, "
8155 : "t.tableoid, t.oid "
8156 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8157 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8158 : "WHERE NOT tgisinternal "
8159 : "ORDER BY t.tgrelid, t.tgname",
8160 : tbloids->data);
8161 : }
8162 :
8163 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8164 :
8165 304 : ntups = PQntuples(res);
8166 :
8167 304 : i_tableoid = PQfnumber(res, "tableoid");
8168 304 : i_oid = PQfnumber(res, "oid");
8169 304 : i_tgrelid = PQfnumber(res, "tgrelid");
8170 304 : i_tgname = PQfnumber(res, "tgname");
8171 304 : i_tgenabled = PQfnumber(res, "tgenabled");
8172 304 : i_tgispartition = PQfnumber(res, "tgispartition");
8173 304 : i_tgdef = PQfnumber(res, "tgdef");
8174 :
8175 304 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8176 :
8177 : /*
8178 : * Outer loop iterates once per table, not once per row. Incrementing of
8179 : * j is handled by the inner loop.
8180 : */
8181 304 : curtblindx = -1;
8182 886 : for (int j = 0; j < ntups;)
8183 : {
8184 582 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8185 582 : TableInfo *tbinfo = NULL;
8186 : int numtrigs;
8187 :
8188 : /* Count rows for this table */
8189 986 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8190 888 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8191 484 : break;
8192 :
8193 : /*
8194 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8195 : * order.
8196 : */
8197 30066 : while (++curtblindx < numTables)
8198 : {
8199 30066 : tbinfo = &tblinfo[curtblindx];
8200 30066 : if (tbinfo->dobj.catId.oid == tgrelid)
8201 582 : break;
8202 : }
8203 582 : if (curtblindx >= numTables)
8204 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8205 :
8206 : /* Save data for this table */
8207 582 : tbinfo->triggers = tginfo + j;
8208 582 : tbinfo->numTriggers = numtrigs;
8209 :
8210 1568 : for (int c = 0; c < numtrigs; c++, j++)
8211 : {
8212 986 : tginfo[j].dobj.objType = DO_TRIGGER;
8213 986 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8214 986 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8215 986 : AssignDumpId(&tginfo[j].dobj);
8216 986 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8217 986 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8218 986 : tginfo[j].tgtable = tbinfo;
8219 986 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8220 986 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8221 986 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8222 : }
8223 : }
8224 :
8225 304 : PQclear(res);
8226 :
8227 304 : destroyPQExpBuffer(query);
8228 304 : destroyPQExpBuffer(tbloids);
8229 304 : }
8230 :
8231 : /*
8232 : * getEventTriggers
8233 : * get information about event triggers
8234 : */
8235 : EventTriggerInfo *
8236 304 : getEventTriggers(Archive *fout, int *numEventTriggers)
8237 : {
8238 : int i;
8239 : PQExpBuffer query;
8240 : PGresult *res;
8241 : EventTriggerInfo *evtinfo;
8242 : int i_tableoid,
8243 : i_oid,
8244 : i_evtname,
8245 : i_evtevent,
8246 : i_evtowner,
8247 : i_evttags,
8248 : i_evtfname,
8249 : i_evtenabled;
8250 : int ntups;
8251 :
8252 : /* Before 9.3, there are no event triggers */
8253 304 : if (fout->remoteVersion < 90300)
8254 : {
8255 0 : *numEventTriggers = 0;
8256 0 : return NULL;
8257 : }
8258 :
8259 304 : query = createPQExpBuffer();
8260 :
8261 304 : appendPQExpBufferStr(query,
8262 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8263 : "evtevent, evtowner, "
8264 : "array_to_string(array("
8265 : "select quote_literal(x) "
8266 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8267 : "e.evtfoid::regproc as evtfname "
8268 : "FROM pg_event_trigger e "
8269 : "ORDER BY e.oid");
8270 :
8271 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8272 :
8273 304 : ntups = PQntuples(res);
8274 :
8275 304 : *numEventTriggers = ntups;
8276 :
8277 304 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8278 :
8279 304 : i_tableoid = PQfnumber(res, "tableoid");
8280 304 : i_oid = PQfnumber(res, "oid");
8281 304 : i_evtname = PQfnumber(res, "evtname");
8282 304 : i_evtevent = PQfnumber(res, "evtevent");
8283 304 : i_evtowner = PQfnumber(res, "evtowner");
8284 304 : i_evttags = PQfnumber(res, "evttags");
8285 304 : i_evtfname = PQfnumber(res, "evtfname");
8286 304 : i_evtenabled = PQfnumber(res, "evtenabled");
8287 :
8288 400 : for (i = 0; i < ntups; i++)
8289 : {
8290 96 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8291 96 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8292 96 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8293 96 : AssignDumpId(&evtinfo[i].dobj);
8294 96 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8295 96 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8296 96 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8297 96 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8298 96 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8299 96 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8300 96 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8301 :
8302 : /* Decide whether we want to dump it */
8303 96 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8304 : }
8305 :
8306 304 : PQclear(res);
8307 :
8308 304 : destroyPQExpBuffer(query);
8309 :
8310 304 : return evtinfo;
8311 : }
8312 :
8313 : /*
8314 : * getProcLangs
8315 : * get basic information about every procedural language in the system
8316 : *
8317 : * numProcLangs is set to the number of langs read in
8318 : *
8319 : * NB: this must run after getFuncs() because we assume we can do
8320 : * findFuncByOid().
8321 : */
8322 : ProcLangInfo *
8323 304 : getProcLangs(Archive *fout, int *numProcLangs)
8324 : {
8325 : PGresult *res;
8326 : int ntups;
8327 : int i;
8328 304 : PQExpBuffer query = createPQExpBuffer();
8329 : ProcLangInfo *planginfo;
8330 : int i_tableoid;
8331 : int i_oid;
8332 : int i_lanname;
8333 : int i_lanpltrusted;
8334 : int i_lanplcallfoid;
8335 : int i_laninline;
8336 : int i_lanvalidator;
8337 : int i_lanacl;
8338 : int i_acldefault;
8339 : int i_lanowner;
8340 :
8341 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8342 : "lanname, lanpltrusted, lanplcallfoid, "
8343 : "laninline, lanvalidator, "
8344 : "lanacl, "
8345 : "acldefault('l', lanowner) AS acldefault, "
8346 : "lanowner "
8347 : "FROM pg_language "
8348 : "WHERE lanispl "
8349 : "ORDER BY oid");
8350 :
8351 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8352 :
8353 304 : ntups = PQntuples(res);
8354 :
8355 304 : *numProcLangs = ntups;
8356 :
8357 304 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8358 :
8359 304 : i_tableoid = PQfnumber(res, "tableoid");
8360 304 : i_oid = PQfnumber(res, "oid");
8361 304 : i_lanname = PQfnumber(res, "lanname");
8362 304 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8363 304 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8364 304 : i_laninline = PQfnumber(res, "laninline");
8365 304 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8366 304 : i_lanacl = PQfnumber(res, "lanacl");
8367 304 : i_acldefault = PQfnumber(res, "acldefault");
8368 304 : i_lanowner = PQfnumber(res, "lanowner");
8369 :
8370 694 : for (i = 0; i < ntups; i++)
8371 : {
8372 390 : planginfo[i].dobj.objType = DO_PROCLANG;
8373 390 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8374 390 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8375 390 : AssignDumpId(&planginfo[i].dobj);
8376 :
8377 390 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8378 390 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8379 390 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8380 390 : planginfo[i].dacl.privtype = 0;
8381 390 : planginfo[i].dacl.initprivs = NULL;
8382 390 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8383 390 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8384 390 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8385 390 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8386 390 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8387 :
8388 : /* Decide whether we want to dump it */
8389 390 : selectDumpableProcLang(&(planginfo[i]), fout);
8390 :
8391 : /* Mark whether language has an ACL */
8392 390 : if (!PQgetisnull(res, i, i_lanacl))
8393 86 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8394 : }
8395 :
8396 304 : PQclear(res);
8397 :
8398 304 : destroyPQExpBuffer(query);
8399 :
8400 304 : return planginfo;
8401 : }
8402 :
8403 : /*
8404 : * getCasts
8405 : * get basic information about most casts in the system
8406 : *
8407 : * numCasts is set to the number of casts read in
8408 : *
8409 : * Skip casts from a range to its multirange, since we'll create those
8410 : * automatically.
8411 : */
8412 : CastInfo *
8413 304 : getCasts(Archive *fout, int *numCasts)
8414 : {
8415 : PGresult *res;
8416 : int ntups;
8417 : int i;
8418 304 : PQExpBuffer query = createPQExpBuffer();
8419 : CastInfo *castinfo;
8420 : int i_tableoid;
8421 : int i_oid;
8422 : int i_castsource;
8423 : int i_casttarget;
8424 : int i_castfunc;
8425 : int i_castcontext;
8426 : int i_castmethod;
8427 :
8428 304 : if (fout->remoteVersion >= 140000)
8429 : {
8430 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8431 : "castsource, casttarget, castfunc, castcontext, "
8432 : "castmethod "
8433 : "FROM pg_cast c "
8434 : "WHERE NOT EXISTS ( "
8435 : "SELECT 1 FROM pg_range r "
8436 : "WHERE c.castsource = r.rngtypid "
8437 : "AND c.casttarget = r.rngmultitypid "
8438 : ") "
8439 : "ORDER BY 3,4");
8440 : }
8441 : else
8442 : {
8443 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8444 : "castsource, casttarget, castfunc, castcontext, "
8445 : "castmethod "
8446 : "FROM pg_cast ORDER BY 3,4");
8447 : }
8448 :
8449 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8450 :
8451 304 : ntups = PQntuples(res);
8452 :
8453 304 : *numCasts = ntups;
8454 :
8455 304 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8456 :
8457 304 : i_tableoid = PQfnumber(res, "tableoid");
8458 304 : i_oid = PQfnumber(res, "oid");
8459 304 : i_castsource = PQfnumber(res, "castsource");
8460 304 : i_casttarget = PQfnumber(res, "casttarget");
8461 304 : i_castfunc = PQfnumber(res, "castfunc");
8462 304 : i_castcontext = PQfnumber(res, "castcontext");
8463 304 : i_castmethod = PQfnumber(res, "castmethod");
8464 :
8465 68242 : for (i = 0; i < ntups; i++)
8466 : {
8467 : PQExpBufferData namebuf;
8468 : TypeInfo *sTypeInfo;
8469 : TypeInfo *tTypeInfo;
8470 :
8471 67938 : castinfo[i].dobj.objType = DO_CAST;
8472 67938 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8473 67938 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8474 67938 : AssignDumpId(&castinfo[i].dobj);
8475 67938 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8476 67938 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8477 67938 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8478 67938 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8479 67938 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8480 :
8481 : /*
8482 : * Try to name cast as concatenation of typnames. This is only used
8483 : * for purposes of sorting. If we fail to find either type, the name
8484 : * will be an empty string.
8485 : */
8486 67938 : initPQExpBuffer(&namebuf);
8487 67938 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8488 67938 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8489 67938 : if (sTypeInfo && tTypeInfo)
8490 67938 : appendPQExpBuffer(&namebuf, "%s %s",
8491 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8492 67938 : castinfo[i].dobj.name = namebuf.data;
8493 :
8494 : /* Decide whether we want to dump it */
8495 67938 : selectDumpableCast(&(castinfo[i]), fout);
8496 : }
8497 :
8498 304 : PQclear(res);
8499 :
8500 304 : destroyPQExpBuffer(query);
8501 :
8502 304 : return castinfo;
8503 : }
8504 :
8505 : static char *
8506 170 : get_language_name(Archive *fout, Oid langid)
8507 : {
8508 : PQExpBuffer query;
8509 : PGresult *res;
8510 : char *lanname;
8511 :
8512 170 : query = createPQExpBuffer();
8513 170 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8514 170 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8515 170 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8516 170 : destroyPQExpBuffer(query);
8517 170 : PQclear(res);
8518 :
8519 170 : return lanname;
8520 : }
8521 :
8522 : /*
8523 : * getTransforms
8524 : * get basic information about every transform in the system
8525 : *
8526 : * numTransforms is set to the number of transforms read in
8527 : */
8528 : TransformInfo *
8529 304 : getTransforms(Archive *fout, int *numTransforms)
8530 : {
8531 : PGresult *res;
8532 : int ntups;
8533 : int i;
8534 : PQExpBuffer query;
8535 : TransformInfo *transforminfo;
8536 : int i_tableoid;
8537 : int i_oid;
8538 : int i_trftype;
8539 : int i_trflang;
8540 : int i_trffromsql;
8541 : int i_trftosql;
8542 :
8543 : /* Transforms didn't exist pre-9.5 */
8544 304 : if (fout->remoteVersion < 90500)
8545 : {
8546 0 : *numTransforms = 0;
8547 0 : return NULL;
8548 : }
8549 :
8550 304 : query = createPQExpBuffer();
8551 :
8552 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8553 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8554 : "FROM pg_transform "
8555 : "ORDER BY 3,4");
8556 :
8557 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8558 :
8559 304 : ntups = PQntuples(res);
8560 :
8561 304 : *numTransforms = ntups;
8562 :
8563 304 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8564 :
8565 304 : i_tableoid = PQfnumber(res, "tableoid");
8566 304 : i_oid = PQfnumber(res, "oid");
8567 304 : i_trftype = PQfnumber(res, "trftype");
8568 304 : i_trflang = PQfnumber(res, "trflang");
8569 304 : i_trffromsql = PQfnumber(res, "trffromsql");
8570 304 : i_trftosql = PQfnumber(res, "trftosql");
8571 :
8572 400 : for (i = 0; i < ntups; i++)
8573 : {
8574 : PQExpBufferData namebuf;
8575 : TypeInfo *typeInfo;
8576 : char *lanname;
8577 :
8578 96 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8579 96 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8580 96 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8581 96 : AssignDumpId(&transforminfo[i].dobj);
8582 96 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8583 96 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8584 96 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8585 96 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8586 :
8587 : /*
8588 : * Try to name transform as concatenation of type and language name.
8589 : * This is only used for purposes of sorting. If we fail to find
8590 : * either, the name will be an empty string.
8591 : */
8592 96 : initPQExpBuffer(&namebuf);
8593 96 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8594 96 : lanname = get_language_name(fout, transforminfo[i].trflang);
8595 96 : if (typeInfo && lanname)
8596 96 : appendPQExpBuffer(&namebuf, "%s %s",
8597 : typeInfo->dobj.name, lanname);
8598 96 : transforminfo[i].dobj.name = namebuf.data;
8599 96 : free(lanname);
8600 :
8601 : /* Decide whether we want to dump it */
8602 96 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8603 : }
8604 :
8605 304 : PQclear(res);
8606 :
8607 304 : destroyPQExpBuffer(query);
8608 :
8609 304 : return transforminfo;
8610 : }
8611 :
8612 : /*
8613 : * getTableAttrs -
8614 : * for each interesting table, read info about its attributes
8615 : * (names, types, default values, CHECK constraints, etc)
8616 : *
8617 : * modifies tblinfo
8618 : */
8619 : void
8620 304 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8621 : {
8622 304 : DumpOptions *dopt = fout->dopt;
8623 304 : PQExpBuffer q = createPQExpBuffer();
8624 304 : PQExpBuffer tbloids = createPQExpBuffer();
8625 304 : PQExpBuffer checkoids = createPQExpBuffer();
8626 : PGresult *res;
8627 : int ntups;
8628 : int curtblindx;
8629 : int i_attrelid;
8630 : int i_attnum;
8631 : int i_attname;
8632 : int i_atttypname;
8633 : int i_attstattarget;
8634 : int i_attstorage;
8635 : int i_typstorage;
8636 : int i_attidentity;
8637 : int i_attgenerated;
8638 : int i_attisdropped;
8639 : int i_attlen;
8640 : int i_attalign;
8641 : int i_attislocal;
8642 : int i_notnull_name;
8643 : int i_notnull_noinherit;
8644 : int i_notnull_is_pk;
8645 : int i_notnull_inh;
8646 : int i_attoptions;
8647 : int i_attcollation;
8648 : int i_attcompression;
8649 : int i_attfdwoptions;
8650 : int i_attmissingval;
8651 : int i_atthasdef;
8652 :
8653 : /*
8654 : * We want to perform just one query against pg_attribute, and then just
8655 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
8656 : * (for CHECK constraints and for NOT NULL constraints). However, we
8657 : * mustn't try to select every row of those catalogs and then sort it out
8658 : * on the client side, because some of the server-side functions we need
8659 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8660 : * build an array of the OIDs of tables we care about (and now have lock
8661 : * on!), and use a WHERE clause to constrain which rows are selected.
8662 : */
8663 304 : appendPQExpBufferChar(tbloids, '{');
8664 304 : appendPQExpBufferChar(checkoids, '{');
8665 77278 : for (int i = 0; i < numTables; i++)
8666 : {
8667 76974 : TableInfo *tbinfo = &tblinfo[i];
8668 :
8669 : /* Don't bother to collect info for sequences */
8670 76974 : if (tbinfo->relkind == RELKIND_SEQUENCE)
8671 1012 : continue;
8672 :
8673 : /* Don't bother with uninteresting tables, either */
8674 75962 : if (!tbinfo->interesting)
8675 65064 : continue;
8676 :
8677 : /* OK, we need info for this table */
8678 10898 : if (tbloids->len > 1) /* do we have more than the '{'? */
8679 10698 : appendPQExpBufferChar(tbloids, ',');
8680 10898 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8681 :
8682 10898 : if (tbinfo->ncheck > 0)
8683 : {
8684 : /* Also make a list of the ones with check constraints */
8685 884 : if (checkoids->len > 1) /* do we have more than the '{'? */
8686 752 : appendPQExpBufferChar(checkoids, ',');
8687 884 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
8688 : }
8689 : }
8690 304 : appendPQExpBufferChar(tbloids, '}');
8691 304 : appendPQExpBufferChar(checkoids, '}');
8692 :
8693 : /*
8694 : * Find all the user attributes and their types.
8695 : *
8696 : * Since we only want to dump COLLATE clauses for attributes whose
8697 : * collation is different from their type's default, we use a CASE here to
8698 : * suppress uninteresting attcollations cheaply.
8699 : */
8700 304 : appendPQExpBufferStr(q,
8701 : "SELECT\n"
8702 : "a.attrelid,\n"
8703 : "a.attnum,\n"
8704 : "a.attname,\n"
8705 : "a.attstattarget,\n"
8706 : "a.attstorage,\n"
8707 : "t.typstorage,\n"
8708 : "a.atthasdef,\n"
8709 : "a.attisdropped,\n"
8710 : "a.attlen,\n"
8711 : "a.attalign,\n"
8712 : "a.attislocal,\n"
8713 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
8714 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
8715 : "CASE WHEN a.attcollation <> t.typcollation "
8716 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
8717 : "pg_catalog.array_to_string(ARRAY("
8718 : "SELECT pg_catalog.quote_ident(option_name) || "
8719 : "' ' || pg_catalog.quote_literal(option_value) "
8720 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8721 : "ORDER BY option_name"
8722 : "), E',\n ') AS attfdwoptions,\n");
8723 :
8724 : /*
8725 : * Find out any NOT NULL markings for each column. In 17 and up we have
8726 : * to read pg_constraint, and keep track whether it's NO INHERIT; in older
8727 : * versions we rely on pg_attribute.attnotnull.
8728 : *
8729 : * We also track whether the constraint was defined directly in this table
8730 : * or via an ancestor, for binary upgrade.
8731 : *
8732 : * Lastly, we need to know if the PK for the table involves each column;
8733 : * for columns that are there we need a NOT NULL marking even if there's
8734 : * no explicit constraint, to avoid the table having to be scanned for
8735 : * NULLs after the data is loaded when the PK is created, later in the
8736 : * dump; for this case we add throwaway constraints that are dropped once
8737 : * the PK is created.
8738 : */
8739 304 : if (fout->remoteVersion >= 170000)
8740 304 : appendPQExpBufferStr(q,
8741 : "co.conname AS notnull_name,\n"
8742 : "co.connoinherit AS notnull_noinherit,\n"
8743 : "copk.conname IS NOT NULL as notnull_is_pk,\n"
8744 : "coalesce(NOT co.conislocal, true) AS notnull_inh,\n");
8745 : else
8746 0 : appendPQExpBufferStr(q,
8747 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
8748 : "false AS notnull_noinherit,\n"
8749 : "copk.conname IS NOT NULL AS notnull_is_pk,\n"
8750 : "NOT a.attislocal AS notnull_inh,\n");
8751 :
8752 304 : if (fout->remoteVersion >= 140000)
8753 304 : appendPQExpBufferStr(q,
8754 : "a.attcompression AS attcompression,\n");
8755 : else
8756 0 : appendPQExpBufferStr(q,
8757 : "'' AS attcompression,\n");
8758 :
8759 304 : if (fout->remoteVersion >= 100000)
8760 304 : appendPQExpBufferStr(q,
8761 : "a.attidentity,\n");
8762 : else
8763 0 : appendPQExpBufferStr(q,
8764 : "'' AS attidentity,\n");
8765 :
8766 304 : if (fout->remoteVersion >= 110000)
8767 304 : appendPQExpBufferStr(q,
8768 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8769 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8770 : else
8771 0 : appendPQExpBufferStr(q,
8772 : "NULL AS attmissingval,\n");
8773 :
8774 304 : if (fout->remoteVersion >= 120000)
8775 304 : appendPQExpBufferStr(q,
8776 : "a.attgenerated\n");
8777 : else
8778 0 : appendPQExpBufferStr(q,
8779 : "'' AS attgenerated\n");
8780 :
8781 : /* need left join to pg_type to not fail on dropped columns ... */
8782 304 : appendPQExpBuffer(q,
8783 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8784 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
8785 : "LEFT JOIN pg_catalog.pg_type t "
8786 : "ON (a.atttypid = t.oid)\n",
8787 : tbloids->data);
8788 :
8789 : /*
8790 : * In versions 16 and up, we need pg_constraint for explicit NOT NULL
8791 : * entries. Also, we need to know if the NOT NULL for each column is
8792 : * backing a primary key.
8793 : */
8794 304 : if (fout->remoteVersion >= 170000)
8795 304 : appendPQExpBufferStr(q,
8796 : " LEFT JOIN pg_catalog.pg_constraint co ON "
8797 : "(a.attrelid = co.conrelid\n"
8798 : " AND co.contype = 'n' AND "
8799 : "co.conkey = array[a.attnum])\n");
8800 :
8801 304 : appendPQExpBufferStr(q,
8802 : "LEFT JOIN pg_catalog.pg_constraint copk ON "
8803 : "(copk.conrelid = src.tbloid\n"
8804 : " AND copk.contype = 'p' AND "
8805 : "copk.conkey @> array[a.attnum])\n"
8806 : "WHERE a.attnum > 0::pg_catalog.int2\n"
8807 : "ORDER BY a.attrelid, a.attnum");
8808 :
8809 304 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8810 :
8811 304 : ntups = PQntuples(res);
8812 :
8813 304 : i_attrelid = PQfnumber(res, "attrelid");
8814 304 : i_attnum = PQfnumber(res, "attnum");
8815 304 : i_attname = PQfnumber(res, "attname");
8816 304 : i_atttypname = PQfnumber(res, "atttypname");
8817 304 : i_attstattarget = PQfnumber(res, "attstattarget");
8818 304 : i_attstorage = PQfnumber(res, "attstorage");
8819 304 : i_typstorage = PQfnumber(res, "typstorage");
8820 304 : i_attidentity = PQfnumber(res, "attidentity");
8821 304 : i_attgenerated = PQfnumber(res, "attgenerated");
8822 304 : i_attisdropped = PQfnumber(res, "attisdropped");
8823 304 : i_attlen = PQfnumber(res, "attlen");
8824 304 : i_attalign = PQfnumber(res, "attalign");
8825 304 : i_attislocal = PQfnumber(res, "attislocal");
8826 304 : i_notnull_name = PQfnumber(res, "notnull_name");
8827 304 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
8828 304 : i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
8829 304 : i_notnull_inh = PQfnumber(res, "notnull_inh");
8830 304 : i_attoptions = PQfnumber(res, "attoptions");
8831 304 : i_attcollation = PQfnumber(res, "attcollation");
8832 304 : i_attcompression = PQfnumber(res, "attcompression");
8833 304 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
8834 304 : i_attmissingval = PQfnumber(res, "attmissingval");
8835 304 : i_atthasdef = PQfnumber(res, "atthasdef");
8836 :
8837 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
8838 304 : resetPQExpBuffer(tbloids);
8839 304 : appendPQExpBufferChar(tbloids, '{');
8840 :
8841 : /*
8842 : * Outer loop iterates once per table, not once per row. Incrementing of
8843 : * r is handled by the inner loop.
8844 : */
8845 304 : curtblindx = -1;
8846 10948 : for (int r = 0; r < ntups;)
8847 : {
8848 10644 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
8849 10644 : TableInfo *tbinfo = NULL;
8850 : int numatts;
8851 : bool hasdefaults;
8852 : int notnullcount;
8853 :
8854 : /* Count rows for this table */
8855 39208 : for (numatts = 1; numatts < ntups - r; numatts++)
8856 39014 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
8857 10450 : break;
8858 :
8859 : /*
8860 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8861 : * order.
8862 : */
8863 53186 : while (++curtblindx < numTables)
8864 : {
8865 53186 : tbinfo = &tblinfo[curtblindx];
8866 53186 : if (tbinfo->dobj.catId.oid == attrelid)
8867 10644 : break;
8868 : }
8869 10644 : if (curtblindx >= numTables)
8870 0 : pg_fatal("unrecognized table OID %u", attrelid);
8871 : /* cross-check that we only got requested tables */
8872 10644 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
8873 10644 : !tbinfo->interesting)
8874 0 : pg_fatal("unexpected column data for table \"%s\"",
8875 : tbinfo->dobj.name);
8876 :
8877 10644 : notnullcount = 0;
8878 :
8879 : /* Save data for this table */
8880 10644 : tbinfo->numatts = numatts;
8881 10644 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
8882 10644 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
8883 10644 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
8884 10644 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
8885 10644 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
8886 10644 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
8887 10644 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
8888 10644 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
8889 10644 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
8890 10644 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
8891 10644 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
8892 10644 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
8893 10644 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
8894 10644 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
8895 10644 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
8896 10644 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
8897 10644 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
8898 10644 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
8899 10644 : tbinfo->notnull_throwaway = (bool *) pg_malloc(numatts * sizeof(bool));
8900 10644 : tbinfo->notnull_inh = (bool *) pg_malloc(numatts * sizeof(bool));
8901 10644 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
8902 10644 : hasdefaults = false;
8903 :
8904 49852 : for (int j = 0; j < numatts; j++, r++)
8905 : {
8906 39208 : bool use_named_notnull = false;
8907 39208 : bool use_unnamed_notnull = false;
8908 39208 : bool use_throwaway_notnull = false;
8909 :
8910 39208 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
8911 0 : pg_fatal("invalid column numbering in table \"%s\"",
8912 : tbinfo->dobj.name);
8913 39208 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
8914 39208 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
8915 39208 : if (PQgetisnull(res, r, i_attstattarget))
8916 39134 : tbinfo->attstattarget[j] = -1;
8917 : else
8918 74 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
8919 39208 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
8920 39208 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
8921 39208 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
8922 39208 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
8923 39208 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
8924 39208 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
8925 39208 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
8926 39208 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
8927 39208 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
8928 :
8929 : /*
8930 : * Not-null constraints require a jumping through a few hoops.
8931 : * First, if the user has specified a constraint name that's not
8932 : * the system-assigned default name, then we need to preserve
8933 : * that. But if they haven't, then we don't want to use the
8934 : * verbose syntax in the dump output. (Also, in versions prior to
8935 : * 17, there was no constraint name at all.)
8936 : *
8937 : * (XXX Comparing the name this way to a supposed default name is
8938 : * a bit of a hack, but it beats having to store a boolean flag in
8939 : * pg_constraint just for this, or having to compute the knowledge
8940 : * at pg_dump time from the server.)
8941 : *
8942 : * We also need to know if a column is part of the primary key. In
8943 : * that case, we want to mark the column as not-null at table
8944 : * creation time, so that the table doesn't have to be scanned to
8945 : * check for nulls when the PK is created afterwards; this is
8946 : * especially critical during pg_upgrade (where the data would not
8947 : * be scanned at all otherwise.) If the column is part of the PK
8948 : * and does not have any other not-null constraint, then we
8949 : * fabricate a throwaway constraint name that we later use to
8950 : * remove the constraint after the PK has been created.
8951 : *
8952 : * For inheritance child tables, we don't want to print not-null
8953 : * when the constraint was defined at the parent level instead of
8954 : * locally.
8955 : */
8956 :
8957 : /*
8958 : * We use notnull_inh to suppress unwanted not-null constraints in
8959 : * inheritance children, when said constraints come from the
8960 : * parent(s).
8961 : */
8962 39208 : tbinfo->notnull_inh[j] = PQgetvalue(res, r, i_notnull_inh)[0] == 't';
8963 :
8964 39208 : if (fout->remoteVersion < 170000)
8965 : {
8966 0 : if (!PQgetisnull(res, r, i_notnull_name) &&
8967 0 : dopt->binary_upgrade &&
8968 0 : !tbinfo->ispartition &&
8969 0 : tbinfo->notnull_inh[j])
8970 : {
8971 0 : use_named_notnull = true;
8972 : /* XXX should match ChooseConstraintName better */
8973 0 : tbinfo->notnull_constrs[j] =
8974 0 : psprintf("%s_%s_not_null", tbinfo->dobj.name,
8975 0 : tbinfo->attnames[j]);
8976 : }
8977 0 : else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
8978 0 : use_throwaway_notnull = true;
8979 0 : else if (!PQgetisnull(res, r, i_notnull_name))
8980 0 : use_unnamed_notnull = true;
8981 : }
8982 : else
8983 : {
8984 39208 : if (!PQgetisnull(res, r, i_notnull_name))
8985 : {
8986 : /*
8987 : * In binary upgrade of inheritance child tables, must
8988 : * have a constraint name that we can UPDATE later.
8989 : */
8990 2326 : if (dopt->binary_upgrade &&
8991 264 : !tbinfo->ispartition &&
8992 174 : tbinfo->notnull_inh[j])
8993 : {
8994 32 : use_named_notnull = true;
8995 32 : tbinfo->notnull_constrs[j] =
8996 32 : pstrdup(PQgetvalue(res, r, i_notnull_name));
8997 :
8998 : }
8999 : else
9000 : {
9001 : char *default_name;
9002 :
9003 : /* XXX should match ChooseConstraintName better */
9004 2294 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9005 2294 : tbinfo->attnames[j]);
9006 2294 : if (strcmp(default_name,
9007 2294 : PQgetvalue(res, r, i_notnull_name)) == 0)
9008 1444 : use_unnamed_notnull = true;
9009 : else
9010 : {
9011 850 : use_named_notnull = true;
9012 850 : tbinfo->notnull_constrs[j] =
9013 850 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9014 : }
9015 : }
9016 : }
9017 36882 : else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
9018 1608 : use_throwaway_notnull = true;
9019 : }
9020 :
9021 39208 : if (use_unnamed_notnull)
9022 : {
9023 1444 : tbinfo->notnull_constrs[j] = "";
9024 1444 : tbinfo->notnull_throwaway[j] = false;
9025 : }
9026 37764 : else if (use_named_notnull)
9027 : {
9028 : /* The name itself has already been determined */
9029 882 : tbinfo->notnull_throwaway[j] = false;
9030 : }
9031 36882 : else if (use_throwaway_notnull)
9032 : {
9033 3216 : tbinfo->notnull_constrs[j] =
9034 1608 : psprintf("pgdump_throwaway_notnull_%d", notnullcount++);
9035 1608 : tbinfo->notnull_throwaway[j] = true;
9036 1608 : tbinfo->notnull_inh[j] = false;
9037 : }
9038 : else
9039 : {
9040 35274 : tbinfo->notnull_constrs[j] = NULL;
9041 35274 : tbinfo->notnull_throwaway[j] = false;
9042 : }
9043 :
9044 : /*
9045 : * Throwaway constraints must always be NO INHERIT; otherwise do
9046 : * what the catalog says.
9047 : */
9048 76808 : tbinfo->notnull_noinh[j] = use_throwaway_notnull ||
9049 37600 : PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9050 :
9051 39208 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9052 39208 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9053 39208 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9054 39208 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9055 39208 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9056 39208 : tbinfo->attrdefs[j] = NULL; /* fix below */
9057 39208 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9058 1736 : hasdefaults = true;
9059 : }
9060 :
9061 10644 : if (hasdefaults)
9062 : {
9063 : /* Collect OIDs of interesting tables that have defaults */
9064 1440 : if (tbloids->len > 1) /* do we have more than the '{'? */
9065 1344 : appendPQExpBufferChar(tbloids, ',');
9066 1440 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9067 : }
9068 : }
9069 :
9070 304 : PQclear(res);
9071 :
9072 : /*
9073 : * Now get info about column defaults. This is skipped for a data-only
9074 : * dump, as it is only needed for table schemas.
9075 : */
9076 304 : if (!dopt->dataOnly && tbloids->len > 1)
9077 : {
9078 : AttrDefInfo *attrdefs;
9079 : int numDefaults;
9080 88 : TableInfo *tbinfo = NULL;
9081 :
9082 88 : pg_log_info("finding table default expressions");
9083 :
9084 88 : appendPQExpBufferChar(tbloids, '}');
9085 :
9086 88 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9087 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9088 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9089 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9090 : "ORDER BY a.adrelid, a.adnum",
9091 : tbloids->data);
9092 :
9093 88 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9094 :
9095 88 : numDefaults = PQntuples(res);
9096 88 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9097 :
9098 88 : curtblindx = -1;
9099 1744 : for (int j = 0; j < numDefaults; j++)
9100 : {
9101 1656 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9102 1656 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9103 1656 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9104 1656 : int adnum = atoi(PQgetvalue(res, j, 3));
9105 1656 : char *adsrc = PQgetvalue(res, j, 4);
9106 :
9107 : /*
9108 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9109 : * OID order.
9110 : */
9111 1656 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9112 : {
9113 29530 : while (++curtblindx < numTables)
9114 : {
9115 29530 : tbinfo = &tblinfo[curtblindx];
9116 29530 : if (tbinfo->dobj.catId.oid == adrelid)
9117 1372 : break;
9118 : }
9119 1372 : if (curtblindx >= numTables)
9120 0 : pg_fatal("unrecognized table OID %u", adrelid);
9121 : }
9122 :
9123 1656 : if (adnum <= 0 || adnum > tbinfo->numatts)
9124 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9125 : adnum, tbinfo->dobj.name);
9126 :
9127 : /*
9128 : * dropped columns shouldn't have defaults, but just in case,
9129 : * ignore 'em
9130 : */
9131 1656 : if (tbinfo->attisdropped[adnum - 1])
9132 0 : continue;
9133 :
9134 1656 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9135 1656 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9136 1656 : attrdefs[j].dobj.catId.oid = adoid;
9137 1656 : AssignDumpId(&attrdefs[j].dobj);
9138 1656 : attrdefs[j].adtable = tbinfo;
9139 1656 : attrdefs[j].adnum = adnum;
9140 1656 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9141 :
9142 1656 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9143 1656 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9144 :
9145 1656 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9146 :
9147 : /*
9148 : * Figure out whether the default/generation expression should be
9149 : * dumped as part of the main CREATE TABLE (or similar) command or
9150 : * as a separate ALTER TABLE (or similar) command. The preference
9151 : * is to put it into the CREATE command, but in some cases that's
9152 : * not possible.
9153 : */
9154 1656 : if (tbinfo->attgenerated[adnum - 1])
9155 : {
9156 : /*
9157 : * Column generation expressions cannot be dumped separately,
9158 : * because there is no syntax for it. By setting separate to
9159 : * false here we prevent the "default" from being processed as
9160 : * its own dumpable object. Later, flagInhAttrs() will mark
9161 : * it as not to be dumped at all, if possible (that is, if it
9162 : * can be inherited from a parent).
9163 : */
9164 690 : attrdefs[j].separate = false;
9165 : }
9166 966 : else if (tbinfo->relkind == RELKIND_VIEW)
9167 : {
9168 : /*
9169 : * Defaults on a VIEW must always be dumped as separate ALTER
9170 : * TABLE commands.
9171 : */
9172 66 : attrdefs[j].separate = true;
9173 : }
9174 900 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9175 : {
9176 : /* column will be suppressed, print default separately */
9177 8 : attrdefs[j].separate = true;
9178 : }
9179 : else
9180 : {
9181 892 : attrdefs[j].separate = false;
9182 : }
9183 :
9184 1656 : if (!attrdefs[j].separate)
9185 : {
9186 : /*
9187 : * Mark the default as needing to appear before the table, so
9188 : * that any dependencies it has must be emitted before the
9189 : * CREATE TABLE. If this is not possible, we'll change to
9190 : * "separate" mode while sorting dependencies.
9191 : */
9192 1582 : addObjectDependency(&tbinfo->dobj,
9193 1582 : attrdefs[j].dobj.dumpId);
9194 : }
9195 :
9196 1656 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9197 : }
9198 :
9199 88 : PQclear(res);
9200 : }
9201 :
9202 : /*
9203 : * Get info about table CHECK constraints. This is skipped for a
9204 : * data-only dump, as it is only needed for table schemas.
9205 : */
9206 304 : if (!dopt->dataOnly && checkoids->len > 2)
9207 : {
9208 : ConstraintInfo *constrs;
9209 : int numConstrs;
9210 : int i_tableoid;
9211 : int i_oid;
9212 : int i_conrelid;
9213 : int i_conname;
9214 : int i_consrc;
9215 : int i_conislocal;
9216 : int i_convalidated;
9217 :
9218 122 : pg_log_info("finding table check constraints");
9219 :
9220 122 : resetPQExpBuffer(q);
9221 122 : appendPQExpBuffer(q,
9222 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9223 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9224 : "conislocal, convalidated "
9225 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9226 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9227 : "WHERE contype = 'c' "
9228 : "ORDER BY c.conrelid, c.conname",
9229 : checkoids->data);
9230 :
9231 122 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9232 :
9233 122 : numConstrs = PQntuples(res);
9234 122 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9235 :
9236 122 : i_tableoid = PQfnumber(res, "tableoid");
9237 122 : i_oid = PQfnumber(res, "oid");
9238 122 : i_conrelid = PQfnumber(res, "conrelid");
9239 122 : i_conname = PQfnumber(res, "conname");
9240 122 : i_consrc = PQfnumber(res, "consrc");
9241 122 : i_conislocal = PQfnumber(res, "conislocal");
9242 122 : i_convalidated = PQfnumber(res, "convalidated");
9243 :
9244 : /* As above, this loop iterates once per table, not once per row */
9245 122 : curtblindx = -1;
9246 954 : for (int j = 0; j < numConstrs;)
9247 : {
9248 832 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9249 832 : TableInfo *tbinfo = NULL;
9250 : int numcons;
9251 :
9252 : /* Count rows for this table */
9253 1096 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9254 974 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9255 710 : break;
9256 :
9257 : /*
9258 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9259 : * OID order.
9260 : */
9261 35058 : while (++curtblindx < numTables)
9262 : {
9263 35058 : tbinfo = &tblinfo[curtblindx];
9264 35058 : if (tbinfo->dobj.catId.oid == conrelid)
9265 832 : break;
9266 : }
9267 832 : if (curtblindx >= numTables)
9268 0 : pg_fatal("unrecognized table OID %u", conrelid);
9269 :
9270 832 : if (numcons != tbinfo->ncheck)
9271 : {
9272 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9273 : "expected %d check constraints on table \"%s\" but found %d",
9274 : tbinfo->ncheck),
9275 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9276 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9277 0 : exit_nicely(1);
9278 : }
9279 :
9280 832 : tbinfo->checkexprs = constrs + j;
9281 :
9282 1928 : for (int c = 0; c < numcons; c++, j++)
9283 : {
9284 1096 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9285 :
9286 1096 : constrs[j].dobj.objType = DO_CONSTRAINT;
9287 1096 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9288 1096 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9289 1096 : AssignDumpId(&constrs[j].dobj);
9290 1096 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9291 1096 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9292 1096 : constrs[j].contable = tbinfo;
9293 1096 : constrs[j].condomain = NULL;
9294 1096 : constrs[j].contype = 'c';
9295 1096 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9296 1096 : constrs[j].confrelid = InvalidOid;
9297 1096 : constrs[j].conindex = 0;
9298 1096 : constrs[j].condeferrable = false;
9299 1096 : constrs[j].condeferred = false;
9300 1096 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9301 :
9302 : /*
9303 : * An unvalidated constraint needs to be dumped separately, so
9304 : * that potentially-violating existing data is loaded before
9305 : * the constraint.
9306 : */
9307 1096 : constrs[j].separate = !validated;
9308 :
9309 1096 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9310 :
9311 : /*
9312 : * Mark the constraint as needing to appear before the table
9313 : * --- this is so that any other dependencies of the
9314 : * constraint will be emitted before we try to create the
9315 : * table. If the constraint is to be dumped separately, it
9316 : * will be dumped after data is loaded anyway, so don't do it.
9317 : * (There's an automatic dependency in the opposite direction
9318 : * anyway, so don't need to add one manually here.)
9319 : */
9320 1096 : if (!constrs[j].separate)
9321 1026 : addObjectDependency(&tbinfo->dobj,
9322 1026 : constrs[j].dobj.dumpId);
9323 :
9324 : /*
9325 : * We will detect later whether the constraint must be split
9326 : * out from the table definition.
9327 : */
9328 : }
9329 : }
9330 :
9331 122 : PQclear(res);
9332 : }
9333 :
9334 304 : destroyPQExpBuffer(q);
9335 304 : destroyPQExpBuffer(tbloids);
9336 304 : destroyPQExpBuffer(checkoids);
9337 304 : }
9338 :
9339 : /*
9340 : * Test whether a column should be printed as part of table's CREATE TABLE.
9341 : * Column number is zero-based.
9342 : *
9343 : * Normally this is always true, but it's false for dropped columns, as well
9344 : * as those that were inherited without any local definition. (If we print
9345 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9346 : * For partitions, it's always true, because we want the partitions to be
9347 : * created independently and ATTACH PARTITION used afterwards.
9348 : *
9349 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9350 : * attisdropped state later, so as to keep control of the physical column
9351 : * order.
9352 : *
9353 : * This function exists because there are scattered nonobvious places that
9354 : * must be kept in sync with this decision.
9355 : */
9356 : bool
9357 70770 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9358 : {
9359 70770 : if (dopt->binary_upgrade)
9360 11706 : return true;
9361 59064 : if (tbinfo->attisdropped[colno])
9362 688 : return false;
9363 58376 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9364 : }
9365 :
9366 :
9367 : /*
9368 : * getTSParsers:
9369 : * read all text search parsers in the system catalogs and return them
9370 : * in the TSParserInfo* structure
9371 : *
9372 : * numTSParsers is set to the number of parsers read in
9373 : */
9374 : TSParserInfo *
9375 304 : getTSParsers(Archive *fout, int *numTSParsers)
9376 : {
9377 : PGresult *res;
9378 : int ntups;
9379 : int i;
9380 : PQExpBuffer query;
9381 : TSParserInfo *prsinfo;
9382 : int i_tableoid;
9383 : int i_oid;
9384 : int i_prsname;
9385 : int i_prsnamespace;
9386 : int i_prsstart;
9387 : int i_prstoken;
9388 : int i_prsend;
9389 : int i_prsheadline;
9390 : int i_prslextype;
9391 :
9392 304 : query = createPQExpBuffer();
9393 :
9394 : /*
9395 : * find all text search objects, including builtin ones; we filter out
9396 : * system-defined objects at dump-out time.
9397 : */
9398 :
9399 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9400 : "prsstart::oid, prstoken::oid, "
9401 : "prsend::oid, prsheadline::oid, prslextype::oid "
9402 : "FROM pg_ts_parser");
9403 :
9404 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9405 :
9406 304 : ntups = PQntuples(res);
9407 304 : *numTSParsers = ntups;
9408 :
9409 304 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9410 :
9411 304 : i_tableoid = PQfnumber(res, "tableoid");
9412 304 : i_oid = PQfnumber(res, "oid");
9413 304 : i_prsname = PQfnumber(res, "prsname");
9414 304 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9415 304 : i_prsstart = PQfnumber(res, "prsstart");
9416 304 : i_prstoken = PQfnumber(res, "prstoken");
9417 304 : i_prsend = PQfnumber(res, "prsend");
9418 304 : i_prsheadline = PQfnumber(res, "prsheadline");
9419 304 : i_prslextype = PQfnumber(res, "prslextype");
9420 :
9421 694 : for (i = 0; i < ntups; i++)
9422 : {
9423 390 : prsinfo[i].dobj.objType = DO_TSPARSER;
9424 390 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9425 390 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9426 390 : AssignDumpId(&prsinfo[i].dobj);
9427 390 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9428 780 : prsinfo[i].dobj.namespace =
9429 390 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9430 390 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9431 390 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9432 390 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9433 390 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9434 390 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9435 :
9436 : /* Decide whether we want to dump it */
9437 390 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9438 : }
9439 :
9440 304 : PQclear(res);
9441 :
9442 304 : destroyPQExpBuffer(query);
9443 :
9444 304 : return prsinfo;
9445 : }
9446 :
9447 : /*
9448 : * getTSDictionaries:
9449 : * read all text search dictionaries in the system catalogs and return them
9450 : * in the TSDictInfo* structure
9451 : *
9452 : * numTSDicts is set to the number of dictionaries read in
9453 : */
9454 : TSDictInfo *
9455 304 : getTSDictionaries(Archive *fout, int *numTSDicts)
9456 : {
9457 : PGresult *res;
9458 : int ntups;
9459 : int i;
9460 : PQExpBuffer query;
9461 : TSDictInfo *dictinfo;
9462 : int i_tableoid;
9463 : int i_oid;
9464 : int i_dictname;
9465 : int i_dictnamespace;
9466 : int i_dictowner;
9467 : int i_dicttemplate;
9468 : int i_dictinitoption;
9469 :
9470 304 : query = createPQExpBuffer();
9471 :
9472 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9473 : "dictnamespace, dictowner, "
9474 : "dicttemplate, dictinitoption "
9475 : "FROM pg_ts_dict");
9476 :
9477 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9478 :
9479 304 : ntups = PQntuples(res);
9480 304 : *numTSDicts = ntups;
9481 :
9482 304 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9483 :
9484 304 : i_tableoid = PQfnumber(res, "tableoid");
9485 304 : i_oid = PQfnumber(res, "oid");
9486 304 : i_dictname = PQfnumber(res, "dictname");
9487 304 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9488 304 : i_dictowner = PQfnumber(res, "dictowner");
9489 304 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9490 304 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9491 :
9492 9296 : for (i = 0; i < ntups; i++)
9493 : {
9494 8992 : dictinfo[i].dobj.objType = DO_TSDICT;
9495 8992 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9496 8992 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9497 8992 : AssignDumpId(&dictinfo[i].dobj);
9498 8992 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9499 17984 : dictinfo[i].dobj.namespace =
9500 8992 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9501 8992 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9502 8992 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9503 8992 : if (PQgetisnull(res, i, i_dictinitoption))
9504 390 : dictinfo[i].dictinitoption = NULL;
9505 : else
9506 8602 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9507 :
9508 : /* Decide whether we want to dump it */
9509 8992 : selectDumpableObject(&(dictinfo[i].dobj), fout);
9510 : }
9511 :
9512 304 : PQclear(res);
9513 :
9514 304 : destroyPQExpBuffer(query);
9515 :
9516 304 : return dictinfo;
9517 : }
9518 :
9519 : /*
9520 : * getTSTemplates:
9521 : * read all text search templates in the system catalogs and return them
9522 : * in the TSTemplateInfo* structure
9523 : *
9524 : * numTSTemplates is set to the number of templates read in
9525 : */
9526 : TSTemplateInfo *
9527 304 : getTSTemplates(Archive *fout, int *numTSTemplates)
9528 : {
9529 : PGresult *res;
9530 : int ntups;
9531 : int i;
9532 : PQExpBuffer query;
9533 : TSTemplateInfo *tmplinfo;
9534 : int i_tableoid;
9535 : int i_oid;
9536 : int i_tmplname;
9537 : int i_tmplnamespace;
9538 : int i_tmplinit;
9539 : int i_tmpllexize;
9540 :
9541 304 : query = createPQExpBuffer();
9542 :
9543 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9544 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9545 : "FROM pg_ts_template");
9546 :
9547 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9548 :
9549 304 : ntups = PQntuples(res);
9550 304 : *numTSTemplates = ntups;
9551 :
9552 304 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9553 :
9554 304 : i_tableoid = PQfnumber(res, "tableoid");
9555 304 : i_oid = PQfnumber(res, "oid");
9556 304 : i_tmplname = PQfnumber(res, "tmplname");
9557 304 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9558 304 : i_tmplinit = PQfnumber(res, "tmplinit");
9559 304 : i_tmpllexize = PQfnumber(res, "tmpllexize");
9560 :
9561 1910 : for (i = 0; i < ntups; i++)
9562 : {
9563 1606 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9564 1606 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9565 1606 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9566 1606 : AssignDumpId(&tmplinfo[i].dobj);
9567 1606 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9568 3212 : tmplinfo[i].dobj.namespace =
9569 1606 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9570 1606 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9571 1606 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9572 :
9573 : /* Decide whether we want to dump it */
9574 1606 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
9575 : }
9576 :
9577 304 : PQclear(res);
9578 :
9579 304 : destroyPQExpBuffer(query);
9580 :
9581 304 : return tmplinfo;
9582 : }
9583 :
9584 : /*
9585 : * getTSConfigurations:
9586 : * read all text search configurations in the system catalogs and return
9587 : * them in the TSConfigInfo* structure
9588 : *
9589 : * numTSConfigs is set to the number of configurations read in
9590 : */
9591 : TSConfigInfo *
9592 304 : getTSConfigurations(Archive *fout, int *numTSConfigs)
9593 : {
9594 : PGresult *res;
9595 : int ntups;
9596 : int i;
9597 : PQExpBuffer query;
9598 : TSConfigInfo *cfginfo;
9599 : int i_tableoid;
9600 : int i_oid;
9601 : int i_cfgname;
9602 : int i_cfgnamespace;
9603 : int i_cfgowner;
9604 : int i_cfgparser;
9605 :
9606 304 : query = createPQExpBuffer();
9607 :
9608 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9609 : "cfgnamespace, cfgowner, cfgparser "
9610 : "FROM pg_ts_config");
9611 :
9612 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9613 :
9614 304 : ntups = PQntuples(res);
9615 304 : *numTSConfigs = ntups;
9616 :
9617 304 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9618 :
9619 304 : i_tableoid = PQfnumber(res, "tableoid");
9620 304 : i_oid = PQfnumber(res, "oid");
9621 304 : i_cfgname = PQfnumber(res, "cfgname");
9622 304 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9623 304 : i_cfgowner = PQfnumber(res, "cfgowner");
9624 304 : i_cfgparser = PQfnumber(res, "cfgparser");
9625 :
9626 9246 : for (i = 0; i < ntups; i++)
9627 : {
9628 8942 : cfginfo[i].dobj.objType = DO_TSCONFIG;
9629 8942 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9630 8942 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9631 8942 : AssignDumpId(&cfginfo[i].dobj);
9632 8942 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9633 17884 : cfginfo[i].dobj.namespace =
9634 8942 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9635 8942 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9636 8942 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9637 :
9638 : /* Decide whether we want to dump it */
9639 8942 : selectDumpableObject(&(cfginfo[i].dobj), fout);
9640 : }
9641 :
9642 304 : PQclear(res);
9643 :
9644 304 : destroyPQExpBuffer(query);
9645 :
9646 304 : return cfginfo;
9647 : }
9648 :
9649 : /*
9650 : * getForeignDataWrappers:
9651 : * read all foreign-data wrappers in the system catalogs and return
9652 : * them in the FdwInfo* structure
9653 : *
9654 : * numForeignDataWrappers is set to the number of fdws read in
9655 : */
9656 : FdwInfo *
9657 304 : getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
9658 : {
9659 : PGresult *res;
9660 : int ntups;
9661 : int i;
9662 : PQExpBuffer query;
9663 : FdwInfo *fdwinfo;
9664 : int i_tableoid;
9665 : int i_oid;
9666 : int i_fdwname;
9667 : int i_fdwowner;
9668 : int i_fdwhandler;
9669 : int i_fdwvalidator;
9670 : int i_fdwacl;
9671 : int i_acldefault;
9672 : int i_fdwoptions;
9673 :
9674 304 : query = createPQExpBuffer();
9675 :
9676 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9677 : "fdwowner, "
9678 : "fdwhandler::pg_catalog.regproc, "
9679 : "fdwvalidator::pg_catalog.regproc, "
9680 : "fdwacl, "
9681 : "acldefault('F', fdwowner) AS acldefault, "
9682 : "array_to_string(ARRAY("
9683 : "SELECT quote_ident(option_name) || ' ' || "
9684 : "quote_literal(option_value) "
9685 : "FROM pg_options_to_table(fdwoptions) "
9686 : "ORDER BY option_name"
9687 : "), E',\n ') AS fdwoptions "
9688 : "FROM pg_foreign_data_wrapper");
9689 :
9690 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9691 :
9692 304 : ntups = PQntuples(res);
9693 304 : *numForeignDataWrappers = ntups;
9694 :
9695 304 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9696 :
9697 304 : i_tableoid = PQfnumber(res, "tableoid");
9698 304 : i_oid = PQfnumber(res, "oid");
9699 304 : i_fdwname = PQfnumber(res, "fdwname");
9700 304 : i_fdwowner = PQfnumber(res, "fdwowner");
9701 304 : i_fdwhandler = PQfnumber(res, "fdwhandler");
9702 304 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9703 304 : i_fdwacl = PQfnumber(res, "fdwacl");
9704 304 : i_acldefault = PQfnumber(res, "acldefault");
9705 304 : i_fdwoptions = PQfnumber(res, "fdwoptions");
9706 :
9707 438 : for (i = 0; i < ntups; i++)
9708 : {
9709 134 : fdwinfo[i].dobj.objType = DO_FDW;
9710 134 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9711 134 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9712 134 : AssignDumpId(&fdwinfo[i].dobj);
9713 134 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9714 134 : fdwinfo[i].dobj.namespace = NULL;
9715 134 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9716 134 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9717 134 : fdwinfo[i].dacl.privtype = 0;
9718 134 : fdwinfo[i].dacl.initprivs = NULL;
9719 134 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
9720 134 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9721 134 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9722 134 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9723 :
9724 : /* Decide whether we want to dump it */
9725 134 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
9726 :
9727 : /* Mark whether FDW has an ACL */
9728 134 : if (!PQgetisnull(res, i, i_fdwacl))
9729 86 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9730 : }
9731 :
9732 304 : PQclear(res);
9733 :
9734 304 : destroyPQExpBuffer(query);
9735 :
9736 304 : return fdwinfo;
9737 : }
9738 :
9739 : /*
9740 : * getForeignServers:
9741 : * read all foreign servers in the system catalogs and return
9742 : * them in the ForeignServerInfo * structure
9743 : *
9744 : * numForeignServers is set to the number of servers read in
9745 : */
9746 : ForeignServerInfo *
9747 304 : getForeignServers(Archive *fout, int *numForeignServers)
9748 : {
9749 : PGresult *res;
9750 : int ntups;
9751 : int i;
9752 : PQExpBuffer query;
9753 : ForeignServerInfo *srvinfo;
9754 : int i_tableoid;
9755 : int i_oid;
9756 : int i_srvname;
9757 : int i_srvowner;
9758 : int i_srvfdw;
9759 : int i_srvtype;
9760 : int i_srvversion;
9761 : int i_srvacl;
9762 : int i_acldefault;
9763 : int i_srvoptions;
9764 :
9765 304 : query = createPQExpBuffer();
9766 :
9767 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
9768 : "srvowner, "
9769 : "srvfdw, srvtype, srvversion, srvacl, "
9770 : "acldefault('S', srvowner) AS acldefault, "
9771 : "array_to_string(ARRAY("
9772 : "SELECT quote_ident(option_name) || ' ' || "
9773 : "quote_literal(option_value) "
9774 : "FROM pg_options_to_table(srvoptions) "
9775 : "ORDER BY option_name"
9776 : "), E',\n ') AS srvoptions "
9777 : "FROM pg_foreign_server");
9778 :
9779 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9780 :
9781 304 : ntups = PQntuples(res);
9782 304 : *numForeignServers = ntups;
9783 :
9784 304 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9785 :
9786 304 : i_tableoid = PQfnumber(res, "tableoid");
9787 304 : i_oid = PQfnumber(res, "oid");
9788 304 : i_srvname = PQfnumber(res, "srvname");
9789 304 : i_srvowner = PQfnumber(res, "srvowner");
9790 304 : i_srvfdw = PQfnumber(res, "srvfdw");
9791 304 : i_srvtype = PQfnumber(res, "srvtype");
9792 304 : i_srvversion = PQfnumber(res, "srvversion");
9793 304 : i_srvacl = PQfnumber(res, "srvacl");
9794 304 : i_acldefault = PQfnumber(res, "acldefault");
9795 304 : i_srvoptions = PQfnumber(res, "srvoptions");
9796 :
9797 446 : for (i = 0; i < ntups; i++)
9798 : {
9799 142 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9800 142 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9801 142 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9802 142 : AssignDumpId(&srvinfo[i].dobj);
9803 142 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9804 142 : srvinfo[i].dobj.namespace = NULL;
9805 142 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9806 142 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9807 142 : srvinfo[i].dacl.privtype = 0;
9808 142 : srvinfo[i].dacl.initprivs = NULL;
9809 142 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
9810 142 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9811 142 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9812 142 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9813 142 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9814 :
9815 : /* Decide whether we want to dump it */
9816 142 : selectDumpableObject(&(srvinfo[i].dobj), fout);
9817 :
9818 : /* Servers have user mappings */
9819 142 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
9820 :
9821 : /* Mark whether server has an ACL */
9822 142 : if (!PQgetisnull(res, i, i_srvacl))
9823 86 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9824 : }
9825 :
9826 304 : PQclear(res);
9827 :
9828 304 : destroyPQExpBuffer(query);
9829 :
9830 304 : return srvinfo;
9831 : }
9832 :
9833 : /*
9834 : * getDefaultACLs:
9835 : * read all default ACL information in the system catalogs and return
9836 : * them in the DefaultACLInfo structure
9837 : *
9838 : * numDefaultACLs is set to the number of ACLs read in
9839 : */
9840 : DefaultACLInfo *
9841 304 : getDefaultACLs(Archive *fout, int *numDefaultACLs)
9842 : {
9843 304 : DumpOptions *dopt = fout->dopt;
9844 : DefaultACLInfo *daclinfo;
9845 : PQExpBuffer query;
9846 : PGresult *res;
9847 : int i_oid;
9848 : int i_tableoid;
9849 : int i_defaclrole;
9850 : int i_defaclnamespace;
9851 : int i_defaclobjtype;
9852 : int i_defaclacl;
9853 : int i_acldefault;
9854 : int i,
9855 : ntups;
9856 :
9857 304 : query = createPQExpBuffer();
9858 :
9859 : /*
9860 : * Global entries (with defaclnamespace=0) replace the hard-wired default
9861 : * ACL for their object type. We should dump them as deltas from the
9862 : * default ACL, since that will be used as a starting point for
9863 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
9864 : * non-global entries can only add privileges not revoke them. We must
9865 : * dump those as-is (i.e., as deltas from an empty ACL).
9866 : *
9867 : * We can use defaclobjtype as the object type for acldefault(), except
9868 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
9869 : * 's'.
9870 : */
9871 304 : appendPQExpBufferStr(query,
9872 : "SELECT oid, tableoid, "
9873 : "defaclrole, "
9874 : "defaclnamespace, "
9875 : "defaclobjtype, "
9876 : "defaclacl, "
9877 : "CASE WHEN defaclnamespace = 0 THEN "
9878 : "acldefault(CASE WHEN defaclobjtype = 'S' "
9879 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
9880 : "defaclrole) ELSE '{}' END AS acldefault "
9881 : "FROM pg_default_acl");
9882 :
9883 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9884 :
9885 304 : ntups = PQntuples(res);
9886 304 : *numDefaultACLs = ntups;
9887 :
9888 304 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9889 :
9890 304 : i_oid = PQfnumber(res, "oid");
9891 304 : i_tableoid = PQfnumber(res, "tableoid");
9892 304 : i_defaclrole = PQfnumber(res, "defaclrole");
9893 304 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9894 304 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9895 304 : i_defaclacl = PQfnumber(res, "defaclacl");
9896 304 : i_acldefault = PQfnumber(res, "acldefault");
9897 :
9898 648 : for (i = 0; i < ntups; i++)
9899 : {
9900 344 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9901 :
9902 344 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9903 344 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9904 344 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9905 344 : AssignDumpId(&daclinfo[i].dobj);
9906 : /* cheesy ... is it worth coming up with a better object name? */
9907 344 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
9908 :
9909 344 : if (nspid != InvalidOid)
9910 172 : daclinfo[i].dobj.namespace = findNamespace(nspid);
9911 : else
9912 172 : daclinfo[i].dobj.namespace = NULL;
9913 :
9914 344 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
9915 344 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9916 344 : daclinfo[i].dacl.privtype = 0;
9917 344 : daclinfo[i].dacl.initprivs = NULL;
9918 344 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
9919 344 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
9920 :
9921 : /* Default ACLs are ACLs, of course */
9922 344 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9923 :
9924 : /* Decide whether we want to dump it */
9925 344 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
9926 : }
9927 :
9928 304 : PQclear(res);
9929 :
9930 304 : destroyPQExpBuffer(query);
9931 :
9932 304 : return daclinfo;
9933 : }
9934 :
9935 : /*
9936 : * getRoleName -- look up the name of a role, given its OID
9937 : *
9938 : * In current usage, we don't expect failures, so error out for a bad OID.
9939 : */
9940 : static const char *
9941 943016 : getRoleName(const char *roleoid_str)
9942 : {
9943 943016 : Oid roleoid = atooid(roleoid_str);
9944 :
9945 : /*
9946 : * Do binary search to find the appropriate item.
9947 : */
9948 943016 : if (nrolenames > 0)
9949 : {
9950 943016 : RoleNameItem *low = &rolenames[0];
9951 943016 : RoleNameItem *high = &rolenames[nrolenames - 1];
9952 :
9953 3771840 : while (low <= high)
9954 : {
9955 3771840 : RoleNameItem *middle = low + (high - low) / 2;
9956 :
9957 3771840 : if (roleoid < middle->roleoid)
9958 2826854 : high = middle - 1;
9959 944986 : else if (roleoid > middle->roleoid)
9960 1970 : low = middle + 1;
9961 : else
9962 943016 : return middle->rolename; /* found a match */
9963 : }
9964 : }
9965 :
9966 0 : pg_fatal("role with OID %u does not exist", roleoid);
9967 : return NULL; /* keep compiler quiet */
9968 : }
9969 :
9970 : /*
9971 : * collectRoleNames --
9972 : *
9973 : * Construct a table of all known roles.
9974 : * The table is sorted by OID for speed in lookup.
9975 : */
9976 : static void
9977 306 : collectRoleNames(Archive *fout)
9978 : {
9979 : PGresult *res;
9980 : const char *query;
9981 : int i;
9982 :
9983 306 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
9984 :
9985 306 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
9986 :
9987 306 : nrolenames = PQntuples(res);
9988 :
9989 306 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
9990 :
9991 5602 : for (i = 0; i < nrolenames; i++)
9992 : {
9993 5296 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
9994 5296 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
9995 : }
9996 :
9997 306 : PQclear(res);
9998 306 : }
9999 :
10000 : /*
10001 : * getAdditionalACLs
10002 : *
10003 : * We have now created all the DumpableObjects, and collected the ACL data
10004 : * that appears in the directly-associated catalog entries. However, there's
10005 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10006 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10007 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10008 : * Also, in versions having the pg_init_privs catalog, read that and load the
10009 : * information into the relevant DumpableObjects.
10010 : */
10011 : static void
10012 300 : getAdditionalACLs(Archive *fout)
10013 : {
10014 300 : PQExpBuffer query = createPQExpBuffer();
10015 : PGresult *res;
10016 : int ntups,
10017 : i;
10018 :
10019 : /* Check for per-column ACLs */
10020 300 : appendPQExpBufferStr(query,
10021 : "SELECT DISTINCT attrelid FROM pg_attribute "
10022 : "WHERE attacl IS NOT NULL");
10023 :
10024 300 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10025 :
10026 300 : ntups = PQntuples(res);
10027 920 : for (i = 0; i < ntups; i++)
10028 : {
10029 620 : Oid relid = atooid(PQgetvalue(res, i, 0));
10030 : TableInfo *tblinfo;
10031 :
10032 620 : tblinfo = findTableByOid(relid);
10033 : /* OK to ignore tables we haven't got a DumpableObject for */
10034 620 : if (tblinfo)
10035 : {
10036 620 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10037 620 : tblinfo->hascolumnACLs = true;
10038 : }
10039 : }
10040 300 : PQclear(res);
10041 :
10042 : /* Fetch initial-privileges data */
10043 300 : if (fout->remoteVersion >= 90600)
10044 : {
10045 300 : printfPQExpBuffer(query,
10046 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10047 : "FROM pg_init_privs");
10048 :
10049 300 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10050 :
10051 300 : ntups = PQntuples(res);
10052 67816 : for (i = 0; i < ntups; i++)
10053 : {
10054 67516 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10055 67516 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10056 67516 : int objsubid = atoi(PQgetvalue(res, i, 2));
10057 67516 : char privtype = *(PQgetvalue(res, i, 3));
10058 67516 : char *initprivs = PQgetvalue(res, i, 4);
10059 : CatalogId objId;
10060 : DumpableObject *dobj;
10061 :
10062 67516 : objId.tableoid = classoid;
10063 67516 : objId.oid = objoid;
10064 67516 : dobj = findObjectByCatalogId(objId);
10065 : /* OK to ignore entries we haven't got a DumpableObject for */
10066 67516 : if (dobj)
10067 : {
10068 : /* Cope with sub-object initprivs */
10069 49000 : if (objsubid != 0)
10070 : {
10071 5148 : if (dobj->objType == DO_TABLE)
10072 : {
10073 : /* For a column initprivs, set the table's ACL flags */
10074 5148 : dobj->components |= DUMP_COMPONENT_ACL;
10075 5148 : ((TableInfo *) dobj)->hascolumnACLs = true;
10076 : }
10077 : else
10078 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10079 : classoid, objoid, objsubid);
10080 5440 : continue;
10081 : }
10082 :
10083 : /*
10084 : * We ignore any pg_init_privs.initprivs entry for the public
10085 : * schema, as explained in getNamespaces().
10086 : */
10087 43852 : if (dobj->objType == DO_NAMESPACE &&
10088 592 : strcmp(dobj->name, "public") == 0)
10089 292 : continue;
10090 :
10091 : /* Else it had better be of a type we think has ACLs */
10092 43560 : if (dobj->objType == DO_NAMESPACE ||
10093 43260 : dobj->objType == DO_TYPE ||
10094 43212 : dobj->objType == DO_FUNC ||
10095 43032 : dobj->objType == DO_AGG ||
10096 42984 : dobj->objType == DO_TABLE ||
10097 0 : dobj->objType == DO_PROCLANG ||
10098 0 : dobj->objType == DO_FDW ||
10099 0 : dobj->objType == DO_FOREIGN_SERVER)
10100 43560 : {
10101 43560 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10102 :
10103 43560 : daobj->dacl.privtype = privtype;
10104 43560 : daobj->dacl.initprivs = pstrdup(initprivs);
10105 : }
10106 : else
10107 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10108 : classoid, objoid, objsubid);
10109 : }
10110 : }
10111 300 : PQclear(res);
10112 : }
10113 :
10114 300 : destroyPQExpBuffer(query);
10115 300 : }
10116 :
10117 : /*
10118 : * dumpCommentExtended --
10119 : *
10120 : * This routine is used to dump any comments associated with the
10121 : * object handed to this routine. The routine takes the object type
10122 : * and object name (ready to print, except for schema decoration), plus
10123 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10124 : * plus catalog ID and subid which are the lookup key for pg_description,
10125 : * plus the dump ID for the object (for setting a dependency).
10126 : * If a matching pg_description entry is found, it is dumped.
10127 : *
10128 : * Note: in some cases, such as comments for triggers and rules, the "type"
10129 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10130 : * but it doesn't seem worth complicating the API for all callers to make
10131 : * it cleaner.
10132 : *
10133 : * Note: although this routine takes a dumpId for dependency purposes,
10134 : * that purpose is just to mark the dependency in the emitted dump file
10135 : * for possible future use by pg_restore. We do NOT use it for determining
10136 : * ordering of the comment in the dump file, because this routine is called
10137 : * after dependency sorting occurs. This routine should be called just after
10138 : * calling ArchiveEntry() for the specified object.
10139 : */
10140 : static void
10141 5138 : dumpCommentExtended(Archive *fout, const char *type,
10142 : const char *name, const char *namespace,
10143 : const char *owner, CatalogId catalogId,
10144 : int subid, DumpId dumpId,
10145 : const char *initdb_comment)
10146 : {
10147 5138 : DumpOptions *dopt = fout->dopt;
10148 : CommentItem *comments;
10149 : int ncomments;
10150 :
10151 : /* do nothing, if --no-comments is supplied */
10152 5138 : if (dopt->no_comments)
10153 0 : return;
10154 :
10155 : /* Comments are schema not data ... except LO comments are data */
10156 5138 : if (strcmp(type, "LARGE OBJECT") != 0)
10157 : {
10158 5050 : if (dopt->dataOnly)
10159 0 : return;
10160 : }
10161 : else
10162 : {
10163 : /* We do dump LO comments in binary-upgrade mode */
10164 88 : if (dopt->schemaOnly && !dopt->binary_upgrade)
10165 0 : return;
10166 : }
10167 :
10168 : /* Search for comments associated with catalogId, using table */
10169 5138 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10170 : &comments);
10171 :
10172 : /* Is there one matching the subid? */
10173 5138 : while (ncomments > 0)
10174 : {
10175 5064 : if (comments->objsubid == subid)
10176 5064 : break;
10177 0 : comments++;
10178 0 : ncomments--;
10179 : }
10180 :
10181 5138 : if (initdb_comment != NULL)
10182 : {
10183 : static CommentItem empty_comment = {.descr = ""};
10184 :
10185 : /*
10186 : * initdb creates this object with a comment. Skip dumping the
10187 : * initdb-provided comment, which would complicate matters for
10188 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10189 : * comment, replicate that.
10190 : */
10191 220 : if (ncomments == 0)
10192 : {
10193 8 : comments = &empty_comment;
10194 8 : ncomments = 1;
10195 : }
10196 212 : else if (strcmp(comments->descr, initdb_comment) == 0)
10197 212 : ncomments = 0;
10198 : }
10199 :
10200 : /* If a comment exists, build COMMENT ON statement */
10201 5138 : if (ncomments > 0)
10202 : {
10203 4860 : PQExpBuffer query = createPQExpBuffer();
10204 4860 : PQExpBuffer tag = createPQExpBuffer();
10205 :
10206 4860 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10207 4860 : if (namespace && *namespace)
10208 4570 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10209 4860 : appendPQExpBuffer(query, "%s IS ", name);
10210 4860 : appendStringLiteralAH(query, comments->descr, fout);
10211 4860 : appendPQExpBufferStr(query, ";\n");
10212 :
10213 4860 : appendPQExpBuffer(tag, "%s %s", type, name);
10214 :
10215 : /*
10216 : * We mark comments as SECTION_NONE because they really belong in the
10217 : * same section as their parent, whether that is pre-data or
10218 : * post-data.
10219 : */
10220 4860 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10221 4860 : ARCHIVE_OPTS(.tag = tag->data,
10222 : .namespace = namespace,
10223 : .owner = owner,
10224 : .description = "COMMENT",
10225 : .section = SECTION_NONE,
10226 : .createStmt = query->data,
10227 : .deps = &dumpId,
10228 : .nDeps = 1));
10229 :
10230 4860 : destroyPQExpBuffer(query);
10231 4860 : destroyPQExpBuffer(tag);
10232 : }
10233 : }
10234 :
10235 : /*
10236 : * dumpComment --
10237 : *
10238 : * Typical simplification of the above function.
10239 : */
10240 : static inline void
10241 4888 : dumpComment(Archive *fout, const char *type,
10242 : const char *name, const char *namespace,
10243 : const char *owner, CatalogId catalogId,
10244 : int subid, DumpId dumpId)
10245 : {
10246 4888 : dumpCommentExtended(fout, type, name, namespace, owner,
10247 : catalogId, subid, dumpId, NULL);
10248 4888 : }
10249 :
10250 : /*
10251 : * dumpTableComment --
10252 : *
10253 : * As above, but dump comments for both the specified table (or view)
10254 : * and its columns.
10255 : */
10256 : static void
10257 152 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10258 : const char *reltypename)
10259 : {
10260 152 : DumpOptions *dopt = fout->dopt;
10261 : CommentItem *comments;
10262 : int ncomments;
10263 : PQExpBuffer query;
10264 : PQExpBuffer tag;
10265 :
10266 : /* do nothing, if --no-comments is supplied */
10267 152 : if (dopt->no_comments)
10268 0 : return;
10269 :
10270 : /* Comments are SCHEMA not data */
10271 152 : if (dopt->dataOnly)
10272 0 : return;
10273 :
10274 : /* Search for comments associated with relation, using table */
10275 152 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
10276 : tbinfo->dobj.catId.oid,
10277 : &comments);
10278 :
10279 : /* If comments exist, build COMMENT ON statements */
10280 152 : if (ncomments <= 0)
10281 0 : return;
10282 :
10283 152 : query = createPQExpBuffer();
10284 152 : tag = createPQExpBuffer();
10285 :
10286 436 : while (ncomments > 0)
10287 : {
10288 284 : const char *descr = comments->descr;
10289 284 : int objsubid = comments->objsubid;
10290 :
10291 284 : if (objsubid == 0)
10292 : {
10293 66 : resetPQExpBuffer(tag);
10294 66 : appendPQExpBuffer(tag, "%s %s", reltypename,
10295 66 : fmtId(tbinfo->dobj.name));
10296 :
10297 66 : resetPQExpBuffer(query);
10298 66 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10299 66 : fmtQualifiedDumpable(tbinfo));
10300 66 : appendStringLiteralAH(query, descr, fout);
10301 66 : appendPQExpBufferStr(query, ";\n");
10302 :
10303 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10304 66 : ARCHIVE_OPTS(.tag = tag->data,
10305 : .namespace = tbinfo->dobj.namespace->dobj.name,
10306 : .owner = tbinfo->rolname,
10307 : .description = "COMMENT",
10308 : .section = SECTION_NONE,
10309 : .createStmt = query->data,
10310 : .deps = &(tbinfo->dobj.dumpId),
10311 : .nDeps = 1));
10312 : }
10313 218 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10314 : {
10315 218 : resetPQExpBuffer(tag);
10316 218 : appendPQExpBuffer(tag, "COLUMN %s.",
10317 218 : fmtId(tbinfo->dobj.name));
10318 218 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10319 :
10320 218 : resetPQExpBuffer(query);
10321 218 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10322 218 : fmtQualifiedDumpable(tbinfo));
10323 218 : appendPQExpBuffer(query, "%s IS ",
10324 218 : fmtId(tbinfo->attnames[objsubid - 1]));
10325 218 : appendStringLiteralAH(query, descr, fout);
10326 218 : appendPQExpBufferStr(query, ";\n");
10327 :
10328 218 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10329 218 : ARCHIVE_OPTS(.tag = tag->data,
10330 : .namespace = tbinfo->dobj.namespace->dobj.name,
10331 : .owner = tbinfo->rolname,
10332 : .description = "COMMENT",
10333 : .section = SECTION_NONE,
10334 : .createStmt = query->data,
10335 : .deps = &(tbinfo->dobj.dumpId),
10336 : .nDeps = 1));
10337 : }
10338 :
10339 284 : comments++;
10340 284 : ncomments--;
10341 : }
10342 :
10343 152 : destroyPQExpBuffer(query);
10344 152 : destroyPQExpBuffer(tag);
10345 : }
10346 :
10347 : /*
10348 : * findComments --
10349 : *
10350 : * Find the comment(s), if any, associated with the given object. All the
10351 : * objsubid values associated with the given classoid/objoid are found with
10352 : * one search.
10353 : */
10354 : static int
10355 5356 : findComments(Oid classoid, Oid objoid, CommentItem **items)
10356 : {
10357 5356 : CommentItem *middle = NULL;
10358 : CommentItem *low;
10359 : CommentItem *high;
10360 : int nmatch;
10361 :
10362 : /*
10363 : * Do binary search to find some item matching the object.
10364 : */
10365 5356 : low = &comments[0];
10366 5356 : high = &comments[ncomments - 1];
10367 53442 : while (low <= high)
10368 : {
10369 53368 : middle = low + (high - low) / 2;
10370 :
10371 53368 : if (classoid < middle->classoid)
10372 9020 : high = middle - 1;
10373 44348 : else if (classoid > middle->classoid)
10374 7782 : low = middle + 1;
10375 36566 : else if (objoid < middle->objoid)
10376 14324 : high = middle - 1;
10377 22242 : else if (objoid > middle->objoid)
10378 16960 : low = middle + 1;
10379 : else
10380 5282 : break; /* found a match */
10381 : }
10382 :
10383 5356 : if (low > high) /* no matches */
10384 : {
10385 74 : *items = NULL;
10386 74 : return 0;
10387 : }
10388 :
10389 : /*
10390 : * Now determine how many items match the object. The search loop
10391 : * invariant still holds: only items between low and high inclusive could
10392 : * match.
10393 : */
10394 5282 : nmatch = 1;
10395 5282 : while (middle > low)
10396 : {
10397 2420 : if (classoid != middle[-1].classoid ||
10398 2248 : objoid != middle[-1].objoid)
10399 : break;
10400 0 : middle--;
10401 0 : nmatch++;
10402 : }
10403 :
10404 5282 : *items = middle;
10405 :
10406 5282 : middle += nmatch;
10407 5414 : while (middle <= high)
10408 : {
10409 2762 : if (classoid != middle->classoid ||
10410 2302 : objoid != middle->objoid)
10411 : break;
10412 132 : middle++;
10413 132 : nmatch++;
10414 : }
10415 :
10416 5282 : return nmatch;
10417 : }
10418 :
10419 : /*
10420 : * collectComments --
10421 : *
10422 : * Construct a table of all comments available for database objects;
10423 : * also set the has-comment component flag for each relevant object.
10424 : *
10425 : * We used to do per-object queries for the comments, but it's much faster
10426 : * to pull them all over at once, and on most databases the memory cost
10427 : * isn't high.
10428 : *
10429 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
10430 : */
10431 : static void
10432 304 : collectComments(Archive *fout)
10433 : {
10434 : PGresult *res;
10435 : PQExpBuffer query;
10436 : int i_description;
10437 : int i_classoid;
10438 : int i_objoid;
10439 : int i_objsubid;
10440 : int ntups;
10441 : int i;
10442 : DumpableObject *dobj;
10443 :
10444 304 : query = createPQExpBuffer();
10445 :
10446 304 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10447 : "FROM pg_catalog.pg_description "
10448 : "ORDER BY classoid, objoid, objsubid");
10449 :
10450 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10451 :
10452 : /* Construct lookup table containing OIDs in numeric form */
10453 :
10454 304 : i_description = PQfnumber(res, "description");
10455 304 : i_classoid = PQfnumber(res, "classoid");
10456 304 : i_objoid = PQfnumber(res, "objoid");
10457 304 : i_objsubid = PQfnumber(res, "objsubid");
10458 :
10459 304 : ntups = PQntuples(res);
10460 :
10461 304 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10462 304 : ncomments = 0;
10463 304 : dobj = NULL;
10464 :
10465 1584506 : for (i = 0; i < ntups; i++)
10466 : {
10467 : CatalogId objId;
10468 : int subid;
10469 :
10470 1584202 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
10471 1584202 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
10472 1584202 : subid = atoi(PQgetvalue(res, i, i_objsubid));
10473 :
10474 : /* We needn't remember comments that don't match any dumpable object */
10475 1584202 : if (dobj == NULL ||
10476 575924 : dobj->catId.tableoid != objId.tableoid ||
10477 572138 : dobj->catId.oid != objId.oid)
10478 1584030 : dobj = findObjectByCatalogId(objId);
10479 1584202 : if (dobj == NULL)
10480 1007982 : continue;
10481 :
10482 : /*
10483 : * Comments on columns of composite types are linked to the type's
10484 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
10485 : * in the type's own DumpableObject.
10486 : */
10487 576220 : if (subid != 0 && dobj->objType == DO_TABLE &&
10488 364 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
10489 86 : {
10490 : TypeInfo *cTypeInfo;
10491 :
10492 86 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
10493 86 : if (cTypeInfo)
10494 86 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
10495 : }
10496 : else
10497 576134 : dobj->components |= DUMP_COMPONENT_COMMENT;
10498 :
10499 576220 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
10500 576220 : comments[ncomments].classoid = objId.tableoid;
10501 576220 : comments[ncomments].objoid = objId.oid;
10502 576220 : comments[ncomments].objsubid = subid;
10503 576220 : ncomments++;
10504 : }
10505 :
10506 304 : PQclear(res);
10507 304 : destroyPQExpBuffer(query);
10508 304 : }
10509 :
10510 : /*
10511 : * dumpDumpableObject
10512 : *
10513 : * This routine and its subsidiaries are responsible for creating
10514 : * ArchiveEntries (TOC objects) for each object to be dumped.
10515 : */
10516 : static void
10517 1098624 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
10518 : {
10519 : /*
10520 : * Clear any dump-request bits for components that don't exist for this
10521 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
10522 : * request for every kind of object.)
10523 : */
10524 1098624 : dobj->dump &= dobj->components;
10525 :
10526 : /* Now, short-circuit if there's nothing to be done here. */
10527 1098624 : if (dobj->dump == 0)
10528 985614 : return;
10529 :
10530 113010 : switch (dobj->objType)
10531 : {
10532 770 : case DO_NAMESPACE:
10533 770 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
10534 770 : break;
10535 38 : case DO_EXTENSION:
10536 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
10537 38 : break;
10538 1258 : case DO_TYPE:
10539 1258 : dumpType(fout, (const TypeInfo *) dobj);
10540 1258 : break;
10541 142 : case DO_SHELL_TYPE:
10542 142 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
10543 142 : break;
10544 3518 : case DO_FUNC:
10545 3518 : dumpFunc(fout, (const FuncInfo *) dobj);
10546 3518 : break;
10547 580 : case DO_AGG:
10548 580 : dumpAgg(fout, (const AggInfo *) dobj);
10549 580 : break;
10550 1808 : case DO_OPERATOR:
10551 1808 : dumpOpr(fout, (const OprInfo *) dobj);
10552 1808 : break;
10553 152 : case DO_ACCESS_METHOD:
10554 152 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
10555 152 : break;
10556 600 : case DO_OPCLASS:
10557 600 : dumpOpclass(fout, (const OpclassInfo *) dobj);
10558 600 : break;
10559 506 : case DO_OPFAMILY:
10560 506 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
10561 506 : break;
10562 1744 : case DO_COLLATION:
10563 1744 : dumpCollation(fout, (const CollInfo *) dobj);
10564 1744 : break;
10565 328 : case DO_CONVERSION:
10566 328 : dumpConversion(fout, (const ConvInfo *) dobj);
10567 328 : break;
10568 49404 : case DO_TABLE:
10569 49404 : dumpTable(fout, (const TableInfo *) dobj);
10570 49404 : break;
10571 2476 : case DO_TABLE_ATTACH:
10572 2476 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
10573 2476 : break;
10574 1570 : case DO_ATTRDEF:
10575 1570 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
10576 1570 : break;
10577 4324 : case DO_INDEX:
10578 4324 : dumpIndex(fout, (const IndxInfo *) dobj);
10579 4324 : break;
10580 1086 : case DO_INDEX_ATTACH:
10581 1086 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
10582 1086 : break;
10583 254 : case DO_STATSEXT:
10584 254 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
10585 254 : break;
10586 676 : case DO_REFRESH_MATVIEW:
10587 676 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
10588 676 : break;
10589 1820 : case DO_RULE:
10590 1820 : dumpRule(fout, (const RuleInfo *) dobj);
10591 1820 : break;
10592 986 : case DO_TRIGGER:
10593 986 : dumpTrigger(fout, (const TriggerInfo *) dobj);
10594 986 : break;
10595 80 : case DO_EVENT_TRIGGER:
10596 80 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
10597 80 : break;
10598 3572 : case DO_CONSTRAINT:
10599 3572 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
10600 3572 : break;
10601 344 : case DO_FK_CONSTRAINT:
10602 344 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
10603 344 : break;
10604 156 : case DO_PROCLANG:
10605 156 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
10606 156 : break;
10607 130 : case DO_CAST:
10608 130 : dumpCast(fout, (const CastInfo *) dobj);
10609 130 : break;
10610 80 : case DO_TRANSFORM:
10611 80 : dumpTransform(fout, (const TransformInfo *) dobj);
10612 80 : break;
10613 752 : case DO_SEQUENCE_SET:
10614 752 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
10615 752 : break;
10616 7052 : case DO_TABLE_DATA:
10617 7052 : dumpTableData(fout, (const TableDataInfo *) dobj);
10618 7052 : break;
10619 23168 : case DO_DUMMY_TYPE:
10620 : /* table rowtypes and array types are never dumped separately */
10621 23168 : break;
10622 74 : case DO_TSPARSER:
10623 74 : dumpTSParser(fout, (const TSParserInfo *) dobj);
10624 74 : break;
10625 220 : case DO_TSDICT:
10626 220 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
10627 220 : break;
10628 82 : case DO_TSTEMPLATE:
10629 82 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
10630 82 : break;
10631 170 : case DO_TSCONFIG:
10632 170 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
10633 170 : break;
10634 100 : case DO_FDW:
10635 100 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
10636 100 : break;
10637 108 : case DO_FOREIGN_SERVER:
10638 108 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
10639 108 : break;
10640 284 : case DO_DEFAULT_ACL:
10641 284 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
10642 284 : break;
10643 166 : case DO_LARGE_OBJECT:
10644 166 : dumpLO(fout, (const LoInfo *) dobj);
10645 166 : break;
10646 78 : case DO_LARGE_OBJECT_DATA:
10647 78 : if (dobj->dump & DUMP_COMPONENT_DATA)
10648 : {
10649 : TocEntry *te;
10650 :
10651 78 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
10652 78 : ARCHIVE_OPTS(.tag = dobj->name,
10653 : .description = "BLOBS",
10654 : .section = SECTION_DATA,
10655 : .dumpFn = dumpLOs));
10656 :
10657 : /*
10658 : * Set the TocEntry's dataLength in case we are doing a
10659 : * parallel dump and want to order dump jobs by table size.
10660 : * (We need some size estimate for every TocEntry with a
10661 : * DataDumper function.) We don't currently have any cheap
10662 : * way to estimate the size of LOs, but it doesn't matter;
10663 : * let's just set the size to a large value so parallel dumps
10664 : * will launch this job first. If there's lots of LOs, we
10665 : * win, and if there aren't, we don't lose much. (If you want
10666 : * to improve on this, really what you should be thinking
10667 : * about is allowing LO dumping to be parallelized, not just
10668 : * getting a smarter estimate for the single TOC entry.)
10669 : */
10670 78 : te->dataLength = INT_MAX;
10671 : }
10672 78 : break;
10673 638 : case DO_POLICY:
10674 638 : dumpPolicy(fout, (const PolicyInfo *) dobj);
10675 638 : break;
10676 282 : case DO_PUBLICATION:
10677 282 : dumpPublication(fout, (const PublicationInfo *) dobj);
10678 282 : break;
10679 470 : case DO_PUBLICATION_REL:
10680 470 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
10681 470 : break;
10682 138 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
10683 138 : dumpPublicationNamespace(fout,
10684 : (const PublicationSchemaInfo *) dobj);
10685 138 : break;
10686 214 : case DO_SUBSCRIPTION:
10687 214 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
10688 214 : break;
10689 4 : case DO_SUBSCRIPTION_REL:
10690 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
10691 4 : break;
10692 608 : case DO_PRE_DATA_BOUNDARY:
10693 : case DO_POST_DATA_BOUNDARY:
10694 : /* never dumped, nothing to do */
10695 608 : break;
10696 : }
10697 : }
10698 :
10699 : /*
10700 : * dumpNamespace
10701 : * writes out to fout the queries to recreate a user-defined namespace
10702 : */
10703 : static void
10704 770 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
10705 : {
10706 770 : DumpOptions *dopt = fout->dopt;
10707 : PQExpBuffer q;
10708 : PQExpBuffer delq;
10709 : char *qnspname;
10710 :
10711 : /* Do nothing in data-only dump */
10712 770 : if (dopt->dataOnly)
10713 32 : return;
10714 :
10715 738 : q = createPQExpBuffer();
10716 738 : delq = createPQExpBuffer();
10717 :
10718 738 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
10719 :
10720 738 : if (nspinfo->create)
10721 : {
10722 494 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
10723 494 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
10724 : }
10725 : else
10726 : {
10727 : /* see selectDumpableNamespace() */
10728 244 : appendPQExpBufferStr(delq,
10729 : "-- *not* dropping schema, since initdb creates it\n");
10730 244 : appendPQExpBufferStr(q,
10731 : "-- *not* creating schema, since initdb creates it\n");
10732 : }
10733 :
10734 738 : if (dopt->binary_upgrade)
10735 80 : binary_upgrade_extension_member(q, &nspinfo->dobj,
10736 : "SCHEMA", qnspname, NULL);
10737 :
10738 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10739 312 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
10740 312 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
10741 : .owner = nspinfo->rolname,
10742 : .description = "SCHEMA",
10743 : .section = SECTION_PRE_DATA,
10744 : .createStmt = q->data,
10745 : .dropStmt = delq->data));
10746 :
10747 : /* Dump Schema Comments and Security Labels */
10748 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10749 : {
10750 250 : const char *initdb_comment = NULL;
10751 :
10752 250 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
10753 220 : initdb_comment = "standard public schema";
10754 250 : dumpCommentExtended(fout, "SCHEMA", qnspname,
10755 : NULL, nspinfo->rolname,
10756 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
10757 : initdb_comment);
10758 : }
10759 :
10760 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10761 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
10762 : NULL, nspinfo->rolname,
10763 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
10764 :
10765 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
10766 572 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
10767 : qnspname, NULL, NULL,
10768 : nspinfo->rolname, &nspinfo->dacl);
10769 :
10770 738 : free(qnspname);
10771 :
10772 738 : destroyPQExpBuffer(q);
10773 738 : destroyPQExpBuffer(delq);
10774 : }
10775 :
10776 : /*
10777 : * dumpExtension
10778 : * writes out to fout the queries to recreate an extension
10779 : */
10780 : static void
10781 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
10782 : {
10783 38 : DumpOptions *dopt = fout->dopt;
10784 : PQExpBuffer q;
10785 : PQExpBuffer delq;
10786 : char *qextname;
10787 :
10788 : /* Do nothing in data-only dump */
10789 38 : if (dopt->dataOnly)
10790 2 : return;
10791 :
10792 36 : q = createPQExpBuffer();
10793 36 : delq = createPQExpBuffer();
10794 :
10795 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
10796 :
10797 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
10798 :
10799 36 : if (!dopt->binary_upgrade)
10800 : {
10801 : /*
10802 : * In a regular dump, we simply create the extension, intentionally
10803 : * not specifying a version, so that the destination installation's
10804 : * default version is used.
10805 : *
10806 : * Use of IF NOT EXISTS here is unlike our behavior for other object
10807 : * types; but there are various scenarios in which it's convenient to
10808 : * manually create the desired extension before restoring, so we
10809 : * prefer to allow it to exist already.
10810 : */
10811 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
10812 34 : qextname, fmtId(extinfo->namespace));
10813 : }
10814 : else
10815 : {
10816 : /*
10817 : * In binary-upgrade mode, it's critical to reproduce the state of the
10818 : * database exactly, so our procedure is to create an empty extension,
10819 : * restore all the contained objects normally, and add them to the
10820 : * extension one by one. This function performs just the first of
10821 : * those steps. binary_upgrade_extension_member() takes care of
10822 : * adding member objects as they're created.
10823 : */
10824 : int i;
10825 : int n;
10826 :
10827 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
10828 :
10829 : /*
10830 : * We unconditionally create the extension, so we must drop it if it
10831 : * exists. This could happen if the user deleted 'plpgsql' and then
10832 : * readded it, causing its oid to be greater than g_last_builtin_oid.
10833 : */
10834 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
10835 :
10836 2 : appendPQExpBufferStr(q,
10837 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
10838 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
10839 2 : appendPQExpBufferStr(q, ", ");
10840 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
10841 2 : appendPQExpBufferStr(q, ", ");
10842 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
10843 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
10844 2 : appendPQExpBufferStr(q, ", ");
10845 :
10846 : /*
10847 : * Note that we're pushing extconfig (an OID array) back into
10848 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
10849 : * preserved in binary upgrade.
10850 : */
10851 2 : if (strlen(extinfo->extconfig) > 2)
10852 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
10853 : else
10854 0 : appendPQExpBufferStr(q, "NULL");
10855 2 : appendPQExpBufferStr(q, ", ");
10856 2 : if (strlen(extinfo->extcondition) > 2)
10857 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
10858 : else
10859 0 : appendPQExpBufferStr(q, "NULL");
10860 2 : appendPQExpBufferStr(q, ", ");
10861 2 : appendPQExpBufferStr(q, "ARRAY[");
10862 2 : n = 0;
10863 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
10864 : {
10865 : DumpableObject *extobj;
10866 :
10867 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
10868 2 : if (extobj && extobj->objType == DO_EXTENSION)
10869 : {
10870 0 : if (n++ > 0)
10871 0 : appendPQExpBufferChar(q, ',');
10872 0 : appendStringLiteralAH(q, extobj->name, fout);
10873 : }
10874 : }
10875 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
10876 2 : appendPQExpBufferStr(q, ");\n");
10877 : }
10878 :
10879 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10880 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
10881 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
10882 : .description = "EXTENSION",
10883 : .section = SECTION_PRE_DATA,
10884 : .createStmt = q->data,
10885 : .dropStmt = delq->data));
10886 :
10887 : /* Dump Extension Comments and Security Labels */
10888 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10889 36 : dumpComment(fout, "EXTENSION", qextname,
10890 : NULL, "",
10891 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10892 :
10893 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10894 0 : dumpSecLabel(fout, "EXTENSION", qextname,
10895 : NULL, "",
10896 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10897 :
10898 36 : free(qextname);
10899 :
10900 36 : destroyPQExpBuffer(q);
10901 36 : destroyPQExpBuffer(delq);
10902 : }
10903 :
10904 : /*
10905 : * dumpType
10906 : * writes out to fout the queries to recreate a user-defined type
10907 : */
10908 : static void
10909 1258 : dumpType(Archive *fout, const TypeInfo *tyinfo)
10910 : {
10911 1258 : DumpOptions *dopt = fout->dopt;
10912 :
10913 : /* Do nothing in data-only dump */
10914 1258 : if (dopt->dataOnly)
10915 44 : return;
10916 :
10917 : /* Dump out in proper style */
10918 1214 : if (tyinfo->typtype == TYPTYPE_BASE)
10919 276 : dumpBaseType(fout, tyinfo);
10920 938 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
10921 256 : dumpDomain(fout, tyinfo);
10922 682 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
10923 262 : dumpCompositeType(fout, tyinfo);
10924 420 : else if (tyinfo->typtype == TYPTYPE_ENUM)
10925 110 : dumpEnumType(fout, tyinfo);
10926 310 : else if (tyinfo->typtype == TYPTYPE_RANGE)
10927 184 : dumpRangeType(fout, tyinfo);
10928 126 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
10929 76 : dumpUndefinedType(fout, tyinfo);
10930 : else
10931 50 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
10932 : tyinfo->dobj.name);
10933 : }
10934 :
10935 : /*
10936 : * dumpEnumType
10937 : * writes out to fout the queries to recreate a user-defined enum type
10938 : */
10939 : static void
10940 110 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
10941 : {
10942 110 : DumpOptions *dopt = fout->dopt;
10943 110 : PQExpBuffer q = createPQExpBuffer();
10944 110 : PQExpBuffer delq = createPQExpBuffer();
10945 110 : PQExpBuffer query = createPQExpBuffer();
10946 : PGresult *res;
10947 : int num,
10948 : i;
10949 : Oid enum_oid;
10950 : char *qtypname;
10951 : char *qualtypname;
10952 : char *label;
10953 : int i_enumlabel;
10954 : int i_oid;
10955 :
10956 110 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
10957 : {
10958 : /* Set up query for enum-specific details */
10959 80 : appendPQExpBufferStr(query,
10960 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
10961 : "SELECT oid, enumlabel "
10962 : "FROM pg_catalog.pg_enum "
10963 : "WHERE enumtypid = $1 "
10964 : "ORDER BY enumsortorder");
10965 :
10966 80 : ExecuteSqlStatement(fout, query->data);
10967 :
10968 80 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
10969 : }
10970 :
10971 110 : printfPQExpBuffer(query,
10972 : "EXECUTE dumpEnumType('%u')",
10973 : tyinfo->dobj.catId.oid);
10974 :
10975 110 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10976 :
10977 110 : num = PQntuples(res);
10978 :
10979 110 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
10980 110 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
10981 :
10982 : /*
10983 : * CASCADE shouldn't be required here as for normal types since the I/O
10984 : * functions are generic and do not get dropped.
10985 : */
10986 110 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
10987 :
10988 110 : if (dopt->binary_upgrade)
10989 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
10990 : tyinfo->dobj.catId.oid,
10991 : false, false);
10992 :
10993 110 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
10994 : qualtypname);
10995 :
10996 110 : if (!dopt->binary_upgrade)
10997 : {
10998 100 : i_enumlabel = PQfnumber(res, "enumlabel");
10999 :
11000 : /* Labels with server-assigned oids */
11001 732 : for (i = 0; i < num; i++)
11002 : {
11003 632 : label = PQgetvalue(res, i, i_enumlabel);
11004 632 : if (i > 0)
11005 532 : appendPQExpBufferChar(q, ',');
11006 632 : appendPQExpBufferStr(q, "\n ");
11007 632 : appendStringLiteralAH(q, label, fout);
11008 : }
11009 : }
11010 :
11011 110 : appendPQExpBufferStr(q, "\n);\n");
11012 :
11013 110 : if (dopt->binary_upgrade)
11014 : {
11015 10 : i_oid = PQfnumber(res, "oid");
11016 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11017 :
11018 : /* Labels with dump-assigned (preserved) oids */
11019 116 : for (i = 0; i < num; i++)
11020 : {
11021 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11022 106 : label = PQgetvalue(res, i, i_enumlabel);
11023 :
11024 106 : if (i == 0)
11025 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11026 106 : appendPQExpBuffer(q,
11027 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11028 : enum_oid);
11029 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11030 106 : appendStringLiteralAH(q, label, fout);
11031 106 : appendPQExpBufferStr(q, ";\n\n");
11032 : }
11033 : }
11034 :
11035 110 : if (dopt->binary_upgrade)
11036 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11037 : "TYPE", qtypname,
11038 10 : tyinfo->dobj.namespace->dobj.name);
11039 :
11040 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11041 110 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11042 110 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11043 : .namespace = tyinfo->dobj.namespace->dobj.name,
11044 : .owner = tyinfo->rolname,
11045 : .description = "TYPE",
11046 : .section = SECTION_PRE_DATA,
11047 : .createStmt = q->data,
11048 : .dropStmt = delq->data));
11049 :
11050 : /* Dump Type Comments and Security Labels */
11051 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11052 66 : dumpComment(fout, "TYPE", qtypname,
11053 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11054 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11055 :
11056 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11057 0 : dumpSecLabel(fout, "TYPE", qtypname,
11058 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11059 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11060 :
11061 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11062 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11063 : qtypname, NULL,
11064 66 : tyinfo->dobj.namespace->dobj.name,
11065 : tyinfo->rolname, &tyinfo->dacl);
11066 :
11067 110 : PQclear(res);
11068 110 : destroyPQExpBuffer(q);
11069 110 : destroyPQExpBuffer(delq);
11070 110 : destroyPQExpBuffer(query);
11071 110 : free(qtypname);
11072 110 : free(qualtypname);
11073 110 : }
11074 :
11075 : /*
11076 : * dumpRangeType
11077 : * writes out to fout the queries to recreate a user-defined range type
11078 : */
11079 : static void
11080 184 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11081 : {
11082 184 : DumpOptions *dopt = fout->dopt;
11083 184 : PQExpBuffer q = createPQExpBuffer();
11084 184 : PQExpBuffer delq = createPQExpBuffer();
11085 184 : PQExpBuffer query = createPQExpBuffer();
11086 : PGresult *res;
11087 : Oid collationOid;
11088 : char *qtypname;
11089 : char *qualtypname;
11090 : char *procname;
11091 :
11092 184 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11093 : {
11094 : /* Set up query for range-specific details */
11095 78 : appendPQExpBufferStr(query,
11096 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11097 :
11098 78 : appendPQExpBufferStr(query,
11099 : "SELECT ");
11100 :
11101 78 : if (fout->remoteVersion >= 140000)
11102 78 : appendPQExpBufferStr(query,
11103 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11104 : else
11105 0 : appendPQExpBufferStr(query,
11106 : "NULL AS rngmultitype, ");
11107 :
11108 78 : appendPQExpBufferStr(query,
11109 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11110 : "opc.opcname AS opcname, "
11111 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11112 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11113 : "opc.opcdefault, "
11114 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
11115 : " ELSE rngcollation END AS collation, "
11116 : "rngcanonical, rngsubdiff "
11117 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11118 : " pg_catalog.pg_opclass opc "
11119 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11120 : "rngtypid = $1");
11121 :
11122 78 : ExecuteSqlStatement(fout, query->data);
11123 :
11124 78 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11125 : }
11126 :
11127 184 : printfPQExpBuffer(query,
11128 : "EXECUTE dumpRangeType('%u')",
11129 : tyinfo->dobj.catId.oid);
11130 :
11131 184 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11132 :
11133 184 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11134 184 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11135 :
11136 : /*
11137 : * CASCADE shouldn't be required here as for normal types since the I/O
11138 : * functions are generic and do not get dropped.
11139 : */
11140 184 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11141 :
11142 184 : if (dopt->binary_upgrade)
11143 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11144 : tyinfo->dobj.catId.oid,
11145 : false, true);
11146 :
11147 184 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
11148 : qualtypname);
11149 :
11150 184 : appendPQExpBuffer(q, "\n subtype = %s",
11151 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
11152 :
11153 184 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
11154 184 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
11155 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
11156 :
11157 : /* print subtype_opclass only if not default for subtype */
11158 184 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
11159 : {
11160 66 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
11161 66 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
11162 :
11163 66 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
11164 : fmtId(nspname));
11165 66 : appendPQExpBufferStr(q, fmtId(opcname));
11166 : }
11167 :
11168 184 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
11169 184 : if (OidIsValid(collationOid))
11170 : {
11171 76 : CollInfo *coll = findCollationByOid(collationOid);
11172 :
11173 76 : if (coll)
11174 76 : appendPQExpBuffer(q, ",\n collation = %s",
11175 76 : fmtQualifiedDumpable(coll));
11176 : }
11177 :
11178 184 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
11179 184 : if (strcmp(procname, "-") != 0)
11180 6 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
11181 :
11182 184 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
11183 184 : if (strcmp(procname, "-") != 0)
11184 22 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
11185 :
11186 184 : appendPQExpBufferStr(q, "\n);\n");
11187 :
11188 184 : if (dopt->binary_upgrade)
11189 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11190 : "TYPE", qtypname,
11191 12 : tyinfo->dobj.namespace->dobj.name);
11192 :
11193 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11194 184 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11195 184 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11196 : .namespace = tyinfo->dobj.namespace->dobj.name,
11197 : .owner = tyinfo->rolname,
11198 : .description = "TYPE",
11199 : .section = SECTION_PRE_DATA,
11200 : .createStmt = q->data,
11201 : .dropStmt = delq->data));
11202 :
11203 : /* Dump Type Comments and Security Labels */
11204 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11205 78 : dumpComment(fout, "TYPE", qtypname,
11206 78 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11207 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11208 :
11209 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11210 0 : dumpSecLabel(fout, "TYPE", qtypname,
11211 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11212 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11213 :
11214 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11215 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11216 : qtypname, NULL,
11217 66 : tyinfo->dobj.namespace->dobj.name,
11218 : tyinfo->rolname, &tyinfo->dacl);
11219 :
11220 184 : PQclear(res);
11221 184 : destroyPQExpBuffer(q);
11222 184 : destroyPQExpBuffer(delq);
11223 184 : destroyPQExpBuffer(query);
11224 184 : free(qtypname);
11225 184 : free(qualtypname);
11226 184 : }
11227 :
11228 : /*
11229 : * dumpUndefinedType
11230 : * writes out to fout the queries to recreate a !typisdefined type
11231 : *
11232 : * This is a shell type, but we use different terminology to distinguish
11233 : * this case from where we have to emit a shell type definition to break
11234 : * circular dependencies. An undefined type shouldn't ever have anything
11235 : * depending on it.
11236 : */
11237 : static void
11238 76 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
11239 : {
11240 76 : DumpOptions *dopt = fout->dopt;
11241 76 : PQExpBuffer q = createPQExpBuffer();
11242 76 : PQExpBuffer delq = createPQExpBuffer();
11243 : char *qtypname;
11244 : char *qualtypname;
11245 :
11246 76 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11247 76 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11248 :
11249 76 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11250 :
11251 76 : if (dopt->binary_upgrade)
11252 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11253 : tyinfo->dobj.catId.oid,
11254 : false, false);
11255 :
11256 76 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11257 : qualtypname);
11258 :
11259 76 : if (dopt->binary_upgrade)
11260 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11261 : "TYPE", qtypname,
11262 4 : tyinfo->dobj.namespace->dobj.name);
11263 :
11264 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11265 76 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11266 76 : 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));
11273 :
11274 : /* Dump Type Comments and Security Labels */
11275 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11276 66 : dumpComment(fout, "TYPE", qtypname,
11277 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11278 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11279 :
11280 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11281 0 : dumpSecLabel(fout, "TYPE", qtypname,
11282 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11283 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11284 :
11285 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11286 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11287 : qtypname, NULL,
11288 0 : tyinfo->dobj.namespace->dobj.name,
11289 : tyinfo->rolname, &tyinfo->dacl);
11290 :
11291 76 : destroyPQExpBuffer(q);
11292 76 : destroyPQExpBuffer(delq);
11293 76 : free(qtypname);
11294 76 : free(qualtypname);
11295 76 : }
11296 :
11297 : /*
11298 : * dumpBaseType
11299 : * writes out to fout the queries to recreate a user-defined base type
11300 : */
11301 : static void
11302 276 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11303 : {
11304 276 : DumpOptions *dopt = fout->dopt;
11305 276 : PQExpBuffer q = createPQExpBuffer();
11306 276 : PQExpBuffer delq = createPQExpBuffer();
11307 276 : PQExpBuffer query = createPQExpBuffer();
11308 : PGresult *res;
11309 : char *qtypname;
11310 : char *qualtypname;
11311 : char *typlen;
11312 : char *typinput;
11313 : char *typoutput;
11314 : char *typreceive;
11315 : char *typsend;
11316 : char *typmodin;
11317 : char *typmodout;
11318 : char *typanalyze;
11319 : char *typsubscript;
11320 : Oid typreceiveoid;
11321 : Oid typsendoid;
11322 : Oid typmodinoid;
11323 : Oid typmodoutoid;
11324 : Oid typanalyzeoid;
11325 : Oid typsubscriptoid;
11326 : char *typcategory;
11327 : char *typispreferred;
11328 : char *typdelim;
11329 : char *typbyval;
11330 : char *typalign;
11331 : char *typstorage;
11332 : char *typcollatable;
11333 : char *typdefault;
11334 276 : bool typdefault_is_literal = false;
11335 :
11336 276 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11337 : {
11338 : /* Set up query for type-specific details */
11339 78 : appendPQExpBufferStr(query,
11340 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11341 : "SELECT typlen, "
11342 : "typinput, typoutput, typreceive, typsend, "
11343 : "typreceive::pg_catalog.oid AS typreceiveoid, "
11344 : "typsend::pg_catalog.oid AS typsendoid, "
11345 : "typanalyze, "
11346 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11347 : "typdelim, typbyval, typalign, typstorage, "
11348 : "typmodin, typmodout, "
11349 : "typmodin::pg_catalog.oid AS typmodinoid, "
11350 : "typmodout::pg_catalog.oid AS typmodoutoid, "
11351 : "typcategory, typispreferred, "
11352 : "(typcollation <> 0) AS typcollatable, "
11353 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11354 :
11355 78 : if (fout->remoteVersion >= 140000)
11356 78 : appendPQExpBufferStr(query,
11357 : "typsubscript, "
11358 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11359 : else
11360 0 : appendPQExpBufferStr(query,
11361 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
11362 :
11363 78 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11364 : "WHERE oid = $1");
11365 :
11366 78 : ExecuteSqlStatement(fout, query->data);
11367 :
11368 78 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11369 : }
11370 :
11371 276 : printfPQExpBuffer(query,
11372 : "EXECUTE dumpBaseType('%u')",
11373 : tyinfo->dobj.catId.oid);
11374 :
11375 276 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11376 :
11377 276 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11378 276 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11379 276 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11380 276 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11381 276 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11382 276 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11383 276 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11384 276 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11385 276 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11386 276 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11387 276 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11388 276 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11389 276 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11390 276 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11391 276 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11392 276 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11393 276 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11394 276 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11395 276 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11396 276 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11397 276 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11398 276 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11399 276 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11400 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11401 276 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11402 : {
11403 86 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11404 86 : typdefault_is_literal = true; /* it needs quotes */
11405 : }
11406 : else
11407 190 : typdefault = NULL;
11408 :
11409 276 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11410 276 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11411 :
11412 : /*
11413 : * The reason we include CASCADE is that the circular dependency between
11414 : * the type and its I/O functions makes it impossible to drop the type any
11415 : * other way.
11416 : */
11417 276 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11418 :
11419 : /*
11420 : * We might already have a shell type, but setting pg_type_oid is
11421 : * harmless, and in any case we'd better set the array type OID.
11422 : */
11423 276 : if (dopt->binary_upgrade)
11424 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11425 : tyinfo->dobj.catId.oid,
11426 : false, false);
11427 :
11428 276 : appendPQExpBuffer(q,
11429 : "CREATE TYPE %s (\n"
11430 : " INTERNALLENGTH = %s",
11431 : qualtypname,
11432 276 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11433 :
11434 : /* regproc result is sufficiently quoted already */
11435 276 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11436 276 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11437 276 : if (OidIsValid(typreceiveoid))
11438 136 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11439 276 : if (OidIsValid(typsendoid))
11440 136 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11441 276 : if (OidIsValid(typmodinoid))
11442 30 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11443 276 : if (OidIsValid(typmodoutoid))
11444 30 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11445 276 : if (OidIsValid(typanalyzeoid))
11446 2 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11447 :
11448 276 : if (strcmp(typcollatable, "t") == 0)
11449 20 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11450 :
11451 276 : if (typdefault != NULL)
11452 : {
11453 86 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
11454 86 : if (typdefault_is_literal)
11455 86 : appendStringLiteralAH(q, typdefault, fout);
11456 : else
11457 0 : appendPQExpBufferStr(q, typdefault);
11458 : }
11459 :
11460 276 : if (OidIsValid(typsubscriptoid))
11461 26 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
11462 :
11463 276 : if (OidIsValid(tyinfo->typelem))
11464 24 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
11465 : getFormattedTypeName(fout, tyinfo->typelem,
11466 : zeroIsError));
11467 :
11468 276 : if (strcmp(typcategory, "U") != 0)
11469 : {
11470 110 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
11471 110 : appendStringLiteralAH(q, typcategory, fout);
11472 : }
11473 :
11474 276 : if (strcmp(typispreferred, "t") == 0)
11475 26 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
11476 :
11477 276 : if (typdelim && strcmp(typdelim, ",") != 0)
11478 : {
11479 2 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
11480 2 : appendStringLiteralAH(q, typdelim, fout);
11481 : }
11482 :
11483 276 : if (*typalign == TYPALIGN_CHAR)
11484 8 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
11485 268 : else if (*typalign == TYPALIGN_SHORT)
11486 4 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
11487 264 : else if (*typalign == TYPALIGN_INT)
11488 194 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
11489 70 : else if (*typalign == TYPALIGN_DOUBLE)
11490 70 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
11491 :
11492 276 : if (*typstorage == TYPSTORAGE_PLAIN)
11493 226 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
11494 50 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
11495 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
11496 50 : else if (*typstorage == TYPSTORAGE_EXTENDED)
11497 44 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
11498 6 : else if (*typstorage == TYPSTORAGE_MAIN)
11499 6 : appendPQExpBufferStr(q, ",\n STORAGE = main");
11500 :
11501 276 : if (strcmp(typbyval, "t") == 0)
11502 152 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
11503 :
11504 276 : appendPQExpBufferStr(q, "\n);\n");
11505 :
11506 276 : if (dopt->binary_upgrade)
11507 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11508 : "TYPE", qtypname,
11509 16 : tyinfo->dobj.namespace->dobj.name);
11510 :
11511 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11512 276 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11513 276 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11514 : .namespace = tyinfo->dobj.namespace->dobj.name,
11515 : .owner = tyinfo->rolname,
11516 : .description = "TYPE",
11517 : .section = SECTION_PRE_DATA,
11518 : .createStmt = q->data,
11519 : .dropStmt = delq->data));
11520 :
11521 : /* Dump Type Comments and Security Labels */
11522 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11523 206 : dumpComment(fout, "TYPE", qtypname,
11524 206 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11525 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11526 :
11527 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11528 0 : dumpSecLabel(fout, "TYPE", qtypname,
11529 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11530 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11531 :
11532 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11533 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11534 : qtypname, NULL,
11535 66 : tyinfo->dobj.namespace->dobj.name,
11536 : tyinfo->rolname, &tyinfo->dacl);
11537 :
11538 276 : PQclear(res);
11539 276 : destroyPQExpBuffer(q);
11540 276 : destroyPQExpBuffer(delq);
11541 276 : destroyPQExpBuffer(query);
11542 276 : free(qtypname);
11543 276 : free(qualtypname);
11544 276 : }
11545 :
11546 : /*
11547 : * dumpDomain
11548 : * writes out to fout the queries to recreate a user-defined domain
11549 : */
11550 : static void
11551 256 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
11552 : {
11553 256 : DumpOptions *dopt = fout->dopt;
11554 256 : PQExpBuffer q = createPQExpBuffer();
11555 256 : PQExpBuffer delq = createPQExpBuffer();
11556 256 : PQExpBuffer query = createPQExpBuffer();
11557 : PGresult *res;
11558 : int i;
11559 : char *qtypname;
11560 : char *qualtypname;
11561 : char *typnotnull;
11562 : char *typdefn;
11563 : char *typdefault;
11564 : Oid typcollation;
11565 256 : bool typdefault_is_literal = false;
11566 :
11567 256 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
11568 : {
11569 : /* Set up query for domain-specific details */
11570 76 : appendPQExpBufferStr(query,
11571 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
11572 :
11573 76 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
11574 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
11575 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
11576 : "t.typdefault, "
11577 : "CASE WHEN t.typcollation <> u.typcollation "
11578 : "THEN t.typcollation ELSE 0 END AS typcollation "
11579 : "FROM pg_catalog.pg_type t "
11580 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
11581 : "WHERE t.oid = $1");
11582 :
11583 76 : ExecuteSqlStatement(fout, query->data);
11584 :
11585 76 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
11586 : }
11587 :
11588 256 : printfPQExpBuffer(query,
11589 : "EXECUTE dumpDomain('%u')",
11590 : tyinfo->dobj.catId.oid);
11591 :
11592 256 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11593 :
11594 256 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
11595 256 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
11596 256 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11597 76 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11598 180 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11599 : {
11600 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11601 0 : typdefault_is_literal = true; /* it needs quotes */
11602 : }
11603 : else
11604 180 : typdefault = NULL;
11605 256 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
11606 :
11607 256 : if (dopt->binary_upgrade)
11608 40 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11609 : tyinfo->dobj.catId.oid,
11610 : true, /* force array type */
11611 : false); /* force multirange type */
11612 :
11613 256 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11614 256 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11615 :
11616 256 : appendPQExpBuffer(q,
11617 : "CREATE DOMAIN %s AS %s",
11618 : qualtypname,
11619 : typdefn);
11620 :
11621 : /* Print collation only if different from base type's collation */
11622 256 : if (OidIsValid(typcollation))
11623 : {
11624 : CollInfo *coll;
11625 :
11626 66 : coll = findCollationByOid(typcollation);
11627 66 : if (coll)
11628 66 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
11629 : }
11630 :
11631 256 : if (typnotnull[0] == 't')
11632 30 : appendPQExpBufferStr(q, " NOT NULL");
11633 :
11634 256 : if (typdefault != NULL)
11635 : {
11636 76 : appendPQExpBufferStr(q, " DEFAULT ");
11637 76 : if (typdefault_is_literal)
11638 0 : appendStringLiteralAH(q, typdefault, fout);
11639 : else
11640 76 : appendPQExpBufferStr(q, typdefault);
11641 : }
11642 :
11643 256 : PQclear(res);
11644 :
11645 : /*
11646 : * Add any CHECK constraints for the domain
11647 : */
11648 422 : for (i = 0; i < tyinfo->nDomChecks; i++)
11649 : {
11650 166 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11651 :
11652 166 : if (!domcheck->separate)
11653 166 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
11654 166 : fmtId(domcheck->dobj.name), domcheck->condef);
11655 : }
11656 :
11657 256 : appendPQExpBufferStr(q, ";\n");
11658 :
11659 256 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
11660 :
11661 256 : if (dopt->binary_upgrade)
11662 40 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11663 : "DOMAIN", qtypname,
11664 40 : tyinfo->dobj.namespace->dobj.name);
11665 :
11666 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11667 256 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11668 256 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11669 : .namespace = tyinfo->dobj.namespace->dobj.name,
11670 : .owner = tyinfo->rolname,
11671 : .description = "DOMAIN",
11672 : .section = SECTION_PRE_DATA,
11673 : .createStmt = q->data,
11674 : .dropStmt = delq->data));
11675 :
11676 : /* Dump Domain Comments and Security Labels */
11677 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11678 0 : dumpComment(fout, "DOMAIN", qtypname,
11679 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11680 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11681 :
11682 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11683 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
11684 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11685 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11686 :
11687 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11688 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11689 : qtypname, NULL,
11690 66 : tyinfo->dobj.namespace->dobj.name,
11691 : tyinfo->rolname, &tyinfo->dacl);
11692 :
11693 : /* Dump any per-constraint comments */
11694 422 : for (i = 0; i < tyinfo->nDomChecks; i++)
11695 : {
11696 166 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11697 166 : PQExpBuffer conprefix = createPQExpBuffer();
11698 :
11699 166 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
11700 166 : fmtId(domcheck->dobj.name));
11701 :
11702 166 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
11703 66 : dumpComment(fout, conprefix->data, qtypname,
11704 66 : tyinfo->dobj.namespace->dobj.name,
11705 : tyinfo->rolname,
11706 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
11707 :
11708 166 : destroyPQExpBuffer(conprefix);
11709 : }
11710 :
11711 256 : destroyPQExpBuffer(q);
11712 256 : destroyPQExpBuffer(delq);
11713 256 : destroyPQExpBuffer(query);
11714 256 : free(qtypname);
11715 256 : free(qualtypname);
11716 256 : }
11717 :
11718 : /*
11719 : * dumpCompositeType
11720 : * writes out to fout the queries to recreate a user-defined stand-alone
11721 : * composite type
11722 : */
11723 : static void
11724 262 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
11725 : {
11726 262 : DumpOptions *dopt = fout->dopt;
11727 262 : PQExpBuffer q = createPQExpBuffer();
11728 262 : PQExpBuffer dropped = createPQExpBuffer();
11729 262 : PQExpBuffer delq = createPQExpBuffer();
11730 262 : PQExpBuffer query = createPQExpBuffer();
11731 : PGresult *res;
11732 : char *qtypname;
11733 : char *qualtypname;
11734 : int ntups;
11735 : int i_attname;
11736 : int i_atttypdefn;
11737 : int i_attlen;
11738 : int i_attalign;
11739 : int i_attisdropped;
11740 : int i_attcollation;
11741 : int i;
11742 : int actual_atts;
11743 :
11744 262 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
11745 : {
11746 : /*
11747 : * Set up query for type-specific details.
11748 : *
11749 : * Since we only want to dump COLLATE clauses for attributes whose
11750 : * collation is different from their type's default, we use a CASE
11751 : * here to suppress uninteresting attcollations cheaply. atttypid
11752 : * will be 0 for dropped columns; collation does not matter for those.
11753 : */
11754 112 : appendPQExpBufferStr(query,
11755 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
11756 : "SELECT a.attname, a.attnum, "
11757 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
11758 : "a.attlen, a.attalign, a.attisdropped, "
11759 : "CASE WHEN a.attcollation <> at.typcollation "
11760 : "THEN a.attcollation ELSE 0 END AS attcollation "
11761 : "FROM pg_catalog.pg_type ct "
11762 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
11763 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
11764 : "WHERE ct.oid = $1 "
11765 : "ORDER BY a.attnum");
11766 :
11767 112 : ExecuteSqlStatement(fout, query->data);
11768 :
11769 112 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
11770 : }
11771 :
11772 262 : printfPQExpBuffer(query,
11773 : "EXECUTE dumpCompositeType('%u')",
11774 : tyinfo->dobj.catId.oid);
11775 :
11776 262 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11777 :
11778 262 : ntups = PQntuples(res);
11779 :
11780 262 : i_attname = PQfnumber(res, "attname");
11781 262 : i_atttypdefn = PQfnumber(res, "atttypdefn");
11782 262 : i_attlen = PQfnumber(res, "attlen");
11783 262 : i_attalign = PQfnumber(res, "attalign");
11784 262 : i_attisdropped = PQfnumber(res, "attisdropped");
11785 262 : i_attcollation = PQfnumber(res, "attcollation");
11786 :
11787 262 : if (dopt->binary_upgrade)
11788 : {
11789 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11790 : tyinfo->dobj.catId.oid,
11791 : false, false);
11792 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
11793 : }
11794 :
11795 262 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11796 262 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11797 :
11798 262 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
11799 : qualtypname);
11800 :
11801 262 : actual_atts = 0;
11802 830 : for (i = 0; i < ntups; i++)
11803 : {
11804 : char *attname;
11805 : char *atttypdefn;
11806 : char *attlen;
11807 : char *attalign;
11808 : bool attisdropped;
11809 : Oid attcollation;
11810 :
11811 568 : attname = PQgetvalue(res, i, i_attname);
11812 568 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
11813 568 : attlen = PQgetvalue(res, i, i_attlen);
11814 568 : attalign = PQgetvalue(res, i, i_attalign);
11815 568 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
11816 568 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
11817 :
11818 568 : if (attisdropped && !dopt->binary_upgrade)
11819 16 : continue;
11820 :
11821 : /* Format properly if not first attr */
11822 552 : if (actual_atts++ > 0)
11823 290 : appendPQExpBufferChar(q, ',');
11824 552 : appendPQExpBufferStr(q, "\n\t");
11825 :
11826 552 : if (!attisdropped)
11827 : {
11828 548 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
11829 :
11830 : /* Add collation if not default for the column type */
11831 548 : if (OidIsValid(attcollation))
11832 : {
11833 : CollInfo *coll;
11834 :
11835 0 : coll = findCollationByOid(attcollation);
11836 0 : if (coll)
11837 0 : appendPQExpBuffer(q, " COLLATE %s",
11838 0 : fmtQualifiedDumpable(coll));
11839 : }
11840 : }
11841 : else
11842 : {
11843 : /*
11844 : * This is a dropped attribute and we're in binary_upgrade mode.
11845 : * Insert a placeholder for it in the CREATE TYPE command, and set
11846 : * length and alignment with direct UPDATE to the catalogs
11847 : * afterwards. See similar code in dumpTableSchema().
11848 : */
11849 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
11850 :
11851 : /* stash separately for insertion after the CREATE TYPE */
11852 4 : appendPQExpBufferStr(dropped,
11853 : "\n-- For binary upgrade, recreate dropped column.\n");
11854 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
11855 : "SET attlen = %s, "
11856 : "attalign = '%s', attbyval = false\n"
11857 : "WHERE attname = ", attlen, attalign);
11858 4 : appendStringLiteralAH(dropped, attname, fout);
11859 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
11860 4 : appendStringLiteralAH(dropped, qualtypname, fout);
11861 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
11862 :
11863 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
11864 : qualtypname);
11865 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
11866 : fmtId(attname));
11867 : }
11868 : }
11869 262 : appendPQExpBufferStr(q, "\n);\n");
11870 262 : appendPQExpBufferStr(q, dropped->data);
11871 :
11872 262 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11873 :
11874 262 : if (dopt->binary_upgrade)
11875 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11876 : "TYPE", qtypname,
11877 36 : tyinfo->dobj.namespace->dobj.name);
11878 :
11879 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11880 228 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11881 228 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11882 : .namespace = tyinfo->dobj.namespace->dobj.name,
11883 : .owner = tyinfo->rolname,
11884 : .description = "TYPE",
11885 : .section = SECTION_PRE_DATA,
11886 : .createStmt = q->data,
11887 : .dropStmt = delq->data));
11888 :
11889 :
11890 : /* Dump Type Comments and Security Labels */
11891 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11892 66 : dumpComment(fout, "TYPE", qtypname,
11893 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11894 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11895 :
11896 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11897 0 : dumpSecLabel(fout, "TYPE", qtypname,
11898 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11899 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11900 :
11901 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11902 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11903 : qtypname, NULL,
11904 36 : tyinfo->dobj.namespace->dobj.name,
11905 : tyinfo->rolname, &tyinfo->dacl);
11906 :
11907 : /* Dump any per-column comments */
11908 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11909 66 : dumpCompositeTypeColComments(fout, tyinfo, res);
11910 :
11911 262 : PQclear(res);
11912 262 : destroyPQExpBuffer(q);
11913 262 : destroyPQExpBuffer(dropped);
11914 262 : destroyPQExpBuffer(delq);
11915 262 : destroyPQExpBuffer(query);
11916 262 : free(qtypname);
11917 262 : free(qualtypname);
11918 262 : }
11919 :
11920 : /*
11921 : * dumpCompositeTypeColComments
11922 : * writes out to fout the queries to recreate comments on the columns of
11923 : * a user-defined stand-alone composite type.
11924 : *
11925 : * The caller has already made a query to collect the names and attnums
11926 : * of the type's columns, so we just pass that result into here rather
11927 : * than reading them again.
11928 : */
11929 : static void
11930 66 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
11931 : PGresult *res)
11932 : {
11933 : CommentItem *comments;
11934 : int ncomments;
11935 : PQExpBuffer query;
11936 : PQExpBuffer target;
11937 : int i;
11938 : int ntups;
11939 : int i_attname;
11940 : int i_attnum;
11941 : int i_attisdropped;
11942 :
11943 : /* do nothing, if --no-comments is supplied */
11944 66 : if (fout->dopt->no_comments)
11945 0 : return;
11946 :
11947 : /* Search for comments associated with type's pg_class OID */
11948 66 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
11949 : &comments);
11950 :
11951 : /* If no comments exist, we're done */
11952 66 : if (ncomments <= 0)
11953 0 : return;
11954 :
11955 : /* Build COMMENT ON statements */
11956 66 : query = createPQExpBuffer();
11957 66 : target = createPQExpBuffer();
11958 :
11959 66 : ntups = PQntuples(res);
11960 66 : i_attnum = PQfnumber(res, "attnum");
11961 66 : i_attname = PQfnumber(res, "attname");
11962 66 : i_attisdropped = PQfnumber(res, "attisdropped");
11963 132 : while (ncomments > 0)
11964 : {
11965 : const char *attname;
11966 :
11967 66 : attname = NULL;
11968 66 : for (i = 0; i < ntups; i++)
11969 : {
11970 66 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
11971 66 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
11972 : {
11973 66 : attname = PQgetvalue(res, i, i_attname);
11974 66 : break;
11975 : }
11976 : }
11977 66 : if (attname) /* just in case we don't find it */
11978 : {
11979 66 : const char *descr = comments->descr;
11980 :
11981 66 : resetPQExpBuffer(target);
11982 66 : appendPQExpBuffer(target, "COLUMN %s.",
11983 66 : fmtId(tyinfo->dobj.name));
11984 66 : appendPQExpBufferStr(target, fmtId(attname));
11985 :
11986 66 : resetPQExpBuffer(query);
11987 66 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11988 66 : fmtQualifiedDumpable(tyinfo));
11989 66 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
11990 66 : appendStringLiteralAH(query, descr, fout);
11991 66 : appendPQExpBufferStr(query, ";\n");
11992 :
11993 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11994 66 : ARCHIVE_OPTS(.tag = target->data,
11995 : .namespace = tyinfo->dobj.namespace->dobj.name,
11996 : .owner = tyinfo->rolname,
11997 : .description = "COMMENT",
11998 : .section = SECTION_NONE,
11999 : .createStmt = query->data,
12000 : .deps = &(tyinfo->dobj.dumpId),
12001 : .nDeps = 1));
12002 : }
12003 :
12004 66 : comments++;
12005 66 : ncomments--;
12006 : }
12007 :
12008 66 : destroyPQExpBuffer(query);
12009 66 : destroyPQExpBuffer(target);
12010 : }
12011 :
12012 : /*
12013 : * dumpShellType
12014 : * writes out to fout the queries to create a shell type
12015 : *
12016 : * We dump a shell definition in advance of the I/O functions for the type.
12017 : */
12018 : static void
12019 142 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12020 : {
12021 142 : DumpOptions *dopt = fout->dopt;
12022 : PQExpBuffer q;
12023 :
12024 : /* Do nothing in data-only dump */
12025 142 : if (dopt->dataOnly)
12026 6 : return;
12027 :
12028 136 : q = createPQExpBuffer();
12029 :
12030 : /*
12031 : * Note the lack of a DROP command for the shell type; any required DROP
12032 : * is driven off the base type entry, instead. This interacts with
12033 : * _printTocEntry()'s use of the presence of a DROP command to decide
12034 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12035 : * the shell type's owner immediately on creation; that should happen only
12036 : * after it's filled in, otherwise the backend complains.
12037 : */
12038 :
12039 136 : if (dopt->binary_upgrade)
12040 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12041 16 : stinfo->baseType->dobj.catId.oid,
12042 : false, false);
12043 :
12044 136 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12045 136 : fmtQualifiedDumpable(stinfo));
12046 :
12047 136 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12048 136 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12049 136 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12050 : .namespace = stinfo->dobj.namespace->dobj.name,
12051 : .owner = stinfo->baseType->rolname,
12052 : .description = "SHELL TYPE",
12053 : .section = SECTION_PRE_DATA,
12054 : .createStmt = q->data));
12055 :
12056 136 : destroyPQExpBuffer(q);
12057 : }
12058 :
12059 : /*
12060 : * dumpProcLang
12061 : * writes out to fout the queries to recreate a user-defined
12062 : * procedural language
12063 : */
12064 : static void
12065 156 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12066 : {
12067 156 : DumpOptions *dopt = fout->dopt;
12068 : PQExpBuffer defqry;
12069 : PQExpBuffer delqry;
12070 : bool useParams;
12071 : char *qlanname;
12072 : FuncInfo *funcInfo;
12073 156 : FuncInfo *inlineInfo = NULL;
12074 156 : FuncInfo *validatorInfo = NULL;
12075 :
12076 : /* Do nothing in data-only dump */
12077 156 : if (dopt->dataOnly)
12078 14 : return;
12079 :
12080 : /*
12081 : * Try to find the support function(s). It is not an error if we don't
12082 : * find them --- if the functions are in the pg_catalog schema, as is
12083 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12084 : * we will emit a parameterless CREATE LANGUAGE command, which will
12085 : * require PL template knowledge in the backend to reload.)
12086 : */
12087 :
12088 142 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12089 142 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12090 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12091 :
12092 142 : if (OidIsValid(plang->laninline))
12093 : {
12094 78 : inlineInfo = findFuncByOid(plang->laninline);
12095 78 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12096 2 : inlineInfo = NULL;
12097 : }
12098 :
12099 142 : if (OidIsValid(plang->lanvalidator))
12100 : {
12101 78 : validatorInfo = findFuncByOid(plang->lanvalidator);
12102 78 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12103 2 : validatorInfo = NULL;
12104 : }
12105 :
12106 : /*
12107 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12108 : * parameters. Otherwise, we'll write a parameterless command, which will
12109 : * be interpreted as CREATE EXTENSION.
12110 : */
12111 62 : useParams = (funcInfo != NULL &&
12112 266 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12113 62 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12114 :
12115 142 : defqry = createPQExpBuffer();
12116 142 : delqry = createPQExpBuffer();
12117 :
12118 142 : qlanname = pg_strdup(fmtId(plang->dobj.name));
12119 :
12120 142 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12121 : qlanname);
12122 :
12123 142 : if (useParams)
12124 : {
12125 62 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12126 62 : plang->lanpltrusted ? "TRUSTED " : "",
12127 : qlanname);
12128 62 : appendPQExpBuffer(defqry, " HANDLER %s",
12129 62 : fmtQualifiedDumpable(funcInfo));
12130 62 : if (OidIsValid(plang->laninline))
12131 0 : appendPQExpBuffer(defqry, " INLINE %s",
12132 0 : fmtQualifiedDumpable(inlineInfo));
12133 62 : if (OidIsValid(plang->lanvalidator))
12134 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
12135 0 : fmtQualifiedDumpable(validatorInfo));
12136 : }
12137 : else
12138 : {
12139 : /*
12140 : * If not dumping parameters, then use CREATE OR REPLACE so that the
12141 : * command will not fail if the language is preinstalled in the target
12142 : * database.
12143 : *
12144 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
12145 : * EXISTS; perhaps we should emit that instead? But it might just add
12146 : * confusion.
12147 : */
12148 80 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
12149 : qlanname);
12150 : }
12151 142 : appendPQExpBufferStr(defqry, ";\n");
12152 :
12153 142 : if (dopt->binary_upgrade)
12154 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
12155 : "LANGUAGE", qlanname, NULL);
12156 :
12157 142 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
12158 64 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
12159 64 : ARCHIVE_OPTS(.tag = plang->dobj.name,
12160 : .owner = plang->lanowner,
12161 : .description = "PROCEDURAL LANGUAGE",
12162 : .section = SECTION_PRE_DATA,
12163 : .createStmt = defqry->data,
12164 : .dropStmt = delqry->data,
12165 : ));
12166 :
12167 : /* Dump Proc Lang Comments and Security Labels */
12168 142 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
12169 0 : dumpComment(fout, "LANGUAGE", qlanname,
12170 : NULL, plang->lanowner,
12171 : plang->dobj.catId, 0, plang->dobj.dumpId);
12172 :
12173 142 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
12174 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
12175 : NULL, plang->lanowner,
12176 : plang->dobj.catId, 0, plang->dobj.dumpId);
12177 :
12178 142 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
12179 78 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
12180 : qlanname, NULL, NULL,
12181 : plang->lanowner, &plang->dacl);
12182 :
12183 142 : free(qlanname);
12184 :
12185 142 : destroyPQExpBuffer(defqry);
12186 142 : destroyPQExpBuffer(delqry);
12187 : }
12188 :
12189 : /*
12190 : * format_function_arguments: generate function name and argument list
12191 : *
12192 : * This is used when we can rely on pg_get_function_arguments to format
12193 : * the argument list. Note, however, that pg_get_function_arguments
12194 : * does not special-case zero-argument aggregates.
12195 : */
12196 : static char *
12197 8052 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
12198 : {
12199 : PQExpBufferData fn;
12200 :
12201 8052 : initPQExpBuffer(&fn);
12202 8052 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
12203 8052 : if (is_agg && finfo->nargs == 0)
12204 160 : appendPQExpBufferStr(&fn, "(*)");
12205 : else
12206 7892 : appendPQExpBuffer(&fn, "(%s)", funcargs);
12207 8052 : return fn.data;
12208 : }
12209 :
12210 : /*
12211 : * format_function_signature: generate function name and argument list
12212 : *
12213 : * Only a minimal list of input argument types is generated; this is
12214 : * sufficient to reference the function, but not to define it.
12215 : *
12216 : * If honor_quotes is false then the function name is never quoted.
12217 : * This is appropriate for use in TOC tags, but not in SQL commands.
12218 : */
12219 : static char *
12220 4248 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
12221 : {
12222 : PQExpBufferData fn;
12223 : int j;
12224 :
12225 4248 : initPQExpBuffer(&fn);
12226 4248 : if (honor_quotes)
12227 794 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
12228 : else
12229 3454 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
12230 7808 : for (j = 0; j < finfo->nargs; j++)
12231 : {
12232 3560 : if (j > 0)
12233 834 : appendPQExpBufferStr(&fn, ", ");
12234 :
12235 3560 : appendPQExpBufferStr(&fn,
12236 3560 : getFormattedTypeName(fout, finfo->argtypes[j],
12237 : zeroIsError));
12238 : }
12239 4248 : appendPQExpBufferChar(&fn, ')');
12240 4248 : return fn.data;
12241 : }
12242 :
12243 :
12244 : /*
12245 : * dumpFunc:
12246 : * dump out one function
12247 : */
12248 : static void
12249 3518 : dumpFunc(Archive *fout, const FuncInfo *finfo)
12250 : {
12251 3518 : DumpOptions *dopt = fout->dopt;
12252 : PQExpBuffer query;
12253 : PQExpBuffer q;
12254 : PQExpBuffer delqry;
12255 : PQExpBuffer asPart;
12256 : PGresult *res;
12257 : char *funcsig; /* identity signature */
12258 3518 : char *funcfullsig = NULL; /* full signature */
12259 : char *funcsig_tag;
12260 : char *qual_funcsig;
12261 : char *proretset;
12262 : char *prosrc;
12263 : char *probin;
12264 : char *prosqlbody;
12265 : char *funcargs;
12266 : char *funciargs;
12267 : char *funcresult;
12268 : char *protrftypes;
12269 : char *prokind;
12270 : char *provolatile;
12271 : char *proisstrict;
12272 : char *prosecdef;
12273 : char *proleakproof;
12274 : char *proconfig;
12275 : char *procost;
12276 : char *prorows;
12277 : char *prosupport;
12278 : char *proparallel;
12279 : char *lanname;
12280 3518 : char **configitems = NULL;
12281 3518 : int nconfigitems = 0;
12282 : const char *keyword;
12283 :
12284 : /* Do nothing in data-only dump */
12285 3518 : if (dopt->dataOnly)
12286 64 : return;
12287 :
12288 3454 : query = createPQExpBuffer();
12289 3454 : q = createPQExpBuffer();
12290 3454 : delqry = createPQExpBuffer();
12291 3454 : asPart = createPQExpBuffer();
12292 :
12293 3454 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12294 : {
12295 : /* Set up query for function-specific details */
12296 120 : appendPQExpBufferStr(query,
12297 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12298 :
12299 120 : appendPQExpBufferStr(query,
12300 : "SELECT\n"
12301 : "proretset,\n"
12302 : "prosrc,\n"
12303 : "probin,\n"
12304 : "provolatile,\n"
12305 : "proisstrict,\n"
12306 : "prosecdef,\n"
12307 : "lanname,\n"
12308 : "proconfig,\n"
12309 : "procost,\n"
12310 : "prorows,\n"
12311 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12312 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12313 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12314 : "proleakproof,\n");
12315 :
12316 120 : if (fout->remoteVersion >= 90500)
12317 120 : appendPQExpBufferStr(query,
12318 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12319 : else
12320 0 : appendPQExpBufferStr(query,
12321 : "NULL AS protrftypes,\n");
12322 :
12323 120 : if (fout->remoteVersion >= 90600)
12324 120 : appendPQExpBufferStr(query,
12325 : "proparallel,\n");
12326 : else
12327 0 : appendPQExpBufferStr(query,
12328 : "'u' AS proparallel,\n");
12329 :
12330 120 : if (fout->remoteVersion >= 110000)
12331 120 : appendPQExpBufferStr(query,
12332 : "prokind,\n");
12333 : else
12334 0 : appendPQExpBufferStr(query,
12335 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12336 :
12337 120 : if (fout->remoteVersion >= 120000)
12338 120 : appendPQExpBufferStr(query,
12339 : "prosupport,\n");
12340 : else
12341 0 : appendPQExpBufferStr(query,
12342 : "'-' AS prosupport,\n");
12343 :
12344 120 : if (fout->remoteVersion >= 140000)
12345 120 : appendPQExpBufferStr(query,
12346 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12347 : else
12348 0 : appendPQExpBufferStr(query,
12349 : "NULL AS prosqlbody\n");
12350 :
12351 120 : appendPQExpBufferStr(query,
12352 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12353 : "WHERE p.oid = $1 "
12354 : "AND l.oid = p.prolang");
12355 :
12356 120 : ExecuteSqlStatement(fout, query->data);
12357 :
12358 120 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12359 : }
12360 :
12361 3454 : printfPQExpBuffer(query,
12362 : "EXECUTE dumpFunc('%u')",
12363 : finfo->dobj.catId.oid);
12364 :
12365 3454 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12366 :
12367 3454 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12368 3454 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12369 : {
12370 3356 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12371 3356 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12372 3356 : prosqlbody = NULL;
12373 : }
12374 : else
12375 : {
12376 98 : prosrc = NULL;
12377 98 : probin = NULL;
12378 98 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12379 : }
12380 3454 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12381 3454 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12382 3454 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12383 3454 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12384 3454 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12385 3454 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12386 3454 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12387 3454 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12388 3454 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12389 3454 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12390 3454 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12391 3454 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12392 3454 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12393 3454 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12394 3454 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12395 :
12396 : /*
12397 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
12398 : * is used.
12399 : */
12400 3454 : if (prosqlbody)
12401 : {
12402 98 : appendPQExpBufferStr(asPart, prosqlbody);
12403 : }
12404 3356 : else if (probin[0] != '\0')
12405 : {
12406 286 : appendPQExpBufferStr(asPart, "AS ");
12407 286 : appendStringLiteralAH(asPart, probin, fout);
12408 286 : if (prosrc[0] != '\0')
12409 : {
12410 286 : appendPQExpBufferStr(asPart, ", ");
12411 :
12412 : /*
12413 : * where we have bin, use dollar quoting if allowed and src
12414 : * contains quote or backslash; else use regular quoting.
12415 : */
12416 286 : if (dopt->disable_dollar_quoting ||
12417 286 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12418 286 : appendStringLiteralAH(asPart, prosrc, fout);
12419 : else
12420 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
12421 : }
12422 : }
12423 : else
12424 : {
12425 3070 : appendPQExpBufferStr(asPart, "AS ");
12426 : /* with no bin, dollar quote src unconditionally if allowed */
12427 3070 : if (dopt->disable_dollar_quoting)
12428 0 : appendStringLiteralAH(asPart, prosrc, fout);
12429 : else
12430 3070 : appendStringLiteralDQ(asPart, prosrc, NULL);
12431 : }
12432 :
12433 3454 : if (*proconfig)
12434 : {
12435 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12436 0 : pg_fatal("could not parse %s array", "proconfig");
12437 : }
12438 : else
12439 : {
12440 3424 : configitems = NULL;
12441 3424 : nconfigitems = 0;
12442 : }
12443 :
12444 3454 : funcfullsig = format_function_arguments(finfo, funcargs, false);
12445 3454 : funcsig = format_function_arguments(finfo, funciargs, false);
12446 :
12447 3454 : funcsig_tag = format_function_signature(fout, finfo, false);
12448 :
12449 3454 : qual_funcsig = psprintf("%s.%s",
12450 3454 : fmtId(finfo->dobj.namespace->dobj.name),
12451 : funcsig);
12452 :
12453 3454 : if (prokind[0] == PROKIND_PROCEDURE)
12454 156 : keyword = "PROCEDURE";
12455 : else
12456 3298 : keyword = "FUNCTION"; /* works for window functions too */
12457 :
12458 3454 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
12459 : keyword, qual_funcsig);
12460 :
12461 6908 : appendPQExpBuffer(q, "CREATE %s %s.%s",
12462 : keyword,
12463 3454 : fmtId(finfo->dobj.namespace->dobj.name),
12464 : funcfullsig ? funcfullsig :
12465 : funcsig);
12466 :
12467 3454 : if (prokind[0] == PROKIND_PROCEDURE)
12468 : /* no result type to output */ ;
12469 3298 : else if (funcresult)
12470 3298 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
12471 : else
12472 0 : appendPQExpBuffer(q, " RETURNS %s%s",
12473 0 : (proretset[0] == 't') ? "SETOF " : "",
12474 : getFormattedTypeName(fout, finfo->prorettype,
12475 : zeroIsError));
12476 :
12477 3454 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
12478 :
12479 3454 : if (*protrftypes)
12480 : {
12481 0 : Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid));
12482 : int i;
12483 :
12484 0 : appendPQExpBufferStr(q, " TRANSFORM ");
12485 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
12486 0 : for (i = 0; typeids[i]; i++)
12487 : {
12488 0 : if (i != 0)
12489 0 : appendPQExpBufferStr(q, ", ");
12490 0 : appendPQExpBuffer(q, "FOR TYPE %s",
12491 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
12492 : }
12493 : }
12494 :
12495 3454 : if (prokind[0] == PROKIND_WINDOW)
12496 10 : appendPQExpBufferStr(q, " WINDOW");
12497 :
12498 3454 : if (provolatile[0] != PROVOLATILE_VOLATILE)
12499 : {
12500 688 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
12501 656 : appendPQExpBufferStr(q, " IMMUTABLE");
12502 32 : else if (provolatile[0] == PROVOLATILE_STABLE)
12503 32 : appendPQExpBufferStr(q, " STABLE");
12504 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
12505 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
12506 : finfo->dobj.name);
12507 : }
12508 :
12509 3454 : if (proisstrict[0] == 't')
12510 694 : appendPQExpBufferStr(q, " STRICT");
12511 :
12512 3454 : if (prosecdef[0] == 't')
12513 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
12514 :
12515 3454 : if (proleakproof[0] == 't')
12516 20 : appendPQExpBufferStr(q, " LEAKPROOF");
12517 :
12518 : /*
12519 : * COST and ROWS are emitted only if present and not default, so as not to
12520 : * break backwards-compatibility of the dump without need. Keep this code
12521 : * in sync with the defaults in functioncmds.c.
12522 : */
12523 3454 : if (strcmp(procost, "0") != 0)
12524 : {
12525 3454 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12526 : {
12527 : /* default cost is 1 */
12528 744 : if (strcmp(procost, "1") != 0)
12529 0 : appendPQExpBuffer(q, " COST %s", procost);
12530 : }
12531 : else
12532 : {
12533 : /* default cost is 100 */
12534 2710 : if (strcmp(procost, "100") != 0)
12535 12 : appendPQExpBuffer(q, " COST %s", procost);
12536 : }
12537 : }
12538 3454 : if (proretset[0] == 't' &&
12539 376 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12540 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
12541 :
12542 3454 : if (strcmp(prosupport, "-") != 0)
12543 : {
12544 : /* We rely on regprocout to provide quoting and qualification */
12545 86 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12546 : }
12547 :
12548 3454 : if (proparallel[0] != PROPARALLEL_UNSAFE)
12549 : {
12550 228 : if (proparallel[0] == PROPARALLEL_SAFE)
12551 218 : appendPQExpBufferStr(q, " PARALLEL SAFE");
12552 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12553 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12554 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
12555 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
12556 : finfo->dobj.name);
12557 : }
12558 :
12559 3524 : for (int i = 0; i < nconfigitems; i++)
12560 : {
12561 : /* we feel free to scribble on configitems[] here */
12562 70 : char *configitem = configitems[i];
12563 : char *pos;
12564 :
12565 70 : pos = strchr(configitem, '=');
12566 70 : if (pos == NULL)
12567 0 : continue;
12568 70 : *pos++ = '\0';
12569 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
12570 :
12571 : /*
12572 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
12573 : * by flatten_set_variable_args() before they were put into the
12574 : * proconfig array. However, because the quoting rules used there
12575 : * aren't exactly like SQL's, we have to break the list value apart
12576 : * and then quote the elements as string literals. (The elements may
12577 : * be double-quoted as-is, but we can't just feed them to the SQL
12578 : * parser; it would do the wrong thing with elements that are
12579 : * zero-length or longer than NAMEDATALEN.)
12580 : *
12581 : * Variables that are not so marked should just be emitted as simple
12582 : * string literals. If the variable is not known to
12583 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
12584 : * to use GUC_LIST_QUOTE for extension variables.
12585 : */
12586 70 : if (variable_is_guc_list_quote(configitem))
12587 : {
12588 : char **namelist;
12589 : char **nameptr;
12590 :
12591 : /* Parse string into list of identifiers */
12592 : /* this shouldn't fail really */
12593 20 : if (SplitGUCList(pos, ',', &namelist))
12594 : {
12595 70 : for (nameptr = namelist; *nameptr; nameptr++)
12596 : {
12597 50 : if (nameptr != namelist)
12598 30 : appendPQExpBufferStr(q, ", ");
12599 50 : appendStringLiteralAH(q, *nameptr, fout);
12600 : }
12601 : }
12602 20 : pg_free(namelist);
12603 : }
12604 : else
12605 50 : appendStringLiteralAH(q, pos, fout);
12606 : }
12607 :
12608 3454 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
12609 :
12610 3454 : append_depends_on_extension(fout, q, &finfo->dobj,
12611 : "pg_catalog.pg_proc", keyword,
12612 : qual_funcsig);
12613 :
12614 3454 : if (dopt->binary_upgrade)
12615 564 : binary_upgrade_extension_member(q, &finfo->dobj,
12616 : keyword, funcsig,
12617 564 : finfo->dobj.namespace->dobj.name);
12618 :
12619 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12620 3258 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
12621 3258 : ARCHIVE_OPTS(.tag = funcsig_tag,
12622 : .namespace = finfo->dobj.namespace->dobj.name,
12623 : .owner = finfo->rolname,
12624 : .description = keyword,
12625 : .section = finfo->postponed_def ?
12626 : SECTION_POST_DATA : SECTION_PRE_DATA,
12627 : .createStmt = q->data,
12628 : .dropStmt = delqry->data));
12629 :
12630 : /* Dump Function Comments and Security Labels */
12631 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12632 10 : dumpComment(fout, keyword, funcsig,
12633 10 : finfo->dobj.namespace->dobj.name, finfo->rolname,
12634 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
12635 :
12636 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12637 0 : dumpSecLabel(fout, keyword, funcsig,
12638 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
12639 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
12640 :
12641 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12642 204 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
12643 : funcsig, NULL,
12644 204 : finfo->dobj.namespace->dobj.name,
12645 : finfo->rolname, &finfo->dacl);
12646 :
12647 3454 : PQclear(res);
12648 :
12649 3454 : destroyPQExpBuffer(query);
12650 3454 : destroyPQExpBuffer(q);
12651 3454 : destroyPQExpBuffer(delqry);
12652 3454 : destroyPQExpBuffer(asPart);
12653 3454 : free(funcsig);
12654 3454 : free(funcfullsig);
12655 3454 : free(funcsig_tag);
12656 3454 : free(qual_funcsig);
12657 3454 : free(configitems);
12658 : }
12659 :
12660 :
12661 : /*
12662 : * Dump a user-defined cast
12663 : */
12664 : static void
12665 130 : dumpCast(Archive *fout, const CastInfo *cast)
12666 : {
12667 130 : DumpOptions *dopt = fout->dopt;
12668 : PQExpBuffer defqry;
12669 : PQExpBuffer delqry;
12670 : PQExpBuffer labelq;
12671 : PQExpBuffer castargs;
12672 130 : FuncInfo *funcInfo = NULL;
12673 : const char *sourceType;
12674 : const char *targetType;
12675 :
12676 : /* Do nothing in data-only dump */
12677 130 : if (dopt->dataOnly)
12678 6 : return;
12679 :
12680 : /* Cannot dump if we don't have the cast function's info */
12681 124 : if (OidIsValid(cast->castfunc))
12682 : {
12683 74 : funcInfo = findFuncByOid(cast->castfunc);
12684 74 : if (funcInfo == NULL)
12685 0 : pg_fatal("could not find function definition for function with OID %u",
12686 : cast->castfunc);
12687 : }
12688 :
12689 124 : defqry = createPQExpBuffer();
12690 124 : delqry = createPQExpBuffer();
12691 124 : labelq = createPQExpBuffer();
12692 124 : castargs = createPQExpBuffer();
12693 :
12694 124 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
12695 124 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
12696 124 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
12697 : sourceType, targetType);
12698 :
12699 124 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
12700 : sourceType, targetType);
12701 :
12702 124 : switch (cast->castmethod)
12703 : {
12704 50 : case COERCION_METHOD_BINARY:
12705 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
12706 50 : break;
12707 0 : case COERCION_METHOD_INOUT:
12708 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
12709 0 : break;
12710 74 : case COERCION_METHOD_FUNCTION:
12711 74 : if (funcInfo)
12712 : {
12713 74 : char *fsig = format_function_signature(fout, funcInfo, true);
12714 :
12715 : /*
12716 : * Always qualify the function name (format_function_signature
12717 : * won't qualify it).
12718 : */
12719 74 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
12720 74 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
12721 74 : free(fsig);
12722 : }
12723 : else
12724 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
12725 74 : break;
12726 0 : default:
12727 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
12728 : }
12729 :
12730 124 : if (cast->castcontext == 'a')
12731 64 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
12732 60 : else if (cast->castcontext == 'i')
12733 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
12734 124 : appendPQExpBufferStr(defqry, ";\n");
12735 :
12736 124 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
12737 : sourceType, targetType);
12738 :
12739 124 : appendPQExpBuffer(castargs, "(%s AS %s)",
12740 : sourceType, targetType);
12741 :
12742 124 : if (dopt->binary_upgrade)
12743 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
12744 14 : "CAST", castargs->data, NULL);
12745 :
12746 124 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
12747 124 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
12748 124 : ARCHIVE_OPTS(.tag = labelq->data,
12749 : .description = "CAST",
12750 : .section = SECTION_PRE_DATA,
12751 : .createStmt = defqry->data,
12752 : .dropStmt = delqry->data));
12753 :
12754 : /* Dump Cast Comments */
12755 124 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
12756 0 : dumpComment(fout, "CAST", castargs->data,
12757 : NULL, "",
12758 : cast->dobj.catId, 0, cast->dobj.dumpId);
12759 :
12760 124 : destroyPQExpBuffer(defqry);
12761 124 : destroyPQExpBuffer(delqry);
12762 124 : destroyPQExpBuffer(labelq);
12763 124 : destroyPQExpBuffer(castargs);
12764 : }
12765 :
12766 : /*
12767 : * Dump a transform
12768 : */
12769 : static void
12770 80 : dumpTransform(Archive *fout, const TransformInfo *transform)
12771 : {
12772 80 : DumpOptions *dopt = fout->dopt;
12773 : PQExpBuffer defqry;
12774 : PQExpBuffer delqry;
12775 : PQExpBuffer labelq;
12776 : PQExpBuffer transformargs;
12777 80 : FuncInfo *fromsqlFuncInfo = NULL;
12778 80 : FuncInfo *tosqlFuncInfo = NULL;
12779 : char *lanname;
12780 : const char *transformType;
12781 :
12782 : /* Do nothing in data-only dump */
12783 80 : if (dopt->dataOnly)
12784 6 : return;
12785 :
12786 : /* Cannot dump if we don't have the transform functions' info */
12787 74 : if (OidIsValid(transform->trffromsql))
12788 : {
12789 74 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
12790 74 : if (fromsqlFuncInfo == NULL)
12791 0 : pg_fatal("could not find function definition for function with OID %u",
12792 : transform->trffromsql);
12793 : }
12794 74 : if (OidIsValid(transform->trftosql))
12795 : {
12796 74 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
12797 74 : if (tosqlFuncInfo == NULL)
12798 0 : pg_fatal("could not find function definition for function with OID %u",
12799 : transform->trftosql);
12800 : }
12801 :
12802 74 : defqry = createPQExpBuffer();
12803 74 : delqry = createPQExpBuffer();
12804 74 : labelq = createPQExpBuffer();
12805 74 : transformargs = createPQExpBuffer();
12806 :
12807 74 : lanname = get_language_name(fout, transform->trflang);
12808 74 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
12809 :
12810 74 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
12811 : transformType, lanname);
12812 :
12813 74 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
12814 : transformType, lanname);
12815 :
12816 74 : if (!transform->trffromsql && !transform->trftosql)
12817 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
12818 :
12819 74 : if (transform->trffromsql)
12820 : {
12821 74 : if (fromsqlFuncInfo)
12822 : {
12823 74 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
12824 :
12825 : /*
12826 : * Always qualify the function name (format_function_signature
12827 : * won't qualify it).
12828 : */
12829 74 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
12830 74 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
12831 74 : free(fsig);
12832 : }
12833 : else
12834 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
12835 : }
12836 :
12837 74 : if (transform->trftosql)
12838 : {
12839 74 : if (transform->trffromsql)
12840 74 : appendPQExpBufferStr(defqry, ", ");
12841 :
12842 74 : if (tosqlFuncInfo)
12843 : {
12844 74 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
12845 :
12846 : /*
12847 : * Always qualify the function name (format_function_signature
12848 : * won't qualify it).
12849 : */
12850 74 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
12851 74 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
12852 74 : free(fsig);
12853 : }
12854 : else
12855 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
12856 : }
12857 :
12858 74 : appendPQExpBufferStr(defqry, ");\n");
12859 :
12860 74 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
12861 : transformType, lanname);
12862 :
12863 74 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
12864 : transformType, lanname);
12865 :
12866 74 : if (dopt->binary_upgrade)
12867 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
12868 4 : "TRANSFORM", transformargs->data, NULL);
12869 :
12870 74 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
12871 74 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
12872 74 : ARCHIVE_OPTS(.tag = labelq->data,
12873 : .description = "TRANSFORM",
12874 : .section = SECTION_PRE_DATA,
12875 : .createStmt = defqry->data,
12876 : .dropStmt = delqry->data,
12877 : .deps = transform->dobj.dependencies,
12878 : .nDeps = transform->dobj.nDeps));
12879 :
12880 : /* Dump Transform Comments */
12881 74 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
12882 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
12883 : NULL, "",
12884 : transform->dobj.catId, 0, transform->dobj.dumpId);
12885 :
12886 74 : free(lanname);
12887 74 : destroyPQExpBuffer(defqry);
12888 74 : destroyPQExpBuffer(delqry);
12889 74 : destroyPQExpBuffer(labelq);
12890 74 : destroyPQExpBuffer(transformargs);
12891 : }
12892 :
12893 :
12894 : /*
12895 : * dumpOpr
12896 : * write out a single operator definition
12897 : */
12898 : static void
12899 1808 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
12900 : {
12901 1808 : DumpOptions *dopt = fout->dopt;
12902 : PQExpBuffer query;
12903 : PQExpBuffer q;
12904 : PQExpBuffer delq;
12905 : PQExpBuffer oprid;
12906 : PQExpBuffer details;
12907 : PGresult *res;
12908 : int i_oprkind;
12909 : int i_oprcode;
12910 : int i_oprleft;
12911 : int i_oprright;
12912 : int i_oprcom;
12913 : int i_oprnegate;
12914 : int i_oprrest;
12915 : int i_oprjoin;
12916 : int i_oprcanmerge;
12917 : int i_oprcanhash;
12918 : char *oprkind;
12919 : char *oprcode;
12920 : char *oprleft;
12921 : char *oprright;
12922 : char *oprcom;
12923 : char *oprnegate;
12924 : char *oprrest;
12925 : char *oprjoin;
12926 : char *oprcanmerge;
12927 : char *oprcanhash;
12928 : char *oprregproc;
12929 : char *oprref;
12930 :
12931 : /* Do nothing in data-only dump */
12932 1808 : if (dopt->dataOnly)
12933 6 : return;
12934 :
12935 : /*
12936 : * some operators are invalid because they were the result of user
12937 : * defining operators before commutators exist
12938 : */
12939 1802 : if (!OidIsValid(oprinfo->oprcode))
12940 28 : return;
12941 :
12942 1774 : query = createPQExpBuffer();
12943 1774 : q = createPQExpBuffer();
12944 1774 : delq = createPQExpBuffer();
12945 1774 : oprid = createPQExpBuffer();
12946 1774 : details = createPQExpBuffer();
12947 :
12948 1774 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
12949 : {
12950 : /* Set up query for operator-specific details */
12951 78 : appendPQExpBufferStr(query,
12952 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
12953 : "SELECT oprkind, "
12954 : "oprcode::pg_catalog.regprocedure, "
12955 : "oprleft::pg_catalog.regtype, "
12956 : "oprright::pg_catalog.regtype, "
12957 : "oprcom, "
12958 : "oprnegate, "
12959 : "oprrest::pg_catalog.regprocedure, "
12960 : "oprjoin::pg_catalog.regprocedure, "
12961 : "oprcanmerge, oprcanhash "
12962 : "FROM pg_catalog.pg_operator "
12963 : "WHERE oid = $1");
12964 :
12965 78 : ExecuteSqlStatement(fout, query->data);
12966 :
12967 78 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
12968 : }
12969 :
12970 1774 : printfPQExpBuffer(query,
12971 : "EXECUTE dumpOpr('%u')",
12972 : oprinfo->dobj.catId.oid);
12973 :
12974 1774 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12975 :
12976 1774 : i_oprkind = PQfnumber(res, "oprkind");
12977 1774 : i_oprcode = PQfnumber(res, "oprcode");
12978 1774 : i_oprleft = PQfnumber(res, "oprleft");
12979 1774 : i_oprright = PQfnumber(res, "oprright");
12980 1774 : i_oprcom = PQfnumber(res, "oprcom");
12981 1774 : i_oprnegate = PQfnumber(res, "oprnegate");
12982 1774 : i_oprrest = PQfnumber(res, "oprrest");
12983 1774 : i_oprjoin = PQfnumber(res, "oprjoin");
12984 1774 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
12985 1774 : i_oprcanhash = PQfnumber(res, "oprcanhash");
12986 :
12987 1774 : oprkind = PQgetvalue(res, 0, i_oprkind);
12988 1774 : oprcode = PQgetvalue(res, 0, i_oprcode);
12989 1774 : oprleft = PQgetvalue(res, 0, i_oprleft);
12990 1774 : oprright = PQgetvalue(res, 0, i_oprright);
12991 1774 : oprcom = PQgetvalue(res, 0, i_oprcom);
12992 1774 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
12993 1774 : oprrest = PQgetvalue(res, 0, i_oprrest);
12994 1774 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
12995 1774 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
12996 1774 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
12997 :
12998 : /* In PG14 upwards postfix operator support does not exist anymore. */
12999 1774 : if (strcmp(oprkind, "r") == 0)
13000 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13001 : oprcode);
13002 :
13003 1774 : oprregproc = convertRegProcReference(oprcode);
13004 1774 : if (oprregproc)
13005 : {
13006 1774 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13007 1774 : free(oprregproc);
13008 : }
13009 :
13010 1774 : appendPQExpBuffer(oprid, "%s (",
13011 : oprinfo->dobj.name);
13012 :
13013 : /*
13014 : * right unary means there's a left arg and left unary means there's a
13015 : * right arg. (Although the "r" case is dead code for PG14 and later,
13016 : * continue to support it in case we're dumping from an old server.)
13017 : */
13018 1774 : if (strcmp(oprkind, "r") == 0 ||
13019 1774 : strcmp(oprkind, "b") == 0)
13020 : {
13021 1652 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13022 1652 : appendPQExpBufferStr(oprid, oprleft);
13023 : }
13024 : else
13025 122 : appendPQExpBufferStr(oprid, "NONE");
13026 :
13027 1774 : if (strcmp(oprkind, "l") == 0 ||
13028 1652 : strcmp(oprkind, "b") == 0)
13029 : {
13030 1774 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13031 1774 : appendPQExpBuffer(oprid, ", %s)", oprright);
13032 : }
13033 : else
13034 0 : appendPQExpBufferStr(oprid, ", NONE)");
13035 :
13036 1774 : oprref = getFormattedOperatorName(oprcom);
13037 1774 : if (oprref)
13038 : {
13039 1134 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13040 1134 : free(oprref);
13041 : }
13042 :
13043 1774 : oprref = getFormattedOperatorName(oprnegate);
13044 1774 : if (oprref)
13045 : {
13046 782 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13047 782 : free(oprref);
13048 : }
13049 :
13050 1774 : if (strcmp(oprcanmerge, "t") == 0)
13051 150 : appendPQExpBufferStr(details, ",\n MERGES");
13052 :
13053 1774 : if (strcmp(oprcanhash, "t") == 0)
13054 92 : appendPQExpBufferStr(details, ",\n HASHES");
13055 :
13056 1774 : oprregproc = convertRegProcReference(oprrest);
13057 1774 : if (oprregproc)
13058 : {
13059 1036 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13060 1036 : free(oprregproc);
13061 : }
13062 :
13063 1774 : oprregproc = convertRegProcReference(oprjoin);
13064 1774 : if (oprregproc)
13065 : {
13066 1036 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13067 1036 : free(oprregproc);
13068 : }
13069 :
13070 1774 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13071 1774 : fmtId(oprinfo->dobj.namespace->dobj.name),
13072 : oprid->data);
13073 :
13074 1774 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13075 1774 : fmtId(oprinfo->dobj.namespace->dobj.name),
13076 : oprinfo->dobj.name, details->data);
13077 :
13078 1774 : if (dopt->binary_upgrade)
13079 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13080 24 : "OPERATOR", oprid->data,
13081 24 : oprinfo->dobj.namespace->dobj.name);
13082 :
13083 1774 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13084 1774 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13085 1774 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13086 : .namespace = oprinfo->dobj.namespace->dobj.name,
13087 : .owner = oprinfo->rolname,
13088 : .description = "OPERATOR",
13089 : .section = SECTION_PRE_DATA,
13090 : .createStmt = q->data,
13091 : .dropStmt = delq->data));
13092 :
13093 : /* Dump Operator Comments */
13094 1774 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13095 1598 : dumpComment(fout, "OPERATOR", oprid->data,
13096 1598 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13097 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13098 :
13099 1774 : PQclear(res);
13100 :
13101 1774 : destroyPQExpBuffer(query);
13102 1774 : destroyPQExpBuffer(q);
13103 1774 : destroyPQExpBuffer(delq);
13104 1774 : destroyPQExpBuffer(oprid);
13105 1774 : destroyPQExpBuffer(details);
13106 : }
13107 :
13108 : /*
13109 : * Convert a function reference obtained from pg_operator
13110 : *
13111 : * Returns allocated string of what to print, or NULL if function references
13112 : * is InvalidOid. Returned string is expected to be free'd by the caller.
13113 : *
13114 : * The input is a REGPROCEDURE display; we have to strip the argument-types
13115 : * part.
13116 : */
13117 : static char *
13118 5322 : convertRegProcReference(const char *proc)
13119 : {
13120 : char *name;
13121 : char *paren;
13122 : bool inquote;
13123 :
13124 : /* In all cases "-" means a null reference */
13125 5322 : if (strcmp(proc, "-") == 0)
13126 1476 : return NULL;
13127 :
13128 3846 : name = pg_strdup(proc);
13129 : /* find non-double-quoted left paren */
13130 3846 : inquote = false;
13131 46214 : for (paren = name; *paren; paren++)
13132 : {
13133 46214 : if (*paren == '(' && !inquote)
13134 : {
13135 3846 : *paren = '\0';
13136 3846 : break;
13137 : }
13138 42368 : if (*paren == '"')
13139 100 : inquote = !inquote;
13140 : }
13141 3846 : return name;
13142 : }
13143 :
13144 : /*
13145 : * getFormattedOperatorName - retrieve the operator name for the
13146 : * given operator OID (presented in string form).
13147 : *
13148 : * Returns an allocated string, or NULL if the given OID is invalid.
13149 : * Caller is responsible for free'ing result string.
13150 : *
13151 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
13152 : * useful in commands where the operator's argument types can be inferred from
13153 : * context. We always schema-qualify the name, though. The predecessor to
13154 : * this code tried to skip the schema qualification if possible, but that led
13155 : * to wrong results in corner cases, such as if an operator and its negator
13156 : * are in different schemas.
13157 : */
13158 : static char *
13159 4120 : getFormattedOperatorName(const char *oproid)
13160 : {
13161 : OprInfo *oprInfo;
13162 :
13163 : /* In all cases "0" means a null reference */
13164 4120 : if (strcmp(oproid, "0") == 0)
13165 2204 : return NULL;
13166 :
13167 1916 : oprInfo = findOprByOid(atooid(oproid));
13168 1916 : if (oprInfo == NULL)
13169 : {
13170 0 : pg_log_warning("could not find operator with OID %s",
13171 : oproid);
13172 0 : return NULL;
13173 : }
13174 :
13175 1916 : return psprintf("OPERATOR(%s.%s)",
13176 1916 : fmtId(oprInfo->dobj.namespace->dobj.name),
13177 : oprInfo->dobj.name);
13178 : }
13179 :
13180 : /*
13181 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
13182 : *
13183 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
13184 : * argument lists of these functions are predetermined. Note that the
13185 : * caller should ensure we are in the proper schema, because the results
13186 : * are search path dependent!
13187 : */
13188 : static char *
13189 360 : convertTSFunction(Archive *fout, Oid funcOid)
13190 : {
13191 : char *result;
13192 : char query[128];
13193 : PGresult *res;
13194 :
13195 360 : snprintf(query, sizeof(query),
13196 : "SELECT '%u'::pg_catalog.regproc", funcOid);
13197 360 : res = ExecuteSqlQueryForSingleRow(fout, query);
13198 :
13199 360 : result = pg_strdup(PQgetvalue(res, 0, 0));
13200 :
13201 360 : PQclear(res);
13202 :
13203 360 : return result;
13204 : }
13205 :
13206 : /*
13207 : * dumpAccessMethod
13208 : * write out a single access method definition
13209 : */
13210 : static void
13211 152 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
13212 : {
13213 152 : DumpOptions *dopt = fout->dopt;
13214 : PQExpBuffer q;
13215 : PQExpBuffer delq;
13216 : char *qamname;
13217 :
13218 : /* Do nothing in data-only dump */
13219 152 : if (dopt->dataOnly)
13220 12 : return;
13221 :
13222 140 : q = createPQExpBuffer();
13223 140 : delq = createPQExpBuffer();
13224 :
13225 140 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
13226 :
13227 140 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
13228 :
13229 140 : switch (aminfo->amtype)
13230 : {
13231 66 : case AMTYPE_INDEX:
13232 66 : appendPQExpBufferStr(q, "TYPE INDEX ");
13233 66 : break;
13234 74 : case AMTYPE_TABLE:
13235 74 : appendPQExpBufferStr(q, "TYPE TABLE ");
13236 74 : break;
13237 0 : default:
13238 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
13239 : aminfo->amtype, qamname);
13240 0 : destroyPQExpBuffer(q);
13241 0 : destroyPQExpBuffer(delq);
13242 0 : free(qamname);
13243 0 : return;
13244 : }
13245 :
13246 140 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13247 :
13248 140 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13249 : qamname);
13250 :
13251 140 : if (dopt->binary_upgrade)
13252 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
13253 : "ACCESS METHOD", qamname, NULL);
13254 :
13255 140 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13256 140 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13257 140 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13258 : .description = "ACCESS METHOD",
13259 : .section = SECTION_PRE_DATA,
13260 : .createStmt = q->data,
13261 : .dropStmt = delq->data));
13262 :
13263 : /* Dump Access Method Comments */
13264 140 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13265 0 : dumpComment(fout, "ACCESS METHOD", qamname,
13266 : NULL, "",
13267 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13268 :
13269 140 : destroyPQExpBuffer(q);
13270 140 : destroyPQExpBuffer(delq);
13271 140 : free(qamname);
13272 : }
13273 :
13274 : /*
13275 : * dumpOpclass
13276 : * write out a single operator class definition
13277 : */
13278 : static void
13279 600 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13280 : {
13281 600 : DumpOptions *dopt = fout->dopt;
13282 : PQExpBuffer query;
13283 : PQExpBuffer q;
13284 : PQExpBuffer delq;
13285 : PQExpBuffer nameusing;
13286 : PGresult *res;
13287 : int ntups;
13288 : int i_opcintype;
13289 : int i_opckeytype;
13290 : int i_opcdefault;
13291 : int i_opcfamily;
13292 : int i_opcfamilyname;
13293 : int i_opcfamilynsp;
13294 : int i_amname;
13295 : int i_amopstrategy;
13296 : int i_amopopr;
13297 : int i_sortfamily;
13298 : int i_sortfamilynsp;
13299 : int i_amprocnum;
13300 : int i_amproc;
13301 : int i_amproclefttype;
13302 : int i_amprocrighttype;
13303 : char *opcintype;
13304 : char *opckeytype;
13305 : char *opcdefault;
13306 : char *opcfamily;
13307 : char *opcfamilyname;
13308 : char *opcfamilynsp;
13309 : char *amname;
13310 : char *amopstrategy;
13311 : char *amopopr;
13312 : char *sortfamily;
13313 : char *sortfamilynsp;
13314 : char *amprocnum;
13315 : char *amproc;
13316 : char *amproclefttype;
13317 : char *amprocrighttype;
13318 : bool needComma;
13319 : int i;
13320 :
13321 : /* Do nothing in data-only dump */
13322 600 : if (dopt->dataOnly)
13323 18 : return;
13324 :
13325 582 : query = createPQExpBuffer();
13326 582 : q = createPQExpBuffer();
13327 582 : delq = createPQExpBuffer();
13328 582 : nameusing = createPQExpBuffer();
13329 :
13330 : /* Get additional fields from the pg_opclass row */
13331 582 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13332 : "opckeytype::pg_catalog.regtype, "
13333 : "opcdefault, opcfamily, "
13334 : "opfname AS opcfamilyname, "
13335 : "nspname AS opcfamilynsp, "
13336 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13337 : "FROM pg_catalog.pg_opclass c "
13338 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13339 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13340 : "WHERE c.oid = '%u'::pg_catalog.oid",
13341 : opcinfo->dobj.catId.oid);
13342 :
13343 582 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13344 :
13345 582 : i_opcintype = PQfnumber(res, "opcintype");
13346 582 : i_opckeytype = PQfnumber(res, "opckeytype");
13347 582 : i_opcdefault = PQfnumber(res, "opcdefault");
13348 582 : i_opcfamily = PQfnumber(res, "opcfamily");
13349 582 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13350 582 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13351 582 : i_amname = PQfnumber(res, "amname");
13352 :
13353 : /* opcintype may still be needed after we PQclear res */
13354 582 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13355 582 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
13356 582 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
13357 : /* opcfamily will still be needed after we PQclear res */
13358 582 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13359 582 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13360 582 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13361 : /* amname will still be needed after we PQclear res */
13362 582 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13363 :
13364 582 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13365 582 : fmtQualifiedDumpable(opcinfo));
13366 582 : appendPQExpBuffer(delq, " USING %s;\n",
13367 : fmtId(amname));
13368 :
13369 : /* Build the fixed portion of the CREATE command */
13370 582 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13371 582 : fmtQualifiedDumpable(opcinfo));
13372 582 : if (strcmp(opcdefault, "t") == 0)
13373 238 : appendPQExpBufferStr(q, "DEFAULT ");
13374 582 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13375 : opcintype,
13376 : fmtId(amname));
13377 582 : if (strlen(opcfamilyname) > 0)
13378 : {
13379 582 : appendPQExpBufferStr(q, " FAMILY ");
13380 582 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13381 582 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
13382 : }
13383 582 : appendPQExpBufferStr(q, " AS\n ");
13384 :
13385 582 : needComma = false;
13386 :
13387 582 : if (strcmp(opckeytype, "-") != 0)
13388 : {
13389 168 : appendPQExpBuffer(q, "STORAGE %s",
13390 : opckeytype);
13391 168 : needComma = true;
13392 : }
13393 :
13394 582 : PQclear(res);
13395 :
13396 : /*
13397 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13398 : *
13399 : * Print only those opfamily members that are tied to the opclass by
13400 : * pg_depend entries.
13401 : */
13402 582 : resetPQExpBuffer(query);
13403 582 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13404 : "amopopr::pg_catalog.regoperator, "
13405 : "opfname AS sortfamily, "
13406 : "nspname AS sortfamilynsp "
13407 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13408 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13409 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13410 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13411 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13412 : "AND refobjid = '%u'::pg_catalog.oid "
13413 : "AND amopfamily = '%s'::pg_catalog.oid "
13414 : "ORDER BY amopstrategy",
13415 : opcinfo->dobj.catId.oid,
13416 : opcfamily);
13417 :
13418 582 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13419 :
13420 582 : ntups = PQntuples(res);
13421 :
13422 582 : i_amopstrategy = PQfnumber(res, "amopstrategy");
13423 582 : i_amopopr = PQfnumber(res, "amopopr");
13424 582 : i_sortfamily = PQfnumber(res, "sortfamily");
13425 582 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13426 :
13427 998 : for (i = 0; i < ntups; i++)
13428 : {
13429 416 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13430 416 : amopopr = PQgetvalue(res, i, i_amopopr);
13431 416 : sortfamily = PQgetvalue(res, i, i_sortfamily);
13432 416 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13433 :
13434 416 : if (needComma)
13435 264 : appendPQExpBufferStr(q, " ,\n ");
13436 :
13437 416 : appendPQExpBuffer(q, "OPERATOR %s %s",
13438 : amopstrategy, amopopr);
13439 :
13440 416 : if (strlen(sortfamily) > 0)
13441 : {
13442 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13443 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13444 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13445 : }
13446 :
13447 416 : needComma = true;
13448 : }
13449 :
13450 582 : PQclear(res);
13451 :
13452 : /*
13453 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13454 : *
13455 : * Print only those opfamily members that are tied to the opclass by
13456 : * pg_depend entries.
13457 : *
13458 : * We print the amproclefttype/amprocrighttype even though in most cases
13459 : * the backend could deduce the right values, because of the corner case
13460 : * of a btree sort support function for a cross-type comparison.
13461 : */
13462 582 : resetPQExpBuffer(query);
13463 :
13464 582 : appendPQExpBuffer(query, "SELECT amprocnum, "
13465 : "amproc::pg_catalog.regprocedure, "
13466 : "amproclefttype::pg_catalog.regtype, "
13467 : "amprocrighttype::pg_catalog.regtype "
13468 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13469 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13470 : "AND refobjid = '%u'::pg_catalog.oid "
13471 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13472 : "AND objid = ap.oid "
13473 : "ORDER BY amprocnum",
13474 : opcinfo->dobj.catId.oid);
13475 :
13476 582 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13477 :
13478 582 : ntups = PQntuples(res);
13479 :
13480 582 : i_amprocnum = PQfnumber(res, "amprocnum");
13481 582 : i_amproc = PQfnumber(res, "amproc");
13482 582 : i_amproclefttype = PQfnumber(res, "amproclefttype");
13483 582 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13484 :
13485 648 : for (i = 0; i < ntups; i++)
13486 : {
13487 66 : amprocnum = PQgetvalue(res, i, i_amprocnum);
13488 66 : amproc = PQgetvalue(res, i, i_amproc);
13489 66 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13490 66 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13491 :
13492 66 : if (needComma)
13493 66 : appendPQExpBufferStr(q, " ,\n ");
13494 :
13495 66 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13496 :
13497 66 : if (*amproclefttype && *amprocrighttype)
13498 66 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13499 :
13500 66 : appendPQExpBuffer(q, " %s", amproc);
13501 :
13502 66 : needComma = true;
13503 : }
13504 :
13505 582 : PQclear(res);
13506 :
13507 : /*
13508 : * If needComma is still false it means we haven't added anything after
13509 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13510 : * clause with the same datatype. This isn't sanctioned by the
13511 : * documentation, but actually DefineOpClass will treat it as a no-op.
13512 : */
13513 582 : if (!needComma)
13514 262 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
13515 :
13516 582 : appendPQExpBufferStr(q, ";\n");
13517 :
13518 582 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13519 582 : appendPQExpBuffer(nameusing, " USING %s",
13520 : fmtId(amname));
13521 :
13522 582 : if (dopt->binary_upgrade)
13523 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
13524 12 : "OPERATOR CLASS", nameusing->data,
13525 12 : opcinfo->dobj.namespace->dobj.name);
13526 :
13527 582 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13528 582 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13529 582 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13530 : .namespace = opcinfo->dobj.namespace->dobj.name,
13531 : .owner = opcinfo->rolname,
13532 : .description = "OPERATOR CLASS",
13533 : .section = SECTION_PRE_DATA,
13534 : .createStmt = q->data,
13535 : .dropStmt = delq->data));
13536 :
13537 : /* Dump Operator Class Comments */
13538 582 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13539 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13540 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13541 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13542 :
13543 582 : free(opcintype);
13544 582 : free(opcfamily);
13545 582 : free(amname);
13546 582 : destroyPQExpBuffer(query);
13547 582 : destroyPQExpBuffer(q);
13548 582 : destroyPQExpBuffer(delq);
13549 582 : destroyPQExpBuffer(nameusing);
13550 : }
13551 :
13552 : /*
13553 : * dumpOpfamily
13554 : * write out a single operator family definition
13555 : *
13556 : * Note: this also dumps any "loose" operator members that aren't bound to a
13557 : * specific opclass within the opfamily.
13558 : */
13559 : static void
13560 506 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
13561 : {
13562 506 : DumpOptions *dopt = fout->dopt;
13563 : PQExpBuffer query;
13564 : PQExpBuffer q;
13565 : PQExpBuffer delq;
13566 : PQExpBuffer nameusing;
13567 : PGresult *res;
13568 : PGresult *res_ops;
13569 : PGresult *res_procs;
13570 : int ntups;
13571 : int i_amname;
13572 : int i_amopstrategy;
13573 : int i_amopopr;
13574 : int i_sortfamily;
13575 : int i_sortfamilynsp;
13576 : int i_amprocnum;
13577 : int i_amproc;
13578 : int i_amproclefttype;
13579 : int i_amprocrighttype;
13580 : char *amname;
13581 : char *amopstrategy;
13582 : char *amopopr;
13583 : char *sortfamily;
13584 : char *sortfamilynsp;
13585 : char *amprocnum;
13586 : char *amproc;
13587 : char *amproclefttype;
13588 : char *amprocrighttype;
13589 : bool needComma;
13590 : int i;
13591 :
13592 : /* Do nothing in data-only dump */
13593 506 : if (dopt->dataOnly)
13594 12 : return;
13595 :
13596 494 : query = createPQExpBuffer();
13597 494 : q = createPQExpBuffer();
13598 494 : delq = createPQExpBuffer();
13599 494 : nameusing = createPQExpBuffer();
13600 :
13601 : /*
13602 : * Fetch only those opfamily members that are tied directly to the
13603 : * opfamily by pg_depend entries.
13604 : */
13605 494 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13606 : "amopopr::pg_catalog.regoperator, "
13607 : "opfname AS sortfamily, "
13608 : "nspname AS sortfamilynsp "
13609 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13610 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13611 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13612 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13613 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13614 : "AND refobjid = '%u'::pg_catalog.oid "
13615 : "AND amopfamily = '%u'::pg_catalog.oid "
13616 : "ORDER BY amopstrategy",
13617 : opfinfo->dobj.catId.oid,
13618 : opfinfo->dobj.catId.oid);
13619 :
13620 494 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13621 :
13622 494 : resetPQExpBuffer(query);
13623 :
13624 494 : appendPQExpBuffer(query, "SELECT amprocnum, "
13625 : "amproc::pg_catalog.regprocedure, "
13626 : "amproclefttype::pg_catalog.regtype, "
13627 : "amprocrighttype::pg_catalog.regtype "
13628 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13629 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13630 : "AND refobjid = '%u'::pg_catalog.oid "
13631 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13632 : "AND objid = ap.oid "
13633 : "ORDER BY amprocnum",
13634 : opfinfo->dobj.catId.oid);
13635 :
13636 494 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13637 :
13638 : /* Get additional fields from the pg_opfamily row */
13639 494 : resetPQExpBuffer(query);
13640 :
13641 494 : appendPQExpBuffer(query, "SELECT "
13642 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
13643 : "FROM pg_catalog.pg_opfamily "
13644 : "WHERE oid = '%u'::pg_catalog.oid",
13645 : opfinfo->dobj.catId.oid);
13646 :
13647 494 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13648 :
13649 494 : i_amname = PQfnumber(res, "amname");
13650 :
13651 : /* amname will still be needed after we PQclear res */
13652 494 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13653 :
13654 494 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
13655 494 : fmtQualifiedDumpable(opfinfo));
13656 494 : appendPQExpBuffer(delq, " USING %s;\n",
13657 : fmtId(amname));
13658 :
13659 : /* Build the fixed portion of the CREATE command */
13660 494 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
13661 494 : fmtQualifiedDumpable(opfinfo));
13662 494 : appendPQExpBuffer(q, " USING %s;\n",
13663 : fmtId(amname));
13664 :
13665 494 : PQclear(res);
13666 :
13667 : /* Do we need an ALTER to add loose members? */
13668 494 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
13669 : {
13670 96 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
13671 96 : fmtQualifiedDumpable(opfinfo));
13672 96 : appendPQExpBuffer(q, " USING %s ADD\n ",
13673 : fmtId(amname));
13674 :
13675 96 : needComma = false;
13676 :
13677 : /*
13678 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13679 : */
13680 96 : ntups = PQntuples(res_ops);
13681 :
13682 96 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
13683 96 : i_amopopr = PQfnumber(res_ops, "amopopr");
13684 96 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
13685 96 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
13686 :
13687 426 : for (i = 0; i < ntups; i++)
13688 : {
13689 330 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
13690 330 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
13691 330 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
13692 330 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
13693 :
13694 330 : if (needComma)
13695 264 : appendPQExpBufferStr(q, " ,\n ");
13696 :
13697 330 : appendPQExpBuffer(q, "OPERATOR %s %s",
13698 : amopstrategy, amopopr);
13699 :
13700 330 : if (strlen(sortfamily) > 0)
13701 : {
13702 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13703 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13704 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13705 : }
13706 :
13707 330 : needComma = true;
13708 : }
13709 :
13710 : /*
13711 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13712 : */
13713 96 : ntups = PQntuples(res_procs);
13714 :
13715 96 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
13716 96 : i_amproc = PQfnumber(res_procs, "amproc");
13717 96 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
13718 96 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
13719 :
13720 456 : for (i = 0; i < ntups; i++)
13721 : {
13722 360 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
13723 360 : amproc = PQgetvalue(res_procs, i, i_amproc);
13724 360 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
13725 360 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
13726 :
13727 360 : if (needComma)
13728 330 : appendPQExpBufferStr(q, " ,\n ");
13729 :
13730 360 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
13731 : amprocnum, amproclefttype, amprocrighttype,
13732 : amproc);
13733 :
13734 360 : needComma = true;
13735 : }
13736 :
13737 96 : appendPQExpBufferStr(q, ";\n");
13738 : }
13739 :
13740 494 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
13741 494 : appendPQExpBuffer(nameusing, " USING %s",
13742 : fmtId(amname));
13743 :
13744 494 : if (dopt->binary_upgrade)
13745 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
13746 18 : "OPERATOR FAMILY", nameusing->data,
13747 18 : opfinfo->dobj.namespace->dobj.name);
13748 :
13749 494 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13750 494 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
13751 494 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
13752 : .namespace = opfinfo->dobj.namespace->dobj.name,
13753 : .owner = opfinfo->rolname,
13754 : .description = "OPERATOR FAMILY",
13755 : .section = SECTION_PRE_DATA,
13756 : .createStmt = q->data,
13757 : .dropStmt = delq->data));
13758 :
13759 : /* Dump Operator Family Comments */
13760 494 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13761 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
13762 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
13763 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
13764 :
13765 494 : free(amname);
13766 494 : PQclear(res_ops);
13767 494 : PQclear(res_procs);
13768 494 : destroyPQExpBuffer(query);
13769 494 : destroyPQExpBuffer(q);
13770 494 : destroyPQExpBuffer(delq);
13771 494 : destroyPQExpBuffer(nameusing);
13772 : }
13773 :
13774 : /*
13775 : * dumpCollation
13776 : * write out a single collation definition
13777 : */
13778 : static void
13779 1744 : dumpCollation(Archive *fout, const CollInfo *collinfo)
13780 : {
13781 1744 : DumpOptions *dopt = fout->dopt;
13782 : PQExpBuffer query;
13783 : PQExpBuffer q;
13784 : PQExpBuffer delq;
13785 : char *qcollname;
13786 : PGresult *res;
13787 : int i_collprovider;
13788 : int i_collisdeterministic;
13789 : int i_collcollate;
13790 : int i_collctype;
13791 : int i_colllocale;
13792 : int i_collicurules;
13793 : const char *collprovider;
13794 : const char *collcollate;
13795 : const char *collctype;
13796 : const char *colllocale;
13797 : const char *collicurules;
13798 :
13799 : /* Do nothing in data-only dump */
13800 1744 : if (dopt->dataOnly)
13801 12 : return;
13802 :
13803 1732 : query = createPQExpBuffer();
13804 1732 : q = createPQExpBuffer();
13805 1732 : delq = createPQExpBuffer();
13806 :
13807 1732 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
13808 :
13809 : /* Get collation-specific details */
13810 1732 : appendPQExpBufferStr(query, "SELECT ");
13811 :
13812 1732 : if (fout->remoteVersion >= 100000)
13813 1732 : appendPQExpBufferStr(query,
13814 : "collprovider, "
13815 : "collversion, ");
13816 : else
13817 0 : appendPQExpBufferStr(query,
13818 : "'c' AS collprovider, "
13819 : "NULL AS collversion, ");
13820 :
13821 1732 : if (fout->remoteVersion >= 120000)
13822 1732 : appendPQExpBufferStr(query,
13823 : "collisdeterministic, ");
13824 : else
13825 0 : appendPQExpBufferStr(query,
13826 : "true AS collisdeterministic, ");
13827 :
13828 1732 : if (fout->remoteVersion >= 170000)
13829 1732 : appendPQExpBufferStr(query,
13830 : "colllocale, ");
13831 0 : else if (fout->remoteVersion >= 150000)
13832 0 : appendPQExpBufferStr(query,
13833 : "colliculocale AS colllocale, ");
13834 : else
13835 0 : appendPQExpBufferStr(query,
13836 : "NULL AS colllocale, ");
13837 :
13838 1732 : if (fout->remoteVersion >= 160000)
13839 1732 : appendPQExpBufferStr(query,
13840 : "collicurules, ");
13841 : else
13842 0 : appendPQExpBufferStr(query,
13843 : "NULL AS collicurules, ");
13844 :
13845 1732 : appendPQExpBuffer(query,
13846 : "collcollate, "
13847 : "collctype "
13848 : "FROM pg_catalog.pg_collation c "
13849 : "WHERE c.oid = '%u'::pg_catalog.oid",
13850 : collinfo->dobj.catId.oid);
13851 :
13852 1732 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13853 :
13854 1732 : i_collprovider = PQfnumber(res, "collprovider");
13855 1732 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
13856 1732 : i_collcollate = PQfnumber(res, "collcollate");
13857 1732 : i_collctype = PQfnumber(res, "collctype");
13858 1732 : i_colllocale = PQfnumber(res, "colllocale");
13859 1732 : i_collicurules = PQfnumber(res, "collicurules");
13860 :
13861 1732 : collprovider = PQgetvalue(res, 0, i_collprovider);
13862 :
13863 1732 : if (!PQgetisnull(res, 0, i_collcollate))
13864 74 : collcollate = PQgetvalue(res, 0, i_collcollate);
13865 : else
13866 1658 : collcollate = NULL;
13867 :
13868 1732 : if (!PQgetisnull(res, 0, i_collctype))
13869 74 : collctype = PQgetvalue(res, 0, i_collctype);
13870 : else
13871 1658 : collctype = NULL;
13872 :
13873 : /*
13874 : * Before version 15, collcollate and collctype were of type NAME and
13875 : * non-nullable. Treat empty strings as NULL for consistency.
13876 : */
13877 1732 : if (fout->remoteVersion < 150000)
13878 : {
13879 0 : if (collcollate[0] == '\0')
13880 0 : collcollate = NULL;
13881 0 : if (collctype[0] == '\0')
13882 0 : collctype = NULL;
13883 : }
13884 :
13885 1732 : if (!PQgetisnull(res, 0, i_colllocale))
13886 1656 : colllocale = PQgetvalue(res, 0, i_colllocale);
13887 : else
13888 76 : colllocale = NULL;
13889 :
13890 1732 : if (!PQgetisnull(res, 0, i_collicurules))
13891 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
13892 : else
13893 1732 : collicurules = NULL;
13894 :
13895 1732 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
13896 1732 : fmtQualifiedDumpable(collinfo));
13897 :
13898 1732 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
13899 1732 : fmtQualifiedDumpable(collinfo));
13900 :
13901 1732 : appendPQExpBufferStr(q, "provider = ");
13902 1732 : if (collprovider[0] == 'b')
13903 14 : appendPQExpBufferStr(q, "builtin");
13904 1718 : else if (collprovider[0] == 'c')
13905 74 : appendPQExpBufferStr(q, "libc");
13906 1644 : else if (collprovider[0] == 'i')
13907 1642 : appendPQExpBufferStr(q, "icu");
13908 2 : else if (collprovider[0] == 'd')
13909 : /* to allow dumping pg_catalog; not accepted on input */
13910 2 : appendPQExpBufferStr(q, "default");
13911 : else
13912 0 : pg_fatal("unrecognized collation provider: %s",
13913 : collprovider);
13914 :
13915 1732 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
13916 0 : appendPQExpBufferStr(q, ", deterministic = false");
13917 :
13918 1732 : if (collprovider[0] == 'd')
13919 : {
13920 2 : if (collcollate || collctype || colllocale || collicurules)
13921 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13922 :
13923 : /* no locale -- the default collation cannot be reloaded anyway */
13924 : }
13925 1730 : else if (collprovider[0] == 'b')
13926 : {
13927 14 : if (collcollate || collctype || !colllocale || collicurules)
13928 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13929 :
13930 14 : appendPQExpBufferStr(q, ", locale = ");
13931 14 : appendStringLiteralAH(q, colllocale ? colllocale : "",
13932 : fout);
13933 : }
13934 1716 : else if (collprovider[0] == 'i')
13935 : {
13936 1642 : if (fout->remoteVersion >= 150000)
13937 : {
13938 1642 : if (collcollate || collctype || !colllocale)
13939 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13940 :
13941 1642 : appendPQExpBufferStr(q, ", locale = ");
13942 1642 : appendStringLiteralAH(q, colllocale ? colllocale : "",
13943 : fout);
13944 : }
13945 : else
13946 : {
13947 0 : if (!collcollate || !collctype || colllocale ||
13948 0 : strcmp(collcollate, collctype) != 0)
13949 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13950 :
13951 0 : appendPQExpBufferStr(q, ", locale = ");
13952 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
13953 : }
13954 :
13955 1642 : if (collicurules)
13956 : {
13957 0 : appendPQExpBufferStr(q, ", rules = ");
13958 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
13959 : }
13960 : }
13961 74 : else if (collprovider[0] == 'c')
13962 : {
13963 74 : if (colllocale || collicurules || !collcollate || !collctype)
13964 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
13965 :
13966 74 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
13967 : {
13968 74 : appendPQExpBufferStr(q, ", locale = ");
13969 74 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
13970 : }
13971 : else
13972 : {
13973 0 : appendPQExpBufferStr(q, ", lc_collate = ");
13974 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
13975 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
13976 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
13977 : }
13978 : }
13979 : else
13980 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
13981 :
13982 : /*
13983 : * For binary upgrade, carry over the collation version. For normal
13984 : * dump/restore, omit the version, so that it is computed upon restore.
13985 : */
13986 1732 : if (dopt->binary_upgrade)
13987 : {
13988 : int i_collversion;
13989 :
13990 8 : i_collversion = PQfnumber(res, "collversion");
13991 8 : if (!PQgetisnull(res, 0, i_collversion))
13992 : {
13993 4 : appendPQExpBufferStr(q, ", version = ");
13994 4 : appendStringLiteralAH(q,
13995 : PQgetvalue(res, 0, i_collversion),
13996 : fout);
13997 : }
13998 : }
13999 :
14000 1732 : appendPQExpBufferStr(q, ");\n");
14001 :
14002 1732 : if (dopt->binary_upgrade)
14003 8 : binary_upgrade_extension_member(q, &collinfo->dobj,
14004 : "COLLATION", qcollname,
14005 8 : collinfo->dobj.namespace->dobj.name);
14006 :
14007 1732 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14008 1732 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14009 1732 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14010 : .namespace = collinfo->dobj.namespace->dobj.name,
14011 : .owner = collinfo->rolname,
14012 : .description = "COLLATION",
14013 : .section = SECTION_PRE_DATA,
14014 : .createStmt = q->data,
14015 : .dropStmt = delq->data));
14016 :
14017 : /* Dump Collation Comments */
14018 1732 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14019 1620 : dumpComment(fout, "COLLATION", qcollname,
14020 1620 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14021 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14022 :
14023 1732 : PQclear(res);
14024 :
14025 1732 : destroyPQExpBuffer(query);
14026 1732 : destroyPQExpBuffer(q);
14027 1732 : destroyPQExpBuffer(delq);
14028 1732 : free(qcollname);
14029 : }
14030 :
14031 : /*
14032 : * dumpConversion
14033 : * write out a single conversion definition
14034 : */
14035 : static void
14036 328 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14037 : {
14038 328 : DumpOptions *dopt = fout->dopt;
14039 : PQExpBuffer query;
14040 : PQExpBuffer q;
14041 : PQExpBuffer delq;
14042 : char *qconvname;
14043 : PGresult *res;
14044 : int i_conforencoding;
14045 : int i_contoencoding;
14046 : int i_conproc;
14047 : int i_condefault;
14048 : const char *conforencoding;
14049 : const char *contoencoding;
14050 : const char *conproc;
14051 : bool condefault;
14052 :
14053 : /* Do nothing in data-only dump */
14054 328 : if (dopt->dataOnly)
14055 6 : return;
14056 :
14057 322 : query = createPQExpBuffer();
14058 322 : q = createPQExpBuffer();
14059 322 : delq = createPQExpBuffer();
14060 :
14061 322 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14062 :
14063 : /* Get conversion-specific details */
14064 322 : appendPQExpBuffer(query, "SELECT "
14065 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14066 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14067 : "conproc, condefault "
14068 : "FROM pg_catalog.pg_conversion c "
14069 : "WHERE c.oid = '%u'::pg_catalog.oid",
14070 : convinfo->dobj.catId.oid);
14071 :
14072 322 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14073 :
14074 322 : i_conforencoding = PQfnumber(res, "conforencoding");
14075 322 : i_contoencoding = PQfnumber(res, "contoencoding");
14076 322 : i_conproc = PQfnumber(res, "conproc");
14077 322 : i_condefault = PQfnumber(res, "condefault");
14078 :
14079 322 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14080 322 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14081 322 : conproc = PQgetvalue(res, 0, i_conproc);
14082 322 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14083 :
14084 322 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14085 322 : fmtQualifiedDumpable(convinfo));
14086 :
14087 322 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14088 : (condefault) ? "DEFAULT " : "",
14089 322 : fmtQualifiedDumpable(convinfo));
14090 322 : appendStringLiteralAH(q, conforencoding, fout);
14091 322 : appendPQExpBufferStr(q, " TO ");
14092 322 : appendStringLiteralAH(q, contoencoding, fout);
14093 : /* regproc output is already sufficiently quoted */
14094 322 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14095 :
14096 322 : if (dopt->binary_upgrade)
14097 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14098 : "CONVERSION", qconvname,
14099 2 : convinfo->dobj.namespace->dobj.name);
14100 :
14101 322 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14102 322 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14103 322 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14104 : .namespace = convinfo->dobj.namespace->dobj.name,
14105 : .owner = convinfo->rolname,
14106 : .description = "CONVERSION",
14107 : .section = SECTION_PRE_DATA,
14108 : .createStmt = q->data,
14109 : .dropStmt = delq->data));
14110 :
14111 : /* Dump Conversion Comments */
14112 322 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14113 322 : dumpComment(fout, "CONVERSION", qconvname,
14114 322 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14115 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14116 :
14117 322 : PQclear(res);
14118 :
14119 322 : destroyPQExpBuffer(query);
14120 322 : destroyPQExpBuffer(q);
14121 322 : destroyPQExpBuffer(delq);
14122 322 : free(qconvname);
14123 : }
14124 :
14125 : /*
14126 : * format_aggregate_signature: generate aggregate name and argument list
14127 : *
14128 : * The argument type names are qualified if needed. The aggregate name
14129 : * is never qualified.
14130 : */
14131 : static char *
14132 572 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
14133 : {
14134 : PQExpBufferData buf;
14135 : int j;
14136 :
14137 572 : initPQExpBuffer(&buf);
14138 572 : if (honor_quotes)
14139 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
14140 : else
14141 572 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
14142 :
14143 572 : if (agginfo->aggfn.nargs == 0)
14144 80 : appendPQExpBufferStr(&buf, "(*)");
14145 : else
14146 : {
14147 492 : appendPQExpBufferChar(&buf, '(');
14148 1074 : for (j = 0; j < agginfo->aggfn.nargs; j++)
14149 582 : appendPQExpBuffer(&buf, "%s%s",
14150 : (j > 0) ? ", " : "",
14151 : getFormattedTypeName(fout,
14152 582 : agginfo->aggfn.argtypes[j],
14153 : zeroIsError));
14154 492 : appendPQExpBufferChar(&buf, ')');
14155 : }
14156 572 : return buf.data;
14157 : }
14158 :
14159 : /*
14160 : * dumpAgg
14161 : * write out a single aggregate definition
14162 : */
14163 : static void
14164 580 : dumpAgg(Archive *fout, const AggInfo *agginfo)
14165 : {
14166 580 : DumpOptions *dopt = fout->dopt;
14167 : PQExpBuffer query;
14168 : PQExpBuffer q;
14169 : PQExpBuffer delq;
14170 : PQExpBuffer details;
14171 : char *aggsig; /* identity signature */
14172 580 : char *aggfullsig = NULL; /* full signature */
14173 : char *aggsig_tag;
14174 : PGresult *res;
14175 : int i_agginitval;
14176 : int i_aggminitval;
14177 : const char *aggtransfn;
14178 : const char *aggfinalfn;
14179 : const char *aggcombinefn;
14180 : const char *aggserialfn;
14181 : const char *aggdeserialfn;
14182 : const char *aggmtransfn;
14183 : const char *aggminvtransfn;
14184 : const char *aggmfinalfn;
14185 : bool aggfinalextra;
14186 : bool aggmfinalextra;
14187 : char aggfinalmodify;
14188 : char aggmfinalmodify;
14189 : const char *aggsortop;
14190 : char *aggsortconvop;
14191 : char aggkind;
14192 : const char *aggtranstype;
14193 : const char *aggtransspace;
14194 : const char *aggmtranstype;
14195 : const char *aggmtransspace;
14196 : const char *agginitval;
14197 : const char *aggminitval;
14198 : const char *proparallel;
14199 : char defaultfinalmodify;
14200 :
14201 : /* Do nothing in data-only dump */
14202 580 : if (dopt->dataOnly)
14203 8 : return;
14204 :
14205 572 : query = createPQExpBuffer();
14206 572 : q = createPQExpBuffer();
14207 572 : delq = createPQExpBuffer();
14208 572 : details = createPQExpBuffer();
14209 :
14210 572 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
14211 : {
14212 : /* Set up query for aggregate-specific details */
14213 112 : appendPQExpBufferStr(query,
14214 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
14215 :
14216 112 : appendPQExpBufferStr(query,
14217 : "SELECT "
14218 : "aggtransfn,\n"
14219 : "aggfinalfn,\n"
14220 : "aggtranstype::pg_catalog.regtype,\n"
14221 : "agginitval,\n"
14222 : "aggsortop,\n"
14223 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
14224 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
14225 :
14226 112 : if (fout->remoteVersion >= 90400)
14227 112 : appendPQExpBufferStr(query,
14228 : "aggkind,\n"
14229 : "aggmtransfn,\n"
14230 : "aggminvtransfn,\n"
14231 : "aggmfinalfn,\n"
14232 : "aggmtranstype::pg_catalog.regtype,\n"
14233 : "aggfinalextra,\n"
14234 : "aggmfinalextra,\n"
14235 : "aggtransspace,\n"
14236 : "aggmtransspace,\n"
14237 : "aggminitval,\n");
14238 : else
14239 0 : appendPQExpBufferStr(query,
14240 : "'n' AS aggkind,\n"
14241 : "'-' AS aggmtransfn,\n"
14242 : "'-' AS aggminvtransfn,\n"
14243 : "'-' AS aggmfinalfn,\n"
14244 : "0 AS aggmtranstype,\n"
14245 : "false AS aggfinalextra,\n"
14246 : "false AS aggmfinalextra,\n"
14247 : "0 AS aggtransspace,\n"
14248 : "0 AS aggmtransspace,\n"
14249 : "NULL AS aggminitval,\n");
14250 :
14251 112 : if (fout->remoteVersion >= 90600)
14252 112 : appendPQExpBufferStr(query,
14253 : "aggcombinefn,\n"
14254 : "aggserialfn,\n"
14255 : "aggdeserialfn,\n"
14256 : "proparallel,\n");
14257 : else
14258 0 : appendPQExpBufferStr(query,
14259 : "'-' AS aggcombinefn,\n"
14260 : "'-' AS aggserialfn,\n"
14261 : "'-' AS aggdeserialfn,\n"
14262 : "'u' AS proparallel,\n");
14263 :
14264 112 : if (fout->remoteVersion >= 110000)
14265 112 : appendPQExpBufferStr(query,
14266 : "aggfinalmodify,\n"
14267 : "aggmfinalmodify\n");
14268 : else
14269 0 : appendPQExpBufferStr(query,
14270 : "'0' AS aggfinalmodify,\n"
14271 : "'0' AS aggmfinalmodify\n");
14272 :
14273 112 : appendPQExpBufferStr(query,
14274 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14275 : "WHERE a.aggfnoid = p.oid "
14276 : "AND p.oid = $1");
14277 :
14278 112 : ExecuteSqlStatement(fout, query->data);
14279 :
14280 112 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14281 : }
14282 :
14283 572 : printfPQExpBuffer(query,
14284 : "EXECUTE dumpAgg('%u')",
14285 : agginfo->aggfn.dobj.catId.oid);
14286 :
14287 572 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14288 :
14289 572 : i_agginitval = PQfnumber(res, "agginitval");
14290 572 : i_aggminitval = PQfnumber(res, "aggminitval");
14291 :
14292 572 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14293 572 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14294 572 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14295 572 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14296 572 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14297 572 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14298 572 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14299 572 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14300 572 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14301 572 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14302 572 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14303 572 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14304 572 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14305 572 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14306 572 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14307 572 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14308 572 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14309 572 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14310 572 : agginitval = PQgetvalue(res, 0, i_agginitval);
14311 572 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
14312 572 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14313 :
14314 : {
14315 : char *funcargs;
14316 : char *funciargs;
14317 :
14318 572 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14319 572 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14320 572 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14321 572 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14322 : }
14323 :
14324 572 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14325 :
14326 : /* identify default modify flag for aggkind (must match DefineAggregate) */
14327 572 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14328 : /* replace omitted flags for old versions */
14329 572 : if (aggfinalmodify == '0')
14330 0 : aggfinalmodify = defaultfinalmodify;
14331 572 : if (aggmfinalmodify == '0')
14332 0 : aggmfinalmodify = defaultfinalmodify;
14333 :
14334 : /* regproc and regtype output is already sufficiently quoted */
14335 572 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14336 : aggtransfn, aggtranstype);
14337 :
14338 572 : if (strcmp(aggtransspace, "0") != 0)
14339 : {
14340 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
14341 : aggtransspace);
14342 : }
14343 :
14344 572 : if (!PQgetisnull(res, 0, i_agginitval))
14345 : {
14346 416 : appendPQExpBufferStr(details, ",\n INITCOND = ");
14347 416 : appendStringLiteralAH(details, agginitval, fout);
14348 : }
14349 :
14350 572 : if (strcmp(aggfinalfn, "-") != 0)
14351 : {
14352 266 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14353 : aggfinalfn);
14354 266 : if (aggfinalextra)
14355 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14356 266 : if (aggfinalmodify != defaultfinalmodify)
14357 : {
14358 66 : switch (aggfinalmodify)
14359 : {
14360 0 : case AGGMODIFY_READ_ONLY:
14361 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14362 0 : break;
14363 66 : case AGGMODIFY_SHAREABLE:
14364 66 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14365 66 : break;
14366 0 : case AGGMODIFY_READ_WRITE:
14367 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14368 0 : break;
14369 0 : default:
14370 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14371 : agginfo->aggfn.dobj.name);
14372 : break;
14373 : }
14374 506 : }
14375 : }
14376 :
14377 572 : if (strcmp(aggcombinefn, "-") != 0)
14378 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14379 :
14380 572 : if (strcmp(aggserialfn, "-") != 0)
14381 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14382 :
14383 572 : if (strcmp(aggdeserialfn, "-") != 0)
14384 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14385 :
14386 572 : if (strcmp(aggmtransfn, "-") != 0)
14387 : {
14388 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14389 : aggmtransfn,
14390 : aggminvtransfn,
14391 : aggmtranstype);
14392 : }
14393 :
14394 572 : if (strcmp(aggmtransspace, "0") != 0)
14395 : {
14396 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
14397 : aggmtransspace);
14398 : }
14399 :
14400 572 : if (!PQgetisnull(res, 0, i_aggminitval))
14401 : {
14402 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
14403 20 : appendStringLiteralAH(details, aggminitval, fout);
14404 : }
14405 :
14406 572 : if (strcmp(aggmfinalfn, "-") != 0)
14407 : {
14408 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14409 : aggmfinalfn);
14410 0 : if (aggmfinalextra)
14411 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14412 0 : if (aggmfinalmodify != defaultfinalmodify)
14413 : {
14414 0 : switch (aggmfinalmodify)
14415 : {
14416 0 : case AGGMODIFY_READ_ONLY:
14417 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14418 0 : break;
14419 0 : case AGGMODIFY_SHAREABLE:
14420 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14421 0 : break;
14422 0 : case AGGMODIFY_READ_WRITE:
14423 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14424 0 : break;
14425 0 : default:
14426 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14427 : agginfo->aggfn.dobj.name);
14428 : break;
14429 : }
14430 572 : }
14431 : }
14432 :
14433 572 : aggsortconvop = getFormattedOperatorName(aggsortop);
14434 572 : if (aggsortconvop)
14435 : {
14436 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
14437 : aggsortconvop);
14438 0 : free(aggsortconvop);
14439 : }
14440 :
14441 572 : if (aggkind == AGGKIND_HYPOTHETICAL)
14442 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14443 :
14444 572 : if (proparallel[0] != PROPARALLEL_UNSAFE)
14445 : {
14446 10 : if (proparallel[0] == PROPARALLEL_SAFE)
14447 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14448 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14449 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14450 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
14451 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
14452 : agginfo->aggfn.dobj.name);
14453 : }
14454 :
14455 572 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14456 572 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14457 : aggsig);
14458 :
14459 1144 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14460 572 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14461 : aggfullsig ? aggfullsig : aggsig, details->data);
14462 :
14463 572 : if (dopt->binary_upgrade)
14464 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14465 : "AGGREGATE", aggsig,
14466 98 : agginfo->aggfn.dobj.namespace->dobj.name);
14467 :
14468 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14469 538 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14470 : agginfo->aggfn.dobj.dumpId,
14471 538 : ARCHIVE_OPTS(.tag = aggsig_tag,
14472 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14473 : .owner = agginfo->aggfn.rolname,
14474 : .description = "AGGREGATE",
14475 : .section = SECTION_PRE_DATA,
14476 : .createStmt = q->data,
14477 : .dropStmt = delq->data));
14478 :
14479 : /* Dump Aggregate Comments */
14480 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14481 20 : dumpComment(fout, "AGGREGATE", aggsig,
14482 20 : agginfo->aggfn.dobj.namespace->dobj.name,
14483 : agginfo->aggfn.rolname,
14484 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14485 :
14486 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14487 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
14488 0 : agginfo->aggfn.dobj.namespace->dobj.name,
14489 : agginfo->aggfn.rolname,
14490 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14491 :
14492 : /*
14493 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14494 : * command look like a function's GRANT; in particular this affects the
14495 : * syntax for zero-argument aggregates and ordered-set aggregates.
14496 : */
14497 572 : free(aggsig);
14498 :
14499 572 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14500 :
14501 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14502 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
14503 : "FUNCTION", aggsig, NULL,
14504 36 : agginfo->aggfn.dobj.namespace->dobj.name,
14505 : agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
14506 :
14507 572 : free(aggsig);
14508 572 : free(aggfullsig);
14509 572 : free(aggsig_tag);
14510 :
14511 572 : PQclear(res);
14512 :
14513 572 : destroyPQExpBuffer(query);
14514 572 : destroyPQExpBuffer(q);
14515 572 : destroyPQExpBuffer(delq);
14516 572 : destroyPQExpBuffer(details);
14517 : }
14518 :
14519 : /*
14520 : * dumpTSParser
14521 : * write out a single text search parser
14522 : */
14523 : static void
14524 74 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
14525 : {
14526 74 : DumpOptions *dopt = fout->dopt;
14527 : PQExpBuffer q;
14528 : PQExpBuffer delq;
14529 : char *qprsname;
14530 :
14531 : /* Do nothing in data-only dump */
14532 74 : if (dopt->dataOnly)
14533 6 : return;
14534 :
14535 68 : q = createPQExpBuffer();
14536 68 : delq = createPQExpBuffer();
14537 :
14538 68 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14539 :
14540 68 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14541 68 : fmtQualifiedDumpable(prsinfo));
14542 :
14543 68 : appendPQExpBuffer(q, " START = %s,\n",
14544 : convertTSFunction(fout, prsinfo->prsstart));
14545 68 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14546 : convertTSFunction(fout, prsinfo->prstoken));
14547 68 : appendPQExpBuffer(q, " END = %s,\n",
14548 : convertTSFunction(fout, prsinfo->prsend));
14549 68 : if (prsinfo->prsheadline != InvalidOid)
14550 2 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
14551 : convertTSFunction(fout, prsinfo->prsheadline));
14552 68 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14553 : convertTSFunction(fout, prsinfo->prslextype));
14554 :
14555 68 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14556 68 : fmtQualifiedDumpable(prsinfo));
14557 :
14558 68 : if (dopt->binary_upgrade)
14559 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
14560 : "TEXT SEARCH PARSER", qprsname,
14561 2 : prsinfo->dobj.namespace->dobj.name);
14562 :
14563 68 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14564 68 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
14565 68 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
14566 : .namespace = prsinfo->dobj.namespace->dobj.name,
14567 : .description = "TEXT SEARCH PARSER",
14568 : .section = SECTION_PRE_DATA,
14569 : .createStmt = q->data,
14570 : .dropStmt = delq->data));
14571 :
14572 : /* Dump Parser Comments */
14573 68 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14574 68 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
14575 68 : prsinfo->dobj.namespace->dobj.name, "",
14576 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
14577 :
14578 68 : destroyPQExpBuffer(q);
14579 68 : destroyPQExpBuffer(delq);
14580 68 : free(qprsname);
14581 : }
14582 :
14583 : /*
14584 : * dumpTSDictionary
14585 : * write out a single text search dictionary
14586 : */
14587 : static void
14588 220 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
14589 : {
14590 220 : DumpOptions *dopt = fout->dopt;
14591 : PQExpBuffer q;
14592 : PQExpBuffer delq;
14593 : PQExpBuffer query;
14594 : char *qdictname;
14595 : PGresult *res;
14596 : char *nspname;
14597 : char *tmplname;
14598 :
14599 : /* Do nothing in data-only dump */
14600 220 : if (dopt->dataOnly)
14601 6 : return;
14602 :
14603 214 : q = createPQExpBuffer();
14604 214 : delq = createPQExpBuffer();
14605 214 : query = createPQExpBuffer();
14606 :
14607 214 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
14608 :
14609 : /* Fetch name and namespace of the dictionary's template */
14610 214 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
14611 : "FROM pg_ts_template p, pg_namespace n "
14612 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
14613 : dictinfo->dicttemplate);
14614 214 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14615 214 : nspname = PQgetvalue(res, 0, 0);
14616 214 : tmplname = PQgetvalue(res, 0, 1);
14617 :
14618 214 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
14619 214 : fmtQualifiedDumpable(dictinfo));
14620 :
14621 214 : appendPQExpBufferStr(q, " TEMPLATE = ");
14622 214 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
14623 214 : appendPQExpBufferStr(q, fmtId(tmplname));
14624 :
14625 214 : PQclear(res);
14626 :
14627 : /* the dictinitoption can be dumped straight into the command */
14628 214 : if (dictinfo->dictinitoption)
14629 146 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
14630 :
14631 214 : appendPQExpBufferStr(q, " );\n");
14632 :
14633 214 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
14634 214 : fmtQualifiedDumpable(dictinfo));
14635 :
14636 214 : if (dopt->binary_upgrade)
14637 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
14638 : "TEXT SEARCH DICTIONARY", qdictname,
14639 20 : dictinfo->dobj.namespace->dobj.name);
14640 :
14641 214 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14642 214 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
14643 214 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
14644 : .namespace = dictinfo->dobj.namespace->dobj.name,
14645 : .owner = dictinfo->rolname,
14646 : .description = "TEXT SEARCH DICTIONARY",
14647 : .section = SECTION_PRE_DATA,
14648 : .createStmt = q->data,
14649 : .dropStmt = delq->data));
14650 :
14651 : /* Dump Dictionary Comments */
14652 214 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14653 124 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
14654 124 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
14655 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
14656 :
14657 214 : destroyPQExpBuffer(q);
14658 214 : destroyPQExpBuffer(delq);
14659 214 : destroyPQExpBuffer(query);
14660 214 : free(qdictname);
14661 : }
14662 :
14663 : /*
14664 : * dumpTSTemplate
14665 : * write out a single text search template
14666 : */
14667 : static void
14668 82 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
14669 : {
14670 82 : DumpOptions *dopt = fout->dopt;
14671 : PQExpBuffer q;
14672 : PQExpBuffer delq;
14673 : char *qtmplname;
14674 :
14675 : /* Do nothing in data-only dump */
14676 82 : if (dopt->dataOnly)
14677 6 : return;
14678 :
14679 76 : q = createPQExpBuffer();
14680 76 : delq = createPQExpBuffer();
14681 :
14682 76 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
14683 :
14684 76 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
14685 76 : fmtQualifiedDumpable(tmplinfo));
14686 :
14687 76 : if (tmplinfo->tmplinit != InvalidOid)
14688 10 : appendPQExpBuffer(q, " INIT = %s,\n",
14689 : convertTSFunction(fout, tmplinfo->tmplinit));
14690 76 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
14691 : convertTSFunction(fout, tmplinfo->tmpllexize));
14692 :
14693 76 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
14694 76 : fmtQualifiedDumpable(tmplinfo));
14695 :
14696 76 : if (dopt->binary_upgrade)
14697 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
14698 : "TEXT SEARCH TEMPLATE", qtmplname,
14699 2 : tmplinfo->dobj.namespace->dobj.name);
14700 :
14701 76 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14702 76 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
14703 76 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
14704 : .namespace = tmplinfo->dobj.namespace->dobj.name,
14705 : .description = "TEXT SEARCH TEMPLATE",
14706 : .section = SECTION_PRE_DATA,
14707 : .createStmt = q->data,
14708 : .dropStmt = delq->data));
14709 :
14710 : /* Dump Template Comments */
14711 76 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14712 76 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
14713 76 : tmplinfo->dobj.namespace->dobj.name, "",
14714 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
14715 :
14716 76 : destroyPQExpBuffer(q);
14717 76 : destroyPQExpBuffer(delq);
14718 76 : free(qtmplname);
14719 : }
14720 :
14721 : /*
14722 : * dumpTSConfig
14723 : * write out a single text search configuration
14724 : */
14725 : static void
14726 170 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
14727 : {
14728 170 : DumpOptions *dopt = fout->dopt;
14729 : PQExpBuffer q;
14730 : PQExpBuffer delq;
14731 : PQExpBuffer query;
14732 : char *qcfgname;
14733 : PGresult *res;
14734 : char *nspname;
14735 : char *prsname;
14736 : int ntups,
14737 : i;
14738 : int i_tokenname;
14739 : int i_dictname;
14740 :
14741 : /* Do nothing in data-only dump */
14742 170 : if (dopt->dataOnly)
14743 6 : return;
14744 :
14745 164 : q = createPQExpBuffer();
14746 164 : delq = createPQExpBuffer();
14747 164 : query = createPQExpBuffer();
14748 :
14749 164 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
14750 :
14751 : /* Fetch name and namespace of the config's parser */
14752 164 : appendPQExpBuffer(query, "SELECT nspname, prsname "
14753 : "FROM pg_ts_parser p, pg_namespace n "
14754 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
14755 : cfginfo->cfgparser);
14756 164 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14757 164 : nspname = PQgetvalue(res, 0, 0);
14758 164 : prsname = PQgetvalue(res, 0, 1);
14759 :
14760 164 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
14761 164 : fmtQualifiedDumpable(cfginfo));
14762 :
14763 164 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
14764 164 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
14765 :
14766 164 : PQclear(res);
14767 :
14768 164 : resetPQExpBuffer(query);
14769 164 : appendPQExpBuffer(query,
14770 : "SELECT\n"
14771 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
14772 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
14773 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
14774 : "FROM pg_catalog.pg_ts_config_map AS m\n"
14775 : "WHERE m.mapcfg = '%u'\n"
14776 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
14777 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
14778 :
14779 164 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14780 164 : ntups = PQntuples(res);
14781 :
14782 164 : i_tokenname = PQfnumber(res, "tokenname");
14783 164 : i_dictname = PQfnumber(res, "dictname");
14784 :
14785 3550 : for (i = 0; i < ntups; i++)
14786 : {
14787 3386 : char *tokenname = PQgetvalue(res, i, i_tokenname);
14788 3386 : char *dictname = PQgetvalue(res, i, i_dictname);
14789 :
14790 3386 : if (i == 0 ||
14791 3222 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
14792 : {
14793 : /* starting a new token type, so start a new command */
14794 3116 : if (i > 0)
14795 2952 : appendPQExpBufferStr(q, ";\n");
14796 3116 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
14797 3116 : fmtQualifiedDumpable(cfginfo));
14798 : /* tokenname needs quoting, dictname does NOT */
14799 3116 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
14800 : fmtId(tokenname), dictname);
14801 : }
14802 : else
14803 270 : appendPQExpBuffer(q, ", %s", dictname);
14804 : }
14805 :
14806 164 : if (ntups > 0)
14807 164 : appendPQExpBufferStr(q, ";\n");
14808 :
14809 164 : PQclear(res);
14810 :
14811 164 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
14812 164 : fmtQualifiedDumpable(cfginfo));
14813 :
14814 164 : if (dopt->binary_upgrade)
14815 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
14816 : "TEXT SEARCH CONFIGURATION", qcfgname,
14817 10 : cfginfo->dobj.namespace->dobj.name);
14818 :
14819 164 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14820 164 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
14821 164 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
14822 : .namespace = cfginfo->dobj.namespace->dobj.name,
14823 : .owner = cfginfo->rolname,
14824 : .description = "TEXT SEARCH CONFIGURATION",
14825 : .section = SECTION_PRE_DATA,
14826 : .createStmt = q->data,
14827 : .dropStmt = delq->data));
14828 :
14829 : /* Dump Configuration Comments */
14830 164 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14831 124 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
14832 124 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
14833 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
14834 :
14835 164 : destroyPQExpBuffer(q);
14836 164 : destroyPQExpBuffer(delq);
14837 164 : destroyPQExpBuffer(query);
14838 164 : free(qcfgname);
14839 : }
14840 :
14841 : /*
14842 : * dumpForeignDataWrapper
14843 : * write out a single foreign-data wrapper definition
14844 : */
14845 : static void
14846 100 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
14847 : {
14848 100 : DumpOptions *dopt = fout->dopt;
14849 : PQExpBuffer q;
14850 : PQExpBuffer delq;
14851 : char *qfdwname;
14852 :
14853 : /* Do nothing in data-only dump */
14854 100 : if (dopt->dataOnly)
14855 8 : return;
14856 :
14857 92 : q = createPQExpBuffer();
14858 92 : delq = createPQExpBuffer();
14859 :
14860 92 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
14861 :
14862 92 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
14863 : qfdwname);
14864 :
14865 92 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
14866 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
14867 :
14868 92 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
14869 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
14870 :
14871 92 : if (strlen(fdwinfo->fdwoptions) > 0)
14872 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
14873 :
14874 92 : appendPQExpBufferStr(q, ";\n");
14875 :
14876 92 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
14877 : qfdwname);
14878 :
14879 92 : if (dopt->binary_upgrade)
14880 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
14881 : "FOREIGN DATA WRAPPER", qfdwname,
14882 : NULL);
14883 :
14884 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14885 92 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14886 92 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
14887 : .owner = fdwinfo->rolname,
14888 : .description = "FOREIGN DATA WRAPPER",
14889 : .section = SECTION_PRE_DATA,
14890 : .createStmt = q->data,
14891 : .dropStmt = delq->data));
14892 :
14893 : /* Dump Foreign Data Wrapper Comments */
14894 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14895 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
14896 : NULL, fdwinfo->rolname,
14897 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14898 :
14899 : /* Handle the ACL */
14900 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14901 64 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
14902 : "FOREIGN DATA WRAPPER", qfdwname, NULL,
14903 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
14904 :
14905 92 : free(qfdwname);
14906 :
14907 92 : destroyPQExpBuffer(q);
14908 92 : destroyPQExpBuffer(delq);
14909 : }
14910 :
14911 : /*
14912 : * dumpForeignServer
14913 : * write out a foreign server definition
14914 : */
14915 : static void
14916 108 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
14917 : {
14918 108 : DumpOptions *dopt = fout->dopt;
14919 : PQExpBuffer q;
14920 : PQExpBuffer delq;
14921 : PQExpBuffer query;
14922 : PGresult *res;
14923 : char *qsrvname;
14924 : char *fdwname;
14925 :
14926 : /* Do nothing in data-only dump */
14927 108 : if (dopt->dataOnly)
14928 12 : return;
14929 :
14930 96 : q = createPQExpBuffer();
14931 96 : delq = createPQExpBuffer();
14932 96 : query = createPQExpBuffer();
14933 :
14934 96 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
14935 :
14936 : /* look up the foreign-data wrapper */
14937 96 : appendPQExpBuffer(query, "SELECT fdwname "
14938 : "FROM pg_foreign_data_wrapper w "
14939 : "WHERE w.oid = '%u'",
14940 : srvinfo->srvfdw);
14941 96 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14942 96 : fdwname = PQgetvalue(res, 0, 0);
14943 :
14944 96 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
14945 96 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
14946 : {
14947 0 : appendPQExpBufferStr(q, " TYPE ");
14948 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
14949 : }
14950 96 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
14951 : {
14952 0 : appendPQExpBufferStr(q, " VERSION ");
14953 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
14954 : }
14955 :
14956 96 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
14957 96 : appendPQExpBufferStr(q, fmtId(fdwname));
14958 :
14959 96 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
14960 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
14961 :
14962 96 : appendPQExpBufferStr(q, ";\n");
14963 :
14964 96 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
14965 : qsrvname);
14966 :
14967 96 : if (dopt->binary_upgrade)
14968 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
14969 : "SERVER", qsrvname, NULL);
14970 :
14971 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14972 96 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
14973 96 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
14974 : .owner = srvinfo->rolname,
14975 : .description = "SERVER",
14976 : .section = SECTION_PRE_DATA,
14977 : .createStmt = q->data,
14978 : .dropStmt = delq->data));
14979 :
14980 : /* Dump Foreign Server Comments */
14981 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14982 0 : dumpComment(fout, "SERVER", qsrvname,
14983 : NULL, srvinfo->rolname,
14984 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
14985 :
14986 : /* Handle the ACL */
14987 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
14988 64 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
14989 : "FOREIGN SERVER", qsrvname, NULL,
14990 : NULL, srvinfo->rolname, &srvinfo->dacl);
14991 :
14992 : /* Dump user mappings */
14993 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
14994 96 : dumpUserMappings(fout,
14995 96 : srvinfo->dobj.name, NULL,
14996 : srvinfo->rolname,
14997 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
14998 :
14999 96 : PQclear(res);
15000 :
15001 96 : free(qsrvname);
15002 :
15003 96 : destroyPQExpBuffer(q);
15004 96 : destroyPQExpBuffer(delq);
15005 96 : destroyPQExpBuffer(query);
15006 : }
15007 :
15008 : /*
15009 : * dumpUserMappings
15010 : *
15011 : * This routine is used to dump any user mappings associated with the
15012 : * server handed to this routine. Should be called after ArchiveEntry()
15013 : * for the server.
15014 : */
15015 : static void
15016 96 : dumpUserMappings(Archive *fout,
15017 : const char *servername, const char *namespace,
15018 : const char *owner,
15019 : CatalogId catalogId, DumpId dumpId)
15020 : {
15021 : PQExpBuffer q;
15022 : PQExpBuffer delq;
15023 : PQExpBuffer query;
15024 : PQExpBuffer tag;
15025 : PGresult *res;
15026 : int ntups;
15027 : int i_usename;
15028 : int i_umoptions;
15029 : int i;
15030 :
15031 96 : q = createPQExpBuffer();
15032 96 : tag = createPQExpBuffer();
15033 96 : delq = createPQExpBuffer();
15034 96 : query = createPQExpBuffer();
15035 :
15036 : /*
15037 : * We read from the publicly accessible view pg_user_mappings, so as not
15038 : * to fail if run by a non-superuser. Note that the view will show
15039 : * umoptions as null if the user hasn't got privileges for the associated
15040 : * server; this means that pg_dump will dump such a mapping, but with no
15041 : * OPTIONS clause. A possible alternative is to skip such mappings
15042 : * altogether, but it's not clear that that's an improvement.
15043 : */
15044 96 : appendPQExpBuffer(query,
15045 : "SELECT usename, "
15046 : "array_to_string(ARRAY("
15047 : "SELECT quote_ident(option_name) || ' ' || "
15048 : "quote_literal(option_value) "
15049 : "FROM pg_options_to_table(umoptions) "
15050 : "ORDER BY option_name"
15051 : "), E',\n ') AS umoptions "
15052 : "FROM pg_user_mappings "
15053 : "WHERE srvid = '%u' "
15054 : "ORDER BY usename",
15055 : catalogId.oid);
15056 :
15057 96 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15058 :
15059 96 : ntups = PQntuples(res);
15060 96 : i_usename = PQfnumber(res, "usename");
15061 96 : i_umoptions = PQfnumber(res, "umoptions");
15062 :
15063 160 : for (i = 0; i < ntups; i++)
15064 : {
15065 : char *usename;
15066 : char *umoptions;
15067 :
15068 64 : usename = PQgetvalue(res, i, i_usename);
15069 64 : umoptions = PQgetvalue(res, i, i_umoptions);
15070 :
15071 64 : resetPQExpBuffer(q);
15072 64 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15073 64 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15074 :
15075 64 : if (umoptions && strlen(umoptions) > 0)
15076 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15077 :
15078 64 : appendPQExpBufferStr(q, ";\n");
15079 :
15080 64 : resetPQExpBuffer(delq);
15081 64 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15082 64 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15083 :
15084 64 : resetPQExpBuffer(tag);
15085 64 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15086 : usename, servername);
15087 :
15088 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15089 64 : ARCHIVE_OPTS(.tag = tag->data,
15090 : .namespace = namespace,
15091 : .owner = owner,
15092 : .description = "USER MAPPING",
15093 : .section = SECTION_PRE_DATA,
15094 : .createStmt = q->data,
15095 : .dropStmt = delq->data));
15096 : }
15097 :
15098 96 : PQclear(res);
15099 :
15100 96 : destroyPQExpBuffer(query);
15101 96 : destroyPQExpBuffer(delq);
15102 96 : destroyPQExpBuffer(tag);
15103 96 : destroyPQExpBuffer(q);
15104 96 : }
15105 :
15106 : /*
15107 : * Write out default privileges information
15108 : */
15109 : static void
15110 284 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15111 : {
15112 284 : DumpOptions *dopt = fout->dopt;
15113 : PQExpBuffer q;
15114 : PQExpBuffer tag;
15115 : const char *type;
15116 :
15117 : /* Do nothing in data-only dump, or if we're skipping ACLs */
15118 284 : if (dopt->dataOnly || dopt->aclsSkip)
15119 32 : return;
15120 :
15121 252 : q = createPQExpBuffer();
15122 252 : tag = createPQExpBuffer();
15123 :
15124 252 : switch (daclinfo->defaclobjtype)
15125 : {
15126 126 : case DEFACLOBJ_RELATION:
15127 126 : type = "TABLES";
15128 126 : break;
15129 0 : case DEFACLOBJ_SEQUENCE:
15130 0 : type = "SEQUENCES";
15131 0 : break;
15132 126 : case DEFACLOBJ_FUNCTION:
15133 126 : type = "FUNCTIONS";
15134 126 : break;
15135 0 : case DEFACLOBJ_TYPE:
15136 0 : type = "TYPES";
15137 0 : break;
15138 0 : case DEFACLOBJ_NAMESPACE:
15139 0 : type = "SCHEMAS";
15140 0 : break;
15141 0 : default:
15142 : /* shouldn't get here */
15143 0 : pg_fatal("unrecognized object type in default privileges: %d",
15144 : (int) daclinfo->defaclobjtype);
15145 : type = ""; /* keep compiler quiet */
15146 : }
15147 :
15148 252 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
15149 :
15150 : /* build the actual command(s) for this tuple */
15151 252 : if (!buildDefaultACLCommands(type,
15152 252 : daclinfo->dobj.namespace != NULL ?
15153 128 : daclinfo->dobj.namespace->dobj.name : NULL,
15154 252 : daclinfo->dacl.acl,
15155 252 : daclinfo->dacl.acldefault,
15156 : daclinfo->defaclrole,
15157 : fout->remoteVersion,
15158 : q))
15159 0 : pg_fatal("could not parse default ACL list (%s)",
15160 : daclinfo->dacl.acl);
15161 :
15162 252 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
15163 252 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
15164 252 : ARCHIVE_OPTS(.tag = tag->data,
15165 : .namespace = daclinfo->dobj.namespace ?
15166 : daclinfo->dobj.namespace->dobj.name : NULL,
15167 : .owner = daclinfo->defaclrole,
15168 : .description = "DEFAULT ACL",
15169 : .section = SECTION_POST_DATA,
15170 : .createStmt = q->data));
15171 :
15172 252 : destroyPQExpBuffer(tag);
15173 252 : destroyPQExpBuffer(q);
15174 : }
15175 :
15176 : /*----------
15177 : * Write out grant/revoke information
15178 : *
15179 : * 'objDumpId' is the dump ID of the underlying object.
15180 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
15181 : * or InvalidDumpId if there is no need for a second dependency.
15182 : * 'type' must be one of
15183 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
15184 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
15185 : * 'name' is the formatted name of the object. Must be quoted etc. already.
15186 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
15187 : * (Currently we assume that subname is only provided for table columns.)
15188 : * 'nspname' is the namespace the object is in (NULL if none).
15189 : * 'owner' is the owner, NULL if there is no owner (for languages).
15190 : * 'dacl' is the DumpableAcl struct for the object.
15191 : *
15192 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15193 : * no ACL entry was created.
15194 : *----------
15195 : */
15196 : static DumpId
15197 45174 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
15198 : const char *type, const char *name, const char *subname,
15199 : const char *nspname, const char *owner,
15200 : const DumpableAcl *dacl)
15201 : {
15202 45174 : DumpId aclDumpId = InvalidDumpId;
15203 45174 : DumpOptions *dopt = fout->dopt;
15204 45174 : const char *acls = dacl->acl;
15205 45174 : const char *acldefault = dacl->acldefault;
15206 45174 : char privtype = dacl->privtype;
15207 45174 : const char *initprivs = dacl->initprivs;
15208 : const char *baseacls;
15209 : PQExpBuffer sql;
15210 :
15211 : /* Do nothing if ACL dump is not enabled */
15212 45174 : if (dopt->aclsSkip)
15213 636 : return InvalidDumpId;
15214 :
15215 : /* --data-only skips ACLs *except* large object ACLs */
15216 44538 : if (dopt->dataOnly && strcmp(type, "LARGE OBJECT") != 0)
15217 0 : return InvalidDumpId;
15218 :
15219 44538 : sql = createPQExpBuffer();
15220 :
15221 : /*
15222 : * In binary upgrade mode, we don't run an extension's script but instead
15223 : * dump out the objects independently and then recreate them. To preserve
15224 : * any initial privileges which were set on extension objects, we need to
15225 : * compute the set of GRANT and REVOKE commands necessary to get from the
15226 : * default privileges of an object to its initial privileges as recorded
15227 : * in pg_init_privs.
15228 : *
15229 : * At restore time, we apply these commands after having called
15230 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
15231 : * copy the results into pg_init_privs. This is how we preserve the
15232 : * contents of that catalog across binary upgrades.
15233 : */
15234 44538 : if (dopt->binary_upgrade && privtype == 'e' &&
15235 26 : initprivs && *initprivs != '\0')
15236 : {
15237 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
15238 26 : if (!buildACLCommands(name, subname, nspname, type,
15239 : initprivs, acldefault, owner,
15240 : "", fout->remoteVersion, sql))
15241 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
15242 : initprivs, acldefault, name, type);
15243 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
15244 : }
15245 :
15246 : /*
15247 : * Now figure the GRANT and REVOKE commands needed to get to the object's
15248 : * actual current ACL, starting from the initprivs if given, else from the
15249 : * object-type-specific default. Also, while buildACLCommands will assume
15250 : * that a NULL/empty acls string means it needn't do anything, what that
15251 : * actually represents is the object-type-specific default; so we need to
15252 : * substitute the acldefault string to get the right results in that case.
15253 : */
15254 44538 : if (initprivs && *initprivs != '\0')
15255 : {
15256 41070 : baseacls = initprivs;
15257 41070 : if (acls == NULL || *acls == '\0')
15258 34 : acls = acldefault;
15259 : }
15260 : else
15261 3468 : baseacls = acldefault;
15262 :
15263 44538 : if (!buildACLCommands(name, subname, nspname, type,
15264 : acls, baseacls, owner,
15265 : "", fout->remoteVersion, sql))
15266 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15267 : acls, baseacls, name, type);
15268 :
15269 44538 : if (sql->len > 0)
15270 : {
15271 3636 : PQExpBuffer tag = createPQExpBuffer();
15272 : DumpId aclDeps[2];
15273 3636 : int nDeps = 0;
15274 :
15275 3636 : if (subname)
15276 2146 : appendPQExpBuffer(tag, "COLUMN %s.%s", name, subname);
15277 : else
15278 1490 : appendPQExpBuffer(tag, "%s %s", type, name);
15279 :
15280 3636 : aclDeps[nDeps++] = objDumpId;
15281 3636 : if (altDumpId != InvalidDumpId)
15282 1998 : aclDeps[nDeps++] = altDumpId;
15283 :
15284 3636 : aclDumpId = createDumpId();
15285 :
15286 3636 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
15287 3636 : ARCHIVE_OPTS(.tag = tag->data,
15288 : .namespace = nspname,
15289 : .owner = owner,
15290 : .description = "ACL",
15291 : .section = SECTION_NONE,
15292 : .createStmt = sql->data,
15293 : .deps = aclDeps,
15294 : .nDeps = nDeps));
15295 :
15296 3636 : destroyPQExpBuffer(tag);
15297 : }
15298 :
15299 44538 : destroyPQExpBuffer(sql);
15300 :
15301 44538 : return aclDumpId;
15302 : }
15303 :
15304 : /*
15305 : * dumpSecLabel
15306 : *
15307 : * This routine is used to dump any security labels associated with the
15308 : * object handed to this routine. The routine takes the object type
15309 : * and object name (ready to print, except for schema decoration), plus
15310 : * the namespace and owner of the object (for labeling the ArchiveEntry),
15311 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
15312 : * plus the dump ID for the object (for setting a dependency).
15313 : * If a matching pg_seclabel entry is found, it is dumped.
15314 : *
15315 : * Note: although this routine takes a dumpId for dependency purposes,
15316 : * that purpose is just to mark the dependency in the emitted dump file
15317 : * for possible future use by pg_restore. We do NOT use it for determining
15318 : * ordering of the label in the dump file, because this routine is called
15319 : * after dependency sorting occurs. This routine should be called just after
15320 : * calling ArchiveEntry() for the specified object.
15321 : */
15322 : static void
15323 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
15324 : const char *namespace, const char *owner,
15325 : CatalogId catalogId, int subid, DumpId dumpId)
15326 : {
15327 0 : DumpOptions *dopt = fout->dopt;
15328 : SecLabelItem *labels;
15329 : int nlabels;
15330 : int i;
15331 : PQExpBuffer query;
15332 :
15333 : /* do nothing, if --no-security-labels is supplied */
15334 0 : if (dopt->no_security_labels)
15335 0 : return;
15336 :
15337 : /*
15338 : * Security labels are schema not data ... except large object labels are
15339 : * data
15340 : */
15341 0 : if (strcmp(type, "LARGE OBJECT") != 0)
15342 : {
15343 0 : if (dopt->dataOnly)
15344 0 : return;
15345 : }
15346 : else
15347 : {
15348 : /* We do dump large object security labels in binary-upgrade mode */
15349 0 : if (dopt->schemaOnly && !dopt->binary_upgrade)
15350 0 : return;
15351 : }
15352 :
15353 : /* Search for security labels associated with catalogId, using table */
15354 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15355 :
15356 0 : query = createPQExpBuffer();
15357 :
15358 0 : for (i = 0; i < nlabels; i++)
15359 : {
15360 : /*
15361 : * Ignore label entries for which the subid doesn't match.
15362 : */
15363 0 : if (labels[i].objsubid != subid)
15364 0 : continue;
15365 :
15366 0 : appendPQExpBuffer(query,
15367 : "SECURITY LABEL FOR %s ON %s ",
15368 0 : fmtId(labels[i].provider), type);
15369 0 : if (namespace && *namespace)
15370 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
15371 0 : appendPQExpBuffer(query, "%s IS ", name);
15372 0 : appendStringLiteralAH(query, labels[i].label, fout);
15373 0 : appendPQExpBufferStr(query, ";\n");
15374 : }
15375 :
15376 0 : if (query->len > 0)
15377 : {
15378 0 : PQExpBuffer tag = createPQExpBuffer();
15379 :
15380 0 : appendPQExpBuffer(tag, "%s %s", type, name);
15381 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15382 0 : ARCHIVE_OPTS(.tag = tag->data,
15383 : .namespace = namespace,
15384 : .owner = owner,
15385 : .description = "SECURITY LABEL",
15386 : .section = SECTION_NONE,
15387 : .createStmt = query->data,
15388 : .deps = &dumpId,
15389 : .nDeps = 1));
15390 0 : destroyPQExpBuffer(tag);
15391 : }
15392 :
15393 0 : destroyPQExpBuffer(query);
15394 : }
15395 :
15396 : /*
15397 : * dumpTableSecLabel
15398 : *
15399 : * As above, but dump security label for both the specified table (or view)
15400 : * and its columns.
15401 : */
15402 : static void
15403 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15404 : {
15405 0 : DumpOptions *dopt = fout->dopt;
15406 : SecLabelItem *labels;
15407 : int nlabels;
15408 : int i;
15409 : PQExpBuffer query;
15410 : PQExpBuffer target;
15411 :
15412 : /* do nothing, if --no-security-labels is supplied */
15413 0 : if (dopt->no_security_labels)
15414 0 : return;
15415 :
15416 : /* SecLabel are SCHEMA not data */
15417 0 : if (dopt->dataOnly)
15418 0 : return;
15419 :
15420 : /* Search for comments associated with relation, using table */
15421 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15422 : tbinfo->dobj.catId.oid,
15423 : &labels);
15424 :
15425 : /* If security labels exist, build SECURITY LABEL statements */
15426 0 : if (nlabels <= 0)
15427 0 : return;
15428 :
15429 0 : query = createPQExpBuffer();
15430 0 : target = createPQExpBuffer();
15431 :
15432 0 : for (i = 0; i < nlabels; i++)
15433 : {
15434 : const char *colname;
15435 0 : const char *provider = labels[i].provider;
15436 0 : const char *label = labels[i].label;
15437 0 : int objsubid = labels[i].objsubid;
15438 :
15439 0 : resetPQExpBuffer(target);
15440 0 : if (objsubid == 0)
15441 : {
15442 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15443 0 : fmtQualifiedDumpable(tbinfo));
15444 : }
15445 : else
15446 : {
15447 0 : colname = getAttrName(objsubid, tbinfo);
15448 : /* first fmtXXX result must be consumed before calling again */
15449 0 : appendPQExpBuffer(target, "COLUMN %s",
15450 0 : fmtQualifiedDumpable(tbinfo));
15451 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
15452 : }
15453 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15454 : fmtId(provider), target->data);
15455 0 : appendStringLiteralAH(query, label, fout);
15456 0 : appendPQExpBufferStr(query, ";\n");
15457 : }
15458 0 : if (query->len > 0)
15459 : {
15460 0 : resetPQExpBuffer(target);
15461 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15462 0 : fmtId(tbinfo->dobj.name));
15463 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15464 0 : ARCHIVE_OPTS(.tag = target->data,
15465 : .namespace = tbinfo->dobj.namespace->dobj.name,
15466 : .owner = tbinfo->rolname,
15467 : .description = "SECURITY LABEL",
15468 : .section = SECTION_NONE,
15469 : .createStmt = query->data,
15470 : .deps = &(tbinfo->dobj.dumpId),
15471 : .nDeps = 1));
15472 : }
15473 0 : destroyPQExpBuffer(query);
15474 0 : destroyPQExpBuffer(target);
15475 : }
15476 :
15477 : /*
15478 : * findSecLabels
15479 : *
15480 : * Find the security label(s), if any, associated with the given object.
15481 : * All the objsubid values associated with the given classoid/objoid are
15482 : * found with one search.
15483 : */
15484 : static int
15485 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
15486 : {
15487 0 : SecLabelItem *middle = NULL;
15488 : SecLabelItem *low;
15489 : SecLabelItem *high;
15490 : int nmatch;
15491 :
15492 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
15493 : {
15494 0 : *items = NULL;
15495 0 : return 0;
15496 : }
15497 :
15498 : /*
15499 : * Do binary search to find some item matching the object.
15500 : */
15501 0 : low = &seclabels[0];
15502 0 : high = &seclabels[nseclabels - 1];
15503 0 : while (low <= high)
15504 : {
15505 0 : middle = low + (high - low) / 2;
15506 :
15507 0 : if (classoid < middle->classoid)
15508 0 : high = middle - 1;
15509 0 : else if (classoid > middle->classoid)
15510 0 : low = middle + 1;
15511 0 : else if (objoid < middle->objoid)
15512 0 : high = middle - 1;
15513 0 : else if (objoid > middle->objoid)
15514 0 : low = middle + 1;
15515 : else
15516 0 : break; /* found a match */
15517 : }
15518 :
15519 0 : if (low > high) /* no matches */
15520 : {
15521 0 : *items = NULL;
15522 0 : return 0;
15523 : }
15524 :
15525 : /*
15526 : * Now determine how many items match the object. The search loop
15527 : * invariant still holds: only items between low and high inclusive could
15528 : * match.
15529 : */
15530 0 : nmatch = 1;
15531 0 : while (middle > low)
15532 : {
15533 0 : if (classoid != middle[-1].classoid ||
15534 0 : objoid != middle[-1].objoid)
15535 : break;
15536 0 : middle--;
15537 0 : nmatch++;
15538 : }
15539 :
15540 0 : *items = middle;
15541 :
15542 0 : middle += nmatch;
15543 0 : while (middle <= high)
15544 : {
15545 0 : if (classoid != middle->classoid ||
15546 0 : objoid != middle->objoid)
15547 : break;
15548 0 : middle++;
15549 0 : nmatch++;
15550 : }
15551 :
15552 0 : return nmatch;
15553 : }
15554 :
15555 : /*
15556 : * collectSecLabels
15557 : *
15558 : * Construct a table of all security labels available for database objects;
15559 : * also set the has-seclabel component flag for each relevant object.
15560 : *
15561 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
15562 : */
15563 : static void
15564 304 : collectSecLabels(Archive *fout)
15565 : {
15566 : PGresult *res;
15567 : PQExpBuffer query;
15568 : int i_label;
15569 : int i_provider;
15570 : int i_classoid;
15571 : int i_objoid;
15572 : int i_objsubid;
15573 : int ntups;
15574 : int i;
15575 : DumpableObject *dobj;
15576 :
15577 304 : query = createPQExpBuffer();
15578 :
15579 304 : appendPQExpBufferStr(query,
15580 : "SELECT label, provider, classoid, objoid, objsubid "
15581 : "FROM pg_catalog.pg_seclabel "
15582 : "ORDER BY classoid, objoid, objsubid");
15583 :
15584 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15585 :
15586 : /* Construct lookup table containing OIDs in numeric form */
15587 304 : i_label = PQfnumber(res, "label");
15588 304 : i_provider = PQfnumber(res, "provider");
15589 304 : i_classoid = PQfnumber(res, "classoid");
15590 304 : i_objoid = PQfnumber(res, "objoid");
15591 304 : i_objsubid = PQfnumber(res, "objsubid");
15592 :
15593 304 : ntups = PQntuples(res);
15594 :
15595 304 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
15596 304 : nseclabels = 0;
15597 304 : dobj = NULL;
15598 :
15599 304 : for (i = 0; i < ntups; i++)
15600 : {
15601 : CatalogId objId;
15602 : int subid;
15603 :
15604 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
15605 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
15606 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
15607 :
15608 : /* We needn't remember labels that don't match any dumpable object */
15609 0 : if (dobj == NULL ||
15610 0 : dobj->catId.tableoid != objId.tableoid ||
15611 0 : dobj->catId.oid != objId.oid)
15612 0 : dobj = findObjectByCatalogId(objId);
15613 0 : if (dobj == NULL)
15614 0 : continue;
15615 :
15616 : /*
15617 : * Labels on columns of composite types are linked to the type's
15618 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
15619 : * in the type's own DumpableObject.
15620 : */
15621 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
15622 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
15623 0 : {
15624 : TypeInfo *cTypeInfo;
15625 :
15626 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
15627 0 : if (cTypeInfo)
15628 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
15629 : }
15630 : else
15631 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
15632 :
15633 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
15634 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
15635 0 : seclabels[nseclabels].classoid = objId.tableoid;
15636 0 : seclabels[nseclabels].objoid = objId.oid;
15637 0 : seclabels[nseclabels].objsubid = subid;
15638 0 : nseclabels++;
15639 : }
15640 :
15641 304 : PQclear(res);
15642 304 : destroyPQExpBuffer(query);
15643 304 : }
15644 :
15645 : /*
15646 : * dumpTable
15647 : * write out to fout the declarations (not data) of a user-defined table
15648 : */
15649 : static void
15650 49404 : dumpTable(Archive *fout, const TableInfo *tbinfo)
15651 : {
15652 49404 : DumpOptions *dopt = fout->dopt;
15653 49404 : DumpId tableAclDumpId = InvalidDumpId;
15654 : char *namecopy;
15655 :
15656 : /* Do nothing in data-only dump */
15657 49404 : if (dopt->dataOnly)
15658 1734 : return;
15659 :
15660 47670 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15661 : {
15662 11296 : if (tbinfo->relkind == RELKIND_SEQUENCE)
15663 716 : dumpSequence(fout, tbinfo);
15664 : else
15665 10580 : dumpTableSchema(fout, tbinfo);
15666 : }
15667 :
15668 : /* Handle the ACL here */
15669 47670 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
15670 47670 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15671 : {
15672 37238 : const char *objtype =
15673 37238 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
15674 :
15675 : tableAclDumpId =
15676 37238 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
15677 : objtype, namecopy, NULL,
15678 37238 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15679 : &tbinfo->dacl);
15680 : }
15681 :
15682 : /*
15683 : * Handle column ACLs, if any. Note: we pull these with a separate query
15684 : * rather than trying to fetch them during getTableAttrs, so that we won't
15685 : * miss ACLs on system columns. Doing it this way also allows us to dump
15686 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
15687 : */
15688 47670 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
15689 : {
15690 498 : PQExpBuffer query = createPQExpBuffer();
15691 : PGresult *res;
15692 : int i;
15693 :
15694 498 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
15695 : {
15696 : /* Set up query for column ACLs */
15697 258 : appendPQExpBufferStr(query,
15698 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
15699 :
15700 258 : if (fout->remoteVersion >= 90600)
15701 : {
15702 : /*
15703 : * In principle we should call acldefault('c', relowner) to
15704 : * get the default ACL for a column. However, we don't
15705 : * currently store the numeric OID of the relowner in
15706 : * TableInfo. We could convert the owner name using regrole,
15707 : * but that creates a risk of failure due to concurrent role
15708 : * renames. Given that the default ACL for columns is empty
15709 : * and is likely to stay that way, it's not worth extra cycles
15710 : * and risk to avoid hard-wiring that knowledge here.
15711 : */
15712 258 : appendPQExpBufferStr(query,
15713 : "SELECT at.attname, "
15714 : "at.attacl, "
15715 : "'{}' AS acldefault, "
15716 : "pip.privtype, pip.initprivs "
15717 : "FROM pg_catalog.pg_attribute at "
15718 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
15719 : "(at.attrelid = pip.objoid "
15720 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
15721 : "AND at.attnum = pip.objsubid) "
15722 : "WHERE at.attrelid = $1 AND "
15723 : "NOT at.attisdropped "
15724 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
15725 : "ORDER BY at.attnum");
15726 : }
15727 : else
15728 : {
15729 0 : appendPQExpBufferStr(query,
15730 : "SELECT attname, attacl, '{}' AS acldefault, "
15731 : "NULL AS privtype, NULL AS initprivs "
15732 : "FROM pg_catalog.pg_attribute "
15733 : "WHERE attrelid = $1 AND NOT attisdropped "
15734 : "AND attacl IS NOT NULL "
15735 : "ORDER BY attnum");
15736 : }
15737 :
15738 258 : ExecuteSqlStatement(fout, query->data);
15739 :
15740 258 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
15741 : }
15742 :
15743 498 : printfPQExpBuffer(query,
15744 : "EXECUTE getColumnACLs('%u')",
15745 : tbinfo->dobj.catId.oid);
15746 :
15747 498 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15748 :
15749 6928 : for (i = 0; i < PQntuples(res); i++)
15750 : {
15751 6430 : char *attname = PQgetvalue(res, i, 0);
15752 6430 : char *attacl = PQgetvalue(res, i, 1);
15753 6430 : char *acldefault = PQgetvalue(res, i, 2);
15754 6430 : char privtype = *(PQgetvalue(res, i, 3));
15755 6430 : char *initprivs = PQgetvalue(res, i, 4);
15756 : DumpableAcl coldacl;
15757 : char *attnamecopy;
15758 :
15759 6430 : coldacl.acl = attacl;
15760 6430 : coldacl.acldefault = acldefault;
15761 6430 : coldacl.privtype = privtype;
15762 6430 : coldacl.initprivs = initprivs;
15763 6430 : attnamecopy = pg_strdup(fmtId(attname));
15764 :
15765 : /*
15766 : * Column's GRANT type is always TABLE. Each column ACL depends
15767 : * on the table-level ACL, since we can restore column ACLs in
15768 : * parallel but the table-level ACL has to be done first.
15769 : */
15770 6430 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
15771 : "TABLE", namecopy, attnamecopy,
15772 6430 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
15773 : &coldacl);
15774 6430 : free(attnamecopy);
15775 : }
15776 498 : PQclear(res);
15777 498 : destroyPQExpBuffer(query);
15778 : }
15779 :
15780 47670 : free(namecopy);
15781 : }
15782 :
15783 : /*
15784 : * Create the AS clause for a view or materialized view. The semicolon is
15785 : * stripped because a materialized view must add a WITH NO DATA clause.
15786 : *
15787 : * This returns a new buffer which must be freed by the caller.
15788 : */
15789 : static PQExpBuffer
15790 1366 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
15791 : {
15792 1366 : PQExpBuffer query = createPQExpBuffer();
15793 1366 : PQExpBuffer result = createPQExpBuffer();
15794 : PGresult *res;
15795 : int len;
15796 :
15797 : /* Fetch the view definition */
15798 1366 : appendPQExpBuffer(query,
15799 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
15800 : tbinfo->dobj.catId.oid);
15801 :
15802 1366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15803 :
15804 1366 : if (PQntuples(res) != 1)
15805 : {
15806 0 : if (PQntuples(res) < 1)
15807 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
15808 : tbinfo->dobj.name);
15809 : else
15810 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
15811 : tbinfo->dobj.name);
15812 : }
15813 :
15814 1366 : len = PQgetlength(res, 0, 0);
15815 :
15816 1366 : if (len == 0)
15817 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
15818 : tbinfo->dobj.name);
15819 :
15820 : /* Strip off the trailing semicolon so that other things may follow. */
15821 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
15822 1366 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
15823 :
15824 1366 : PQclear(res);
15825 1366 : destroyPQExpBuffer(query);
15826 :
15827 1366 : return result;
15828 : }
15829 :
15830 : /*
15831 : * Create a dummy AS clause for a view. This is used when the real view
15832 : * definition has to be postponed because of circular dependencies.
15833 : * We must duplicate the view's external properties -- column names and types
15834 : * (including collation) -- so that it works for subsequent references.
15835 : *
15836 : * This returns a new buffer which must be freed by the caller.
15837 : */
15838 : static PQExpBuffer
15839 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
15840 : {
15841 40 : PQExpBuffer result = createPQExpBuffer();
15842 : int j;
15843 :
15844 40 : appendPQExpBufferStr(result, "SELECT");
15845 :
15846 80 : for (j = 0; j < tbinfo->numatts; j++)
15847 : {
15848 40 : if (j > 0)
15849 20 : appendPQExpBufferChar(result, ',');
15850 40 : appendPQExpBufferStr(result, "\n ");
15851 :
15852 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
15853 :
15854 : /*
15855 : * Must add collation if not default for the type, because CREATE OR
15856 : * REPLACE VIEW won't change it
15857 : */
15858 40 : if (OidIsValid(tbinfo->attcollation[j]))
15859 : {
15860 : CollInfo *coll;
15861 :
15862 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
15863 0 : if (coll)
15864 0 : appendPQExpBuffer(result, " COLLATE %s",
15865 0 : fmtQualifiedDumpable(coll));
15866 : }
15867 :
15868 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
15869 : }
15870 :
15871 40 : return result;
15872 : }
15873 :
15874 : /*
15875 : * dumpTableSchema
15876 : * write the declaration (not data) of one user-defined table or view
15877 : */
15878 : static void
15879 10580 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
15880 : {
15881 10580 : DumpOptions *dopt = fout->dopt;
15882 10580 : PQExpBuffer q = createPQExpBuffer();
15883 10580 : PQExpBuffer delq = createPQExpBuffer();
15884 : char *qrelname;
15885 : char *qualrelname;
15886 : int numParents;
15887 : TableInfo **parents;
15888 : int actual_atts; /* number of attrs in this CREATE statement */
15889 : const char *reltypename;
15890 : char *storage;
15891 : int j,
15892 : k;
15893 :
15894 : /* We had better have loaded per-column details about this table */
15895 : Assert(tbinfo->interesting);
15896 :
15897 10580 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
15898 10580 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
15899 :
15900 10580 : if (tbinfo->hasoids)
15901 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
15902 : qrelname);
15903 :
15904 10580 : if (dopt->binary_upgrade)
15905 1520 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
15906 :
15907 : /* Is it a table or a view? */
15908 10580 : if (tbinfo->relkind == RELKIND_VIEW)
15909 : {
15910 : PQExpBuffer result;
15911 :
15912 : /*
15913 : * Note: keep this code in sync with the is_view case in dumpRule()
15914 : */
15915 :
15916 692 : reltypename = "VIEW";
15917 :
15918 692 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
15919 :
15920 692 : if (dopt->binary_upgrade)
15921 96 : binary_upgrade_set_pg_class_oids(fout, q,
15922 : tbinfo->dobj.catId.oid, false);
15923 :
15924 692 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
15925 :
15926 692 : if (tbinfo->dummy_view)
15927 20 : result = createDummyViewAsClause(fout, tbinfo);
15928 : else
15929 : {
15930 672 : if (nonemptyReloptions(tbinfo->reloptions))
15931 : {
15932 112 : appendPQExpBufferStr(q, " WITH (");
15933 112 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
15934 112 : appendPQExpBufferChar(q, ')');
15935 : }
15936 672 : result = createViewAsClause(fout, tbinfo);
15937 : }
15938 692 : appendPQExpBuffer(q, " AS\n%s", result->data);
15939 692 : destroyPQExpBuffer(result);
15940 :
15941 692 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
15942 66 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
15943 692 : appendPQExpBufferStr(q, ";\n");
15944 : }
15945 : else
15946 : {
15947 9888 : char *partkeydef = NULL;
15948 9888 : char *ftoptions = NULL;
15949 9888 : char *srvname = NULL;
15950 9888 : char *foreign = "";
15951 :
15952 : /*
15953 : * Set reltypename, and collect any relkind-specific data that we
15954 : * didn't fetch during getTables().
15955 : */
15956 9888 : switch (tbinfo->relkind)
15957 : {
15958 1006 : case RELKIND_PARTITIONED_TABLE:
15959 : {
15960 1006 : PQExpBuffer query = createPQExpBuffer();
15961 : PGresult *res;
15962 :
15963 1006 : reltypename = "TABLE";
15964 :
15965 : /* retrieve partition key definition */
15966 1006 : appendPQExpBuffer(query,
15967 : "SELECT pg_get_partkeydef('%u')",
15968 : tbinfo->dobj.catId.oid);
15969 1006 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15970 1006 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
15971 1006 : PQclear(res);
15972 1006 : destroyPQExpBuffer(query);
15973 1006 : break;
15974 : }
15975 70 : case RELKIND_FOREIGN_TABLE:
15976 : {
15977 70 : PQExpBuffer query = createPQExpBuffer();
15978 : PGresult *res;
15979 : int i_srvname;
15980 : int i_ftoptions;
15981 :
15982 70 : reltypename = "FOREIGN TABLE";
15983 :
15984 : /* retrieve name of foreign server and generic options */
15985 70 : appendPQExpBuffer(query,
15986 : "SELECT fs.srvname, "
15987 : "pg_catalog.array_to_string(ARRAY("
15988 : "SELECT pg_catalog.quote_ident(option_name) || "
15989 : "' ' || pg_catalog.quote_literal(option_value) "
15990 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
15991 : "ORDER BY option_name"
15992 : "), E',\n ') AS ftoptions "
15993 : "FROM pg_catalog.pg_foreign_table ft "
15994 : "JOIN pg_catalog.pg_foreign_server fs "
15995 : "ON (fs.oid = ft.ftserver) "
15996 : "WHERE ft.ftrelid = '%u'",
15997 : tbinfo->dobj.catId.oid);
15998 70 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15999 70 : i_srvname = PQfnumber(res, "srvname");
16000 70 : i_ftoptions = PQfnumber(res, "ftoptions");
16001 70 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16002 70 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16003 70 : PQclear(res);
16004 70 : destroyPQExpBuffer(query);
16005 :
16006 70 : foreign = "FOREIGN ";
16007 70 : break;
16008 : }
16009 674 : case RELKIND_MATVIEW:
16010 674 : reltypename = "MATERIALIZED VIEW";
16011 674 : break;
16012 8138 : default:
16013 8138 : reltypename = "TABLE";
16014 8138 : break;
16015 : }
16016 :
16017 9888 : numParents = tbinfo->numParents;
16018 9888 : parents = tbinfo->parents;
16019 :
16020 9888 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16021 :
16022 9888 : if (dopt->binary_upgrade)
16023 1424 : binary_upgrade_set_pg_class_oids(fout, q,
16024 : tbinfo->dobj.catId.oid, false);
16025 :
16026 9888 : appendPQExpBuffer(q, "CREATE %s%s %s",
16027 9888 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
16028 : "UNLOGGED " : "",
16029 : reltypename,
16030 : qualrelname);
16031 :
16032 : /*
16033 : * Attach to type, if reloftype; except in case of a binary upgrade,
16034 : * we dump the table normally and attach it to the type afterward.
16035 : */
16036 9888 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16037 48 : appendPQExpBuffer(q, " OF %s",
16038 : getFormattedTypeName(fout, tbinfo->reloftype,
16039 : zeroIsError));
16040 :
16041 9888 : if (tbinfo->relkind != RELKIND_MATVIEW)
16042 : {
16043 : /* Dump the attributes */
16044 9214 : actual_atts = 0;
16045 44152 : for (j = 0; j < tbinfo->numatts; j++)
16046 : {
16047 : /*
16048 : * Normally, dump if it's locally defined in this table, and
16049 : * not dropped. But for binary upgrade, we'll dump all the
16050 : * columns, and then fix up the dropped and nonlocal cases
16051 : * below.
16052 : */
16053 34938 : if (shouldPrintColumn(dopt, tbinfo, j))
16054 : {
16055 : bool print_default;
16056 : bool print_notnull;
16057 :
16058 : /*
16059 : * Default value --- suppress if to be printed separately
16060 : * or not at all.
16061 : */
16062 68224 : print_default = (tbinfo->attrdefs[j] != NULL &&
16063 34820 : tbinfo->attrdefs[j]->dobj.dump &&
16064 1496 : !tbinfo->attrdefs[j]->separate);
16065 :
16066 : /*
16067 : * Not Null constraint --- suppress unless it is locally
16068 : * defined, except if partition, or in binary-upgrade case
16069 : * where that won't work.
16070 : */
16071 33324 : print_notnull =
16072 36916 : (tbinfo->notnull_constrs[j] != NULL &&
16073 3592 : (!tbinfo->notnull_inh[j] || tbinfo->ispartition ||
16074 96 : dopt->binary_upgrade));
16075 :
16076 : /*
16077 : * Skip column if fully defined by reloftype, except in
16078 : * binary upgrade
16079 : */
16080 33324 : if (OidIsValid(tbinfo->reloftype) &&
16081 100 : !print_default && !print_notnull &&
16082 60 : !dopt->binary_upgrade)
16083 48 : continue;
16084 :
16085 : /* Format properly if not first attr */
16086 33276 : if (actual_atts == 0)
16087 8728 : appendPQExpBufferStr(q, " (");
16088 : else
16089 24548 : appendPQExpBufferChar(q, ',');
16090 33276 : appendPQExpBufferStr(q, "\n ");
16091 33276 : actual_atts++;
16092 :
16093 : /* Attribute name */
16094 33276 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16095 :
16096 33276 : if (tbinfo->attisdropped[j])
16097 : {
16098 : /*
16099 : * ALTER TABLE DROP COLUMN clears
16100 : * pg_attribute.atttypid, so we will not have gotten a
16101 : * valid type name; insert INTEGER as a stopgap. We'll
16102 : * clean things up later.
16103 : */
16104 158 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
16105 : /* and skip to the next column */
16106 158 : continue;
16107 : }
16108 :
16109 : /*
16110 : * Attribute type; print it except when creating a typed
16111 : * table ('OF type_name'), but in binary-upgrade mode,
16112 : * print it in that case too.
16113 : */
16114 33118 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16115 : {
16116 33086 : appendPQExpBuffer(q, " %s",
16117 33086 : tbinfo->atttypnames[j]);
16118 : }
16119 :
16120 33118 : if (print_default)
16121 : {
16122 1232 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
16123 530 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
16124 530 : tbinfo->attrdefs[j]->adef_expr);
16125 : else
16126 702 : appendPQExpBuffer(q, " DEFAULT %s",
16127 702 : tbinfo->attrdefs[j]->adef_expr);
16128 : }
16129 :
16130 :
16131 33118 : if (print_notnull)
16132 : {
16133 3528 : if (tbinfo->notnull_constrs[j][0] == '\0')
16134 1174 : appendPQExpBufferStr(q, " NOT NULL");
16135 : else
16136 2354 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
16137 2354 : fmtId(tbinfo->notnull_constrs[j]));
16138 :
16139 3528 : if (tbinfo->notnull_noinh[j])
16140 1578 : appendPQExpBufferStr(q, " NO INHERIT");
16141 : }
16142 :
16143 : /* Add collation if not default for the type */
16144 33118 : if (OidIsValid(tbinfo->attcollation[j]))
16145 : {
16146 : CollInfo *coll;
16147 :
16148 138 : coll = findCollationByOid(tbinfo->attcollation[j]);
16149 138 : if (coll)
16150 138 : appendPQExpBuffer(q, " COLLATE %s",
16151 138 : fmtQualifiedDumpable(coll));
16152 : }
16153 : }
16154 : }
16155 :
16156 : /*
16157 : * Add non-inherited CHECK constraints, if any.
16158 : *
16159 : * For partitions, we need to include check constraints even if
16160 : * they're not defined locally, because the ALTER TABLE ATTACH
16161 : * PARTITION that we'll emit later expects the constraint to be
16162 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
16163 : */
16164 10270 : for (j = 0; j < tbinfo->ncheck; j++)
16165 : {
16166 1056 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16167 :
16168 1056 : if (constr->separate ||
16169 976 : (!constr->conislocal && !tbinfo->ispartition))
16170 156 : continue;
16171 :
16172 900 : if (actual_atts == 0)
16173 32 : appendPQExpBufferStr(q, " (\n ");
16174 : else
16175 868 : appendPQExpBufferStr(q, ",\n ");
16176 :
16177 900 : appendPQExpBuffer(q, "CONSTRAINT %s ",
16178 900 : fmtId(constr->dobj.name));
16179 900 : appendPQExpBufferStr(q, constr->condef);
16180 :
16181 900 : actual_atts++;
16182 : }
16183 :
16184 9214 : if (actual_atts)
16185 8760 : appendPQExpBufferStr(q, "\n)");
16186 454 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
16187 : {
16188 : /*
16189 : * No attributes? we must have a parenthesized attribute list,
16190 : * even though empty, when not using the OF TYPE syntax.
16191 : */
16192 430 : appendPQExpBufferStr(q, " (\n)");
16193 : }
16194 :
16195 : /*
16196 : * Emit the INHERITS clause (not for partitions), except in
16197 : * binary-upgrade mode.
16198 : */
16199 9214 : if (numParents > 0 && !tbinfo->ispartition &&
16200 692 : !dopt->binary_upgrade)
16201 : {
16202 590 : appendPQExpBufferStr(q, "\nINHERITS (");
16203 1236 : for (k = 0; k < numParents; k++)
16204 : {
16205 646 : TableInfo *parentRel = parents[k];
16206 :
16207 646 : if (k > 0)
16208 56 : appendPQExpBufferStr(q, ", ");
16209 646 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
16210 : }
16211 590 : appendPQExpBufferChar(q, ')');
16212 : }
16213 :
16214 9214 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16215 1006 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
16216 :
16217 9214 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
16218 70 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
16219 : }
16220 :
16221 19490 : if (nonemptyReloptions(tbinfo->reloptions) ||
16222 9602 : nonemptyReloptions(tbinfo->toast_reloptions))
16223 : {
16224 286 : bool addcomma = false;
16225 :
16226 286 : appendPQExpBufferStr(q, "\nWITH (");
16227 286 : if (nonemptyReloptions(tbinfo->reloptions))
16228 : {
16229 286 : addcomma = true;
16230 286 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16231 : }
16232 286 : if (nonemptyReloptions(tbinfo->toast_reloptions))
16233 : {
16234 10 : if (addcomma)
16235 10 : appendPQExpBufferStr(q, ", ");
16236 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
16237 : fout);
16238 : }
16239 286 : appendPQExpBufferChar(q, ')');
16240 : }
16241 :
16242 : /* Dump generic options if any */
16243 9888 : if (ftoptions && ftoptions[0])
16244 66 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
16245 :
16246 : /*
16247 : * For materialized views, create the AS clause just like a view. At
16248 : * this point, we always mark the view as not populated.
16249 : */
16250 9888 : if (tbinfo->relkind == RELKIND_MATVIEW)
16251 : {
16252 : PQExpBuffer result;
16253 :
16254 674 : result = createViewAsClause(fout, tbinfo);
16255 674 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
16256 : result->data);
16257 674 : destroyPQExpBuffer(result);
16258 : }
16259 : else
16260 9214 : appendPQExpBufferStr(q, ";\n");
16261 :
16262 : /* Materialized views can depend on extensions */
16263 9888 : if (tbinfo->relkind == RELKIND_MATVIEW)
16264 674 : append_depends_on_extension(fout, q, &tbinfo->dobj,
16265 : "pg_catalog.pg_class",
16266 : "MATERIALIZED VIEW",
16267 : qualrelname);
16268 :
16269 : /*
16270 : * in binary upgrade mode, update the catalog with any missing values
16271 : * that might be present.
16272 : */
16273 9888 : if (dopt->binary_upgrade)
16274 : {
16275 7318 : for (j = 0; j < tbinfo->numatts; j++)
16276 : {
16277 5894 : if (tbinfo->attmissingval[j][0] != '\0')
16278 : {
16279 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
16280 4 : appendPQExpBufferStr(q,
16281 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16282 4 : appendStringLiteralAH(q, qualrelname, fout);
16283 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16284 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16285 4 : appendPQExpBufferChar(q, ',');
16286 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16287 4 : appendPQExpBufferStr(q, ");\n\n");
16288 : }
16289 : }
16290 : }
16291 :
16292 : /*
16293 : * To create binary-compatible heap files, we have to ensure the same
16294 : * physical column order, including dropped columns, as in the
16295 : * original. Therefore, we create dropped columns above and drop them
16296 : * here, also updating their attlen/attalign values so that the
16297 : * dropped column can be skipped properly. (We do not bother with
16298 : * restoring the original attbyval setting.) Also, inheritance
16299 : * relationships are set up by doing ALTER TABLE INHERIT rather than
16300 : * using an INHERITS clause --- the latter would possibly mess up the
16301 : * column order. That also means we have to take care about setting
16302 : * attislocal correctly, plus fix up any inherited CHECK constraints.
16303 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
16304 : *
16305 : * We process foreign and partitioned tables here, even though they
16306 : * lack heap storage, because they can participate in inheritance
16307 : * relationships and we want this stuff to be consistent across the
16308 : * inheritance tree. We can exclude indexes, toast tables, sequences
16309 : * and matviews, even though they have storage, because we don't
16310 : * support altering or dropping columns in them, nor can they be part
16311 : * of inheritance trees.
16312 : */
16313 9888 : if (dopt->binary_upgrade &&
16314 1424 : (tbinfo->relkind == RELKIND_RELATION ||
16315 202 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16316 200 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16317 : {
16318 7244 : for (j = 0; j < tbinfo->numatts; j++)
16319 : {
16320 5854 : if (tbinfo->attisdropped[j])
16321 : {
16322 158 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n");
16323 158 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n"
16324 : "SET attlen = %d, "
16325 : "attalign = '%c', attbyval = false\n"
16326 : "WHERE attname = ",
16327 158 : tbinfo->attlen[j],
16328 158 : tbinfo->attalign[j]);
16329 158 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16330 158 : appendPQExpBufferStr(q, "\n AND attrelid = ");
16331 158 : appendStringLiteralAH(q, qualrelname, fout);
16332 158 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16333 :
16334 158 : if (tbinfo->relkind == RELKIND_RELATION ||
16335 32 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16336 158 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16337 : qualrelname);
16338 : else
16339 0 : appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ",
16340 : qualrelname);
16341 158 : appendPQExpBuffer(q, "DROP COLUMN %s;\n",
16342 158 : fmtId(tbinfo->attnames[j]));
16343 : }
16344 5696 : else if (!tbinfo->attislocal[j])
16345 : {
16346 1122 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n");
16347 1122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16348 : "SET attislocal = false\n"
16349 : "WHERE attname = ");
16350 1122 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16351 1122 : appendPQExpBufferStr(q, "\n AND attrelid = ");
16352 1122 : appendStringLiteralAH(q, qualrelname, fout);
16353 1122 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16354 :
16355 : /*
16356 : * If a not-null constraint comes from inheritance, reset
16357 : * conislocal. The inhcount is fixed later.
16358 : */
16359 1122 : if (tbinfo->notnull_constrs[j] != NULL &&
16360 166 : !tbinfo->notnull_throwaway[j] &&
16361 126 : tbinfo->notnull_inh[j] &&
16362 96 : !tbinfo->ispartition)
16363 : {
16364 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16365 : "SET conislocal = false\n"
16366 : "WHERE contype = 'n' AND conrelid = ");
16367 30 : appendStringLiteralAH(q, qualrelname, fout);
16368 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
16369 : "conname = ");
16370 30 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
16371 30 : appendPQExpBufferStr(q, ";\n");
16372 : }
16373 : }
16374 : }
16375 :
16376 : /*
16377 : * Add inherited CHECK constraints, if any.
16378 : *
16379 : * For partitions, they were already dumped, and conislocal
16380 : * doesn't need fixing.
16381 : */
16382 1492 : for (k = 0; k < tbinfo->ncheck; k++)
16383 : {
16384 102 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
16385 :
16386 102 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
16387 98 : continue;
16388 :
16389 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n");
16390 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
16391 : foreign, qualrelname,
16392 4 : fmtId(constr->dobj.name),
16393 : constr->condef);
16394 4 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16395 : "SET conislocal = false\n"
16396 : "WHERE contype = 'c' AND conname = ");
16397 4 : appendStringLiteralAH(q, constr->dobj.name, fout);
16398 4 : appendPQExpBufferStr(q, "\n AND conrelid = ");
16399 4 : appendStringLiteralAH(q, qualrelname, fout);
16400 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16401 : }
16402 :
16403 1390 : if (numParents > 0 && !tbinfo->ispartition)
16404 : {
16405 102 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
16406 218 : for (k = 0; k < numParents; k++)
16407 : {
16408 116 : TableInfo *parentRel = parents[k];
16409 :
16410 116 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
16411 : qualrelname,
16412 116 : fmtQualifiedDumpable(parentRel));
16413 : }
16414 : }
16415 :
16416 1390 : if (OidIsValid(tbinfo->reloftype))
16417 : {
16418 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
16419 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
16420 : qualrelname,
16421 : getFormattedTypeName(fout, tbinfo->reloftype,
16422 : zeroIsError));
16423 : }
16424 : }
16425 :
16426 : /*
16427 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
16428 : * relminmxid of all vacuumable relations. (While vacuum.c processes
16429 : * TOAST tables semi-independently, here we see them only as children
16430 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
16431 : * child toast table is handled below.)
16432 : */
16433 9888 : if (dopt->binary_upgrade &&
16434 1424 : (tbinfo->relkind == RELKIND_RELATION ||
16435 202 : tbinfo->relkind == RELKIND_MATVIEW))
16436 : {
16437 1256 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
16438 1256 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16439 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16440 : "WHERE oid = ",
16441 : tbinfo->frozenxid, tbinfo->minmxid);
16442 1256 : appendStringLiteralAH(q, qualrelname, fout);
16443 1256 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16444 :
16445 1256 : if (tbinfo->toast_oid)
16446 : {
16447 : /*
16448 : * The toast table will have the same OID at restore, so we
16449 : * can safely target it by OID.
16450 : */
16451 544 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
16452 544 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16453 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16454 : "WHERE oid = '%u';\n",
16455 : tbinfo->toast_frozenxid,
16456 : tbinfo->toast_minmxid, tbinfo->toast_oid);
16457 : }
16458 : }
16459 :
16460 : /*
16461 : * In binary_upgrade mode, restore matviews' populated status by
16462 : * poking pg_class directly. This is pretty ugly, but we can't use
16463 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
16464 : * matview is not populated even though this matview is; in any case,
16465 : * we want to transfer the matview's heap storage, not run REFRESH.
16466 : */
16467 9888 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
16468 34 : tbinfo->relispopulated)
16469 : {
16470 30 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
16471 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
16472 : "SET relispopulated = 't'\n"
16473 : "WHERE oid = ");
16474 30 : appendStringLiteralAH(q, qualrelname, fout);
16475 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16476 : }
16477 :
16478 : /*
16479 : * Dump additional per-column properties that we can't handle in the
16480 : * main CREATE TABLE command.
16481 : */
16482 45586 : for (j = 0; j < tbinfo->numatts; j++)
16483 : {
16484 : /* None of this applies to dropped columns */
16485 35698 : if (tbinfo->attisdropped[j])
16486 846 : continue;
16487 :
16488 : /*
16489 : * If we didn't dump the column definition explicitly above, and
16490 : * it is not-null and did not inherit that property from a parent,
16491 : * we have to mark it separately.
16492 : */
16493 34852 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
16494 926 : tbinfo->notnull_constrs[j] != NULL &&
16495 200 : (!tbinfo->notnull_inh[j] && !tbinfo->ispartition && !dopt->binary_upgrade))
16496 : {
16497 : /* No constraint name desired? */
16498 24 : if (tbinfo->notnull_constrs[j][0] == '\0')
16499 16 : appendPQExpBuffer(q,
16500 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET NOT NULL;\n",
16501 : foreign, qualrelname,
16502 16 : fmtId(tbinfo->attnames[j]));
16503 : else
16504 16 : appendPQExpBuffer(q,
16505 : "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s NOT NULL %s;\n",
16506 : foreign, qualrelname,
16507 8 : tbinfo->notnull_constrs[j],
16508 8 : fmtId(tbinfo->attnames[j]));
16509 : }
16510 :
16511 : /*
16512 : * Dump per-column statistics information. We only issue an ALTER
16513 : * TABLE statement if the attstattarget entry for this column is
16514 : * not the default value.
16515 : */
16516 34852 : if (tbinfo->attstattarget[j] >= 0)
16517 66 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
16518 : foreign, qualrelname,
16519 66 : fmtId(tbinfo->attnames[j]),
16520 66 : tbinfo->attstattarget[j]);
16521 :
16522 : /*
16523 : * Dump per-column storage information. The statement is only
16524 : * dumped if the storage has been changed from the type's default.
16525 : */
16526 34852 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
16527 : {
16528 162 : switch (tbinfo->attstorage[j])
16529 : {
16530 20 : case TYPSTORAGE_PLAIN:
16531 20 : storage = "PLAIN";
16532 20 : break;
16533 76 : case TYPSTORAGE_EXTERNAL:
16534 76 : storage = "EXTERNAL";
16535 76 : break;
16536 0 : case TYPSTORAGE_EXTENDED:
16537 0 : storage = "EXTENDED";
16538 0 : break;
16539 66 : case TYPSTORAGE_MAIN:
16540 66 : storage = "MAIN";
16541 66 : break;
16542 0 : default:
16543 0 : storage = NULL;
16544 : }
16545 :
16546 : /*
16547 : * Only dump the statement if it's a storage type we recognize
16548 : */
16549 162 : if (storage != NULL)
16550 162 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
16551 : foreign, qualrelname,
16552 162 : fmtId(tbinfo->attnames[j]),
16553 : storage);
16554 : }
16555 :
16556 : /*
16557 : * Dump per-column compression, if it's been set.
16558 : */
16559 34852 : if (!dopt->no_toast_compression)
16560 : {
16561 : const char *cmname;
16562 :
16563 34686 : switch (tbinfo->attcompression[j])
16564 : {
16565 104 : case 'p':
16566 104 : cmname = "pglz";
16567 104 : break;
16568 188 : case 'l':
16569 188 : cmname = "lz4";
16570 188 : break;
16571 34394 : default:
16572 34394 : cmname = NULL;
16573 34394 : break;
16574 : }
16575 :
16576 34686 : if (cmname != NULL)
16577 292 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
16578 : foreign, qualrelname,
16579 292 : fmtId(tbinfo->attnames[j]),
16580 : cmname);
16581 : }
16582 :
16583 : /*
16584 : * Dump per-column attributes.
16585 : */
16586 34852 : if (tbinfo->attoptions[j][0] != '\0')
16587 66 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
16588 : foreign, qualrelname,
16589 66 : fmtId(tbinfo->attnames[j]),
16590 66 : tbinfo->attoptions[j]);
16591 :
16592 : /*
16593 : * Dump per-column fdw options.
16594 : */
16595 34852 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
16596 70 : tbinfo->attfdwoptions[j][0] != '\0')
16597 66 : appendPQExpBuffer(q,
16598 : "ALTER FOREIGN TABLE %s ALTER COLUMN %s OPTIONS (\n"
16599 : " %s\n"
16600 : ");\n",
16601 : qualrelname,
16602 66 : fmtId(tbinfo->attnames[j]),
16603 66 : tbinfo->attfdwoptions[j]);
16604 : } /* end loop over columns */
16605 :
16606 9888 : free(partkeydef);
16607 9888 : free(ftoptions);
16608 9888 : free(srvname);
16609 : }
16610 :
16611 : /*
16612 : * dump properties we only have ALTER TABLE syntax for
16613 : */
16614 10580 : if ((tbinfo->relkind == RELKIND_RELATION ||
16615 2442 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
16616 1436 : tbinfo->relkind == RELKIND_MATVIEW) &&
16617 9818 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
16618 : {
16619 128 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
16620 : {
16621 : /* nothing to do, will be set when the index is dumped */
16622 : }
16623 128 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
16624 : {
16625 128 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
16626 : qualrelname);
16627 : }
16628 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
16629 : {
16630 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
16631 : qualrelname);
16632 : }
16633 : }
16634 :
16635 10580 : if (tbinfo->forcerowsec)
16636 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
16637 : qualrelname);
16638 :
16639 10580 : if (dopt->binary_upgrade)
16640 1520 : binary_upgrade_extension_member(q, &tbinfo->dobj,
16641 : reltypename, qrelname,
16642 1520 : tbinfo->dobj.namespace->dobj.name);
16643 :
16644 10580 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16645 : {
16646 10580 : char *tablespace = NULL;
16647 10580 : char *tableam = NULL;
16648 :
16649 : /*
16650 : * _selectTablespace() relies on tablespace-enabled objects in the
16651 : * default tablespace to have a tablespace of "" (empty string) versus
16652 : * non-tablespace-enabled objects to have a tablespace of NULL.
16653 : * getTables() sets tbinfo->reltablespace to "" for the default
16654 : * tablespace (not NULL).
16655 : */
16656 10580 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
16657 9818 : tablespace = tbinfo->reltablespace;
16658 :
16659 10580 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
16660 1768 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16661 9818 : tableam = tbinfo->amname;
16662 :
16663 10580 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16664 10580 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
16665 : .namespace = tbinfo->dobj.namespace->dobj.name,
16666 : .tablespace = tablespace,
16667 : .tableam = tableam,
16668 : .owner = tbinfo->rolname,
16669 : .description = reltypename,
16670 : .section = tbinfo->postponed_def ?
16671 : SECTION_POST_DATA : SECTION_PRE_DATA,
16672 : .createStmt = q->data,
16673 : .dropStmt = delq->data));
16674 : }
16675 :
16676 : /* Dump Table Comments */
16677 10580 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16678 152 : dumpTableComment(fout, tbinfo, reltypename);
16679 :
16680 : /* Dump Table Security Labels */
16681 10580 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16682 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
16683 :
16684 : /* Dump comments on inlined table constraints */
16685 11636 : for (j = 0; j < tbinfo->ncheck; j++)
16686 : {
16687 1056 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16688 :
16689 1056 : if (constr->separate || !constr->conislocal)
16690 428 : continue;
16691 :
16692 628 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
16693 76 : dumpTableConstraintComment(fout, constr);
16694 : }
16695 :
16696 10580 : destroyPQExpBuffer(q);
16697 10580 : destroyPQExpBuffer(delq);
16698 10580 : free(qrelname);
16699 10580 : free(qualrelname);
16700 10580 : }
16701 :
16702 : /*
16703 : * dumpTableAttach
16704 : * write to fout the commands to attach a child partition
16705 : *
16706 : * Child partitions are always made by creating them separately
16707 : * and then using ATTACH PARTITION, rather than using
16708 : * CREATE TABLE ... PARTITION OF. This is important for preserving
16709 : * any possible discrepancy in column layout, to allow assigning the
16710 : * correct tablespace if different, and so that it's possible to restore
16711 : * a partition without restoring its parent. (You'll get an error from
16712 : * the ATTACH PARTITION command, but that can be ignored, or skipped
16713 : * using "pg_restore -L" if you prefer.) The last point motivates
16714 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
16715 : * rather than emitting it within the child partition's ArchiveEntry.
16716 : */
16717 : static void
16718 2476 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
16719 : {
16720 2476 : DumpOptions *dopt = fout->dopt;
16721 : PQExpBuffer q;
16722 : PGresult *res;
16723 : char *partbound;
16724 :
16725 : /* Do nothing in data-only dump */
16726 2476 : if (dopt->dataOnly)
16727 42 : return;
16728 :
16729 2434 : q = createPQExpBuffer();
16730 :
16731 2434 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
16732 : {
16733 : /* Set up query for partbound details */
16734 88 : appendPQExpBufferStr(q,
16735 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
16736 :
16737 88 : appendPQExpBufferStr(q,
16738 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
16739 : "FROM pg_class c "
16740 : "WHERE c.oid = $1");
16741 :
16742 88 : ExecuteSqlStatement(fout, q->data);
16743 :
16744 88 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
16745 : }
16746 :
16747 2434 : printfPQExpBuffer(q,
16748 : "EXECUTE dumpTableAttach('%u')",
16749 2434 : attachinfo->partitionTbl->dobj.catId.oid);
16750 :
16751 2434 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
16752 2434 : partbound = PQgetvalue(res, 0, 0);
16753 :
16754 : /* Perform ALTER TABLE on the parent */
16755 2434 : printfPQExpBuffer(q,
16756 : "ALTER TABLE ONLY %s ",
16757 2434 : fmtQualifiedDumpable(attachinfo->parentTbl));
16758 2434 : appendPQExpBuffer(q,
16759 : "ATTACH PARTITION %s %s;\n",
16760 2434 : fmtQualifiedDumpable(attachinfo->partitionTbl),
16761 : partbound);
16762 :
16763 : /*
16764 : * There is no point in creating a drop query as the drop is done by table
16765 : * drop. (If you think to change this, see also _printTocEntry().)
16766 : * Although this object doesn't really have ownership as such, set the
16767 : * owner field anyway to ensure that the command is run by the correct
16768 : * role at restore time.
16769 : */
16770 2434 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16771 2434 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16772 : .namespace = attachinfo->dobj.namespace->dobj.name,
16773 : .owner = attachinfo->partitionTbl->rolname,
16774 : .description = "TABLE ATTACH",
16775 : .section = SECTION_PRE_DATA,
16776 : .createStmt = q->data));
16777 :
16778 2434 : PQclear(res);
16779 2434 : destroyPQExpBuffer(q);
16780 : }
16781 :
16782 : /*
16783 : * dumpAttrDef --- dump an attribute's default-value declaration
16784 : */
16785 : static void
16786 1570 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
16787 : {
16788 1570 : DumpOptions *dopt = fout->dopt;
16789 1570 : TableInfo *tbinfo = adinfo->adtable;
16790 1570 : int adnum = adinfo->adnum;
16791 : PQExpBuffer q;
16792 : PQExpBuffer delq;
16793 : char *qualrelname;
16794 : char *tag;
16795 : char *foreign;
16796 :
16797 : /* Do nothing in data-only dump */
16798 1570 : if (dopt->dataOnly)
16799 0 : return;
16800 :
16801 : /* Skip if not "separate"; it was dumped in the table's definition */
16802 1570 : if (!adinfo->separate)
16803 1232 : return;
16804 :
16805 338 : q = createPQExpBuffer();
16806 338 : delq = createPQExpBuffer();
16807 :
16808 338 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16809 :
16810 338 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
16811 :
16812 338 : appendPQExpBuffer(q,
16813 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
16814 338 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
16815 : adinfo->adef_expr);
16816 :
16817 338 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
16818 : foreign, qualrelname,
16819 338 : fmtId(tbinfo->attnames[adnum - 1]));
16820 :
16821 338 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
16822 :
16823 338 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16824 338 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
16825 338 : ARCHIVE_OPTS(.tag = tag,
16826 : .namespace = tbinfo->dobj.namespace->dobj.name,
16827 : .owner = tbinfo->rolname,
16828 : .description = "DEFAULT",
16829 : .section = SECTION_PRE_DATA,
16830 : .createStmt = q->data,
16831 : .dropStmt = delq->data));
16832 :
16833 338 : free(tag);
16834 338 : destroyPQExpBuffer(q);
16835 338 : destroyPQExpBuffer(delq);
16836 338 : free(qualrelname);
16837 : }
16838 :
16839 : /*
16840 : * getAttrName: extract the correct name for an attribute
16841 : *
16842 : * The array tblInfo->attnames[] only provides names of user attributes;
16843 : * if a system attribute number is supplied, we have to fake it.
16844 : * We also do a little bit of bounds checking for safety's sake.
16845 : */
16846 : static const char *
16847 3242 : getAttrName(int attrnum, const TableInfo *tblInfo)
16848 : {
16849 3242 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
16850 3242 : return tblInfo->attnames[attrnum - 1];
16851 0 : switch (attrnum)
16852 : {
16853 0 : case SelfItemPointerAttributeNumber:
16854 0 : return "ctid";
16855 0 : case MinTransactionIdAttributeNumber:
16856 0 : return "xmin";
16857 0 : case MinCommandIdAttributeNumber:
16858 0 : return "cmin";
16859 0 : case MaxTransactionIdAttributeNumber:
16860 0 : return "xmax";
16861 0 : case MaxCommandIdAttributeNumber:
16862 0 : return "cmax";
16863 0 : case TableOidAttributeNumber:
16864 0 : return "tableoid";
16865 : }
16866 0 : pg_fatal("invalid column number %d for table \"%s\"",
16867 : attrnum, tblInfo->dobj.name);
16868 : return NULL; /* keep compiler quiet */
16869 : }
16870 :
16871 : /*
16872 : * dumpIndex
16873 : * write out to fout a user-defined index
16874 : */
16875 : static void
16876 4324 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
16877 : {
16878 4324 : DumpOptions *dopt = fout->dopt;
16879 4324 : TableInfo *tbinfo = indxinfo->indextable;
16880 4324 : bool is_constraint = (indxinfo->indexconstraint != 0);
16881 : PQExpBuffer q;
16882 : PQExpBuffer delq;
16883 : char *qindxname;
16884 : char *qqindxname;
16885 :
16886 : /* Do nothing in data-only dump */
16887 4324 : if (dopt->dataOnly)
16888 114 : return;
16889 :
16890 4210 : q = createPQExpBuffer();
16891 4210 : delq = createPQExpBuffer();
16892 :
16893 4210 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
16894 4210 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
16895 :
16896 : /*
16897 : * If there's an associated constraint, don't dump the index per se, but
16898 : * do dump any comment for it. (This is safe because dependency ordering
16899 : * will have ensured the constraint is emitted first.) Note that the
16900 : * emitted comment has to be shown as depending on the constraint, not the
16901 : * index, in such cases.
16902 : */
16903 4210 : if (!is_constraint)
16904 : {
16905 1942 : char *indstatcols = indxinfo->indstatcols;
16906 1942 : char *indstatvals = indxinfo->indstatvals;
16907 1942 : char **indstatcolsarray = NULL;
16908 1942 : char **indstatvalsarray = NULL;
16909 1942 : int nstatcols = 0;
16910 1942 : int nstatvals = 0;
16911 :
16912 1942 : if (dopt->binary_upgrade)
16913 306 : binary_upgrade_set_pg_class_oids(fout, q,
16914 : indxinfo->dobj.catId.oid, true);
16915 :
16916 : /* Plain secondary index */
16917 1942 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
16918 :
16919 : /*
16920 : * Append ALTER TABLE commands as needed to set properties that we
16921 : * only have ALTER TABLE syntax for. Keep this in sync with the
16922 : * similar code in dumpConstraint!
16923 : */
16924 :
16925 : /* If the index is clustered, we need to record that. */
16926 1942 : if (indxinfo->indisclustered)
16927 : {
16928 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
16929 0 : fmtQualifiedDumpable(tbinfo));
16930 : /* index name is not qualified in this syntax */
16931 0 : appendPQExpBuffer(q, " ON %s;\n",
16932 : qindxname);
16933 : }
16934 :
16935 : /*
16936 : * If the index has any statistics on some of its columns, generate
16937 : * the associated ALTER INDEX queries.
16938 : */
16939 1942 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
16940 : {
16941 : int j;
16942 :
16943 66 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
16944 0 : pg_fatal("could not parse index statistic columns");
16945 66 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
16946 0 : pg_fatal("could not parse index statistic values");
16947 66 : if (nstatcols != nstatvals)
16948 0 : pg_fatal("mismatched number of columns and values for index statistics");
16949 :
16950 198 : for (j = 0; j < nstatcols; j++)
16951 : {
16952 132 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
16953 :
16954 : /*
16955 : * Note that this is a column number, so no quotes should be
16956 : * used.
16957 : */
16958 132 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
16959 132 : indstatcolsarray[j]);
16960 132 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
16961 132 : indstatvalsarray[j]);
16962 : }
16963 : }
16964 :
16965 : /* Indexes can depend on extensions */
16966 1942 : append_depends_on_extension(fout, q, &indxinfo->dobj,
16967 : "pg_catalog.pg_class",
16968 : "INDEX", qqindxname);
16969 :
16970 : /* If the index defines identity, we need to record that. */
16971 1942 : if (indxinfo->indisreplident)
16972 : {
16973 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
16974 0 : fmtQualifiedDumpable(tbinfo));
16975 : /* index name is not qualified in this syntax */
16976 0 : appendPQExpBuffer(q, " INDEX %s;\n",
16977 : qindxname);
16978 : }
16979 :
16980 1942 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
16981 :
16982 1942 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16983 1942 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
16984 1942 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
16985 : .namespace = tbinfo->dobj.namespace->dobj.name,
16986 : .tablespace = indxinfo->tablespace,
16987 : .owner = tbinfo->rolname,
16988 : .description = "INDEX",
16989 : .section = SECTION_POST_DATA,
16990 : .createStmt = q->data,
16991 : .dropStmt = delq->data));
16992 :
16993 1942 : free(indstatcolsarray);
16994 1942 : free(indstatvalsarray);
16995 : }
16996 :
16997 : /* Dump Index Comments */
16998 4210 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16999 30 : dumpComment(fout, "INDEX", qindxname,
17000 30 : tbinfo->dobj.namespace->dobj.name,
17001 : tbinfo->rolname,
17002 : indxinfo->dobj.catId, 0,
17003 : is_constraint ? indxinfo->indexconstraint :
17004 : indxinfo->dobj.dumpId);
17005 :
17006 4210 : destroyPQExpBuffer(q);
17007 4210 : destroyPQExpBuffer(delq);
17008 4210 : free(qindxname);
17009 4210 : free(qqindxname);
17010 : }
17011 :
17012 : /*
17013 : * dumpIndexAttach
17014 : * write out to fout a partitioned-index attachment clause
17015 : */
17016 : static void
17017 1086 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
17018 : {
17019 : /* Do nothing in data-only dump */
17020 1086 : if (fout->dopt->dataOnly)
17021 48 : return;
17022 :
17023 1038 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
17024 : {
17025 1038 : PQExpBuffer q = createPQExpBuffer();
17026 :
17027 1038 : appendPQExpBuffer(q, "ALTER INDEX %s ",
17028 1038 : fmtQualifiedDumpable(attachinfo->parentIdx));
17029 1038 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
17030 1038 : fmtQualifiedDumpable(attachinfo->partitionIdx));
17031 :
17032 : /*
17033 : * There is no point in creating a drop query as the drop is done by
17034 : * index drop. (If you think to change this, see also
17035 : * _printTocEntry().) Although this object doesn't really have
17036 : * ownership as such, set the owner field anyway to ensure that the
17037 : * command is run by the correct role at restore time.
17038 : */
17039 1038 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17040 1038 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17041 : .namespace = attachinfo->dobj.namespace->dobj.name,
17042 : .owner = attachinfo->parentIdx->indextable->rolname,
17043 : .description = "INDEX ATTACH",
17044 : .section = SECTION_POST_DATA,
17045 : .createStmt = q->data));
17046 :
17047 1038 : destroyPQExpBuffer(q);
17048 : }
17049 : }
17050 :
17051 : /*
17052 : * dumpStatisticsExt
17053 : * write out to fout an extended statistics object
17054 : */
17055 : static void
17056 254 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
17057 : {
17058 254 : DumpOptions *dopt = fout->dopt;
17059 : PQExpBuffer q;
17060 : PQExpBuffer delq;
17061 : PQExpBuffer query;
17062 : char *qstatsextname;
17063 : PGresult *res;
17064 : char *stxdef;
17065 :
17066 : /* Do nothing in data-only dump */
17067 254 : if (dopt->dataOnly)
17068 18 : return;
17069 :
17070 236 : q = createPQExpBuffer();
17071 236 : delq = createPQExpBuffer();
17072 236 : query = createPQExpBuffer();
17073 :
17074 236 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
17075 :
17076 236 : appendPQExpBuffer(query, "SELECT "
17077 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
17078 : statsextinfo->dobj.catId.oid);
17079 :
17080 236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17081 :
17082 236 : stxdef = PQgetvalue(res, 0, 0);
17083 :
17084 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
17085 236 : appendPQExpBuffer(q, "%s;\n", stxdef);
17086 :
17087 : /*
17088 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
17089 : * for this statistics object is not the default value.
17090 : */
17091 236 : if (statsextinfo->stattarget >= 0)
17092 : {
17093 66 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
17094 66 : fmtQualifiedDumpable(statsextinfo));
17095 66 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
17096 : statsextinfo->stattarget);
17097 : }
17098 :
17099 236 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
17100 236 : fmtQualifiedDumpable(statsextinfo));
17101 :
17102 236 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17103 236 : ArchiveEntry(fout, statsextinfo->dobj.catId,
17104 : statsextinfo->dobj.dumpId,
17105 236 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
17106 : .namespace = statsextinfo->dobj.namespace->dobj.name,
17107 : .owner = statsextinfo->rolname,
17108 : .description = "STATISTICS",
17109 : .section = SECTION_POST_DATA,
17110 : .createStmt = q->data,
17111 : .dropStmt = delq->data));
17112 :
17113 : /* Dump Statistics Comments */
17114 236 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17115 0 : dumpComment(fout, "STATISTICS", qstatsextname,
17116 0 : statsextinfo->dobj.namespace->dobj.name,
17117 : statsextinfo->rolname,
17118 : statsextinfo->dobj.catId, 0,
17119 : statsextinfo->dobj.dumpId);
17120 :
17121 236 : PQclear(res);
17122 236 : destroyPQExpBuffer(q);
17123 236 : destroyPQExpBuffer(delq);
17124 236 : destroyPQExpBuffer(query);
17125 236 : free(qstatsextname);
17126 : }
17127 :
17128 : /*
17129 : * dumpConstraint
17130 : * write out to fout a user-defined constraint
17131 : */
17132 : static void
17133 3916 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
17134 : {
17135 3916 : DumpOptions *dopt = fout->dopt;
17136 3916 : TableInfo *tbinfo = coninfo->contable;
17137 : PQExpBuffer q;
17138 : PQExpBuffer delq;
17139 3916 : char *tag = NULL;
17140 : char *foreign;
17141 :
17142 : /* Do nothing in data-only dump */
17143 3916 : if (dopt->dataOnly)
17144 94 : return;
17145 :
17146 3822 : q = createPQExpBuffer();
17147 3822 : delq = createPQExpBuffer();
17148 :
17149 7478 : foreign = tbinfo &&
17150 3822 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17151 :
17152 3822 : if (coninfo->contype == 'p' ||
17153 1832 : coninfo->contype == 'u' ||
17154 1574 : coninfo->contype == 'x')
17155 2268 : {
17156 : /* Index-related constraint */
17157 : IndxInfo *indxinfo;
17158 : int k;
17159 :
17160 2268 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
17161 :
17162 2268 : if (indxinfo == NULL)
17163 0 : pg_fatal("missing index for constraint \"%s\"",
17164 : coninfo->dobj.name);
17165 :
17166 2268 : if (dopt->binary_upgrade)
17167 260 : binary_upgrade_set_pg_class_oids(fout, q,
17168 : indxinfo->dobj.catId.oid, true);
17169 :
17170 2268 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
17171 2268 : fmtQualifiedDumpable(tbinfo));
17172 2268 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
17173 2268 : fmtId(coninfo->dobj.name));
17174 :
17175 2268 : if (coninfo->condef)
17176 : {
17177 : /* pg_get_constraintdef should have provided everything */
17178 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
17179 : }
17180 : else
17181 : {
17182 2248 : appendPQExpBufferStr(q,
17183 2248 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
17184 :
17185 : /*
17186 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
17187 : * indexes. Being able to create this was fixed, but we need to
17188 : * make the index distinct in order to be able to restore the
17189 : * dump.
17190 : */
17191 2248 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
17192 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
17193 2248 : appendPQExpBufferStr(q, " (");
17194 5410 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
17195 : {
17196 3162 : int indkey = (int) indxinfo->indkeys[k];
17197 : const char *attname;
17198 :
17199 3162 : if (indkey == InvalidAttrNumber)
17200 0 : break;
17201 3162 : attname = getAttrName(indkey, tbinfo);
17202 :
17203 3162 : appendPQExpBuffer(q, "%s%s",
17204 : (k == 0) ? "" : ", ",
17205 : fmtId(attname));
17206 : }
17207 2248 : if (coninfo->conperiod)
17208 222 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
17209 :
17210 2248 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
17211 40 : appendPQExpBufferStr(q, ") INCLUDE (");
17212 :
17213 2328 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
17214 : {
17215 80 : int indkey = (int) indxinfo->indkeys[k];
17216 : const char *attname;
17217 :
17218 80 : if (indkey == InvalidAttrNumber)
17219 0 : break;
17220 80 : attname = getAttrName(indkey, tbinfo);
17221 :
17222 160 : appendPQExpBuffer(q, "%s%s",
17223 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
17224 : fmtId(attname));
17225 : }
17226 :
17227 2248 : appendPQExpBufferChar(q, ')');
17228 :
17229 2248 : if (nonemptyReloptions(indxinfo->indreloptions))
17230 : {
17231 0 : appendPQExpBufferStr(q, " WITH (");
17232 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
17233 0 : appendPQExpBufferChar(q, ')');
17234 : }
17235 :
17236 2248 : if (coninfo->condeferrable)
17237 : {
17238 50 : appendPQExpBufferStr(q, " DEFERRABLE");
17239 50 : if (coninfo->condeferred)
17240 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
17241 : }
17242 :
17243 2248 : appendPQExpBufferStr(q, ";\n");
17244 : }
17245 :
17246 : /*
17247 : * Append ALTER TABLE commands as needed to set properties that we
17248 : * only have ALTER TABLE syntax for. Keep this in sync with the
17249 : * similar code in dumpIndex!
17250 : */
17251 :
17252 : /* Drop any not-null constraints that were added to support the PK */
17253 2268 : if (coninfo->contype == 'p')
17254 7554 : for (int i = 0; i < tbinfo->numatts; i++)
17255 5564 : if (tbinfo->notnull_throwaway[i])
17256 1576 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s DROP CONSTRAINT %s;",
17257 1576 : fmtQualifiedDumpable(tbinfo),
17258 1576 : tbinfo->notnull_constrs[i]);
17259 :
17260 : /* If the index is clustered, we need to record that. */
17261 2268 : if (indxinfo->indisclustered)
17262 : {
17263 66 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17264 66 : fmtQualifiedDumpable(tbinfo));
17265 : /* index name is not qualified in this syntax */
17266 66 : appendPQExpBuffer(q, " ON %s;\n",
17267 66 : fmtId(indxinfo->dobj.name));
17268 : }
17269 :
17270 : /* If the index defines identity, we need to record that. */
17271 2268 : if (indxinfo->indisreplident)
17272 : {
17273 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17274 0 : fmtQualifiedDumpable(tbinfo));
17275 : /* index name is not qualified in this syntax */
17276 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17277 0 : fmtId(indxinfo->dobj.name));
17278 : }
17279 :
17280 : /* Indexes can depend on extensions */
17281 2268 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17282 : "pg_catalog.pg_class", "INDEX",
17283 2268 : fmtQualifiedDumpable(indxinfo));
17284 :
17285 2268 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17286 2268 : fmtQualifiedDumpable(tbinfo));
17287 2268 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17288 2268 : fmtId(coninfo->dobj.name));
17289 :
17290 2268 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17291 :
17292 2268 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17293 2268 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17294 2268 : ARCHIVE_OPTS(.tag = tag,
17295 : .namespace = tbinfo->dobj.namespace->dobj.name,
17296 : .tablespace = indxinfo->tablespace,
17297 : .owner = tbinfo->rolname,
17298 : .description = "CONSTRAINT",
17299 : .section = SECTION_POST_DATA,
17300 : .createStmt = q->data,
17301 : .dropStmt = delq->data));
17302 : }
17303 1554 : else if (coninfo->contype == 'f')
17304 : {
17305 : char *only;
17306 :
17307 : /*
17308 : * Foreign keys on partitioned tables are always declared as
17309 : * inheriting to partitions; for all other cases, emit them as
17310 : * applying ONLY directly to the named table, because that's how they
17311 : * work for regular inherited tables.
17312 : */
17313 332 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17314 :
17315 : /*
17316 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17317 : * current table data is not processed
17318 : */
17319 332 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17320 332 : only, fmtQualifiedDumpable(tbinfo));
17321 332 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17322 332 : fmtId(coninfo->dobj.name),
17323 : coninfo->condef);
17324 :
17325 332 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
17326 332 : only, fmtQualifiedDumpable(tbinfo));
17327 332 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17328 332 : fmtId(coninfo->dobj.name));
17329 :
17330 332 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17331 :
17332 332 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17333 332 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17334 332 : ARCHIVE_OPTS(.tag = tag,
17335 : .namespace = tbinfo->dobj.namespace->dobj.name,
17336 : .owner = tbinfo->rolname,
17337 : .description = "FK CONSTRAINT",
17338 : .section = SECTION_POST_DATA,
17339 : .createStmt = q->data,
17340 : .dropStmt = delq->data));
17341 : }
17342 1222 : else if (coninfo->contype == 'c' && tbinfo)
17343 : {
17344 : /* CHECK constraint on a table */
17345 :
17346 : /* Ignore if not to be dumped separately, or if it was inherited */
17347 1056 : if (coninfo->separate && coninfo->conislocal)
17348 : {
17349 : /* not ONLY since we want it to propagate to children */
17350 50 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
17351 50 : fmtQualifiedDumpable(tbinfo));
17352 50 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17353 50 : fmtId(coninfo->dobj.name),
17354 : coninfo->condef);
17355 :
17356 50 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
17357 50 : fmtQualifiedDumpable(tbinfo));
17358 50 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17359 50 : fmtId(coninfo->dobj.name));
17360 :
17361 50 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17362 :
17363 50 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17364 50 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17365 50 : ARCHIVE_OPTS(.tag = tag,
17366 : .namespace = tbinfo->dobj.namespace->dobj.name,
17367 : .owner = tbinfo->rolname,
17368 : .description = "CHECK CONSTRAINT",
17369 : .section = SECTION_POST_DATA,
17370 : .createStmt = q->data,
17371 : .dropStmt = delq->data));
17372 : }
17373 : }
17374 166 : else if (coninfo->contype == 'c' && tbinfo == NULL)
17375 166 : {
17376 : /* CHECK constraint on a domain */
17377 166 : TypeInfo *tyinfo = coninfo->condomain;
17378 :
17379 : /* Ignore if not to be dumped separately */
17380 166 : if (coninfo->separate)
17381 : {
17382 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
17383 0 : fmtQualifiedDumpable(tyinfo));
17384 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17385 0 : fmtId(coninfo->dobj.name),
17386 : coninfo->condef);
17387 :
17388 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
17389 0 : fmtQualifiedDumpable(tyinfo));
17390 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17391 0 : fmtId(coninfo->dobj.name));
17392 :
17393 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
17394 :
17395 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17396 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17397 0 : ARCHIVE_OPTS(.tag = tag,
17398 : .namespace = tyinfo->dobj.namespace->dobj.name,
17399 : .owner = tyinfo->rolname,
17400 : .description = "CHECK CONSTRAINT",
17401 : .section = SECTION_POST_DATA,
17402 : .createStmt = q->data,
17403 : .dropStmt = delq->data));
17404 : }
17405 : }
17406 : else
17407 : {
17408 0 : pg_fatal("unrecognized constraint type: %c",
17409 : coninfo->contype);
17410 : }
17411 :
17412 : /* Dump Constraint Comments --- only works for table constraints */
17413 3822 : if (tbinfo && coninfo->separate &&
17414 2680 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17415 20 : dumpTableConstraintComment(fout, coninfo);
17416 :
17417 3822 : free(tag);
17418 3822 : destroyPQExpBuffer(q);
17419 3822 : destroyPQExpBuffer(delq);
17420 : }
17421 :
17422 : /*
17423 : * dumpTableConstraintComment --- dump a constraint's comment if any
17424 : *
17425 : * This is split out because we need the function in two different places
17426 : * depending on whether the constraint is dumped as part of CREATE TABLE
17427 : * or as a separate ALTER command.
17428 : */
17429 : static void
17430 96 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
17431 : {
17432 96 : TableInfo *tbinfo = coninfo->contable;
17433 96 : PQExpBuffer conprefix = createPQExpBuffer();
17434 : char *qtabname;
17435 :
17436 96 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17437 :
17438 96 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
17439 96 : fmtId(coninfo->dobj.name));
17440 :
17441 96 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17442 96 : dumpComment(fout, conprefix->data, qtabname,
17443 96 : tbinfo->dobj.namespace->dobj.name,
17444 : tbinfo->rolname,
17445 : coninfo->dobj.catId, 0,
17446 96 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
17447 :
17448 96 : destroyPQExpBuffer(conprefix);
17449 96 : free(qtabname);
17450 96 : }
17451 :
17452 : /*
17453 : * dumpSequence
17454 : * write the declaration (not data) of one user-defined sequence
17455 : */
17456 : static void
17457 716 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
17458 : {
17459 716 : DumpOptions *dopt = fout->dopt;
17460 : PGresult *res;
17461 : char *startv,
17462 : *incby,
17463 : *maxv,
17464 : *minv,
17465 : *cache,
17466 : *seqtype;
17467 : bool cycled;
17468 : bool is_ascending;
17469 : int64 default_minv,
17470 : default_maxv;
17471 : char bufm[32],
17472 : bufx[32];
17473 716 : PQExpBuffer query = createPQExpBuffer();
17474 716 : PQExpBuffer delqry = createPQExpBuffer();
17475 : char *qseqname;
17476 716 : TableInfo *owning_tab = NULL;
17477 :
17478 716 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
17479 :
17480 716 : if (fout->remoteVersion >= 100000)
17481 : {
17482 716 : appendPQExpBuffer(query,
17483 : "SELECT format_type(seqtypid, NULL), "
17484 : "seqstart, seqincrement, "
17485 : "seqmax, seqmin, "
17486 : "seqcache, seqcycle "
17487 : "FROM pg_catalog.pg_sequence "
17488 : "WHERE seqrelid = '%u'::oid",
17489 : tbinfo->dobj.catId.oid);
17490 : }
17491 : else
17492 : {
17493 : /*
17494 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
17495 : *
17496 : * Note: it might seem that 'bigint' potentially needs to be
17497 : * schema-qualified, but actually that's a keyword.
17498 : */
17499 0 : appendPQExpBuffer(query,
17500 : "SELECT 'bigint' AS sequence_type, "
17501 : "start_value, increment_by, max_value, min_value, "
17502 : "cache_value, is_cycled FROM %s",
17503 0 : fmtQualifiedDumpable(tbinfo));
17504 : }
17505 :
17506 716 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17507 :
17508 716 : if (PQntuples(res) != 1)
17509 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17510 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17511 : PQntuples(res)),
17512 : tbinfo->dobj.name, PQntuples(res));
17513 :
17514 716 : seqtype = PQgetvalue(res, 0, 0);
17515 716 : startv = PQgetvalue(res, 0, 1);
17516 716 : incby = PQgetvalue(res, 0, 2);
17517 716 : maxv = PQgetvalue(res, 0, 3);
17518 716 : minv = PQgetvalue(res, 0, 4);
17519 716 : cache = PQgetvalue(res, 0, 5);
17520 716 : cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
17521 :
17522 : /* Calculate default limits for a sequence of this type */
17523 716 : is_ascending = (incby[0] != '-');
17524 716 : if (strcmp(seqtype, "smallint") == 0)
17525 : {
17526 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
17527 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
17528 : }
17529 666 : else if (strcmp(seqtype, "integer") == 0)
17530 : {
17531 554 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
17532 554 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
17533 : }
17534 112 : else if (strcmp(seqtype, "bigint") == 0)
17535 : {
17536 112 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
17537 112 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
17538 : }
17539 : else
17540 : {
17541 0 : pg_fatal("unrecognized sequence type: %s", seqtype);
17542 : default_minv = default_maxv = 0; /* keep compiler quiet */
17543 : }
17544 :
17545 : /*
17546 : * 64-bit strtol() isn't very portable, so convert the limits to strings
17547 : * and compare that way.
17548 : */
17549 716 : snprintf(bufm, sizeof(bufm), INT64_FORMAT, default_minv);
17550 716 : snprintf(bufx, sizeof(bufx), INT64_FORMAT, default_maxv);
17551 :
17552 : /* Don't print minv/maxv if they match the respective default limit */
17553 716 : if (strcmp(minv, bufm) == 0)
17554 686 : minv = NULL;
17555 716 : if (strcmp(maxv, bufx) == 0)
17556 686 : maxv = NULL;
17557 :
17558 : /*
17559 : * Identity sequences are not to be dropped separately.
17560 : */
17561 716 : if (!tbinfo->is_identity_sequence)
17562 : {
17563 460 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
17564 460 : fmtQualifiedDumpable(tbinfo));
17565 : }
17566 :
17567 716 : resetPQExpBuffer(query);
17568 :
17569 716 : if (dopt->binary_upgrade)
17570 : {
17571 116 : binary_upgrade_set_pg_class_oids(fout, query,
17572 : tbinfo->dobj.catId.oid, false);
17573 :
17574 : /*
17575 : * In older PG versions a sequence will have a pg_type entry, but v14
17576 : * and up don't use that, so don't attempt to preserve the type OID.
17577 : */
17578 : }
17579 :
17580 716 : if (tbinfo->is_identity_sequence)
17581 : {
17582 256 : owning_tab = findTableByOid(tbinfo->owning_tab);
17583 :
17584 256 : appendPQExpBuffer(query,
17585 : "ALTER TABLE %s ",
17586 256 : fmtQualifiedDumpable(owning_tab));
17587 256 : appendPQExpBuffer(query,
17588 : "ALTER COLUMN %s ADD GENERATED ",
17589 256 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17590 256 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
17591 176 : appendPQExpBufferStr(query, "ALWAYS");
17592 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
17593 80 : appendPQExpBufferStr(query, "BY DEFAULT");
17594 256 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
17595 256 : fmtQualifiedDumpable(tbinfo));
17596 : }
17597 : else
17598 : {
17599 460 : appendPQExpBuffer(query,
17600 : "CREATE %sSEQUENCE %s\n",
17601 460 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17602 : "UNLOGGED " : "",
17603 460 : fmtQualifiedDumpable(tbinfo));
17604 :
17605 460 : if (strcmp(seqtype, "bigint") != 0)
17606 368 : appendPQExpBuffer(query, " AS %s\n", seqtype);
17607 : }
17608 :
17609 716 : appendPQExpBuffer(query, " START WITH %s\n", startv);
17610 :
17611 716 : appendPQExpBuffer(query, " INCREMENT BY %s\n", incby);
17612 :
17613 716 : if (minv)
17614 30 : appendPQExpBuffer(query, " MINVALUE %s\n", minv);
17615 : else
17616 686 : appendPQExpBufferStr(query, " NO MINVALUE\n");
17617 :
17618 716 : if (maxv)
17619 30 : appendPQExpBuffer(query, " MAXVALUE %s\n", maxv);
17620 : else
17621 686 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
17622 :
17623 716 : appendPQExpBuffer(query,
17624 : " CACHE %s%s",
17625 : cache, (cycled ? "\n CYCLE" : ""));
17626 :
17627 716 : if (tbinfo->is_identity_sequence)
17628 : {
17629 256 : appendPQExpBufferStr(query, "\n);\n");
17630 256 : if (tbinfo->relpersistence != owning_tab->relpersistence)
17631 0 : appendPQExpBuffer(query,
17632 : "ALTER SEQUENCE %s SET %s;\n",
17633 0 : fmtQualifiedDumpable(tbinfo),
17634 0 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17635 : "UNLOGGED" : "LOGGED");
17636 : }
17637 : else
17638 460 : appendPQExpBufferStr(query, ";\n");
17639 :
17640 : /* binary_upgrade: no need to clear TOAST table oid */
17641 :
17642 716 : if (dopt->binary_upgrade)
17643 116 : binary_upgrade_extension_member(query, &tbinfo->dobj,
17644 : "SEQUENCE", qseqname,
17645 116 : tbinfo->dobj.namespace->dobj.name);
17646 :
17647 716 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17648 716 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17649 716 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17650 : .namespace = tbinfo->dobj.namespace->dobj.name,
17651 : .owner = tbinfo->rolname,
17652 : .description = "SEQUENCE",
17653 : .section = SECTION_PRE_DATA,
17654 : .createStmt = query->data,
17655 : .dropStmt = delqry->data));
17656 :
17657 : /*
17658 : * If the sequence is owned by a table column, emit the ALTER for it as a
17659 : * separate TOC entry immediately following the sequence's own entry. It's
17660 : * OK to do this rather than using full sorting logic, because the
17661 : * dependency that tells us it's owned will have forced the table to be
17662 : * created first. We can't just include the ALTER in the TOC entry
17663 : * because it will fail if we haven't reassigned the sequence owner to
17664 : * match the table's owner.
17665 : *
17666 : * We need not schema-qualify the table reference because both sequence
17667 : * and table must be in the same schema.
17668 : */
17669 716 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
17670 : {
17671 278 : owning_tab = findTableByOid(tbinfo->owning_tab);
17672 :
17673 278 : if (owning_tab == NULL)
17674 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
17675 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
17676 :
17677 278 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
17678 : {
17679 274 : resetPQExpBuffer(query);
17680 274 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
17681 274 : fmtQualifiedDumpable(tbinfo));
17682 274 : appendPQExpBuffer(query, " OWNED BY %s",
17683 274 : fmtQualifiedDumpable(owning_tab));
17684 274 : appendPQExpBuffer(query, ".%s;\n",
17685 274 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17686 :
17687 274 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17688 274 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17689 274 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17690 : .namespace = tbinfo->dobj.namespace->dobj.name,
17691 : .owner = tbinfo->rolname,
17692 : .description = "SEQUENCE OWNED BY",
17693 : .section = SECTION_PRE_DATA,
17694 : .createStmt = query->data,
17695 : .deps = &(tbinfo->dobj.dumpId),
17696 : .nDeps = 1));
17697 : }
17698 : }
17699 :
17700 : /* Dump Sequence Comments and Security Labels */
17701 716 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17702 0 : dumpComment(fout, "SEQUENCE", qseqname,
17703 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17704 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17705 :
17706 716 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17707 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
17708 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17709 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17710 :
17711 716 : PQclear(res);
17712 :
17713 716 : destroyPQExpBuffer(query);
17714 716 : destroyPQExpBuffer(delqry);
17715 716 : free(qseqname);
17716 716 : }
17717 :
17718 : /*
17719 : * dumpSequenceData
17720 : * write the data of one user-defined sequence
17721 : */
17722 : static void
17723 752 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
17724 : {
17725 752 : TableInfo *tbinfo = tdinfo->tdtable;
17726 : PGresult *res;
17727 : char *last;
17728 : bool called;
17729 752 : PQExpBuffer query = createPQExpBuffer();
17730 :
17731 752 : appendPQExpBuffer(query,
17732 : "SELECT last_value, is_called FROM %s",
17733 752 : fmtQualifiedDumpable(tbinfo));
17734 :
17735 752 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17736 :
17737 752 : if (PQntuples(res) != 1)
17738 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17739 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17740 : PQntuples(res)),
17741 : tbinfo->dobj.name, PQntuples(res));
17742 :
17743 752 : last = PQgetvalue(res, 0, 0);
17744 752 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
17745 :
17746 752 : resetPQExpBuffer(query);
17747 752 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
17748 752 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
17749 752 : appendPQExpBuffer(query, ", %s, %s);\n",
17750 : last, (called ? "true" : "false"));
17751 :
17752 752 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
17753 752 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17754 752 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17755 : .namespace = tbinfo->dobj.namespace->dobj.name,
17756 : .owner = tbinfo->rolname,
17757 : .description = "SEQUENCE SET",
17758 : .section = SECTION_DATA,
17759 : .createStmt = query->data,
17760 : .deps = &(tbinfo->dobj.dumpId),
17761 : .nDeps = 1));
17762 :
17763 752 : PQclear(res);
17764 :
17765 752 : destroyPQExpBuffer(query);
17766 752 : }
17767 :
17768 : /*
17769 : * dumpTrigger
17770 : * write the declaration of one user-defined table trigger
17771 : */
17772 : static void
17773 986 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
17774 : {
17775 986 : DumpOptions *dopt = fout->dopt;
17776 986 : TableInfo *tbinfo = tginfo->tgtable;
17777 : PQExpBuffer query;
17778 : PQExpBuffer delqry;
17779 : PQExpBuffer trigprefix;
17780 : PQExpBuffer trigidentity;
17781 : char *qtabname;
17782 : char *tag;
17783 :
17784 : /* Do nothing in data-only dump */
17785 986 : if (dopt->dataOnly)
17786 32 : return;
17787 :
17788 954 : query = createPQExpBuffer();
17789 954 : delqry = createPQExpBuffer();
17790 954 : trigprefix = createPQExpBuffer();
17791 954 : trigidentity = createPQExpBuffer();
17792 :
17793 954 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17794 :
17795 954 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
17796 954 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
17797 :
17798 954 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
17799 954 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
17800 :
17801 : /* Triggers can depend on extensions */
17802 954 : append_depends_on_extension(fout, query, &tginfo->dobj,
17803 : "pg_catalog.pg_trigger", "TRIGGER",
17804 954 : trigidentity->data);
17805 :
17806 954 : if (tginfo->tgispartition)
17807 : {
17808 : Assert(tbinfo->ispartition);
17809 :
17810 : /*
17811 : * Partition triggers only appear here because their 'tgenabled' flag
17812 : * differs from its parent's. The trigger is created already, so
17813 : * remove the CREATE and replace it with an ALTER. (Clear out the
17814 : * DROP query too, so that pg_dump --create does not cause errors.)
17815 : */
17816 224 : resetPQExpBuffer(query);
17817 224 : resetPQExpBuffer(delqry);
17818 224 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
17819 224 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
17820 224 : fmtQualifiedDumpable(tbinfo));
17821 224 : switch (tginfo->tgenabled)
17822 : {
17823 78 : case 'f':
17824 : case 'D':
17825 78 : appendPQExpBufferStr(query, "DISABLE");
17826 78 : break;
17827 0 : case 't':
17828 : case 'O':
17829 0 : appendPQExpBufferStr(query, "ENABLE");
17830 0 : break;
17831 68 : case 'R':
17832 68 : appendPQExpBufferStr(query, "ENABLE REPLICA");
17833 68 : break;
17834 78 : case 'A':
17835 78 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
17836 78 : break;
17837 : }
17838 224 : appendPQExpBuffer(query, " TRIGGER %s;\n",
17839 224 : fmtId(tginfo->dobj.name));
17840 : }
17841 730 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
17842 : {
17843 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
17844 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
17845 0 : fmtQualifiedDumpable(tbinfo));
17846 0 : switch (tginfo->tgenabled)
17847 : {
17848 0 : case 'D':
17849 : case 'f':
17850 0 : appendPQExpBufferStr(query, "DISABLE");
17851 0 : break;
17852 0 : case 'A':
17853 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
17854 0 : break;
17855 0 : case 'R':
17856 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
17857 0 : break;
17858 0 : default:
17859 0 : appendPQExpBufferStr(query, "ENABLE");
17860 0 : break;
17861 : }
17862 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
17863 0 : fmtId(tginfo->dobj.name));
17864 : }
17865 :
17866 954 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
17867 954 : fmtId(tginfo->dobj.name));
17868 :
17869 954 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
17870 :
17871 954 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17872 954 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
17873 954 : ARCHIVE_OPTS(.tag = tag,
17874 : .namespace = tbinfo->dobj.namespace->dobj.name,
17875 : .owner = tbinfo->rolname,
17876 : .description = "TRIGGER",
17877 : .section = SECTION_POST_DATA,
17878 : .createStmt = query->data,
17879 : .dropStmt = delqry->data));
17880 :
17881 954 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17882 0 : dumpComment(fout, trigprefix->data, qtabname,
17883 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17884 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
17885 :
17886 954 : free(tag);
17887 954 : destroyPQExpBuffer(query);
17888 954 : destroyPQExpBuffer(delqry);
17889 954 : destroyPQExpBuffer(trigprefix);
17890 954 : destroyPQExpBuffer(trigidentity);
17891 954 : free(qtabname);
17892 : }
17893 :
17894 : /*
17895 : * dumpEventTrigger
17896 : * write the declaration of one user-defined event trigger
17897 : */
17898 : static void
17899 80 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
17900 : {
17901 80 : DumpOptions *dopt = fout->dopt;
17902 : PQExpBuffer query;
17903 : PQExpBuffer delqry;
17904 : char *qevtname;
17905 :
17906 : /* Do nothing in data-only dump */
17907 80 : if (dopt->dataOnly)
17908 6 : return;
17909 :
17910 74 : query = createPQExpBuffer();
17911 74 : delqry = createPQExpBuffer();
17912 :
17913 74 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
17914 :
17915 74 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
17916 74 : appendPQExpBufferStr(query, qevtname);
17917 74 : appendPQExpBufferStr(query, " ON ");
17918 74 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
17919 :
17920 74 : if (strcmp("", evtinfo->evttags) != 0)
17921 : {
17922 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
17923 10 : appendPQExpBufferStr(query, evtinfo->evttags);
17924 10 : appendPQExpBufferChar(query, ')');
17925 : }
17926 :
17927 74 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
17928 74 : appendPQExpBufferStr(query, evtinfo->evtfname);
17929 74 : appendPQExpBufferStr(query, "();\n");
17930 :
17931 74 : if (evtinfo->evtenabled != 'O')
17932 : {
17933 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
17934 : qevtname);
17935 0 : switch (evtinfo->evtenabled)
17936 : {
17937 0 : case 'D':
17938 0 : appendPQExpBufferStr(query, "DISABLE");
17939 0 : break;
17940 0 : case 'A':
17941 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
17942 0 : break;
17943 0 : case 'R':
17944 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
17945 0 : break;
17946 0 : default:
17947 0 : appendPQExpBufferStr(query, "ENABLE");
17948 0 : break;
17949 : }
17950 0 : appendPQExpBufferStr(query, ";\n");
17951 : }
17952 :
17953 74 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
17954 : qevtname);
17955 :
17956 74 : if (dopt->binary_upgrade)
17957 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
17958 : "EVENT TRIGGER", qevtname, NULL);
17959 :
17960 74 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17961 74 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
17962 74 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
17963 : .owner = evtinfo->evtowner,
17964 : .description = "EVENT TRIGGER",
17965 : .section = SECTION_POST_DATA,
17966 : .createStmt = query->data,
17967 : .dropStmt = delqry->data));
17968 :
17969 74 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17970 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
17971 : NULL, evtinfo->evtowner,
17972 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
17973 :
17974 74 : destroyPQExpBuffer(query);
17975 74 : destroyPQExpBuffer(delqry);
17976 74 : free(qevtname);
17977 : }
17978 :
17979 : /*
17980 : * dumpRule
17981 : * Dump a rule
17982 : */
17983 : static void
17984 1820 : dumpRule(Archive *fout, const RuleInfo *rinfo)
17985 : {
17986 1820 : DumpOptions *dopt = fout->dopt;
17987 1820 : TableInfo *tbinfo = rinfo->ruletable;
17988 : bool is_view;
17989 : PQExpBuffer query;
17990 : PQExpBuffer cmd;
17991 : PQExpBuffer delcmd;
17992 : PQExpBuffer ruleprefix;
17993 : char *qtabname;
17994 : PGresult *res;
17995 : char *tag;
17996 :
17997 : /* Do nothing in data-only dump */
17998 1820 : if (dopt->dataOnly)
17999 60 : return;
18000 :
18001 : /*
18002 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
18003 : * we do not want to dump it as a separate object.
18004 : */
18005 1760 : if (!rinfo->separate)
18006 1346 : return;
18007 :
18008 : /*
18009 : * If it's an ON SELECT rule, we want to print it as a view definition,
18010 : * instead of a rule.
18011 : */
18012 414 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
18013 :
18014 414 : query = createPQExpBuffer();
18015 414 : cmd = createPQExpBuffer();
18016 414 : delcmd = createPQExpBuffer();
18017 414 : ruleprefix = createPQExpBuffer();
18018 :
18019 414 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18020 :
18021 414 : if (is_view)
18022 : {
18023 : PQExpBuffer result;
18024 :
18025 : /*
18026 : * We need OR REPLACE here because we'll be replacing a dummy view.
18027 : * Otherwise this should look largely like the regular view dump code.
18028 : */
18029 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
18030 20 : fmtQualifiedDumpable(tbinfo));
18031 20 : if (nonemptyReloptions(tbinfo->reloptions))
18032 : {
18033 0 : appendPQExpBufferStr(cmd, " WITH (");
18034 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
18035 0 : appendPQExpBufferChar(cmd, ')');
18036 : }
18037 20 : result = createViewAsClause(fout, tbinfo);
18038 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
18039 20 : destroyPQExpBuffer(result);
18040 20 : if (tbinfo->checkoption != NULL)
18041 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
18042 : tbinfo->checkoption);
18043 20 : appendPQExpBufferStr(cmd, ";\n");
18044 : }
18045 : else
18046 : {
18047 : /* In the rule case, just print pg_get_ruledef's result verbatim */
18048 394 : appendPQExpBuffer(query,
18049 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
18050 : rinfo->dobj.catId.oid);
18051 :
18052 394 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18053 :
18054 394 : if (PQntuples(res) != 1)
18055 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
18056 : rinfo->dobj.name, tbinfo->dobj.name);
18057 :
18058 394 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
18059 :
18060 394 : PQclear(res);
18061 : }
18062 :
18063 : /*
18064 : * Add the command to alter the rules replication firing semantics if it
18065 : * differs from the default.
18066 : */
18067 414 : if (rinfo->ev_enabled != 'O')
18068 : {
18069 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
18070 30 : switch (rinfo->ev_enabled)
18071 : {
18072 0 : case 'A':
18073 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
18074 0 : fmtId(rinfo->dobj.name));
18075 0 : break;
18076 0 : case 'R':
18077 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
18078 0 : fmtId(rinfo->dobj.name));
18079 0 : break;
18080 30 : case 'D':
18081 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
18082 30 : fmtId(rinfo->dobj.name));
18083 30 : break;
18084 : }
18085 384 : }
18086 :
18087 414 : if (is_view)
18088 : {
18089 : /*
18090 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
18091 : * REPLACE VIEW to replace the rule with something with minimal
18092 : * dependencies.
18093 : */
18094 : PQExpBuffer result;
18095 :
18096 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
18097 20 : fmtQualifiedDumpable(tbinfo));
18098 20 : result = createDummyViewAsClause(fout, tbinfo);
18099 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
18100 20 : destroyPQExpBuffer(result);
18101 : }
18102 : else
18103 : {
18104 394 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
18105 394 : fmtId(rinfo->dobj.name));
18106 394 : appendPQExpBuffer(delcmd, "ON %s;\n",
18107 394 : fmtQualifiedDumpable(tbinfo));
18108 : }
18109 :
18110 414 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
18111 414 : fmtId(rinfo->dobj.name));
18112 :
18113 414 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
18114 :
18115 414 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18116 414 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
18117 414 : ARCHIVE_OPTS(.tag = tag,
18118 : .namespace = tbinfo->dobj.namespace->dobj.name,
18119 : .owner = tbinfo->rolname,
18120 : .description = "RULE",
18121 : .section = SECTION_POST_DATA,
18122 : .createStmt = cmd->data,
18123 : .dropStmt = delcmd->data));
18124 :
18125 : /* Dump rule comments */
18126 414 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18127 0 : dumpComment(fout, ruleprefix->data, qtabname,
18128 0 : tbinfo->dobj.namespace->dobj.name,
18129 : tbinfo->rolname,
18130 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
18131 :
18132 414 : free(tag);
18133 414 : destroyPQExpBuffer(query);
18134 414 : destroyPQExpBuffer(cmd);
18135 414 : destroyPQExpBuffer(delcmd);
18136 414 : destroyPQExpBuffer(ruleprefix);
18137 414 : free(qtabname);
18138 : }
18139 :
18140 : /*
18141 : * getExtensionMembership --- obtain extension membership data
18142 : *
18143 : * We need to identify objects that are extension members as soon as they're
18144 : * loaded, so that we can correctly determine whether they need to be dumped.
18145 : * Generally speaking, extension member objects will get marked as *not* to
18146 : * be dumped, as they will be recreated by the single CREATE EXTENSION
18147 : * command. However, in binary upgrade mode we still need to dump the members
18148 : * individually.
18149 : */
18150 : void
18151 306 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18152 : int numExtensions)
18153 : {
18154 : PQExpBuffer query;
18155 : PGresult *res;
18156 : int ntups,
18157 : i;
18158 : int i_classid,
18159 : i_objid,
18160 : i_refobjid;
18161 : ExtensionInfo *ext;
18162 :
18163 : /* Nothing to do if no extensions */
18164 306 : if (numExtensions == 0)
18165 0 : return;
18166 :
18167 306 : query = createPQExpBuffer();
18168 :
18169 : /* refclassid constraint is redundant but may speed the search */
18170 306 : appendPQExpBufferStr(query, "SELECT "
18171 : "classid, objid, refobjid "
18172 : "FROM pg_depend "
18173 : "WHERE refclassid = 'pg_extension'::regclass "
18174 : "AND deptype = 'e' "
18175 : "ORDER BY 3");
18176 :
18177 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18178 :
18179 306 : ntups = PQntuples(res);
18180 :
18181 306 : i_classid = PQfnumber(res, "classid");
18182 306 : i_objid = PQfnumber(res, "objid");
18183 306 : i_refobjid = PQfnumber(res, "refobjid");
18184 :
18185 : /*
18186 : * Since we ordered the SELECT by referenced ID, we can expect that
18187 : * multiple entries for the same extension will appear together; this
18188 : * saves on searches.
18189 : */
18190 306 : ext = NULL;
18191 :
18192 2730 : for (i = 0; i < ntups; i++)
18193 : {
18194 : CatalogId objId;
18195 : Oid extId;
18196 :
18197 2424 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18198 2424 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18199 2424 : extId = atooid(PQgetvalue(res, i, i_refobjid));
18200 :
18201 2424 : if (ext == NULL ||
18202 2118 : ext->dobj.catId.oid != extId)
18203 356 : ext = findExtensionByOid(extId);
18204 :
18205 2424 : if (ext == NULL)
18206 : {
18207 : /* shouldn't happen */
18208 0 : pg_log_warning("could not find referenced extension %u", extId);
18209 0 : continue;
18210 : }
18211 :
18212 2424 : recordExtensionMembership(objId, ext);
18213 : }
18214 :
18215 306 : PQclear(res);
18216 :
18217 306 : destroyPQExpBuffer(query);
18218 : }
18219 :
18220 : /*
18221 : * processExtensionTables --- deal with extension configuration tables
18222 : *
18223 : * There are two parts to this process:
18224 : *
18225 : * 1. Identify and create dump records for extension configuration tables.
18226 : *
18227 : * Extensions can mark tables as "configuration", which means that the user
18228 : * is able and expected to modify those tables after the extension has been
18229 : * loaded. For these tables, we dump out only the data- the structure is
18230 : * expected to be handled at CREATE EXTENSION time, including any indexes or
18231 : * foreign keys, which brings us to-
18232 : *
18233 : * 2. Record FK dependencies between configuration tables.
18234 : *
18235 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
18236 : * the data is loaded, we have to work out what the best order for reloading
18237 : * the data is, to avoid FK violations when the tables are restored. This is
18238 : * not perfect- we can't handle circular dependencies and if any exist they
18239 : * will cause an invalid dump to be produced (though at least all of the data
18240 : * is included for a user to manually restore). This is currently documented
18241 : * but perhaps we can provide a better solution in the future.
18242 : */
18243 : void
18244 304 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
18245 : int numExtensions)
18246 : {
18247 304 : DumpOptions *dopt = fout->dopt;
18248 : PQExpBuffer query;
18249 : PGresult *res;
18250 : int ntups,
18251 : i;
18252 : int i_conrelid,
18253 : i_confrelid;
18254 :
18255 : /* Nothing to do if no extensions */
18256 304 : if (numExtensions == 0)
18257 0 : return;
18258 :
18259 : /*
18260 : * Identify extension configuration tables and create TableDataInfo
18261 : * objects for them, ensuring their data will be dumped even though the
18262 : * tables themselves won't be.
18263 : *
18264 : * Note that we create TableDataInfo objects even in schemaOnly mode, ie,
18265 : * user data in a configuration table is treated like schema data. This
18266 : * seems appropriate since system data in a config table would get
18267 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
18268 : * list of extensions to be included, none of its data is dumped.
18269 : */
18270 658 : for (i = 0; i < numExtensions; i++)
18271 : {
18272 354 : ExtensionInfo *curext = &(extinfo[i]);
18273 354 : char *extconfig = curext->extconfig;
18274 354 : char *extcondition = curext->extcondition;
18275 354 : char **extconfigarray = NULL;
18276 354 : char **extconditionarray = NULL;
18277 354 : int nconfigitems = 0;
18278 354 : int nconditionitems = 0;
18279 :
18280 : /*
18281 : * Check if this extension is listed as to include in the dump. If
18282 : * not, any table data associated with it is discarded.
18283 : */
18284 354 : if (extension_include_oids.head != NULL &&
18285 16 : !simple_oid_list_member(&extension_include_oids,
18286 : curext->dobj.catId.oid))
18287 12 : continue;
18288 :
18289 : /*
18290 : * Check if this extension is listed as to exclude in the dump. If
18291 : * yes, any table data associated with it is discarded.
18292 : */
18293 354 : if (extension_exclude_oids.head != NULL &&
18294 8 : simple_oid_list_member(&extension_exclude_oids,
18295 : curext->dobj.catId.oid))
18296 4 : continue;
18297 :
18298 342 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
18299 : {
18300 : int j;
18301 :
18302 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
18303 0 : pg_fatal("could not parse %s array", "extconfig");
18304 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
18305 0 : pg_fatal("could not parse %s array", "extcondition");
18306 40 : if (nconfigitems != nconditionitems)
18307 0 : pg_fatal("mismatched number of configurations and conditions for extension");
18308 :
18309 120 : for (j = 0; j < nconfigitems; j++)
18310 : {
18311 : TableInfo *configtbl;
18312 80 : Oid configtbloid = atooid(extconfigarray[j]);
18313 80 : bool dumpobj =
18314 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
18315 :
18316 80 : configtbl = findTableByOid(configtbloid);
18317 80 : if (configtbl == NULL)
18318 0 : continue;
18319 :
18320 : /*
18321 : * Tables of not-to-be-dumped extensions shouldn't be dumped
18322 : * unless the table or its schema is explicitly included
18323 : */
18324 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
18325 : {
18326 : /* check table explicitly requested */
18327 4 : if (table_include_oids.head != NULL &&
18328 0 : simple_oid_list_member(&table_include_oids,
18329 : configtbloid))
18330 0 : dumpobj = true;
18331 :
18332 : /* check table's schema explicitly requested */
18333 4 : if (configtbl->dobj.namespace->dobj.dump &
18334 : DUMP_COMPONENT_DATA)
18335 4 : dumpobj = true;
18336 : }
18337 :
18338 : /* check table excluded by an exclusion switch */
18339 88 : if (table_exclude_oids.head != NULL &&
18340 8 : simple_oid_list_member(&table_exclude_oids,
18341 : configtbloid))
18342 2 : dumpobj = false;
18343 :
18344 : /* check schema excluded by an exclusion switch */
18345 80 : if (simple_oid_list_member(&schema_exclude_oids,
18346 80 : configtbl->dobj.namespace->dobj.catId.oid))
18347 0 : dumpobj = false;
18348 :
18349 80 : if (dumpobj)
18350 : {
18351 78 : makeTableDataInfo(dopt, configtbl);
18352 78 : if (configtbl->dataObj != NULL)
18353 : {
18354 78 : if (strlen(extconditionarray[j]) > 0)
18355 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
18356 : }
18357 : }
18358 : }
18359 : }
18360 342 : if (extconfigarray)
18361 40 : free(extconfigarray);
18362 342 : if (extconditionarray)
18363 40 : free(extconditionarray);
18364 : }
18365 :
18366 : /*
18367 : * Now that all the TableDataInfo objects have been created for all the
18368 : * extensions, check their FK dependencies and register them to try and
18369 : * dump the data out in an order that they can be restored in.
18370 : *
18371 : * Note that this is not a problem for user tables as their FKs are
18372 : * recreated after the data has been loaded.
18373 : */
18374 :
18375 304 : query = createPQExpBuffer();
18376 :
18377 304 : printfPQExpBuffer(query,
18378 : "SELECT conrelid, confrelid "
18379 : "FROM pg_constraint "
18380 : "JOIN pg_depend ON (objid = confrelid) "
18381 : "WHERE contype = 'f' "
18382 : "AND refclassid = 'pg_extension'::regclass "
18383 : "AND classid = 'pg_class'::regclass;");
18384 :
18385 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18386 304 : ntups = PQntuples(res);
18387 :
18388 304 : i_conrelid = PQfnumber(res, "conrelid");
18389 304 : i_confrelid = PQfnumber(res, "confrelid");
18390 :
18391 : /* Now get the dependencies and register them */
18392 304 : for (i = 0; i < ntups; i++)
18393 : {
18394 : Oid conrelid,
18395 : confrelid;
18396 : TableInfo *reftable,
18397 : *contable;
18398 :
18399 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
18400 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
18401 0 : contable = findTableByOid(conrelid);
18402 0 : reftable = findTableByOid(confrelid);
18403 :
18404 0 : if (reftable == NULL ||
18405 0 : reftable->dataObj == NULL ||
18406 0 : contable == NULL ||
18407 0 : contable->dataObj == NULL)
18408 0 : continue;
18409 :
18410 : /*
18411 : * Make referencing TABLE_DATA object depend on the referenced table's
18412 : * TABLE_DATA object.
18413 : */
18414 0 : addObjectDependency(&contable->dataObj->dobj,
18415 0 : reftable->dataObj->dobj.dumpId);
18416 : }
18417 304 : PQclear(res);
18418 304 : destroyPQExpBuffer(query);
18419 : }
18420 :
18421 : /*
18422 : * getDependencies --- obtain available dependency data
18423 : */
18424 : static void
18425 304 : getDependencies(Archive *fout)
18426 : {
18427 : PQExpBuffer query;
18428 : PGresult *res;
18429 : int ntups,
18430 : i;
18431 : int i_classid,
18432 : i_objid,
18433 : i_refclassid,
18434 : i_refobjid,
18435 : i_deptype;
18436 : DumpableObject *dobj,
18437 : *refdobj;
18438 :
18439 304 : pg_log_info("reading dependency data");
18440 :
18441 304 : query = createPQExpBuffer();
18442 :
18443 : /*
18444 : * Messy query to collect the dependency data we need. Note that we
18445 : * ignore the sub-object column, so that dependencies of or on a column
18446 : * look the same as dependencies of or on a whole table.
18447 : *
18448 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
18449 : * already processed by getExtensionMembership.
18450 : */
18451 304 : appendPQExpBufferStr(query, "SELECT "
18452 : "classid, objid, refclassid, refobjid, deptype "
18453 : "FROM pg_depend "
18454 : "WHERE deptype != 'p' AND deptype != 'e'\n");
18455 :
18456 : /*
18457 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
18458 : * have to translate their dependencies into dependencies of their parent
18459 : * opfamily. Ignore internal dependencies though, as those will point to
18460 : * their parent opclass, which we needn't consider here (and if we did,
18461 : * it'd just result in circular dependencies). Also, "loose" opfamily
18462 : * entries will have dependencies on their parent opfamily, which we
18463 : * should drop since they'd likewise become useless self-dependencies.
18464 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
18465 : */
18466 304 : appendPQExpBufferStr(query, "UNION ALL\n"
18467 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
18468 : "FROM pg_depend d, pg_amop o "
18469 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18470 : "classid = 'pg_amop'::regclass AND objid = o.oid "
18471 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
18472 :
18473 : /* Likewise for pg_amproc entries */
18474 304 : appendPQExpBufferStr(query, "UNION ALL\n"
18475 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
18476 : "FROM pg_depend d, pg_amproc p "
18477 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18478 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
18479 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
18480 :
18481 : /* Sort the output for efficiency below */
18482 304 : appendPQExpBufferStr(query, "ORDER BY 1,2");
18483 :
18484 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18485 :
18486 304 : ntups = PQntuples(res);
18487 :
18488 304 : i_classid = PQfnumber(res, "classid");
18489 304 : i_objid = PQfnumber(res, "objid");
18490 304 : i_refclassid = PQfnumber(res, "refclassid");
18491 304 : i_refobjid = PQfnumber(res, "refobjid");
18492 304 : i_deptype = PQfnumber(res, "deptype");
18493 :
18494 : /*
18495 : * Since we ordered the SELECT by referencing ID, we can expect that
18496 : * multiple entries for the same object will appear together; this saves
18497 : * on searches.
18498 : */
18499 304 : dobj = NULL;
18500 :
18501 633980 : for (i = 0; i < ntups; i++)
18502 : {
18503 : CatalogId objId;
18504 : CatalogId refobjId;
18505 : char deptype;
18506 :
18507 633676 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18508 633676 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18509 633676 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
18510 633676 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
18511 633676 : deptype = *(PQgetvalue(res, i, i_deptype));
18512 :
18513 633676 : if (dobj == NULL ||
18514 600776 : dobj->catId.tableoid != objId.tableoid ||
18515 597200 : dobj->catId.oid != objId.oid)
18516 269580 : dobj = findObjectByCatalogId(objId);
18517 :
18518 : /*
18519 : * Failure to find objects mentioned in pg_depend is not unexpected,
18520 : * since for example we don't collect info about TOAST tables.
18521 : */
18522 633676 : if (dobj == NULL)
18523 : {
18524 : #ifdef NOT_USED
18525 : pg_log_warning("no referencing object %u %u",
18526 : objId.tableoid, objId.oid);
18527 : #endif
18528 34098 : continue;
18529 : }
18530 :
18531 601064 : refdobj = findObjectByCatalogId(refobjId);
18532 :
18533 601064 : if (refdobj == NULL)
18534 : {
18535 : #ifdef NOT_USED
18536 : pg_log_warning("no referenced object %u %u",
18537 : refobjId.tableoid, refobjId.oid);
18538 : #endif
18539 1486 : continue;
18540 : }
18541 :
18542 : /*
18543 : * For 'x' dependencies, mark the object for later; we still add the
18544 : * normal dependency, for possible ordering purposes. Currently
18545 : * pg_dump_sort.c knows to put extensions ahead of all object types
18546 : * that could possibly depend on them, but this is safer.
18547 : */
18548 599578 : if (deptype == 'x')
18549 88 : dobj->depends_on_ext = true;
18550 :
18551 : /*
18552 : * Ordinarily, table rowtypes have implicit dependencies on their
18553 : * tables. However, for a composite type the implicit dependency goes
18554 : * the other way in pg_depend; which is the right thing for DROP but
18555 : * it doesn't produce the dependency ordering we need. So in that one
18556 : * case, we reverse the direction of the dependency.
18557 : */
18558 599578 : if (deptype == 'i' &&
18559 164874 : dobj->objType == DO_TABLE &&
18560 1738 : refdobj->objType == DO_TYPE)
18561 296 : addObjectDependency(refdobj, dobj->dumpId);
18562 : else
18563 : /* normal case */
18564 599282 : addObjectDependency(dobj, refdobj->dumpId);
18565 : }
18566 :
18567 304 : PQclear(res);
18568 :
18569 304 : destroyPQExpBuffer(query);
18570 304 : }
18571 :
18572 :
18573 : /*
18574 : * createBoundaryObjects - create dummy DumpableObjects to represent
18575 : * dump section boundaries.
18576 : */
18577 : static DumpableObject *
18578 304 : createBoundaryObjects(void)
18579 : {
18580 : DumpableObject *dobjs;
18581 :
18582 304 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
18583 :
18584 304 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
18585 304 : dobjs[0].catId = nilCatalogId;
18586 304 : AssignDumpId(dobjs + 0);
18587 304 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
18588 :
18589 304 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
18590 304 : dobjs[1].catId = nilCatalogId;
18591 304 : AssignDumpId(dobjs + 1);
18592 304 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
18593 :
18594 304 : return dobjs;
18595 : }
18596 :
18597 : /*
18598 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
18599 : * section boundaries.
18600 : */
18601 : static void
18602 304 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
18603 : DumpableObject *boundaryObjs)
18604 : {
18605 304 : DumpableObject *preDataBound = boundaryObjs + 0;
18606 304 : DumpableObject *postDataBound = boundaryObjs + 1;
18607 : int i;
18608 :
18609 1098928 : for (i = 0; i < numObjs; i++)
18610 : {
18611 1098624 : DumpableObject *dobj = dobjs[i];
18612 :
18613 : /*
18614 : * The classification of object types here must match the SECTION_xxx
18615 : * values assigned during subsequent ArchiveEntry calls!
18616 : */
18617 1098624 : switch (dobj->objType)
18618 : {
18619 1030580 : case DO_NAMESPACE:
18620 : case DO_EXTENSION:
18621 : case DO_TYPE:
18622 : case DO_SHELL_TYPE:
18623 : case DO_FUNC:
18624 : case DO_AGG:
18625 : case DO_OPERATOR:
18626 : case DO_ACCESS_METHOD:
18627 : case DO_OPCLASS:
18628 : case DO_OPFAMILY:
18629 : case DO_COLLATION:
18630 : case DO_CONVERSION:
18631 : case DO_TABLE:
18632 : case DO_TABLE_ATTACH:
18633 : case DO_ATTRDEF:
18634 : case DO_PROCLANG:
18635 : case DO_CAST:
18636 : case DO_DUMMY_TYPE:
18637 : case DO_TSPARSER:
18638 : case DO_TSDICT:
18639 : case DO_TSTEMPLATE:
18640 : case DO_TSCONFIG:
18641 : case DO_FDW:
18642 : case DO_FOREIGN_SERVER:
18643 : case DO_TRANSFORM:
18644 : case DO_LARGE_OBJECT:
18645 : /* Pre-data objects: must come before the pre-data boundary */
18646 1030580 : addObjectDependency(preDataBound, dobj->dumpId);
18647 1030580 : break;
18648 7882 : case DO_TABLE_DATA:
18649 : case DO_SEQUENCE_SET:
18650 : case DO_LARGE_OBJECT_DATA:
18651 : /* Data objects: must come between the boundaries */
18652 7882 : addObjectDependency(dobj, preDataBound->dumpId);
18653 7882 : addObjectDependency(postDataBound, dobj->dumpId);
18654 7882 : break;
18655 9712 : case DO_INDEX:
18656 : case DO_INDEX_ATTACH:
18657 : case DO_STATSEXT:
18658 : case DO_REFRESH_MATVIEW:
18659 : case DO_TRIGGER:
18660 : case DO_EVENT_TRIGGER:
18661 : case DO_DEFAULT_ACL:
18662 : case DO_POLICY:
18663 : case DO_PUBLICATION:
18664 : case DO_PUBLICATION_REL:
18665 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
18666 : case DO_SUBSCRIPTION:
18667 : case DO_SUBSCRIPTION_REL:
18668 : /* Post-data objects: must come after the post-data boundary */
18669 9712 : addObjectDependency(dobj, postDataBound->dumpId);
18670 9712 : break;
18671 45880 : case DO_RULE:
18672 : /* Rules are post-data, but only if dumped separately */
18673 45880 : if (((RuleInfo *) dobj)->separate)
18674 998 : addObjectDependency(dobj, postDataBound->dumpId);
18675 45880 : break;
18676 3962 : case DO_CONSTRAINT:
18677 : case DO_FK_CONSTRAINT:
18678 : /* Constraints are post-data, but only if dumped separately */
18679 3962 : if (((ConstraintInfo *) dobj)->separate)
18680 2764 : addObjectDependency(dobj, postDataBound->dumpId);
18681 3962 : break;
18682 304 : case DO_PRE_DATA_BOUNDARY:
18683 : /* nothing to do */
18684 304 : break;
18685 304 : case DO_POST_DATA_BOUNDARY:
18686 : /* must come after the pre-data boundary */
18687 304 : addObjectDependency(dobj, preDataBound->dumpId);
18688 304 : break;
18689 : }
18690 1098624 : }
18691 304 : }
18692 :
18693 :
18694 : /*
18695 : * BuildArchiveDependencies - create dependency data for archive TOC entries
18696 : *
18697 : * The raw dependency data obtained by getDependencies() is not terribly
18698 : * useful in an archive dump, because in many cases there are dependency
18699 : * chains linking through objects that don't appear explicitly in the dump.
18700 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
18701 : * will depend on other objects --- but the rule will not appear as a separate
18702 : * object in the dump. We need to adjust the view's dependencies to include
18703 : * whatever the rule depends on that is included in the dump.
18704 : *
18705 : * Just to make things more complicated, there are also "special" dependencies
18706 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
18707 : * not rearrange because pg_restore knows that TABLE DATA only depends on
18708 : * its table. In these cases we must leave the dependencies strictly as-is
18709 : * even if they refer to not-to-be-dumped objects.
18710 : *
18711 : * To handle this, the convention is that "special" dependencies are created
18712 : * during ArchiveEntry calls, and an archive TOC item that has any such
18713 : * entries will not be touched here. Otherwise, we recursively search the
18714 : * DumpableObject data structures to build the correct dependencies for each
18715 : * archive TOC item.
18716 : */
18717 : static void
18718 62 : BuildArchiveDependencies(Archive *fout)
18719 : {
18720 62 : ArchiveHandle *AH = (ArchiveHandle *) fout;
18721 : TocEntry *te;
18722 :
18723 : /* Scan all TOC entries in the archive */
18724 9702 : for (te = AH->toc->next; te != AH->toc; te = te->next)
18725 : {
18726 : DumpableObject *dobj;
18727 : DumpId *dependencies;
18728 : int nDeps;
18729 : int allocDeps;
18730 :
18731 : /* No need to process entries that will not be dumped */
18732 9640 : if (te->reqs == 0)
18733 2974 : continue;
18734 : /* Ignore entries that already have "special" dependencies */
18735 9636 : if (te->nDeps > 0)
18736 2402 : continue;
18737 : /* Otherwise, look up the item's original DumpableObject, if any */
18738 7234 : dobj = findObjectByDumpId(te->dumpId);
18739 7234 : if (dobj == NULL)
18740 318 : continue;
18741 : /* No work if it has no dependencies */
18742 6916 : if (dobj->nDeps <= 0)
18743 250 : continue;
18744 : /* Set up work array */
18745 6666 : allocDeps = 64;
18746 6666 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
18747 6666 : nDeps = 0;
18748 : /* Recursively find all dumpable dependencies */
18749 6666 : findDumpableDependencies(AH, dobj,
18750 : &dependencies, &nDeps, &allocDeps);
18751 : /* And save 'em ... */
18752 6666 : if (nDeps > 0)
18753 : {
18754 5056 : dependencies = (DumpId *) pg_realloc(dependencies,
18755 : nDeps * sizeof(DumpId));
18756 5056 : te->dependencies = dependencies;
18757 5056 : te->nDeps = nDeps;
18758 : }
18759 : else
18760 1610 : free(dependencies);
18761 : }
18762 62 : }
18763 :
18764 : /* Recursive search subroutine for BuildArchiveDependencies */
18765 : static void
18766 16422 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
18767 : DumpId **dependencies, int *nDeps, int *allocDeps)
18768 : {
18769 : int i;
18770 :
18771 : /*
18772 : * Ignore section boundary objects: if we search through them, we'll
18773 : * report lots of bogus dependencies.
18774 : */
18775 16422 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
18776 16406 : dobj->objType == DO_POST_DATA_BOUNDARY)
18777 2890 : return;
18778 :
18779 34214 : for (i = 0; i < dobj->nDeps; i++)
18780 : {
18781 20682 : DumpId depid = dobj->dependencies[i];
18782 :
18783 20682 : if (TocIDRequired(AH, depid) != 0)
18784 : {
18785 : /* Object will be dumped, so just reference it as a dependency */
18786 10926 : if (*nDeps >= *allocDeps)
18787 : {
18788 0 : *allocDeps *= 2;
18789 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
18790 0 : *allocDeps * sizeof(DumpId));
18791 : }
18792 10926 : (*dependencies)[*nDeps] = depid;
18793 10926 : (*nDeps)++;
18794 : }
18795 : else
18796 : {
18797 : /*
18798 : * Object will not be dumped, so recursively consider its deps. We
18799 : * rely on the assumption that sortDumpableObjects already broke
18800 : * any dependency loops, else we might recurse infinitely.
18801 : */
18802 9756 : DumpableObject *otherdobj = findObjectByDumpId(depid);
18803 :
18804 9756 : if (otherdobj)
18805 9756 : findDumpableDependencies(AH, otherdobj,
18806 : dependencies, nDeps, allocDeps);
18807 : }
18808 : }
18809 : }
18810 :
18811 :
18812 : /*
18813 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
18814 : * given type OID.
18815 : *
18816 : * This does not guarantee to schema-qualify the output, so it should not
18817 : * be used to create the target object name for CREATE or ALTER commands.
18818 : *
18819 : * Note that the result is cached and must not be freed by the caller.
18820 : */
18821 : static const char *
18822 4548 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
18823 : {
18824 : TypeInfo *typeInfo;
18825 : char *result;
18826 : PQExpBuffer query;
18827 : PGresult *res;
18828 :
18829 4548 : if (oid == 0)
18830 : {
18831 0 : if ((opts & zeroAsStar) != 0)
18832 0 : return "*";
18833 0 : else if ((opts & zeroAsNone) != 0)
18834 0 : return "NONE";
18835 : }
18836 :
18837 : /* see if we have the result cached in the type's TypeInfo record */
18838 4548 : typeInfo = findTypeByOid(oid);
18839 4548 : if (typeInfo && typeInfo->ftypname)
18840 3624 : return typeInfo->ftypname;
18841 :
18842 924 : query = createPQExpBuffer();
18843 924 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
18844 : oid);
18845 :
18846 924 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18847 :
18848 : /* result of format_type is already quoted */
18849 924 : result = pg_strdup(PQgetvalue(res, 0, 0));
18850 :
18851 924 : PQclear(res);
18852 924 : destroyPQExpBuffer(query);
18853 :
18854 : /*
18855 : * Cache the result for re-use in later requests, if possible. If we
18856 : * don't have a TypeInfo for the type, the string will be leaked once the
18857 : * caller is done with it ... but that case really should not happen, so
18858 : * leaking if it does seems acceptable.
18859 : */
18860 924 : if (typeInfo)
18861 924 : typeInfo->ftypname = result;
18862 :
18863 924 : return result;
18864 : }
18865 :
18866 : /*
18867 : * Return a column list clause for the given relation.
18868 : *
18869 : * Special case: if there are no undropped columns in the relation, return
18870 : * "", not an invalid "()" column list.
18871 : */
18872 : static const char *
18873 13692 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
18874 : {
18875 13692 : int numatts = ti->numatts;
18876 13692 : char **attnames = ti->attnames;
18877 13692 : bool *attisdropped = ti->attisdropped;
18878 13692 : char *attgenerated = ti->attgenerated;
18879 : bool needComma;
18880 : int i;
18881 :
18882 13692 : appendPQExpBufferChar(buffer, '(');
18883 13692 : needComma = false;
18884 67408 : for (i = 0; i < numatts; i++)
18885 : {
18886 53716 : if (attisdropped[i])
18887 1116 : continue;
18888 52600 : if (attgenerated[i])
18889 1180 : continue;
18890 51420 : if (needComma)
18891 38160 : appendPQExpBufferStr(buffer, ", ");
18892 51420 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
18893 51420 : needComma = true;
18894 : }
18895 :
18896 13692 : if (!needComma)
18897 432 : return ""; /* no undropped columns */
18898 :
18899 13260 : appendPQExpBufferChar(buffer, ')');
18900 13260 : return buffer->data;
18901 : }
18902 :
18903 : /*
18904 : * Check if a reloptions array is nonempty.
18905 : */
18906 : static bool
18907 23002 : nonemptyReloptions(const char *reloptions)
18908 : {
18909 : /* Don't want to print it if it's just "{}" */
18910 23002 : return (reloptions != NULL && strlen(reloptions) > 2);
18911 : }
18912 :
18913 : /*
18914 : * Format a reloptions array and append it to the given buffer.
18915 : *
18916 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
18917 : */
18918 : static void
18919 408 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
18920 : const char *prefix, Archive *fout)
18921 : {
18922 : bool res;
18923 :
18924 408 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
18925 408 : fout->std_strings);
18926 408 : if (!res)
18927 0 : pg_log_warning("could not parse %s array", "reloptions");
18928 408 : }
18929 :
18930 : /*
18931 : * read_dump_filters - retrieve object identifier patterns from file
18932 : *
18933 : * Parse the specified filter file for include and exclude patterns, and add
18934 : * them to the relevant lists. If the filename is "-" then filters will be
18935 : * read from STDIN rather than a file.
18936 : */
18937 : static void
18938 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
18939 : {
18940 : FilterStateData fstate;
18941 : char *objname;
18942 : FilterCommandType comtype;
18943 : FilterObjectType objtype;
18944 :
18945 52 : filter_init(&fstate, filename, exit_nicely);
18946 :
18947 116 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
18948 : {
18949 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
18950 : {
18951 34 : switch (objtype)
18952 : {
18953 0 : case FILTER_OBJECT_TYPE_NONE:
18954 0 : break;
18955 0 : case FILTER_OBJECT_TYPE_DATABASE:
18956 : case FILTER_OBJECT_TYPE_FUNCTION:
18957 : case FILTER_OBJECT_TYPE_INDEX:
18958 : case FILTER_OBJECT_TYPE_TABLE_DATA:
18959 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
18960 : case FILTER_OBJECT_TYPE_TRIGGER:
18961 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
18962 : "include",
18963 : filter_object_type_name(objtype));
18964 0 : exit_nicely(1);
18965 : break; /* unreachable */
18966 :
18967 2 : case FILTER_OBJECT_TYPE_EXTENSION:
18968 2 : simple_string_list_append(&extension_include_patterns, objname);
18969 2 : break;
18970 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
18971 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
18972 2 : break;
18973 2 : case FILTER_OBJECT_TYPE_SCHEMA:
18974 2 : simple_string_list_append(&schema_include_patterns, objname);
18975 2 : dopt->include_everything = false;
18976 2 : break;
18977 26 : case FILTER_OBJECT_TYPE_TABLE:
18978 26 : simple_string_list_append(&table_include_patterns, objname);
18979 26 : dopt->include_everything = false;
18980 26 : break;
18981 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
18982 2 : simple_string_list_append(&table_include_patterns_and_children,
18983 : objname);
18984 2 : dopt->include_everything = false;
18985 2 : break;
18986 : }
18987 34 : }
18988 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
18989 : {
18990 18 : switch (objtype)
18991 : {
18992 0 : case FILTER_OBJECT_TYPE_NONE:
18993 0 : break;
18994 2 : case FILTER_OBJECT_TYPE_DATABASE:
18995 : case FILTER_OBJECT_TYPE_FUNCTION:
18996 : case FILTER_OBJECT_TYPE_INDEX:
18997 : case FILTER_OBJECT_TYPE_TRIGGER:
18998 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
18999 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19000 : "exclude",
19001 : filter_object_type_name(objtype));
19002 2 : exit_nicely(1);
19003 : break;
19004 :
19005 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19006 2 : simple_string_list_append(&extension_exclude_patterns, objname);
19007 2 : break;
19008 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19009 2 : simple_string_list_append(&tabledata_exclude_patterns,
19010 : objname);
19011 2 : break;
19012 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19013 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
19014 : objname);
19015 2 : break;
19016 4 : case FILTER_OBJECT_TYPE_SCHEMA:
19017 4 : simple_string_list_append(&schema_exclude_patterns, objname);
19018 4 : break;
19019 4 : case FILTER_OBJECT_TYPE_TABLE:
19020 4 : simple_string_list_append(&table_exclude_patterns, objname);
19021 4 : break;
19022 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19023 2 : simple_string_list_append(&table_exclude_patterns_and_children,
19024 : objname);
19025 2 : break;
19026 : }
19027 16 : }
19028 : else
19029 : {
19030 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
19031 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
19032 : }
19033 :
19034 64 : if (objname)
19035 50 : free(objname);
19036 : }
19037 :
19038 44 : filter_free(&fstate);
19039 44 : }
|