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 : * Maximum number of large objects to group into a single ArchiveEntry.
168 : * At some point we might want to make this user-controllable, but for now
169 : * a hard-wired setting will suffice.
170 : */
171 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
172 :
173 : /*
174 : * Macro for producing quoted, schema-qualified name of a dumpable object.
175 : */
176 : #define fmtQualifiedDumpable(obj) \
177 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
178 : (obj)->dobj.name)
179 :
180 : static void help(const char *progname);
181 : static void setup_connection(Archive *AH,
182 : const char *dumpencoding, const char *dumpsnapshot,
183 : char *use_role);
184 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
185 : static void expand_schema_name_patterns(Archive *fout,
186 : SimpleStringList *patterns,
187 : SimpleOidList *oids,
188 : bool strict_names);
189 : static void expand_extension_name_patterns(Archive *fout,
190 : SimpleStringList *patterns,
191 : SimpleOidList *oids,
192 : bool strict_names);
193 : static void expand_foreign_server_name_patterns(Archive *fout,
194 : SimpleStringList *patterns,
195 : SimpleOidList *oids);
196 : static void expand_table_name_patterns(Archive *fout,
197 : SimpleStringList *patterns,
198 : SimpleOidList *oids,
199 : bool strict_names,
200 : bool with_child_tables);
201 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
202 : const char *pattern);
203 :
204 : static NamespaceInfo *findNamespace(Oid nsoid);
205 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
206 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
207 : static const char *getRoleName(const char *roleoid_str);
208 : static void collectRoleNames(Archive *fout);
209 : static void getAdditionalACLs(Archive *fout);
210 : static void dumpCommentExtended(Archive *fout, const char *type,
211 : const char *name, const char *namespace,
212 : const char *owner, CatalogId catalogId,
213 : int subid, DumpId dumpId,
214 : const char *initdb_comment);
215 : static inline void dumpComment(Archive *fout, const char *type,
216 : const char *name, const char *namespace,
217 : const char *owner, CatalogId catalogId,
218 : int subid, DumpId dumpId);
219 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
220 : static void collectComments(Archive *fout);
221 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
222 : const char *namespace, const char *owner,
223 : CatalogId catalogId, int subid, DumpId dumpId);
224 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
225 : static void collectSecLabels(Archive *fout);
226 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
227 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
228 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
229 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
230 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
231 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
232 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
233 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
234 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
235 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
236 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
237 : PGresult *res);
238 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
239 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
240 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
241 : static void dumpCast(Archive *fout, const CastInfo *cast);
242 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
243 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
244 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
245 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
246 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
247 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
248 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
249 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
250 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
251 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
252 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
253 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
254 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
255 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
256 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
257 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
258 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
259 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
260 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
261 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
262 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
263 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
264 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
265 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
266 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
267 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
268 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
269 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
270 : static void dumpUserMappings(Archive *fout,
271 : const char *servername, const char *namespace,
272 : const char *owner, CatalogId catalogId, DumpId dumpId);
273 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
274 :
275 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
276 : const char *type, const char *name, const char *subname,
277 : const char *nspname, const char *tag, const char *owner,
278 : const DumpableAcl *dacl);
279 :
280 : static void getDependencies(Archive *fout);
281 : static void BuildArchiveDependencies(Archive *fout);
282 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
283 : DumpId **dependencies, int *nDeps, int *allocDeps);
284 :
285 : static DumpableObject *createBoundaryObjects(void);
286 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
287 : DumpableObject *boundaryObjs);
288 :
289 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
290 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
291 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
292 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
293 : static void buildMatViewRefreshDependencies(Archive *fout);
294 : static void getTableDataFKConstraints(void);
295 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
296 : bool is_agg);
297 : static char *format_function_signature(Archive *fout,
298 : const FuncInfo *finfo, bool honor_quotes);
299 : static char *convertRegProcReference(const char *proc);
300 : static char *getFormattedOperatorName(const char *oproid);
301 : static char *convertTSFunction(Archive *fout, Oid funcOid);
302 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
303 : static void getLOs(Archive *fout);
304 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
305 : static int dumpLOs(Archive *fout, const void *arg);
306 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
307 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
308 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
309 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
310 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
311 : static void dumpDatabase(Archive *fout);
312 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
313 : const char *dbname, Oid dboid);
314 : static void dumpEncoding(Archive *AH);
315 : static void dumpStdStrings(Archive *AH);
316 : static void dumpSearchPath(Archive *AH);
317 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
318 : PQExpBuffer upgrade_buffer,
319 : Oid pg_type_oid,
320 : bool force_array_type,
321 : bool include_multirange_type);
322 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
323 : PQExpBuffer upgrade_buffer,
324 : const TableInfo *tbinfo);
325 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
326 : PQExpBuffer upgrade_buffer,
327 : Oid pg_class_oid, bool is_index);
328 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
329 : const DumpableObject *dobj,
330 : const char *objtype,
331 : const char *objname,
332 : const char *objnamespace);
333 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
334 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
335 : static bool nonemptyReloptions(const char *reloptions);
336 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
337 : const char *prefix, Archive *fout);
338 : static char *get_synchronized_snapshot(Archive *fout);
339 : static void setupDumpWorker(Archive *AH);
340 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
341 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
342 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
343 :
344 :
345 : int
346 482 : main(int argc, char **argv)
347 : {
348 : int c;
349 482 : const char *filename = NULL;
350 482 : const char *format = "p";
351 : TableInfo *tblinfo;
352 : int numTables;
353 : DumpableObject **dobjs;
354 : int numObjs;
355 : DumpableObject *boundaryObjs;
356 : int i;
357 : int optindex;
358 : RestoreOptions *ropt;
359 : Archive *fout; /* the script file */
360 482 : bool g_verbose = false;
361 482 : const char *dumpencoding = NULL;
362 482 : const char *dumpsnapshot = NULL;
363 482 : char *use_role = NULL;
364 482 : int numWorkers = 1;
365 482 : int plainText = 0;
366 482 : ArchiveFormat archiveFormat = archUnknown;
367 : ArchiveMode archiveMode;
368 482 : pg_compress_specification compression_spec = {0};
369 482 : char *compression_detail = NULL;
370 482 : char *compression_algorithm_str = "none";
371 482 : char *error_detail = NULL;
372 482 : bool user_compression_defined = false;
373 482 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
374 :
375 : static DumpOptions dopt;
376 :
377 : static struct option long_options[] = {
378 : {"data-only", no_argument, NULL, 'a'},
379 : {"blobs", no_argument, NULL, 'b'},
380 : {"large-objects", no_argument, NULL, 'b'},
381 : {"no-blobs", no_argument, NULL, 'B'},
382 : {"no-large-objects", no_argument, NULL, 'B'},
383 : {"clean", no_argument, NULL, 'c'},
384 : {"create", no_argument, NULL, 'C'},
385 : {"dbname", required_argument, NULL, 'd'},
386 : {"extension", required_argument, NULL, 'e'},
387 : {"file", required_argument, NULL, 'f'},
388 : {"format", required_argument, NULL, 'F'},
389 : {"host", required_argument, NULL, 'h'},
390 : {"jobs", 1, NULL, 'j'},
391 : {"no-reconnect", no_argument, NULL, 'R'},
392 : {"no-owner", no_argument, NULL, 'O'},
393 : {"port", required_argument, NULL, 'p'},
394 : {"schema", required_argument, NULL, 'n'},
395 : {"exclude-schema", required_argument, NULL, 'N'},
396 : {"schema-only", no_argument, NULL, 's'},
397 : {"superuser", required_argument, NULL, 'S'},
398 : {"table", required_argument, NULL, 't'},
399 : {"exclude-table", required_argument, NULL, 'T'},
400 : {"no-password", no_argument, NULL, 'w'},
401 : {"password", no_argument, NULL, 'W'},
402 : {"username", required_argument, NULL, 'U'},
403 : {"verbose", no_argument, NULL, 'v'},
404 : {"no-privileges", no_argument, NULL, 'x'},
405 : {"no-acl", no_argument, NULL, 'x'},
406 : {"compress", required_argument, NULL, 'Z'},
407 : {"encoding", required_argument, NULL, 'E'},
408 : {"help", no_argument, NULL, '?'},
409 : {"version", no_argument, NULL, 'V'},
410 :
411 : /*
412 : * the following options don't have an equivalent short option letter
413 : */
414 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
415 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
416 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
417 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
418 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
419 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
420 : {"exclude-table-data", required_argument, NULL, 4},
421 : {"extra-float-digits", required_argument, NULL, 8},
422 : {"if-exists", no_argument, &dopt.if_exists, 1},
423 : {"inserts", no_argument, NULL, 9},
424 : {"lock-wait-timeout", required_argument, NULL, 2},
425 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
426 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
427 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
428 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
429 : {"role", required_argument, NULL, 3},
430 : {"section", required_argument, NULL, 5},
431 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
432 : {"snapshot", required_argument, NULL, 6},
433 : {"strict-names", no_argument, &strict_names, 1},
434 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
435 : {"no-comments", no_argument, &dopt.no_comments, 1},
436 : {"no-publications", no_argument, &dopt.no_publications, 1},
437 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
438 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
439 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
440 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
441 : {"no-sync", no_argument, NULL, 7},
442 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
443 : {"rows-per-insert", required_argument, NULL, 10},
444 : {"include-foreign-data", required_argument, NULL, 11},
445 : {"table-and-children", required_argument, NULL, 12},
446 : {"exclude-table-and-children", required_argument, NULL, 13},
447 : {"exclude-table-data-and-children", required_argument, NULL, 14},
448 : {"sync-method", required_argument, NULL, 15},
449 : {"filter", required_argument, NULL, 16},
450 : {"exclude-extension", required_argument, NULL, 17},
451 :
452 : {NULL, 0, NULL, 0}
453 : };
454 :
455 482 : pg_logging_init(argv[0]);
456 482 : pg_logging_set_level(PG_LOG_WARNING);
457 482 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
458 :
459 : /*
460 : * Initialize what we need for parallel execution, especially for thread
461 : * support on Windows.
462 : */
463 482 : init_parallel_dump_utils();
464 :
465 482 : progname = get_progname(argv[0]);
466 :
467 482 : if (argc > 1)
468 : {
469 482 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
470 : {
471 2 : help(progname);
472 2 : exit_nicely(0);
473 : }
474 480 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
475 : {
476 94 : puts("pg_dump (PostgreSQL) " PG_VERSION);
477 94 : exit_nicely(0);
478 : }
479 : }
480 :
481 386 : InitDumpOptions(&dopt);
482 :
483 1666 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxZ:",
484 : long_options, &optindex)) != -1)
485 : {
486 1296 : switch (c)
487 : {
488 16 : case 'a': /* Dump data only */
489 16 : dopt.dataOnly = true;
490 16 : break;
491 :
492 2 : case 'b': /* Dump LOs */
493 2 : dopt.outputLOs = true;
494 2 : break;
495 :
496 4 : case 'B': /* Don't dump LOs */
497 4 : dopt.dontOutputLOs = true;
498 4 : break;
499 :
500 12 : case 'c': /* clean (i.e., drop) schema prior to create */
501 12 : dopt.outputClean = 1;
502 12 : break;
503 :
504 58 : case 'C': /* Create DB */
505 58 : dopt.outputCreateDB = 1;
506 58 : break;
507 :
508 10 : case 'd': /* database name */
509 10 : dopt.cparams.dbname = pg_strdup(optarg);
510 10 : break;
511 :
512 8 : case 'e': /* include extension(s) */
513 8 : simple_string_list_append(&extension_include_patterns, optarg);
514 8 : dopt.include_everything = false;
515 8 : break;
516 :
517 4 : case 'E': /* Dump encoding */
518 4 : dumpencoding = pg_strdup(optarg);
519 4 : break;
520 :
521 302 : case 'f':
522 302 : filename = pg_strdup(optarg);
523 302 : break;
524 :
525 170 : case 'F':
526 170 : format = pg_strdup(optarg);
527 170 : break;
528 :
529 24 : case 'h': /* server host */
530 24 : dopt.cparams.pghost = pg_strdup(optarg);
531 24 : break;
532 :
533 22 : case 'j': /* number of dump jobs */
534 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
535 : PG_MAX_JOBS,
536 : &numWorkers))
537 2 : exit_nicely(1);
538 20 : break;
539 :
540 30 : case 'n': /* include schema(s) */
541 30 : simple_string_list_append(&schema_include_patterns, optarg);
542 30 : dopt.include_everything = false;
543 30 : break;
544 :
545 2 : case 'N': /* exclude schema(s) */
546 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
547 2 : break;
548 :
549 4 : case 'O': /* Don't reconnect to match owner */
550 4 : dopt.outputNoOwner = 1;
551 4 : break;
552 :
553 96 : case 'p': /* server port */
554 96 : dopt.cparams.pgport = pg_strdup(optarg);
555 96 : break;
556 :
557 4 : case 'R':
558 : /* no-op, still accepted for backwards compatibility */
559 4 : break;
560 :
561 36 : case 's': /* dump schema only */
562 36 : dopt.schemaOnly = true;
563 36 : break;
564 :
565 2 : case 'S': /* Username for superuser in plain text output */
566 2 : dopt.outputSuperuser = pg_strdup(optarg);
567 2 : break;
568 :
569 16 : case 't': /* include table(s) */
570 16 : simple_string_list_append(&table_include_patterns, optarg);
571 16 : dopt.include_everything = false;
572 16 : break;
573 :
574 8 : case 'T': /* exclude table(s) */
575 8 : simple_string_list_append(&table_exclude_patterns, optarg);
576 8 : break;
577 :
578 28 : case 'U':
579 28 : dopt.cparams.username = pg_strdup(optarg);
580 28 : break;
581 :
582 12 : case 'v': /* verbose */
583 12 : g_verbose = true;
584 12 : pg_logging_increase_verbosity();
585 12 : break;
586 :
587 2 : case 'w':
588 2 : dopt.cparams.promptPassword = TRI_NO;
589 2 : break;
590 :
591 0 : case 'W':
592 0 : dopt.cparams.promptPassword = TRI_YES;
593 0 : break;
594 :
595 4 : case 'x': /* skip ACL dump */
596 4 : dopt.aclsSkip = true;
597 4 : break;
598 :
599 24 : case 'Z': /* Compression */
600 24 : parse_compress_options(optarg, &compression_algorithm_str,
601 : &compression_detail);
602 24 : user_compression_defined = true;
603 24 : break;
604 :
605 98 : case 0:
606 : /* This covers the long options. */
607 98 : break;
608 :
609 4 : case 2: /* lock-wait-timeout */
610 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
611 4 : break;
612 :
613 6 : case 3: /* SET ROLE */
614 6 : use_role = pg_strdup(optarg);
615 6 : break;
616 :
617 2 : case 4: /* exclude table(s) data */
618 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
619 2 : break;
620 :
621 12 : case 5: /* section */
622 12 : set_dump_section(optarg, &dopt.dumpSections);
623 12 : break;
624 :
625 0 : case 6: /* snapshot */
626 0 : dumpsnapshot = pg_strdup(optarg);
627 0 : break;
628 :
629 194 : case 7: /* no-sync */
630 194 : dosync = false;
631 194 : break;
632 :
633 2 : case 8:
634 2 : have_extra_float_digits = true;
635 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
636 : &extra_float_digits))
637 2 : exit_nicely(1);
638 0 : break;
639 :
640 4 : case 9: /* inserts */
641 :
642 : /*
643 : * dump_inserts also stores --rows-per-insert, careful not to
644 : * overwrite that.
645 : */
646 4 : if (dopt.dump_inserts == 0)
647 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
648 4 : break;
649 :
650 4 : case 10: /* rows per insert */
651 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
652 : &dopt.dump_inserts))
653 2 : exit_nicely(1);
654 2 : break;
655 :
656 8 : case 11: /* include foreign data */
657 8 : simple_string_list_append(&foreign_servers_include_patterns,
658 : optarg);
659 8 : break;
660 :
661 2 : case 12: /* include table(s) and their children */
662 2 : simple_string_list_append(&table_include_patterns_and_children,
663 : optarg);
664 2 : dopt.include_everything = false;
665 2 : break;
666 :
667 2 : case 13: /* exclude table(s) and their children */
668 2 : simple_string_list_append(&table_exclude_patterns_and_children,
669 : optarg);
670 2 : break;
671 :
672 2 : case 14: /* exclude data of table(s) and children */
673 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
674 : optarg);
675 2 : break;
676 :
677 0 : case 15:
678 0 : if (!parse_sync_method(optarg, &sync_method))
679 0 : exit_nicely(1);
680 0 : break;
681 :
682 52 : case 16: /* read object filters from file */
683 52 : read_dump_filters(optarg, &dopt);
684 44 : break;
685 :
686 2 : case 17: /* exclude extension(s) */
687 2 : simple_string_list_append(&extension_exclude_patterns,
688 : optarg);
689 2 : break;
690 :
691 2 : default:
692 : /* getopt_long already emitted a complaint */
693 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
694 2 : exit_nicely(1);
695 : }
696 : }
697 :
698 : /*
699 : * Non-option argument specifies database name as long as it wasn't
700 : * already specified with -d / --dbname
701 : */
702 370 : if (optind < argc && dopt.cparams.dbname == NULL)
703 306 : dopt.cparams.dbname = argv[optind++];
704 :
705 : /* Complain if any arguments remain */
706 370 : if (optind < argc)
707 : {
708 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
709 : argv[optind]);
710 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
711 2 : exit_nicely(1);
712 : }
713 :
714 : /* --column-inserts implies --inserts */
715 368 : if (dopt.column_inserts && dopt.dump_inserts == 0)
716 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
717 :
718 : /*
719 : * Binary upgrade mode implies dumping sequence data even in schema-only
720 : * mode. This is not exposed as a separate option, but kept separate
721 : * internally for clarity.
722 : */
723 368 : if (dopt.binary_upgrade)
724 28 : dopt.sequence_data = 1;
725 :
726 368 : if (dopt.dataOnly && dopt.schemaOnly)
727 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
728 :
729 366 : if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
730 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
731 :
732 364 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
733 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
734 :
735 362 : if (dopt.dataOnly && dopt.outputClean)
736 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
737 :
738 360 : if (dopt.if_exists && !dopt.outputClean)
739 2 : pg_fatal("option --if-exists requires option -c/--clean");
740 :
741 : /*
742 : * --inserts are already implied above if --column-inserts or
743 : * --rows-per-insert were specified.
744 : */
745 358 : if (dopt.do_nothing && dopt.dump_inserts == 0)
746 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
747 :
748 : /* Identify archive format to emit */
749 356 : archiveFormat = parseArchiveFormat(format, &archiveMode);
750 :
751 : /* archiveFormat specific setup */
752 354 : if (archiveFormat == archNull)
753 290 : plainText = 1;
754 :
755 : /*
756 : * Custom and directory formats are compressed by default with gzip when
757 : * available, not the others. If gzip is not available, no compression is
758 : * done by default.
759 : */
760 354 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
761 58 : !user_compression_defined)
762 : {
763 : #ifdef HAVE_LIBZ
764 48 : compression_algorithm_str = "gzip";
765 : #else
766 : compression_algorithm_str = "none";
767 : #endif
768 : }
769 :
770 : /*
771 : * Compression options
772 : */
773 354 : if (!parse_compress_algorithm(compression_algorithm_str,
774 : &compression_algorithm))
775 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
776 : compression_algorithm_str);
777 :
778 352 : parse_compress_specification(compression_algorithm, compression_detail,
779 : &compression_spec);
780 352 : error_detail = validate_compress_specification(&compression_spec);
781 352 : if (error_detail != NULL)
782 6 : pg_fatal("invalid compression specification: %s",
783 : error_detail);
784 :
785 346 : error_detail = supports_compression(compression_spec);
786 346 : if (error_detail != NULL)
787 0 : pg_fatal("%s", error_detail);
788 :
789 : /*
790 : * Disable support for zstd workers for now - these are based on
791 : * threading, and it's unclear how it interacts with parallel dumps on
792 : * platforms where that relies on threads too (e.g. Windows).
793 : */
794 346 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
795 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
796 : "workers");
797 :
798 : /*
799 : * If emitting an archive format, we always want to emit a DATABASE item,
800 : * in case --create is specified at pg_restore time.
801 : */
802 346 : if (!plainText)
803 64 : dopt.outputCreateDB = 1;
804 :
805 : /* Parallel backup only in the directory archive format so far */
806 346 : if (archiveFormat != archDirectory && numWorkers > 1)
807 2 : pg_fatal("parallel backup only supported by the directory format");
808 :
809 : /* Open the output file */
810 344 : fout = CreateArchive(filename, archiveFormat, compression_spec,
811 : dosync, archiveMode, setupDumpWorker, sync_method);
812 :
813 : /* Make dump options accessible right away */
814 342 : SetArchiveOptions(fout, &dopt, NULL);
815 :
816 : /* Register the cleanup hook */
817 342 : on_exit_close_archive(fout);
818 :
819 : /* Let the archiver know how noisy to be */
820 342 : fout->verbose = g_verbose;
821 :
822 :
823 : /*
824 : * We allow the server to be back to 9.2, and up to any minor release of
825 : * our own major version. (See also version check in pg_dumpall.c.)
826 : */
827 342 : fout->minRemoteVersion = 90200;
828 342 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
829 :
830 342 : fout->numWorkers = numWorkers;
831 :
832 : /*
833 : * Open the database using the Archiver, so it knows about it. Errors mean
834 : * death.
835 : */
836 342 : ConnectDatabase(fout, &dopt.cparams, false);
837 338 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
838 :
839 : /*
840 : * On hot standbys, never try to dump unlogged table data, since it will
841 : * just throw an error.
842 : */
843 338 : if (fout->isStandby)
844 6 : dopt.no_unlogged_table_data = true;
845 :
846 : /*
847 : * Find the last built-in OID, if needed (prior to 8.1)
848 : *
849 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
850 : */
851 338 : g_last_builtin_oid = FirstNormalObjectId - 1;
852 :
853 338 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
854 :
855 : /* Expand schema selection patterns into OID lists */
856 338 : if (schema_include_patterns.head != NULL)
857 : {
858 32 : expand_schema_name_patterns(fout, &schema_include_patterns,
859 : &schema_include_oids,
860 : strict_names);
861 20 : if (schema_include_oids.head == NULL)
862 2 : pg_fatal("no matching schemas were found");
863 : }
864 324 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
865 : &schema_exclude_oids,
866 : false);
867 : /* non-matching exclusion patterns aren't an error */
868 :
869 : /* Expand table selection patterns into OID lists */
870 324 : expand_table_name_patterns(fout, &table_include_patterns,
871 : &table_include_oids,
872 : strict_names, false);
873 314 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
874 : &table_include_oids,
875 : strict_names, true);
876 314 : if ((table_include_patterns.head != NULL ||
877 292 : table_include_patterns_and_children.head != NULL) &&
878 26 : table_include_oids.head == NULL)
879 4 : pg_fatal("no matching tables were found");
880 :
881 310 : expand_table_name_patterns(fout, &table_exclude_patterns,
882 : &table_exclude_oids,
883 : false, false);
884 310 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
885 : &table_exclude_oids,
886 : false, true);
887 :
888 310 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
889 : &tabledata_exclude_oids,
890 : false, false);
891 310 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
892 : &tabledata_exclude_oids,
893 : false, true);
894 :
895 310 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
896 : &foreign_servers_include_oids);
897 :
898 : /* non-matching exclusion patterns aren't an error */
899 :
900 : /* Expand extension selection patterns into OID lists */
901 308 : if (extension_include_patterns.head != NULL)
902 : {
903 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
904 : &extension_include_oids,
905 : strict_names);
906 10 : if (extension_include_oids.head == NULL)
907 2 : pg_fatal("no matching extensions were found");
908 : }
909 306 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
910 : &extension_exclude_oids,
911 : false);
912 : /* non-matching exclusion patterns aren't an error */
913 :
914 : /*
915 : * Dumping LOs is the default for dumps where an inclusion switch is not
916 : * used (an "include everything" dump). -B can be used to exclude LOs
917 : * from those dumps. -b can be used to include LOs even when an inclusion
918 : * switch is used.
919 : *
920 : * -s means "schema only" and LOs are data, not schema, so we never
921 : * include LOs when -s is used.
922 : */
923 306 : if (dopt.include_everything && !dopt.schemaOnly && !dopt.dontOutputLOs)
924 228 : dopt.outputLOs = true;
925 :
926 : /*
927 : * Collect role names so we can map object owner OIDs to names.
928 : */
929 306 : collectRoleNames(fout);
930 :
931 : /*
932 : * Now scan the database and create DumpableObject structs for all the
933 : * objects we intend to dump.
934 : */
935 306 : tblinfo = getSchemaData(fout, &numTables);
936 :
937 304 : if (!dopt.schemaOnly)
938 : {
939 272 : getTableData(&dopt, tblinfo, numTables, 0);
940 272 : buildMatViewRefreshDependencies(fout);
941 272 : if (dopt.dataOnly)
942 12 : getTableDataFKConstraints();
943 : }
944 :
945 304 : if (dopt.schemaOnly && dopt.sequence_data)
946 28 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
947 :
948 : /*
949 : * In binary-upgrade mode, we do not have to worry about the actual LO
950 : * data or the associated metadata that resides in the pg_largeobject and
951 : * pg_largeobject_metadata tables, respectively.
952 : *
953 : * However, we do need to collect LO information as there may be comments
954 : * or other information on LOs that we do need to dump out.
955 : */
956 304 : if (dopt.outputLOs || dopt.binary_upgrade)
957 256 : getLOs(fout);
958 :
959 : /*
960 : * Collect dependency data to assist in ordering the objects.
961 : */
962 304 : getDependencies(fout);
963 :
964 : /*
965 : * Collect ACLs, comments, and security labels, if wanted.
966 : */
967 304 : if (!dopt.aclsSkip)
968 300 : getAdditionalACLs(fout);
969 304 : if (!dopt.no_comments)
970 304 : collectComments(fout);
971 304 : if (!dopt.no_security_labels)
972 304 : collectSecLabels(fout);
973 :
974 : /* Lastly, create dummy objects to represent the section boundaries */
975 304 : boundaryObjs = createBoundaryObjects();
976 :
977 : /* Get pointers to all the known DumpableObjects */
978 304 : getDumpableObjects(&dobjs, &numObjs);
979 :
980 : /*
981 : * Add dummy dependencies to enforce the dump section ordering.
982 : */
983 304 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
984 :
985 : /*
986 : * Sort the objects into a safe dump order (no forward references).
987 : *
988 : * We rely on dependency information to help us determine a safe order, so
989 : * the initial sort is mostly for cosmetic purposes: we sort by name to
990 : * ensure that logically identical schemas will dump identically.
991 : */
992 304 : sortDumpableObjectsByTypeName(dobjs, numObjs);
993 :
994 304 : sortDumpableObjects(dobjs, numObjs,
995 304 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
996 :
997 : /*
998 : * Create archive TOC entries for all the objects to be dumped, in a safe
999 : * order.
1000 : */
1001 :
1002 : /*
1003 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1004 : */
1005 304 : dumpEncoding(fout);
1006 304 : dumpStdStrings(fout);
1007 304 : dumpSearchPath(fout);
1008 :
1009 : /* The database items are always next, unless we don't want them at all */
1010 304 : if (dopt.outputCreateDB)
1011 120 : dumpDatabase(fout);
1012 :
1013 : /* Now the rearrangeable objects. */
1014 1098608 : for (i = 0; i < numObjs; i++)
1015 1098304 : dumpDumpableObject(fout, dobjs[i]);
1016 :
1017 : /*
1018 : * Set up options info to ensure we dump what we want.
1019 : */
1020 304 : ropt = NewRestoreOptions();
1021 304 : ropt->filename = filename;
1022 :
1023 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1024 304 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1025 304 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1026 304 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1027 304 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1028 304 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1029 304 : ropt->dropSchema = dopt.outputClean;
1030 304 : ropt->dataOnly = dopt.dataOnly;
1031 304 : ropt->schemaOnly = dopt.schemaOnly;
1032 304 : ropt->if_exists = dopt.if_exists;
1033 304 : ropt->column_inserts = dopt.column_inserts;
1034 304 : ropt->dumpSections = dopt.dumpSections;
1035 304 : ropt->aclsSkip = dopt.aclsSkip;
1036 304 : ropt->superuser = dopt.outputSuperuser;
1037 304 : ropt->createDB = dopt.outputCreateDB;
1038 304 : ropt->noOwner = dopt.outputNoOwner;
1039 304 : ropt->noTableAm = dopt.outputNoTableAm;
1040 304 : ropt->noTablespace = dopt.outputNoTablespaces;
1041 304 : ropt->disable_triggers = dopt.disable_triggers;
1042 304 : ropt->use_setsessauth = dopt.use_setsessauth;
1043 304 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1044 304 : ropt->dump_inserts = dopt.dump_inserts;
1045 304 : ropt->no_comments = dopt.no_comments;
1046 304 : ropt->no_publications = dopt.no_publications;
1047 304 : ropt->no_security_labels = dopt.no_security_labels;
1048 304 : ropt->no_subscriptions = dopt.no_subscriptions;
1049 304 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1050 304 : ropt->include_everything = dopt.include_everything;
1051 304 : ropt->enable_row_security = dopt.enable_row_security;
1052 304 : ropt->sequence_data = dopt.sequence_data;
1053 304 : ropt->binary_upgrade = dopt.binary_upgrade;
1054 :
1055 304 : ropt->compression_spec = compression_spec;
1056 :
1057 304 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1058 :
1059 304 : SetArchiveOptions(fout, &dopt, ropt);
1060 :
1061 : /* Mark which entries should be output */
1062 304 : ProcessArchiveRestoreOptions(fout);
1063 :
1064 : /*
1065 : * The archive's TOC entries are now marked as to which ones will actually
1066 : * be output, so we can set up their dependency lists properly. This isn't
1067 : * necessary for plain-text output, though.
1068 : */
1069 304 : if (!plainText)
1070 62 : BuildArchiveDependencies(fout);
1071 :
1072 : /*
1073 : * And finally we can do the actual output.
1074 : *
1075 : * Note: for non-plain-text output formats, the output file is written
1076 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1077 : * right now.
1078 : */
1079 304 : if (plainText)
1080 242 : RestoreArchive(fout);
1081 :
1082 302 : CloseArchive(fout);
1083 :
1084 302 : exit_nicely(0);
1085 : }
1086 :
1087 :
1088 : static void
1089 2 : help(const char *progname)
1090 : {
1091 2 : printf(_("%s dumps a database as a text file or to other formats.\n\n"), progname);
1092 2 : printf(_("Usage:\n"));
1093 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1094 :
1095 2 : printf(_("\nGeneral options:\n"));
1096 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1097 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1098 : " plain text (default))\n"));
1099 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1100 2 : printf(_(" -v, --verbose verbose mode\n"));
1101 2 : printf(_(" -V, --version output version information, then exit\n"));
1102 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1103 : " compress as specified\n"));
1104 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1105 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1106 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1107 2 : printf(_(" -?, --help show this help, then exit\n"));
1108 :
1109 2 : printf(_("\nOptions controlling the output content:\n"));
1110 2 : printf(_(" -a, --data-only dump only the data, not the schema\n"));
1111 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1112 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1113 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1114 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1115 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1116 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1117 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1118 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1119 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1120 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1121 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1122 : " plain-text format\n"));
1123 2 : printf(_(" -s, --schema-only dump only the schema, no data\n"));
1124 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1125 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1126 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1127 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1128 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1129 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1130 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1131 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1132 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1133 : " access to)\n"));
1134 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1135 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1136 : " do NOT dump the specified table(s), including\n"
1137 : " child and partition tables\n"));
1138 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1139 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1140 : " do NOT dump data for the specified table(s),\n"
1141 : " including child and partition tables\n"));
1142 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1143 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1144 : " based on expressions in FILENAME\n"));
1145 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1146 2 : printf(_(" --include-foreign-data=PATTERN\n"
1147 : " include data of foreign tables on foreign\n"
1148 : " servers matching PATTERN\n"));
1149 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1150 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1151 2 : printf(_(" --no-comments do not dump comments\n"));
1152 2 : printf(_(" --no-publications do not dump publications\n"));
1153 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1154 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1155 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1156 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1157 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1158 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1159 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1160 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1161 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1162 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1163 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1164 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1165 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1166 : " match at least one entity each\n"));
1167 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1168 : " child and partition tables\n"));
1169 2 : printf(_(" --use-set-session-authorization\n"
1170 : " use SET SESSION AUTHORIZATION commands instead of\n"
1171 : " ALTER OWNER commands to set ownership\n"));
1172 :
1173 2 : printf(_("\nConnection options:\n"));
1174 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1175 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1176 2 : printf(_(" -p, --port=PORT database server port number\n"));
1177 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1178 2 : printf(_(" -w, --no-password never prompt for password\n"));
1179 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1180 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1181 :
1182 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1183 : "variable value is used.\n\n"));
1184 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1185 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1186 2 : }
1187 :
1188 : static void
1189 370 : setup_connection(Archive *AH, const char *dumpencoding,
1190 : const char *dumpsnapshot, char *use_role)
1191 : {
1192 370 : DumpOptions *dopt = AH->dopt;
1193 370 : PGconn *conn = GetConnection(AH);
1194 : const char *std_strings;
1195 :
1196 370 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1197 :
1198 : /*
1199 : * Set the client encoding if requested.
1200 : */
1201 370 : if (dumpencoding)
1202 : {
1203 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1204 0 : pg_fatal("invalid client encoding \"%s\" specified",
1205 : dumpencoding);
1206 : }
1207 :
1208 : /*
1209 : * Get the active encoding and the standard_conforming_strings setting, so
1210 : * we know how to escape strings.
1211 : */
1212 370 : AH->encoding = PQclientEncoding(conn);
1213 :
1214 370 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1215 370 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1216 :
1217 : /*
1218 : * Set the role if requested. In a parallel dump worker, we'll be passed
1219 : * use_role == NULL, but AH->use_role is already set (if user specified it
1220 : * originally) and we should use that.
1221 : */
1222 370 : if (!use_role && AH->use_role)
1223 4 : use_role = AH->use_role;
1224 :
1225 : /* Set the role if requested */
1226 370 : if (use_role)
1227 : {
1228 10 : PQExpBuffer query = createPQExpBuffer();
1229 :
1230 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1231 10 : ExecuteSqlStatement(AH, query->data);
1232 10 : destroyPQExpBuffer(query);
1233 :
1234 : /* save it for possible later use by parallel workers */
1235 10 : if (!AH->use_role)
1236 6 : AH->use_role = pg_strdup(use_role);
1237 : }
1238 :
1239 : /* Set the datestyle to ISO to ensure the dump's portability */
1240 370 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1241 :
1242 : /* Likewise, avoid using sql_standard intervalstyle */
1243 370 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1244 :
1245 : /*
1246 : * Use an explicitly specified extra_float_digits if it has been provided.
1247 : * Otherwise, set extra_float_digits so that we can dump float data
1248 : * exactly (given correctly implemented float I/O code, anyway).
1249 : */
1250 370 : if (have_extra_float_digits)
1251 : {
1252 0 : PQExpBuffer q = createPQExpBuffer();
1253 :
1254 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1255 : extra_float_digits);
1256 0 : ExecuteSqlStatement(AH, q->data);
1257 0 : destroyPQExpBuffer(q);
1258 : }
1259 : else
1260 370 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1261 :
1262 : /*
1263 : * Disable synchronized scanning, to prevent unpredictable changes in row
1264 : * ordering across a dump and reload.
1265 : */
1266 370 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1267 :
1268 : /*
1269 : * Disable timeouts if supported.
1270 : */
1271 370 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1272 370 : if (AH->remoteVersion >= 90300)
1273 370 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1274 370 : if (AH->remoteVersion >= 90600)
1275 370 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1276 370 : if (AH->remoteVersion >= 170000)
1277 370 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1278 :
1279 : /*
1280 : * Quote all identifiers, if requested.
1281 : */
1282 370 : if (quote_all_identifiers)
1283 24 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1284 :
1285 : /*
1286 : * Adjust row-security mode, if supported.
1287 : */
1288 370 : if (AH->remoteVersion >= 90500)
1289 : {
1290 370 : if (dopt->enable_row_security)
1291 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1292 : else
1293 370 : ExecuteSqlStatement(AH, "SET row_security = off");
1294 : }
1295 :
1296 : /*
1297 : * Initialize prepared-query state to "nothing prepared". We do this here
1298 : * so that a parallel dump worker will have its own state.
1299 : */
1300 370 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1301 :
1302 : /*
1303 : * Start transaction-snapshot mode transaction to dump consistent data.
1304 : */
1305 370 : ExecuteSqlStatement(AH, "BEGIN");
1306 :
1307 : /*
1308 : * To support the combination of serializable_deferrable with the jobs
1309 : * option we use REPEATABLE READ for the worker connections that are
1310 : * passed a snapshot. As long as the snapshot is acquired in a
1311 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1312 : * REPEATABLE READ transaction provides the appropriate integrity
1313 : * guarantees. This is a kluge, but safe for back-patching.
1314 : */
1315 370 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1316 0 : ExecuteSqlStatement(AH,
1317 : "SET TRANSACTION ISOLATION LEVEL "
1318 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1319 : else
1320 370 : ExecuteSqlStatement(AH,
1321 : "SET TRANSACTION ISOLATION LEVEL "
1322 : "REPEATABLE READ, READ ONLY");
1323 :
1324 : /*
1325 : * If user specified a snapshot to use, select that. In a parallel dump
1326 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1327 : * is already set (if the server can handle it) and we should use that.
1328 : */
1329 370 : if (dumpsnapshot)
1330 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1331 :
1332 370 : if (AH->sync_snapshot_id)
1333 : {
1334 32 : PQExpBuffer query = createPQExpBuffer();
1335 :
1336 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1337 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1338 32 : ExecuteSqlStatement(AH, query->data);
1339 32 : destroyPQExpBuffer(query);
1340 : }
1341 338 : else if (AH->numWorkers > 1)
1342 : {
1343 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1344 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1345 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1346 : }
1347 370 : }
1348 :
1349 : /* Set up connection for a parallel worker process */
1350 : static void
1351 32 : setupDumpWorker(Archive *AH)
1352 : {
1353 : /*
1354 : * We want to re-select all the same values the leader connection is
1355 : * using. We'll have inherited directly-usable values in
1356 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1357 : * inherited encoding value back to a string to pass to setup_connection.
1358 : */
1359 32 : setup_connection(AH,
1360 : pg_encoding_to_char(AH->encoding),
1361 : NULL,
1362 : NULL);
1363 32 : }
1364 :
1365 : static char *
1366 16 : get_synchronized_snapshot(Archive *fout)
1367 : {
1368 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1369 : char *result;
1370 : PGresult *res;
1371 :
1372 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1373 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1374 16 : PQclear(res);
1375 :
1376 16 : return result;
1377 : }
1378 :
1379 : static ArchiveFormat
1380 356 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1381 : {
1382 : ArchiveFormat archiveFormat;
1383 :
1384 356 : *mode = archModeWrite;
1385 :
1386 356 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1387 : {
1388 : /* This is used by pg_dumpall, and is not documented */
1389 86 : archiveFormat = archNull;
1390 86 : *mode = archModeAppend;
1391 : }
1392 270 : else if (pg_strcasecmp(format, "c") == 0)
1393 8 : archiveFormat = archCustom;
1394 262 : else if (pg_strcasecmp(format, "custom") == 0)
1395 30 : archiveFormat = archCustom;
1396 232 : else if (pg_strcasecmp(format, "d") == 0)
1397 14 : archiveFormat = archDirectory;
1398 218 : else if (pg_strcasecmp(format, "directory") == 0)
1399 6 : archiveFormat = archDirectory;
1400 212 : else if (pg_strcasecmp(format, "p") == 0)
1401 198 : archiveFormat = archNull;
1402 14 : else if (pg_strcasecmp(format, "plain") == 0)
1403 6 : archiveFormat = archNull;
1404 8 : else if (pg_strcasecmp(format, "t") == 0)
1405 4 : archiveFormat = archTar;
1406 4 : else if (pg_strcasecmp(format, "tar") == 0)
1407 2 : archiveFormat = archTar;
1408 : else
1409 2 : pg_fatal("invalid output format \"%s\" specified", format);
1410 354 : return archiveFormat;
1411 : }
1412 :
1413 : /*
1414 : * Find the OIDs of all schemas matching the given list of patterns,
1415 : * and append them to the given OID list.
1416 : */
1417 : static void
1418 356 : expand_schema_name_patterns(Archive *fout,
1419 : SimpleStringList *patterns,
1420 : SimpleOidList *oids,
1421 : bool strict_names)
1422 : {
1423 : PQExpBuffer query;
1424 : PGresult *res;
1425 : SimpleStringListCell *cell;
1426 : int i;
1427 :
1428 356 : if (patterns->head == NULL)
1429 318 : return; /* nothing to do */
1430 :
1431 38 : query = createPQExpBuffer();
1432 :
1433 : /*
1434 : * The loop below runs multiple SELECTs might sometimes result in
1435 : * duplicate entries in the OID list, but we don't care.
1436 : */
1437 :
1438 64 : for (cell = patterns->head; cell; cell = cell->next)
1439 : {
1440 : PQExpBufferData dbbuf;
1441 : int dotcnt;
1442 :
1443 38 : appendPQExpBufferStr(query,
1444 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1445 38 : initPQExpBuffer(&dbbuf);
1446 38 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1447 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1448 : &dotcnt);
1449 38 : if (dotcnt > 1)
1450 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1451 : cell->val);
1452 34 : else if (dotcnt == 1)
1453 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1454 28 : termPQExpBuffer(&dbbuf);
1455 :
1456 28 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1457 28 : if (strict_names && PQntuples(res) == 0)
1458 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1459 :
1460 50 : for (i = 0; i < PQntuples(res); i++)
1461 : {
1462 24 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1463 : }
1464 :
1465 26 : PQclear(res);
1466 26 : resetPQExpBuffer(query);
1467 : }
1468 :
1469 26 : destroyPQExpBuffer(query);
1470 : }
1471 :
1472 : /*
1473 : * Find the OIDs of all extensions matching the given list of patterns,
1474 : * and append them to the given OID list.
1475 : */
1476 : static void
1477 316 : expand_extension_name_patterns(Archive *fout,
1478 : SimpleStringList *patterns,
1479 : SimpleOidList *oids,
1480 : bool strict_names)
1481 : {
1482 : PQExpBuffer query;
1483 : PGresult *res;
1484 : SimpleStringListCell *cell;
1485 : int i;
1486 :
1487 316 : if (patterns->head == NULL)
1488 302 : return; /* nothing to do */
1489 :
1490 14 : query = createPQExpBuffer();
1491 :
1492 : /*
1493 : * The loop below runs multiple SELECTs might sometimes result in
1494 : * duplicate entries in the OID list, but we don't care.
1495 : */
1496 28 : for (cell = patterns->head; cell; cell = cell->next)
1497 : {
1498 : int dotcnt;
1499 :
1500 14 : appendPQExpBufferStr(query,
1501 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1502 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1503 : false, NULL, "e.extname", NULL, NULL, NULL,
1504 : &dotcnt);
1505 14 : if (dotcnt > 0)
1506 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1507 : cell->val);
1508 :
1509 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1510 14 : if (strict_names && PQntuples(res) == 0)
1511 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1512 :
1513 26 : for (i = 0; i < PQntuples(res); i++)
1514 : {
1515 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1516 : }
1517 :
1518 14 : PQclear(res);
1519 14 : resetPQExpBuffer(query);
1520 : }
1521 :
1522 14 : destroyPQExpBuffer(query);
1523 : }
1524 :
1525 : /*
1526 : * Find the OIDs of all foreign servers matching the given list of patterns,
1527 : * and append them to the given OID list.
1528 : */
1529 : static void
1530 310 : expand_foreign_server_name_patterns(Archive *fout,
1531 : SimpleStringList *patterns,
1532 : SimpleOidList *oids)
1533 : {
1534 : PQExpBuffer query;
1535 : PGresult *res;
1536 : SimpleStringListCell *cell;
1537 : int i;
1538 :
1539 310 : if (patterns->head == NULL)
1540 304 : return; /* nothing to do */
1541 :
1542 6 : query = createPQExpBuffer();
1543 :
1544 : /*
1545 : * The loop below runs multiple SELECTs might sometimes result in
1546 : * duplicate entries in the OID list, but we don't care.
1547 : */
1548 :
1549 10 : for (cell = patterns->head; cell; cell = cell->next)
1550 : {
1551 : int dotcnt;
1552 :
1553 6 : appendPQExpBufferStr(query,
1554 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1555 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1556 : false, NULL, "s.srvname", NULL, NULL, NULL,
1557 : &dotcnt);
1558 6 : if (dotcnt > 0)
1559 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1560 : cell->val);
1561 :
1562 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1563 6 : if (PQntuples(res) == 0)
1564 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1565 :
1566 8 : for (i = 0; i < PQntuples(res); i++)
1567 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1568 :
1569 4 : PQclear(res);
1570 4 : resetPQExpBuffer(query);
1571 : }
1572 :
1573 4 : destroyPQExpBuffer(query);
1574 : }
1575 :
1576 : /*
1577 : * Find the OIDs of all tables matching the given list of patterns,
1578 : * and append them to the given OID list. See also expand_dbname_patterns()
1579 : * in pg_dumpall.c
1580 : */
1581 : static void
1582 1878 : expand_table_name_patterns(Archive *fout,
1583 : SimpleStringList *patterns, SimpleOidList *oids,
1584 : bool strict_names, bool with_child_tables)
1585 : {
1586 : PQExpBuffer query;
1587 : PGresult *res;
1588 : SimpleStringListCell *cell;
1589 : int i;
1590 :
1591 1878 : if (patterns->head == NULL)
1592 1820 : return; /* nothing to do */
1593 :
1594 58 : query = createPQExpBuffer();
1595 :
1596 : /*
1597 : * this might sometimes result in duplicate entries in the OID list, but
1598 : * we don't care.
1599 : */
1600 :
1601 118 : for (cell = patterns->head; cell; cell = cell->next)
1602 : {
1603 : PQExpBufferData dbbuf;
1604 : int dotcnt;
1605 :
1606 : /*
1607 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1608 : * would be unnecessary given a pg_table_is_visible() variant taking a
1609 : * search_path argument.
1610 : *
1611 : * For with_child_tables, we start with the basic query's results and
1612 : * recursively search the inheritance tree to add child tables.
1613 : */
1614 70 : if (with_child_tables)
1615 : {
1616 12 : appendPQExpBuffer(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1617 : }
1618 :
1619 70 : appendPQExpBuffer(query,
1620 : "SELECT c.oid"
1621 : "\nFROM pg_catalog.pg_class c"
1622 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1623 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1624 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1625 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1626 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1627 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1628 : RELKIND_PARTITIONED_TABLE);
1629 70 : initPQExpBuffer(&dbbuf);
1630 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1631 : false, "n.nspname", "c.relname", NULL,
1632 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1633 : &dotcnt);
1634 70 : if (dotcnt > 2)
1635 2 : pg_fatal("improper relation name (too many dotted names): %s",
1636 : cell->val);
1637 68 : else if (dotcnt == 2)
1638 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1639 64 : termPQExpBuffer(&dbbuf);
1640 :
1641 64 : if (with_child_tables)
1642 : {
1643 12 : appendPQExpBuffer(query, "UNION"
1644 : "\nSELECT i.inhrelid"
1645 : "\nFROM partition_tree p"
1646 : "\n JOIN pg_catalog.pg_inherits i"
1647 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1648 : "\n)"
1649 : "\nSELECT relid FROM partition_tree");
1650 : }
1651 :
1652 64 : ExecuteSqlStatement(fout, "RESET search_path");
1653 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1654 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1655 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1656 64 : if (strict_names && PQntuples(res) == 0)
1657 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1658 :
1659 148 : for (i = 0; i < PQntuples(res); i++)
1660 : {
1661 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1662 : }
1663 :
1664 60 : PQclear(res);
1665 60 : resetPQExpBuffer(query);
1666 : }
1667 :
1668 48 : destroyPQExpBuffer(query);
1669 : }
1670 :
1671 : /*
1672 : * Verifies that the connected database name matches the given database name,
1673 : * and if not, dies with an error about the given pattern.
1674 : *
1675 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1676 : */
1677 : static void
1678 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1679 : {
1680 : const char *db;
1681 :
1682 10 : db = PQdb(conn);
1683 10 : if (db == NULL)
1684 0 : pg_fatal("You are currently not connected to a database.");
1685 :
1686 10 : if (strcmp(db, dbname) != 0)
1687 10 : pg_fatal("cross-database references are not implemented: %s",
1688 : pattern);
1689 0 : }
1690 :
1691 : /*
1692 : * checkExtensionMembership
1693 : * Determine whether object is an extension member, and if so,
1694 : * record an appropriate dependency and set the object's dump flag.
1695 : *
1696 : * It's important to call this for each object that could be an extension
1697 : * member. Generally, we integrate this with determining the object's
1698 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1699 : *
1700 : * Returns true if object is an extension member, else false.
1701 : */
1702 : static bool
1703 939394 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1704 : {
1705 939394 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1706 :
1707 939394 : if (ext == NULL)
1708 938006 : return false;
1709 :
1710 1388 : dobj->ext_member = true;
1711 :
1712 : /* Record dependency so that getDependencies needn't deal with that */
1713 1388 : addObjectDependency(dobj, ext->dobj.dumpId);
1714 :
1715 : /*
1716 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1717 : * dumped. (Any initial ACLs will be removed later, using data from
1718 : * pg_init_privs, so that we'll dump only the delta from the extension's
1719 : * initial setup.)
1720 : *
1721 : * Prior to 9.6, we do not include any extension member components.
1722 : *
1723 : * In binary upgrades, we still dump all components of the members
1724 : * individually, since the idea is to exactly reproduce the database
1725 : * contents rather than replace the extension contents with something
1726 : * different.
1727 : *
1728 : * Note: it might be interesting someday to implement storage and delta
1729 : * dumping of extension members' RLS policies and/or security labels.
1730 : * However there is a pitfall for RLS policies: trying to dump them
1731 : * requires getting a lock on their tables, and the calling user might not
1732 : * have privileges for that. We need no lock to examine a table's ACLs,
1733 : * so the current feature doesn't have a problem of that sort.
1734 : */
1735 1388 : if (fout->dopt->binary_upgrade)
1736 152 : dobj->dump = ext->dobj.dump;
1737 : else
1738 : {
1739 1236 : if (fout->remoteVersion < 90600)
1740 0 : dobj->dump = DUMP_COMPONENT_NONE;
1741 : else
1742 1236 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1743 : }
1744 :
1745 1388 : return true;
1746 : }
1747 :
1748 : /*
1749 : * selectDumpableNamespace: policy-setting subroutine
1750 : * Mark a namespace as to be dumped or not
1751 : */
1752 : static void
1753 2276 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1754 : {
1755 : /*
1756 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1757 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1758 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1759 : */
1760 2276 : nsinfo->create = true;
1761 :
1762 : /*
1763 : * If specific tables are being dumped, do not dump any complete
1764 : * namespaces. If specific namespaces are being dumped, dump just those
1765 : * namespaces. Otherwise, dump all non-system namespaces.
1766 : */
1767 2276 : if (table_include_oids.head != NULL)
1768 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1769 2176 : else if (schema_include_oids.head != NULL)
1770 98 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1771 98 : simple_oid_list_member(&schema_include_oids,
1772 : nsinfo->dobj.catId.oid) ?
1773 98 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1774 2078 : else if (fout->remoteVersion >= 90600 &&
1775 2078 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1776 : {
1777 : /*
1778 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1779 : * they are interesting (and not the original ACLs which were set at
1780 : * initdb time, see pg_init_privs).
1781 : */
1782 266 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1783 : }
1784 1812 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1785 810 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1786 : {
1787 : /* Other system schemas don't get dumped */
1788 1268 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1789 : }
1790 544 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1791 : {
1792 : /*
1793 : * The public schema is a strange beast that sits in a sort of
1794 : * no-mans-land between being a system object and a user object.
1795 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
1796 : * a comment and an indication of ownership. If the owner is the
1797 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
1798 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
1799 : */
1800 258 : nsinfo->create = false;
1801 258 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1802 258 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
1803 178 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
1804 258 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
1805 :
1806 : /*
1807 : * Also, make like it has a comment even if it doesn't; this is so
1808 : * that we'll emit a command to drop the comment, if appropriate.
1809 : * (Without this, we'd not call dumpCommentExtended for it.)
1810 : */
1811 258 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
1812 : }
1813 : else
1814 286 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
1815 :
1816 : /*
1817 : * In any case, a namespace can be excluded by an exclusion switch
1818 : */
1819 3104 : if (nsinfo->dobj.dump_contains &&
1820 828 : simple_oid_list_member(&schema_exclude_oids,
1821 : nsinfo->dobj.catId.oid))
1822 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1823 :
1824 : /*
1825 : * If the schema belongs to an extension, allow extension membership to
1826 : * override the dump decision for the schema itself. However, this does
1827 : * not change dump_contains, so this won't change what we do with objects
1828 : * within the schema. (If they belong to the extension, they'll get
1829 : * suppressed by it, otherwise not.)
1830 : */
1831 2276 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
1832 2276 : }
1833 :
1834 : /*
1835 : * selectDumpableTable: policy-setting subroutine
1836 : * Mark a table as to be dumped or not
1837 : */
1838 : static void
1839 77126 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
1840 : {
1841 77126 : if (checkExtensionMembership(&tbinfo->dobj, fout))
1842 450 : return; /* extension membership overrides all else */
1843 :
1844 : /*
1845 : * If specific tables are being dumped, dump just those tables; else, dump
1846 : * according to the parent namespace's dump flag.
1847 : */
1848 76676 : if (table_include_oids.head != NULL)
1849 10104 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
1850 : tbinfo->dobj.catId.oid) ?
1851 5052 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1852 : else
1853 71624 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
1854 :
1855 : /*
1856 : * In any case, a table can be excluded by an exclusion switch
1857 : */
1858 126066 : if (tbinfo->dobj.dump &&
1859 49390 : simple_oid_list_member(&table_exclude_oids,
1860 : tbinfo->dobj.catId.oid))
1861 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
1862 : }
1863 :
1864 : /*
1865 : * selectDumpableType: policy-setting subroutine
1866 : * Mark a type as to be dumped or not
1867 : *
1868 : * If it's a table's rowtype or an autogenerated array type, we also apply a
1869 : * special type code to facilitate sorting into the desired order. (We don't
1870 : * want to consider those to be ordinary types because that would bring tables
1871 : * up into the datatype part of the dump order.) We still set the object's
1872 : * dump flag; that's not going to cause the dummy type to be dumped, but we
1873 : * need it so that casts involving such types will be dumped correctly -- see
1874 : * dumpCast. This means the flag should be set the same as for the underlying
1875 : * object (the table or base type).
1876 : */
1877 : static void
1878 213196 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
1879 : {
1880 : /* skip complex types, except for standalone composite types */
1881 213196 : if (OidIsValid(tyinfo->typrelid) &&
1882 75924 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
1883 : {
1884 75628 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
1885 :
1886 75628 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
1887 75628 : if (tytable != NULL)
1888 75628 : tyinfo->dobj.dump = tytable->dobj.dump;
1889 : else
1890 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
1891 75628 : return;
1892 : }
1893 :
1894 : /* skip auto-generated array and multirange types */
1895 137568 : if (tyinfo->isArray || tyinfo->isMultirange)
1896 : {
1897 104178 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
1898 :
1899 : /*
1900 : * Fall through to set the dump flag; we assume that the subsequent
1901 : * rules will do the same thing as they would for the array's base
1902 : * type or multirange's range type. (We cannot reliably look up the
1903 : * base type here, since getTypes may not have processed it yet.)
1904 : */
1905 : }
1906 :
1907 137568 : if (checkExtensionMembership(&tyinfo->dobj, fout))
1908 300 : return; /* extension membership overrides all else */
1909 :
1910 : /* Dump based on if the contents of the namespace are being dumped */
1911 137268 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
1912 : }
1913 :
1914 : /*
1915 : * selectDumpableDefaultACL: policy-setting subroutine
1916 : * Mark a default ACL as to be dumped or not
1917 : *
1918 : * For per-schema default ACLs, dump if the schema is to be dumped.
1919 : * Otherwise dump if we are dumping "everything". Note that dataOnly
1920 : * and aclsSkip are checked separately.
1921 : */
1922 : static void
1923 344 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
1924 : {
1925 : /* Default ACLs can't be extension members */
1926 :
1927 344 : if (dinfo->dobj.namespace)
1928 : /* default ACLs are considered part of the namespace */
1929 172 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
1930 : else
1931 172 : dinfo->dobj.dump = dopt->include_everything ?
1932 172 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1933 344 : }
1934 :
1935 : /*
1936 : * selectDumpableCast: policy-setting subroutine
1937 : * Mark a cast as to be dumped or not
1938 : *
1939 : * Casts do not belong to any particular namespace (since they haven't got
1940 : * names), nor do they have identifiable owners. To distinguish user-defined
1941 : * casts from built-in ones, we must resort to checking whether the cast's
1942 : * OID is in the range reserved for initdb.
1943 : */
1944 : static void
1945 67938 : selectDumpableCast(CastInfo *cast, Archive *fout)
1946 : {
1947 67938 : if (checkExtensionMembership(&cast->dobj, fout))
1948 0 : return; /* extension membership overrides all else */
1949 :
1950 : /*
1951 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
1952 : * support ACLs currently.
1953 : */
1954 67938 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1955 67792 : cast->dobj.dump = DUMP_COMPONENT_NONE;
1956 : else
1957 146 : cast->dobj.dump = fout->dopt->include_everything ?
1958 146 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1959 : }
1960 :
1961 : /*
1962 : * selectDumpableProcLang: policy-setting subroutine
1963 : * Mark a procedural language as to be dumped or not
1964 : *
1965 : * Procedural languages do not belong to any particular namespace. To
1966 : * identify built-in languages, we must resort to checking whether the
1967 : * language's OID is in the range reserved for initdb.
1968 : */
1969 : static void
1970 390 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
1971 : {
1972 390 : if (checkExtensionMembership(&plang->dobj, fout))
1973 304 : return; /* extension membership overrides all else */
1974 :
1975 : /*
1976 : * Only include procedural languages when we are dumping everything.
1977 : *
1978 : * For from-initdb procedural languages, only include ACLs, as we do for
1979 : * the pg_catalog namespace. We need this because procedural languages do
1980 : * not live in any namespace.
1981 : */
1982 86 : if (!fout->dopt->include_everything)
1983 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
1984 : else
1985 : {
1986 70 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
1987 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
1988 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
1989 : else
1990 70 : plang->dobj.dump = DUMP_COMPONENT_ALL;
1991 : }
1992 : }
1993 :
1994 : /*
1995 : * selectDumpableAccessMethod: policy-setting subroutine
1996 : * Mark an access method as to be dumped or not
1997 : *
1998 : * Access methods do not belong to any particular namespace. To identify
1999 : * built-in access methods, we must resort to checking whether the
2000 : * method's OID is in the range reserved for initdb.
2001 : */
2002 : static void
2003 2360 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2004 : {
2005 2360 : if (checkExtensionMembership(&method->dobj, fout))
2006 50 : return; /* extension membership overrides all else */
2007 :
2008 : /*
2009 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2010 : * they do not support ACLs currently.
2011 : */
2012 2310 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2013 2128 : method->dobj.dump = DUMP_COMPONENT_NONE;
2014 : else
2015 182 : method->dobj.dump = fout->dopt->include_everything ?
2016 182 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2017 : }
2018 :
2019 : /*
2020 : * selectDumpableExtension: policy-setting subroutine
2021 : * Mark an extension as to be dumped or not
2022 : *
2023 : * Built-in extensions should be skipped except for checking ACLs, since we
2024 : * assume those will already be installed in the target database. We identify
2025 : * such extensions by their having OIDs in the range reserved for initdb.
2026 : * We dump all user-added extensions by default. No extensions are dumped
2027 : * if include_everything is false (i.e., a --schema or --table switch was
2028 : * given), except if --extension specifies a list of extensions to dump.
2029 : */
2030 : static void
2031 356 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2032 : {
2033 : /*
2034 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2035 : * change permissions on their member objects, if they wish to, and have
2036 : * those changes preserved.
2037 : */
2038 356 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2039 306 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2040 : else
2041 : {
2042 : /* check if there is a list of extensions to dump */
2043 50 : if (extension_include_oids.head != NULL)
2044 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2045 8 : simple_oid_list_member(&extension_include_oids,
2046 : extinfo->dobj.catId.oid) ?
2047 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2048 : else
2049 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2050 42 : dopt->include_everything ?
2051 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2052 :
2053 : /* check that the extension is not explicitly excluded */
2054 92 : if (extinfo->dobj.dump &&
2055 42 : simple_oid_list_member(&extension_exclude_oids,
2056 : extinfo->dobj.catId.oid))
2057 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2058 : }
2059 356 : }
2060 :
2061 : /*
2062 : * selectDumpablePublicationObject: policy-setting subroutine
2063 : * Mark a publication object as to be dumped or not
2064 : *
2065 : * A publication can have schemas and tables which have schemas, but those are
2066 : * ignored in decision making, because publications are only dumped when we are
2067 : * dumping everything.
2068 : */
2069 : static void
2070 652 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2071 : {
2072 652 : if (checkExtensionMembership(dobj, fout))
2073 0 : return; /* extension membership overrides all else */
2074 :
2075 652 : dobj->dump = fout->dopt->include_everything ?
2076 652 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2077 : }
2078 :
2079 : /*
2080 : * selectDumpableStatisticsObject: policy-setting subroutine
2081 : * Mark an extended statistics object as to be dumped or not
2082 : *
2083 : * We dump an extended statistics object if the schema it's in and the table
2084 : * it's for are being dumped. (This'll need more thought if statistics
2085 : * objects ever support cross-table stats.)
2086 : */
2087 : static void
2088 298 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2089 : {
2090 298 : if (checkExtensionMembership(&sobj->dobj, fout))
2091 0 : return; /* extension membership overrides all else */
2092 :
2093 298 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2094 298 : if (sobj->stattable == NULL ||
2095 298 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2096 40 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2097 : }
2098 :
2099 : /*
2100 : * selectDumpableObject: policy-setting subroutine
2101 : * Mark a generic dumpable object as to be dumped or not
2102 : *
2103 : * Use this only for object types without a special-case routine above.
2104 : */
2105 : static void
2106 650786 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2107 : {
2108 650786 : if (checkExtensionMembership(dobj, fout))
2109 234 : return; /* extension membership overrides all else */
2110 :
2111 : /*
2112 : * Default policy is to dump if parent namespace is dumpable, or for
2113 : * non-namespace-associated items, dump if we're dumping "everything".
2114 : */
2115 650552 : if (dobj->namespace)
2116 649484 : dobj->dump = dobj->namespace->dobj.dump_contains;
2117 : else
2118 1068 : dobj->dump = fout->dopt->include_everything ?
2119 1068 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2120 : }
2121 :
2122 : /*
2123 : * Dump a table's contents for loading using the COPY command
2124 : * - this routine is called by the Archiver when it wants the table
2125 : * to be dumped.
2126 : */
2127 : static int
2128 6750 : dumpTableData_copy(Archive *fout, const void *dcontext)
2129 : {
2130 6750 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2131 6750 : TableInfo *tbinfo = tdinfo->tdtable;
2132 6750 : const char *classname = tbinfo->dobj.name;
2133 6750 : PQExpBuffer q = createPQExpBuffer();
2134 :
2135 : /*
2136 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2137 : * which uses it already.
2138 : */
2139 6750 : PQExpBuffer clistBuf = createPQExpBuffer();
2140 6750 : PGconn *conn = GetConnection(fout);
2141 : PGresult *res;
2142 : int ret;
2143 : char *copybuf;
2144 : const char *column_list;
2145 :
2146 6750 : pg_log_info("dumping contents of table \"%s.%s\"",
2147 : tbinfo->dobj.namespace->dobj.name, classname);
2148 :
2149 : /*
2150 : * Specify the column list explicitly so that we have no possibility of
2151 : * retrieving data in the wrong column order. (The default column
2152 : * ordering of COPY will not be what we want in certain corner cases
2153 : * involving ADD COLUMN and inheritance.)
2154 : */
2155 6750 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2156 :
2157 : /*
2158 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2159 : * a filter condition was specified. For other cases a simple COPY
2160 : * suffices.
2161 : */
2162 6750 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2163 : {
2164 2 : appendPQExpBufferStr(q, "COPY (SELECT ");
2165 : /* klugery to get rid of parens in column list */
2166 2 : if (strlen(column_list) > 2)
2167 : {
2168 2 : appendPQExpBufferStr(q, column_list + 1);
2169 2 : q->data[q->len - 1] = ' ';
2170 : }
2171 : else
2172 0 : appendPQExpBufferStr(q, "* ");
2173 :
2174 4 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2175 2 : fmtQualifiedDumpable(tbinfo),
2176 2 : tdinfo->filtercond ? tdinfo->filtercond : "");
2177 : }
2178 : else
2179 : {
2180 6748 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2181 6748 : fmtQualifiedDumpable(tbinfo),
2182 : column_list);
2183 : }
2184 6750 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2185 6748 : PQclear(res);
2186 6748 : destroyPQExpBuffer(clistBuf);
2187 :
2188 : for (;;)
2189 : {
2190 3425918 : ret = PQgetCopyData(conn, ©buf, 0);
2191 :
2192 3425918 : if (ret < 0)
2193 6748 : break; /* done or error */
2194 :
2195 3419170 : if (copybuf)
2196 : {
2197 3419170 : WriteData(fout, copybuf, ret);
2198 3419170 : PQfreemem(copybuf);
2199 : }
2200 :
2201 : /* ----------
2202 : * THROTTLE:
2203 : *
2204 : * There was considerable discussion in late July, 2000 regarding
2205 : * slowing down pg_dump when backing up large tables. Users with both
2206 : * slow & fast (multi-processor) machines experienced performance
2207 : * degradation when doing a backup.
2208 : *
2209 : * Initial attempts based on sleeping for a number of ms for each ms
2210 : * of work were deemed too complex, then a simple 'sleep in each loop'
2211 : * implementation was suggested. The latter failed because the loop
2212 : * was too tight. Finally, the following was implemented:
2213 : *
2214 : * If throttle is non-zero, then
2215 : * See how long since the last sleep.
2216 : * Work out how long to sleep (based on ratio).
2217 : * If sleep is more than 100ms, then
2218 : * sleep
2219 : * reset timer
2220 : * EndIf
2221 : * EndIf
2222 : *
2223 : * where the throttle value was the number of ms to sleep per ms of
2224 : * work. The calculation was done in each loop.
2225 : *
2226 : * Most of the hard work is done in the backend, and this solution
2227 : * still did not work particularly well: on slow machines, the ratio
2228 : * was 50:1, and on medium paced machines, 1:1, and on fast
2229 : * multi-processor machines, it had little or no effect, for reasons
2230 : * that were unclear.
2231 : *
2232 : * Further discussion ensued, and the proposal was dropped.
2233 : *
2234 : * For those people who want this feature, it can be implemented using
2235 : * gettimeofday in each loop, calculating the time since last sleep,
2236 : * multiplying that by the sleep ratio, then if the result is more
2237 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2238 : * function to sleep for a subsecond period ie.
2239 : *
2240 : * select(0, NULL, NULL, NULL, &tvi);
2241 : *
2242 : * This will return after the interval specified in the structure tvi.
2243 : * Finally, call gettimeofday again to save the 'last sleep time'.
2244 : * ----------
2245 : */
2246 : }
2247 6748 : archprintf(fout, "\\.\n\n\n");
2248 :
2249 6748 : if (ret == -2)
2250 : {
2251 : /* copy data transfer failed */
2252 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2253 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2254 0 : pg_log_error_detail("Command was: %s", q->data);
2255 0 : exit_nicely(1);
2256 : }
2257 :
2258 : /* Check command status and return to normal libpq state */
2259 6748 : res = PQgetResult(conn);
2260 6748 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2261 : {
2262 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2263 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2264 0 : pg_log_error_detail("Command was: %s", q->data);
2265 0 : exit_nicely(1);
2266 : }
2267 6748 : PQclear(res);
2268 :
2269 : /* Do this to ensure we've pumped libpq back to idle state */
2270 6748 : if (PQgetResult(conn) != NULL)
2271 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2272 : classname);
2273 :
2274 6748 : destroyPQExpBuffer(q);
2275 6748 : return 1;
2276 : }
2277 :
2278 : /*
2279 : * Dump table data using INSERT commands.
2280 : *
2281 : * Caution: when we restore from an archive file direct to database, the
2282 : * INSERT commands emitted by this function have to be parsed by
2283 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2284 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2285 : */
2286 : static int
2287 138 : dumpTableData_insert(Archive *fout, const void *dcontext)
2288 : {
2289 138 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2290 138 : TableInfo *tbinfo = tdinfo->tdtable;
2291 138 : DumpOptions *dopt = fout->dopt;
2292 138 : PQExpBuffer q = createPQExpBuffer();
2293 138 : PQExpBuffer insertStmt = NULL;
2294 : char *attgenerated;
2295 : PGresult *res;
2296 : int nfields,
2297 : i;
2298 138 : int rows_per_statement = dopt->dump_inserts;
2299 138 : int rows_this_statement = 0;
2300 :
2301 : /*
2302 : * If we're going to emit INSERTs with column names, the most efficient
2303 : * way to deal with generated columns is to exclude them entirely. For
2304 : * INSERTs without column names, we have to emit DEFAULT rather than the
2305 : * actual column value --- but we can save a few cycles by fetching nulls
2306 : * rather than the uninteresting-to-us value.
2307 : */
2308 138 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2309 138 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2310 138 : nfields = 0;
2311 442 : for (i = 0; i < tbinfo->numatts; i++)
2312 : {
2313 304 : if (tbinfo->attisdropped[i])
2314 4 : continue;
2315 300 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2316 10 : continue;
2317 290 : if (nfields > 0)
2318 166 : appendPQExpBufferStr(q, ", ");
2319 290 : if (tbinfo->attgenerated[i])
2320 10 : appendPQExpBufferStr(q, "NULL");
2321 : else
2322 280 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2323 290 : attgenerated[nfields] = tbinfo->attgenerated[i];
2324 290 : nfields++;
2325 : }
2326 : /* Servers before 9.4 will complain about zero-column SELECT */
2327 138 : if (nfields == 0)
2328 14 : appendPQExpBufferStr(q, "NULL");
2329 138 : appendPQExpBuffer(q, " FROM ONLY %s",
2330 138 : fmtQualifiedDumpable(tbinfo));
2331 138 : if (tdinfo->filtercond)
2332 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2333 :
2334 138 : ExecuteSqlStatement(fout, q->data);
2335 :
2336 : while (1)
2337 : {
2338 238 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2339 : PGRES_TUPLES_OK);
2340 :
2341 : /* cross-check field count, allowing for dummy NULL if any */
2342 238 : if (nfields != PQnfields(res) &&
2343 20 : !(nfields == 0 && PQnfields(res) == 1))
2344 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2345 : tbinfo->dobj.name);
2346 :
2347 : /*
2348 : * First time through, we build as much of the INSERT statement as
2349 : * possible in "insertStmt", which we can then just print for each
2350 : * statement. If the table happens to have zero dumpable columns then
2351 : * this will be a complete statement, otherwise it will end in
2352 : * "VALUES" and be ready to have the row's column values printed.
2353 : */
2354 238 : if (insertStmt == NULL)
2355 : {
2356 : TableInfo *targettab;
2357 :
2358 138 : insertStmt = createPQExpBuffer();
2359 :
2360 : /*
2361 : * When load-via-partition-root is set or forced, get the root
2362 : * table name for the partition table, so that we can reload data
2363 : * through the root table.
2364 : */
2365 138 : if (tbinfo->ispartition &&
2366 80 : (dopt->load_via_partition_root ||
2367 40 : forcePartitionRootLoad(tbinfo)))
2368 6 : targettab = getRootTableInfo(tbinfo);
2369 : else
2370 132 : targettab = tbinfo;
2371 :
2372 138 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2373 138 : fmtQualifiedDumpable(targettab));
2374 :
2375 : /* corner case for zero-column table */
2376 138 : if (nfields == 0)
2377 : {
2378 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2379 : }
2380 : else
2381 : {
2382 : /* append the list of column names if required */
2383 124 : if (dopt->column_inserts)
2384 : {
2385 54 : appendPQExpBufferChar(insertStmt, '(');
2386 176 : for (int field = 0; field < nfields; field++)
2387 : {
2388 122 : if (field > 0)
2389 68 : appendPQExpBufferStr(insertStmt, ", ");
2390 122 : appendPQExpBufferStr(insertStmt,
2391 122 : fmtId(PQfname(res, field)));
2392 : }
2393 54 : appendPQExpBufferStr(insertStmt, ") ");
2394 : }
2395 :
2396 124 : if (tbinfo->needs_override)
2397 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2398 :
2399 124 : appendPQExpBufferStr(insertStmt, "VALUES");
2400 : }
2401 : }
2402 :
2403 6380 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2404 : {
2405 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2406 6142 : if (rows_this_statement == 0)
2407 6130 : archputs(insertStmt->data, fout);
2408 :
2409 : /*
2410 : * If it is zero-column table then we've already written the
2411 : * complete statement, which will mean we've disobeyed
2412 : * --rows-per-insert when it's set greater than 1. We do support
2413 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2414 : * UNION ALL ... but that's non-standard so we should avoid it
2415 : * given that using INSERTs is mostly only ever needed for
2416 : * cross-database exports.
2417 : */
2418 6142 : if (nfields == 0)
2419 12 : continue;
2420 :
2421 : /* Emit a row heading */
2422 6130 : if (rows_per_statement == 1)
2423 6112 : archputs(" (", fout);
2424 18 : else if (rows_this_statement > 0)
2425 12 : archputs(",\n\t(", fout);
2426 : else
2427 6 : archputs("\n\t(", fout);
2428 :
2429 18498 : for (int field = 0; field < nfields; field++)
2430 : {
2431 12368 : if (field > 0)
2432 6238 : archputs(", ", fout);
2433 12368 : if (attgenerated[field])
2434 : {
2435 4 : archputs("DEFAULT", fout);
2436 4 : continue;
2437 : }
2438 12364 : if (PQgetisnull(res, tuple, field))
2439 : {
2440 166 : archputs("NULL", fout);
2441 166 : continue;
2442 : }
2443 :
2444 : /* XXX This code is partially duplicated in ruleutils.c */
2445 12198 : switch (PQftype(res, field))
2446 : {
2447 8138 : case INT2OID:
2448 : case INT4OID:
2449 : case INT8OID:
2450 : case OIDOID:
2451 : case FLOAT4OID:
2452 : case FLOAT8OID:
2453 : case NUMERICOID:
2454 : {
2455 : /*
2456 : * These types are printed without quotes unless
2457 : * they contain values that aren't accepted by the
2458 : * scanner unquoted (e.g., 'NaN'). Note that
2459 : * strtod() and friends might accept NaN, so we
2460 : * can't use that to test.
2461 : *
2462 : * In reality we only need to defend against
2463 : * infinity and NaN, so we need not get too crazy
2464 : * about pattern matching here.
2465 : */
2466 8138 : const char *s = PQgetvalue(res, tuple, field);
2467 :
2468 8138 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2469 8134 : archputs(s, fout);
2470 : else
2471 4 : archprintf(fout, "'%s'", s);
2472 : }
2473 8138 : break;
2474 :
2475 4 : case BITOID:
2476 : case VARBITOID:
2477 4 : archprintf(fout, "B'%s'",
2478 : PQgetvalue(res, tuple, field));
2479 4 : break;
2480 :
2481 8 : case BOOLOID:
2482 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2483 4 : archputs("true", fout);
2484 : else
2485 4 : archputs("false", fout);
2486 8 : break;
2487 :
2488 4048 : default:
2489 : /* All other types are printed as string literals. */
2490 4048 : resetPQExpBuffer(q);
2491 4048 : appendStringLiteralAH(q,
2492 : PQgetvalue(res, tuple, field),
2493 : fout);
2494 4048 : archputs(q->data, fout);
2495 4048 : break;
2496 : }
2497 : }
2498 :
2499 : /* Terminate the row ... */
2500 6130 : archputs(")", fout);
2501 :
2502 : /* ... and the statement, if the target no. of rows is reached */
2503 6130 : if (++rows_this_statement >= rows_per_statement)
2504 : {
2505 6116 : if (dopt->do_nothing)
2506 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2507 : else
2508 6116 : archputs(";\n", fout);
2509 : /* Reset the row counter */
2510 6116 : rows_this_statement = 0;
2511 : }
2512 : }
2513 :
2514 238 : if (PQntuples(res) <= 0)
2515 : {
2516 138 : PQclear(res);
2517 138 : break;
2518 : }
2519 100 : PQclear(res);
2520 : }
2521 :
2522 : /* Terminate any statements that didn't make the row count. */
2523 138 : if (rows_this_statement > 0)
2524 : {
2525 2 : if (dopt->do_nothing)
2526 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2527 : else
2528 2 : archputs(";\n", fout);
2529 : }
2530 :
2531 138 : archputs("\n\n", fout);
2532 :
2533 138 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2534 :
2535 138 : destroyPQExpBuffer(q);
2536 138 : if (insertStmt != NULL)
2537 138 : destroyPQExpBuffer(insertStmt);
2538 138 : free(attgenerated);
2539 :
2540 138 : return 1;
2541 : }
2542 :
2543 : /*
2544 : * getRootTableInfo:
2545 : * get the root TableInfo for the given partition table.
2546 : */
2547 : static TableInfo *
2548 18 : getRootTableInfo(const TableInfo *tbinfo)
2549 : {
2550 : TableInfo *parentTbinfo;
2551 :
2552 : Assert(tbinfo->ispartition);
2553 : Assert(tbinfo->numParents == 1);
2554 :
2555 18 : parentTbinfo = tbinfo->parents[0];
2556 18 : while (parentTbinfo->ispartition)
2557 : {
2558 : Assert(parentTbinfo->numParents == 1);
2559 0 : parentTbinfo = parentTbinfo->parents[0];
2560 : }
2561 :
2562 18 : return parentTbinfo;
2563 : }
2564 :
2565 : /*
2566 : * forcePartitionRootLoad
2567 : * Check if we must force load_via_partition_root for this partition.
2568 : *
2569 : * This is required if any level of ancestral partitioned table has an
2570 : * unsafe partitioning scheme.
2571 : */
2572 : static bool
2573 1868 : forcePartitionRootLoad(const TableInfo *tbinfo)
2574 : {
2575 : TableInfo *parentTbinfo;
2576 :
2577 : Assert(tbinfo->ispartition);
2578 : Assert(tbinfo->numParents == 1);
2579 :
2580 1868 : parentTbinfo = tbinfo->parents[0];
2581 1868 : if (parentTbinfo->unsafe_partitions)
2582 18 : return true;
2583 2282 : while (parentTbinfo->ispartition)
2584 : {
2585 : Assert(parentTbinfo->numParents == 1);
2586 432 : parentTbinfo = parentTbinfo->parents[0];
2587 432 : if (parentTbinfo->unsafe_partitions)
2588 0 : return true;
2589 : }
2590 :
2591 1850 : return false;
2592 : }
2593 :
2594 : /*
2595 : * dumpTableData -
2596 : * dump the contents of a single table
2597 : *
2598 : * Actually, this just makes an ArchiveEntry for the table contents.
2599 : */
2600 : static void
2601 7024 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2602 : {
2603 7024 : DumpOptions *dopt = fout->dopt;
2604 7024 : TableInfo *tbinfo = tdinfo->tdtable;
2605 7024 : PQExpBuffer copyBuf = createPQExpBuffer();
2606 7024 : PQExpBuffer clistBuf = createPQExpBuffer();
2607 : DataDumperPtr dumpFn;
2608 7024 : char *tdDefn = NULL;
2609 : char *copyStmt;
2610 : const char *copyFrom;
2611 :
2612 : /* We had better have loaded per-column details about this table */
2613 : Assert(tbinfo->interesting);
2614 :
2615 : /*
2616 : * When load-via-partition-root is set or forced, get the root table name
2617 : * for the partition table, so that we can reload data through the root
2618 : * table. Then construct a comment to be inserted into the TOC entry's
2619 : * defn field, so that such cases can be identified reliably.
2620 : */
2621 7024 : if (tbinfo->ispartition &&
2622 3656 : (dopt->load_via_partition_root ||
2623 1828 : forcePartitionRootLoad(tbinfo)))
2624 12 : {
2625 : TableInfo *parentTbinfo;
2626 :
2627 12 : parentTbinfo = getRootTableInfo(tbinfo);
2628 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2629 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2630 : copyFrom);
2631 12 : tdDefn = pg_strdup(copyBuf->data);
2632 : }
2633 : else
2634 7012 : copyFrom = fmtQualifiedDumpable(tbinfo);
2635 :
2636 7024 : if (dopt->dump_inserts == 0)
2637 : {
2638 : /* Dump/restore using COPY */
2639 6886 : dumpFn = dumpTableData_copy;
2640 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2641 6886 : printfPQExpBuffer(copyBuf, "COPY %s ",
2642 : copyFrom);
2643 6886 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2644 : fmtCopyColumnList(tbinfo, clistBuf));
2645 6886 : copyStmt = copyBuf->data;
2646 : }
2647 : else
2648 : {
2649 : /* Restore using INSERT */
2650 138 : dumpFn = dumpTableData_insert;
2651 138 : copyStmt = NULL;
2652 : }
2653 :
2654 : /*
2655 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2656 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2657 : * See comments for BuildArchiveDependencies.
2658 : */
2659 7024 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2660 : {
2661 : TocEntry *te;
2662 :
2663 7024 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2664 7024 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2665 : .namespace = tbinfo->dobj.namespace->dobj.name,
2666 : .owner = tbinfo->rolname,
2667 : .description = "TABLE DATA",
2668 : .section = SECTION_DATA,
2669 : .createStmt = tdDefn,
2670 : .copyStmt = copyStmt,
2671 : .deps = &(tbinfo->dobj.dumpId),
2672 : .nDeps = 1,
2673 : .dumpFn = dumpFn,
2674 : .dumpArg = tdinfo));
2675 :
2676 : /*
2677 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2678 : * and want to order dump jobs by table size. We choose to measure
2679 : * dataLength in table pages (including TOAST pages) during dump, so
2680 : * no scaling is needed.
2681 : *
2682 : * However, relpages is declared as "integer" in pg_class, and hence
2683 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2684 : * Cast so that we get the right interpretation of table sizes
2685 : * exceeding INT_MAX pages.
2686 : */
2687 7024 : te->dataLength = (BlockNumber) tbinfo->relpages;
2688 7024 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2689 :
2690 : /*
2691 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2692 : * and instead we'd better worry about integer overflow. Clamp to
2693 : * INT_MAX if the correct result exceeds that.
2694 : */
2695 : if (sizeof(te->dataLength) == 4 &&
2696 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2697 : te->dataLength < 0))
2698 : te->dataLength = INT_MAX;
2699 : }
2700 :
2701 7024 : destroyPQExpBuffer(copyBuf);
2702 7024 : destroyPQExpBuffer(clistBuf);
2703 7024 : }
2704 :
2705 : /*
2706 : * refreshMatViewData -
2707 : * load or refresh the contents of a single materialized view
2708 : *
2709 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2710 : * statement.
2711 : */
2712 : static void
2713 676 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2714 : {
2715 676 : TableInfo *tbinfo = tdinfo->tdtable;
2716 : PQExpBuffer q;
2717 :
2718 : /* If the materialized view is not flagged as populated, skip this. */
2719 676 : if (!tbinfo->relispopulated)
2720 136 : return;
2721 :
2722 540 : q = createPQExpBuffer();
2723 :
2724 540 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2725 540 : fmtQualifiedDumpable(tbinfo));
2726 :
2727 540 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2728 540 : ArchiveEntry(fout,
2729 : tdinfo->dobj.catId, /* catalog ID */
2730 : tdinfo->dobj.dumpId, /* dump ID */
2731 540 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2732 : .namespace = tbinfo->dobj.namespace->dobj.name,
2733 : .owner = tbinfo->rolname,
2734 : .description = "MATERIALIZED VIEW DATA",
2735 : .section = SECTION_POST_DATA,
2736 : .createStmt = q->data,
2737 : .deps = tdinfo->dobj.dependencies,
2738 : .nDeps = tdinfo->dobj.nDeps));
2739 :
2740 540 : destroyPQExpBuffer(q);
2741 : }
2742 :
2743 : /*
2744 : * getTableData -
2745 : * set up dumpable objects representing the contents of tables
2746 : */
2747 : static void
2748 300 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2749 : {
2750 : int i;
2751 :
2752 76242 : for (i = 0; i < numTables; i++)
2753 : {
2754 75942 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2755 1620 : (!relkind || tblinfo[i].relkind == relkind))
2756 9916 : makeTableDataInfo(dopt, &(tblinfo[i]));
2757 : }
2758 300 : }
2759 :
2760 : /*
2761 : * Make a dumpable object for the data of this specific table
2762 : *
2763 : * Note: we make a TableDataInfo if and only if we are going to dump the
2764 : * table data; the "dump" field in such objects isn't very interesting.
2765 : */
2766 : static void
2767 9994 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2768 : {
2769 : TableDataInfo *tdinfo;
2770 :
2771 : /*
2772 : * Nothing to do if we already decided to dump the table. This will
2773 : * happen for "config" tables.
2774 : */
2775 9994 : if (tbinfo->dataObj != NULL)
2776 2 : return;
2777 :
2778 : /* Skip VIEWs (no data to dump) */
2779 9992 : if (tbinfo->relkind == RELKIND_VIEW)
2780 600 : return;
2781 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2782 9392 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2783 76 : (foreign_servers_include_oids.head == NULL ||
2784 8 : !simple_oid_list_member(&foreign_servers_include_oids,
2785 : tbinfo->foreign_server)))
2786 74 : return;
2787 : /* Skip partitioned tables (data in partitions) */
2788 9318 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
2789 852 : return;
2790 :
2791 : /* Don't dump data in unlogged tables, if so requested */
2792 8466 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
2793 64 : dopt->no_unlogged_table_data)
2794 28 : return;
2795 :
2796 : /* Check that the data is not explicitly excluded */
2797 8438 : if (simple_oid_list_member(&tabledata_exclude_oids,
2798 : tbinfo->dobj.catId.oid))
2799 16 : return;
2800 :
2801 : /* OK, let's dump it */
2802 8422 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
2803 :
2804 8422 : if (tbinfo->relkind == RELKIND_MATVIEW)
2805 676 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
2806 7746 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
2807 722 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
2808 : else
2809 7024 : tdinfo->dobj.objType = DO_TABLE_DATA;
2810 :
2811 : /*
2812 : * Note: use tableoid 0 so that this object won't be mistaken for
2813 : * something that pg_depend entries apply to.
2814 : */
2815 8422 : tdinfo->dobj.catId.tableoid = 0;
2816 8422 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
2817 8422 : AssignDumpId(&tdinfo->dobj);
2818 8422 : tdinfo->dobj.name = tbinfo->dobj.name;
2819 8422 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
2820 8422 : tdinfo->tdtable = tbinfo;
2821 8422 : tdinfo->filtercond = NULL; /* might get set later */
2822 8422 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
2823 :
2824 : /* A TableDataInfo contains data, of course */
2825 8422 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
2826 :
2827 8422 : tbinfo->dataObj = tdinfo;
2828 :
2829 : /* Make sure that we'll collect per-column info for this table. */
2830 8422 : tbinfo->interesting = true;
2831 : }
2832 :
2833 : /*
2834 : * The refresh for a materialized view must be dependent on the refresh for
2835 : * any materialized view that this one is dependent on.
2836 : *
2837 : * This must be called after all the objects are created, but before they are
2838 : * sorted.
2839 : */
2840 : static void
2841 272 : buildMatViewRefreshDependencies(Archive *fout)
2842 : {
2843 : PQExpBuffer query;
2844 : PGresult *res;
2845 : int ntups,
2846 : i;
2847 : int i_classid,
2848 : i_objid,
2849 : i_refobjid;
2850 :
2851 : /* No Mat Views before 9.3. */
2852 272 : if (fout->remoteVersion < 90300)
2853 0 : return;
2854 :
2855 272 : query = createPQExpBuffer();
2856 :
2857 272 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
2858 : "( "
2859 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
2860 : "FROM pg_depend d1 "
2861 : "JOIN pg_class c1 ON c1.oid = d1.objid "
2862 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
2863 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
2864 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
2865 : "AND d2.objid = r1.oid "
2866 : "AND d2.refobjid <> d1.objid "
2867 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
2868 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2869 : CppAsString2(RELKIND_VIEW) ") "
2870 : "WHERE d1.classid = 'pg_class'::regclass "
2871 : "UNION "
2872 : "SELECT w.objid, d3.refobjid, c3.relkind "
2873 : "FROM w "
2874 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
2875 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
2876 : "AND d3.objid = r3.oid "
2877 : "AND d3.refobjid <> w.refobjid "
2878 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
2879 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
2880 : CppAsString2(RELKIND_VIEW) ") "
2881 : ") "
2882 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
2883 : "FROM w "
2884 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
2885 :
2886 272 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
2887 :
2888 272 : ntups = PQntuples(res);
2889 :
2890 272 : i_classid = PQfnumber(res, "classid");
2891 272 : i_objid = PQfnumber(res, "objid");
2892 272 : i_refobjid = PQfnumber(res, "refobjid");
2893 :
2894 788 : for (i = 0; i < ntups; i++)
2895 : {
2896 : CatalogId objId;
2897 : CatalogId refobjId;
2898 : DumpableObject *dobj;
2899 : DumpableObject *refdobj;
2900 : TableInfo *tbinfo;
2901 : TableInfo *reftbinfo;
2902 :
2903 516 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
2904 516 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
2905 516 : refobjId.tableoid = objId.tableoid;
2906 516 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
2907 :
2908 516 : dobj = findObjectByCatalogId(objId);
2909 516 : if (dobj == NULL)
2910 84 : continue;
2911 :
2912 : Assert(dobj->objType == DO_TABLE);
2913 516 : tbinfo = (TableInfo *) dobj;
2914 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
2915 516 : dobj = (DumpableObject *) tbinfo->dataObj;
2916 516 : if (dobj == NULL)
2917 84 : continue;
2918 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
2919 :
2920 432 : refdobj = findObjectByCatalogId(refobjId);
2921 432 : if (refdobj == NULL)
2922 0 : continue;
2923 :
2924 : Assert(refdobj->objType == DO_TABLE);
2925 432 : reftbinfo = (TableInfo *) refdobj;
2926 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
2927 432 : refdobj = (DumpableObject *) reftbinfo->dataObj;
2928 432 : if (refdobj == NULL)
2929 0 : continue;
2930 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
2931 :
2932 432 : addObjectDependency(dobj, refdobj->dumpId);
2933 :
2934 432 : if (!reftbinfo->relispopulated)
2935 68 : tbinfo->relispopulated = false;
2936 : }
2937 :
2938 272 : PQclear(res);
2939 :
2940 272 : destroyPQExpBuffer(query);
2941 : }
2942 :
2943 : /*
2944 : * getTableDataFKConstraints -
2945 : * add dump-order dependencies reflecting foreign key constraints
2946 : *
2947 : * This code is executed only in a data-only dump --- in schema+data dumps
2948 : * we handle foreign key issues by not creating the FK constraints until
2949 : * after the data is loaded. In a data-only dump, however, we want to
2950 : * order the table data objects in such a way that a table's referenced
2951 : * tables are restored first. (In the presence of circular references or
2952 : * self-references this may be impossible; we'll detect and complain about
2953 : * that during the dependency sorting step.)
2954 : */
2955 : static void
2956 12 : getTableDataFKConstraints(void)
2957 : {
2958 : DumpableObject **dobjs;
2959 : int numObjs;
2960 : int i;
2961 :
2962 : /* Search through all the dumpable objects for FK constraints */
2963 12 : getDumpableObjects(&dobjs, &numObjs);
2964 42770 : for (i = 0; i < numObjs; i++)
2965 : {
2966 42758 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
2967 : {
2968 12 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
2969 : TableInfo *ftable;
2970 :
2971 : /* Not interesting unless both tables are to be dumped */
2972 12 : if (cinfo->contable == NULL ||
2973 12 : cinfo->contable->dataObj == NULL)
2974 6 : continue;
2975 6 : ftable = findTableByOid(cinfo->confrelid);
2976 6 : if (ftable == NULL ||
2977 6 : ftable->dataObj == NULL)
2978 0 : continue;
2979 :
2980 : /*
2981 : * Okay, make referencing table's TABLE_DATA object depend on the
2982 : * referenced table's TABLE_DATA object.
2983 : */
2984 6 : addObjectDependency(&cinfo->contable->dataObj->dobj,
2985 6 : ftable->dataObj->dobj.dumpId);
2986 : }
2987 : }
2988 12 : free(dobjs);
2989 12 : }
2990 :
2991 :
2992 : /*
2993 : * dumpDatabase:
2994 : * dump the database definition
2995 : */
2996 : static void
2997 120 : dumpDatabase(Archive *fout)
2998 : {
2999 120 : DumpOptions *dopt = fout->dopt;
3000 120 : PQExpBuffer dbQry = createPQExpBuffer();
3001 120 : PQExpBuffer delQry = createPQExpBuffer();
3002 120 : PQExpBuffer creaQry = createPQExpBuffer();
3003 120 : PQExpBuffer labelq = createPQExpBuffer();
3004 120 : PGconn *conn = GetConnection(fout);
3005 : PGresult *res;
3006 : int i_tableoid,
3007 : i_oid,
3008 : i_datname,
3009 : i_datdba,
3010 : i_encoding,
3011 : i_datlocprovider,
3012 : i_collate,
3013 : i_ctype,
3014 : i_datlocale,
3015 : i_daticurules,
3016 : i_frozenxid,
3017 : i_minmxid,
3018 : i_datacl,
3019 : i_acldefault,
3020 : i_datistemplate,
3021 : i_datconnlimit,
3022 : i_datcollversion,
3023 : i_tablespace;
3024 : CatalogId dbCatId;
3025 : DumpId dbDumpId;
3026 : DumpableAcl dbdacl;
3027 : const char *datname,
3028 : *dba,
3029 : *encoding,
3030 : *datlocprovider,
3031 : *collate,
3032 : *ctype,
3033 : *locale,
3034 : *icurules,
3035 : *datistemplate,
3036 : *datconnlimit,
3037 : *tablespace;
3038 : uint32 frozenxid,
3039 : minmxid;
3040 : char *qdatname;
3041 :
3042 120 : pg_log_info("saving database definition");
3043 :
3044 : /*
3045 : * Fetch the database-level properties for this database.
3046 : */
3047 120 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3048 : "datdba, "
3049 : "pg_encoding_to_char(encoding) AS encoding, "
3050 : "datcollate, datctype, datfrozenxid, "
3051 : "datacl, acldefault('d', datdba) AS acldefault, "
3052 : "datistemplate, datconnlimit, ");
3053 120 : if (fout->remoteVersion >= 90300)
3054 120 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3055 : else
3056 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3057 120 : if (fout->remoteVersion >= 170000)
3058 120 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3059 0 : else if (fout->remoteVersion >= 150000)
3060 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3061 : else
3062 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3063 120 : if (fout->remoteVersion >= 160000)
3064 120 : appendPQExpBufferStr(dbQry, "daticurules, ");
3065 : else
3066 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3067 120 : appendPQExpBufferStr(dbQry,
3068 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3069 : "shobj_description(oid, 'pg_database') AS description "
3070 : "FROM pg_database "
3071 : "WHERE datname = current_database()");
3072 :
3073 120 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3074 :
3075 120 : i_tableoid = PQfnumber(res, "tableoid");
3076 120 : i_oid = PQfnumber(res, "oid");
3077 120 : i_datname = PQfnumber(res, "datname");
3078 120 : i_datdba = PQfnumber(res, "datdba");
3079 120 : i_encoding = PQfnumber(res, "encoding");
3080 120 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3081 120 : i_collate = PQfnumber(res, "datcollate");
3082 120 : i_ctype = PQfnumber(res, "datctype");
3083 120 : i_datlocale = PQfnumber(res, "datlocale");
3084 120 : i_daticurules = PQfnumber(res, "daticurules");
3085 120 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3086 120 : i_minmxid = PQfnumber(res, "datminmxid");
3087 120 : i_datacl = PQfnumber(res, "datacl");
3088 120 : i_acldefault = PQfnumber(res, "acldefault");
3089 120 : i_datistemplate = PQfnumber(res, "datistemplate");
3090 120 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3091 120 : i_datcollversion = PQfnumber(res, "datcollversion");
3092 120 : i_tablespace = PQfnumber(res, "tablespace");
3093 :
3094 120 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3095 120 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3096 120 : datname = PQgetvalue(res, 0, i_datname);
3097 120 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3098 120 : encoding = PQgetvalue(res, 0, i_encoding);
3099 120 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3100 120 : collate = PQgetvalue(res, 0, i_collate);
3101 120 : ctype = PQgetvalue(res, 0, i_ctype);
3102 120 : if (!PQgetisnull(res, 0, i_datlocale))
3103 28 : locale = PQgetvalue(res, 0, i_datlocale);
3104 : else
3105 92 : locale = NULL;
3106 120 : if (!PQgetisnull(res, 0, i_daticurules))
3107 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3108 : else
3109 120 : icurules = NULL;
3110 120 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3111 120 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3112 120 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3113 120 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3114 120 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3115 120 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3116 120 : tablespace = PQgetvalue(res, 0, i_tablespace);
3117 :
3118 120 : qdatname = pg_strdup(fmtId(datname));
3119 :
3120 : /*
3121 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3122 : * to preserve that), as well as the encoding, locale, and tablespace
3123 : * since those can't be altered later. Other DB properties are left to
3124 : * the DATABASE PROPERTIES entry, so that they can be applied after
3125 : * reconnecting to the target DB.
3126 : */
3127 120 : if (dopt->binary_upgrade)
3128 : {
3129 26 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0 OID = %u",
3130 : qdatname, dbCatId.oid);
3131 : }
3132 : else
3133 : {
3134 94 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3135 : qdatname);
3136 : }
3137 120 : if (strlen(encoding) > 0)
3138 : {
3139 120 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3140 120 : appendStringLiteralAH(creaQry, encoding, fout);
3141 : }
3142 :
3143 120 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3144 120 : if (datlocprovider[0] == 'b')
3145 28 : appendPQExpBufferStr(creaQry, "builtin");
3146 92 : else if (datlocprovider[0] == 'c')
3147 92 : appendPQExpBufferStr(creaQry, "libc");
3148 0 : else if (datlocprovider[0] == 'i')
3149 0 : appendPQExpBufferStr(creaQry, "icu");
3150 : else
3151 0 : pg_fatal("unrecognized locale provider: %s",
3152 : datlocprovider);
3153 :
3154 120 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3155 : {
3156 120 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3157 120 : appendStringLiteralAH(creaQry, collate, fout);
3158 : }
3159 : else
3160 : {
3161 0 : if (strlen(collate) > 0)
3162 : {
3163 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3164 0 : appendStringLiteralAH(creaQry, collate, fout);
3165 : }
3166 0 : if (strlen(ctype) > 0)
3167 : {
3168 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3169 0 : appendStringLiteralAH(creaQry, ctype, fout);
3170 : }
3171 : }
3172 120 : if (locale)
3173 : {
3174 28 : if (datlocprovider[0] == 'b')
3175 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3176 : else
3177 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3178 :
3179 28 : appendStringLiteralAH(creaQry, locale, fout);
3180 : }
3181 :
3182 120 : if (icurules)
3183 : {
3184 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3185 0 : appendStringLiteralAH(creaQry, icurules, fout);
3186 : }
3187 :
3188 : /*
3189 : * For binary upgrade, carry over the collation version. For normal
3190 : * dump/restore, omit the version, so that it is computed upon restore.
3191 : */
3192 120 : if (dopt->binary_upgrade)
3193 : {
3194 26 : if (!PQgetisnull(res, 0, i_datcollversion))
3195 : {
3196 26 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3197 26 : appendStringLiteralAH(creaQry,
3198 : PQgetvalue(res, 0, i_datcollversion),
3199 : fout);
3200 : }
3201 : }
3202 :
3203 : /*
3204 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3205 : * thing; the decision whether to specify a tablespace should be left till
3206 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3207 : * label the DATABASE entry with the tablespace and let the normal
3208 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3209 : * attention to default_tablespace, so that won't work.
3210 : */
3211 120 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3212 0 : !dopt->outputNoTablespaces)
3213 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3214 : fmtId(tablespace));
3215 120 : appendPQExpBufferStr(creaQry, ";\n");
3216 :
3217 120 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3218 : qdatname);
3219 :
3220 120 : dbDumpId = createDumpId();
3221 :
3222 120 : ArchiveEntry(fout,
3223 : dbCatId, /* catalog ID */
3224 : dbDumpId, /* dump ID */
3225 120 : ARCHIVE_OPTS(.tag = datname,
3226 : .owner = dba,
3227 : .description = "DATABASE",
3228 : .section = SECTION_PRE_DATA,
3229 : .createStmt = creaQry->data,
3230 : .dropStmt = delQry->data));
3231 :
3232 : /* Compute correct tag for archive entry */
3233 120 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3234 :
3235 : /* Dump DB comment if any */
3236 : {
3237 : /*
3238 : * 8.2 and up keep comments on shared objects in a shared table, so we
3239 : * cannot use the dumpComment() code used for other database objects.
3240 : * Be careful that the ArchiveEntry parameters match that function.
3241 : */
3242 120 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3243 :
3244 120 : if (comment && *comment && !dopt->no_comments)
3245 : {
3246 50 : resetPQExpBuffer(dbQry);
3247 :
3248 : /*
3249 : * Generates warning when loaded into a differently-named
3250 : * database.
3251 : */
3252 50 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3253 50 : appendStringLiteralAH(dbQry, comment, fout);
3254 50 : appendPQExpBufferStr(dbQry, ";\n");
3255 :
3256 50 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3257 50 : ARCHIVE_OPTS(.tag = labelq->data,
3258 : .owner = dba,
3259 : .description = "COMMENT",
3260 : .section = SECTION_NONE,
3261 : .createStmt = dbQry->data,
3262 : .deps = &dbDumpId,
3263 : .nDeps = 1));
3264 : }
3265 : }
3266 :
3267 : /* Dump DB security label, if enabled */
3268 120 : if (!dopt->no_security_labels)
3269 : {
3270 : PGresult *shres;
3271 : PQExpBuffer seclabelQry;
3272 :
3273 120 : seclabelQry = createPQExpBuffer();
3274 :
3275 120 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3276 120 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3277 120 : resetPQExpBuffer(seclabelQry);
3278 120 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3279 120 : if (seclabelQry->len > 0)
3280 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3281 0 : ARCHIVE_OPTS(.tag = labelq->data,
3282 : .owner = dba,
3283 : .description = "SECURITY LABEL",
3284 : .section = SECTION_NONE,
3285 : .createStmt = seclabelQry->data,
3286 : .deps = &dbDumpId,
3287 : .nDeps = 1));
3288 120 : destroyPQExpBuffer(seclabelQry);
3289 120 : PQclear(shres);
3290 : }
3291 :
3292 : /*
3293 : * Dump ACL if any. Note that we do not support initial privileges
3294 : * (pg_init_privs) on databases.
3295 : */
3296 120 : dbdacl.privtype = 0;
3297 120 : dbdacl.initprivs = NULL;
3298 :
3299 120 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3300 : qdatname, NULL, NULL,
3301 : NULL, dba, &dbdacl);
3302 :
3303 : /*
3304 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3305 : * non-default database-level properties. (The reason this must be
3306 : * separate is that we cannot put any additional commands into the TOC
3307 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3308 : * in an implicit transaction block, and the backend won't allow CREATE
3309 : * DATABASE in that context.)
3310 : */
3311 120 : resetPQExpBuffer(creaQry);
3312 120 : resetPQExpBuffer(delQry);
3313 :
3314 120 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3315 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3316 : qdatname, datconnlimit);
3317 :
3318 120 : if (strcmp(datistemplate, "t") == 0)
3319 : {
3320 8 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3321 : qdatname);
3322 :
3323 : /*
3324 : * The backend won't accept DROP DATABASE on a template database. We
3325 : * can deal with that by removing the template marking before the DROP
3326 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3327 : * since no such command is currently supported, fake it with a direct
3328 : * UPDATE on pg_database.
3329 : */
3330 8 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3331 : "SET datistemplate = false WHERE datname = ");
3332 8 : appendStringLiteralAH(delQry, datname, fout);
3333 8 : appendPQExpBufferStr(delQry, ";\n");
3334 : }
3335 :
3336 : /*
3337 : * We do not restore pg_database.dathasloginevt because it is set
3338 : * automatically on login event trigger creation.
3339 : */
3340 :
3341 : /* Add database-specific SET options */
3342 120 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3343 :
3344 : /*
3345 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3346 : * entry, too, for lack of a better place.
3347 : */
3348 120 : if (dopt->binary_upgrade)
3349 : {
3350 26 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3351 26 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3352 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3353 : "WHERE datname = ",
3354 : frozenxid, minmxid);
3355 26 : appendStringLiteralAH(creaQry, datname, fout);
3356 26 : appendPQExpBufferStr(creaQry, ";\n");
3357 : }
3358 :
3359 120 : if (creaQry->len > 0)
3360 34 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3361 34 : ARCHIVE_OPTS(.tag = datname,
3362 : .owner = dba,
3363 : .description = "DATABASE PROPERTIES",
3364 : .section = SECTION_PRE_DATA,
3365 : .createStmt = creaQry->data,
3366 : .dropStmt = delQry->data,
3367 : .deps = &dbDumpId));
3368 :
3369 : /*
3370 : * pg_largeobject comes from the old system intact, so set its
3371 : * relfrozenxids, relminmxids and relfilenode.
3372 : */
3373 120 : if (dopt->binary_upgrade)
3374 : {
3375 : PGresult *lo_res;
3376 26 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3377 26 : PQExpBuffer loOutQry = createPQExpBuffer();
3378 26 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3379 : int ii_relfrozenxid,
3380 : ii_relfilenode,
3381 : ii_oid,
3382 : ii_relminmxid;
3383 :
3384 : /*
3385 : * pg_largeobject
3386 : */
3387 26 : if (fout->remoteVersion >= 90300)
3388 26 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3389 : "FROM pg_catalog.pg_class\n"
3390 : "WHERE oid IN (%u, %u);\n",
3391 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3392 : else
3393 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3394 : "FROM pg_catalog.pg_class\n"
3395 : "WHERE oid IN (%u, %u);\n",
3396 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3397 :
3398 26 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3399 :
3400 26 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3401 26 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3402 26 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3403 26 : ii_oid = PQfnumber(lo_res, "oid");
3404 :
3405 26 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3406 26 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3407 78 : for (int i = 0; i < PQntuples(lo_res); ++i)
3408 : {
3409 : Oid oid;
3410 : RelFileNumber relfilenumber;
3411 :
3412 52 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3413 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3414 : "WHERE oid = %u;\n",
3415 52 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3416 52 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3417 52 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3418 :
3419 52 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3420 52 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3421 :
3422 52 : if (oid == LargeObjectRelationId)
3423 26 : appendPQExpBuffer(loOutQry,
3424 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3425 : relfilenumber);
3426 26 : else if (oid == LargeObjectLOidPNIndexId)
3427 26 : appendPQExpBuffer(loOutQry,
3428 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3429 : relfilenumber);
3430 : }
3431 :
3432 26 : appendPQExpBufferStr(loOutQry,
3433 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3434 26 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3435 :
3436 26 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3437 26 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3438 : .description = "pg_largeobject",
3439 : .section = SECTION_PRE_DATA,
3440 : .createStmt = loOutQry->data));
3441 :
3442 26 : PQclear(lo_res);
3443 :
3444 26 : destroyPQExpBuffer(loFrozenQry);
3445 26 : destroyPQExpBuffer(loHorizonQry);
3446 26 : destroyPQExpBuffer(loOutQry);
3447 : }
3448 :
3449 120 : PQclear(res);
3450 :
3451 120 : free(qdatname);
3452 120 : destroyPQExpBuffer(dbQry);
3453 120 : destroyPQExpBuffer(delQry);
3454 120 : destroyPQExpBuffer(creaQry);
3455 120 : destroyPQExpBuffer(labelq);
3456 120 : }
3457 :
3458 : /*
3459 : * Collect any database-specific or role-and-database-specific SET options
3460 : * for this database, and append them to outbuf.
3461 : */
3462 : static void
3463 120 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3464 : const char *dbname, Oid dboid)
3465 : {
3466 120 : PGconn *conn = GetConnection(AH);
3467 120 : PQExpBuffer buf = createPQExpBuffer();
3468 : PGresult *res;
3469 :
3470 : /* First collect database-specific options */
3471 120 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3472 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3473 : dboid);
3474 :
3475 120 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3476 :
3477 180 : for (int i = 0; i < PQntuples(res); i++)
3478 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3479 : "DATABASE", dbname, NULL, NULL,
3480 : outbuf);
3481 :
3482 120 : PQclear(res);
3483 :
3484 : /* Now look for role-and-database-specific options */
3485 120 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3486 : "FROM pg_db_role_setting s, pg_roles r "
3487 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3488 : dboid);
3489 :
3490 120 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3491 :
3492 120 : for (int i = 0; i < PQntuples(res); i++)
3493 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3494 0 : "ROLE", PQgetvalue(res, i, 0),
3495 : "DATABASE", dbname,
3496 : outbuf);
3497 :
3498 120 : PQclear(res);
3499 :
3500 120 : destroyPQExpBuffer(buf);
3501 120 : }
3502 :
3503 : /*
3504 : * dumpEncoding: put the correct encoding into the archive
3505 : */
3506 : static void
3507 304 : dumpEncoding(Archive *AH)
3508 : {
3509 304 : const char *encname = pg_encoding_to_char(AH->encoding);
3510 304 : PQExpBuffer qry = createPQExpBuffer();
3511 :
3512 304 : pg_log_info("saving encoding = %s", encname);
3513 :
3514 304 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3515 304 : appendStringLiteralAH(qry, encname, AH);
3516 304 : appendPQExpBufferStr(qry, ";\n");
3517 :
3518 304 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3519 304 : ARCHIVE_OPTS(.tag = "ENCODING",
3520 : .description = "ENCODING",
3521 : .section = SECTION_PRE_DATA,
3522 : .createStmt = qry->data));
3523 :
3524 304 : destroyPQExpBuffer(qry);
3525 304 : }
3526 :
3527 :
3528 : /*
3529 : * dumpStdStrings: put the correct escape string behavior into the archive
3530 : */
3531 : static void
3532 304 : dumpStdStrings(Archive *AH)
3533 : {
3534 304 : const char *stdstrings = AH->std_strings ? "on" : "off";
3535 304 : PQExpBuffer qry = createPQExpBuffer();
3536 :
3537 304 : pg_log_info("saving standard_conforming_strings = %s",
3538 : stdstrings);
3539 :
3540 304 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3541 : stdstrings);
3542 :
3543 304 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3544 304 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3545 : .description = "STDSTRINGS",
3546 : .section = SECTION_PRE_DATA,
3547 : .createStmt = qry->data));
3548 :
3549 304 : destroyPQExpBuffer(qry);
3550 304 : }
3551 :
3552 : /*
3553 : * dumpSearchPath: record the active search_path in the archive
3554 : */
3555 : static void
3556 304 : dumpSearchPath(Archive *AH)
3557 : {
3558 304 : PQExpBuffer qry = createPQExpBuffer();
3559 304 : PQExpBuffer path = createPQExpBuffer();
3560 : PGresult *res;
3561 304 : char **schemanames = NULL;
3562 304 : int nschemanames = 0;
3563 : int i;
3564 :
3565 : /*
3566 : * We use the result of current_schemas(), not the search_path GUC,
3567 : * because that might contain wildcards such as "$user", which won't
3568 : * necessarily have the same value during restore. Also, this way avoids
3569 : * listing schemas that may appear in search_path but not actually exist,
3570 : * which seems like a prudent exclusion.
3571 : */
3572 304 : res = ExecuteSqlQueryForSingleRow(AH,
3573 : "SELECT pg_catalog.current_schemas(false)");
3574 :
3575 304 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3576 0 : pg_fatal("could not parse result of current_schemas()");
3577 :
3578 : /*
3579 : * We use set_config(), not a simple "SET search_path" command, because
3580 : * the latter has less-clean behavior if the search path is empty. While
3581 : * that's likely to get fixed at some point, it seems like a good idea to
3582 : * be as backwards-compatible as possible in what we put into archives.
3583 : */
3584 304 : for (i = 0; i < nschemanames; i++)
3585 : {
3586 0 : if (i > 0)
3587 0 : appendPQExpBufferStr(path, ", ");
3588 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3589 : }
3590 :
3591 304 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3592 304 : appendStringLiteralAH(qry, path->data, AH);
3593 304 : appendPQExpBufferStr(qry, ", false);\n");
3594 :
3595 304 : pg_log_info("saving search_path = %s", path->data);
3596 :
3597 304 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3598 304 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3599 : .description = "SEARCHPATH",
3600 : .section = SECTION_PRE_DATA,
3601 : .createStmt = qry->data));
3602 :
3603 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3604 304 : AH->searchpath = pg_strdup(qry->data);
3605 :
3606 304 : free(schemanames);
3607 304 : PQclear(res);
3608 304 : destroyPQExpBuffer(qry);
3609 304 : destroyPQExpBuffer(path);
3610 304 : }
3611 :
3612 :
3613 : /*
3614 : * getLOs:
3615 : * Collect schema-level data about large objects
3616 : */
3617 : static void
3618 256 : getLOs(Archive *fout)
3619 : {
3620 256 : DumpOptions *dopt = fout->dopt;
3621 256 : PQExpBuffer loQry = createPQExpBuffer();
3622 : PGresult *res;
3623 : int ntups;
3624 : int i;
3625 : int n;
3626 : int i_oid;
3627 : int i_lomowner;
3628 : int i_lomacl;
3629 : int i_acldefault;
3630 :
3631 256 : pg_log_info("reading large objects");
3632 :
3633 : /*
3634 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3635 : * with the same owner/ACL appear together.
3636 : */
3637 256 : appendPQExpBufferStr(loQry,
3638 : "SELECT oid, lomowner, lomacl, "
3639 : "acldefault('L', lomowner) AS acldefault "
3640 : "FROM pg_largeobject_metadata "
3641 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3642 :
3643 256 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3644 :
3645 256 : i_oid = PQfnumber(res, "oid");
3646 256 : i_lomowner = PQfnumber(res, "lomowner");
3647 256 : i_lomacl = PQfnumber(res, "lomacl");
3648 256 : i_acldefault = PQfnumber(res, "acldefault");
3649 :
3650 256 : ntups = PQntuples(res);
3651 :
3652 : /*
3653 : * Group the blobs into suitably-sized groups that have the same owner and
3654 : * ACL setting, and build a metadata and a data DumpableObject for each
3655 : * group. (If we supported initprivs for blobs, we'd have to insist that
3656 : * groups also share initprivs settings, since the DumpableObject only has
3657 : * room for one.) i is the index of the first tuple in the current group,
3658 : * and n is the number of tuples we include in the group.
3659 : */
3660 402 : for (i = 0; i < ntups; i += n)
3661 : {
3662 146 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3663 146 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3664 146 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3665 : LoInfo *loinfo;
3666 : DumpableObject *lodata;
3667 : char namebuf[64];
3668 :
3669 : /* Scan to find first tuple not to be included in group */
3670 146 : n = 1;
3671 166 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3672 : {
3673 88 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3674 88 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3675 : break;
3676 20 : n++;
3677 : }
3678 :
3679 : /* Build the metadata DumpableObject */
3680 146 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3681 :
3682 146 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3683 146 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3684 146 : loinfo->dobj.catId.oid = thisoid;
3685 146 : AssignDumpId(&loinfo->dobj);
3686 :
3687 146 : if (n > 1)
3688 10 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3689 10 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3690 : else
3691 136 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3692 146 : loinfo->dobj.name = pg_strdup(namebuf);
3693 146 : loinfo->dacl.acl = pg_strdup(thisacl);
3694 146 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3695 146 : loinfo->dacl.privtype = 0;
3696 146 : loinfo->dacl.initprivs = NULL;
3697 146 : loinfo->rolname = getRoleName(thisowner);
3698 146 : loinfo->numlos = n;
3699 146 : loinfo->looids[0] = thisoid;
3700 : /* Collect OIDs of the remaining blobs in this group */
3701 166 : for (int k = 1; k < n; k++)
3702 : {
3703 : CatalogId extraID;
3704 :
3705 20 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3706 :
3707 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3708 20 : extraID.tableoid = LargeObjectRelationId;
3709 20 : extraID.oid = loinfo->looids[k];
3710 20 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3711 : }
3712 :
3713 : /* LOs have data */
3714 146 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3715 :
3716 : /* Mark whether LO group has a non-empty ACL */
3717 146 : if (!PQgetisnull(res, i, i_lomacl))
3718 68 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3719 :
3720 : /*
3721 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3722 : * as it will be copied by pg_upgrade, which simply copies the
3723 : * pg_largeobject table. We *do* however dump out anything but the
3724 : * data, as pg_upgrade copies just pg_largeobject, but not
3725 : * pg_largeobject_metadata, after the dump is restored.
3726 : */
3727 146 : if (dopt->binary_upgrade)
3728 6 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3729 :
3730 : /*
3731 : * Create a "BLOBS" data item for the group, too. This is just a
3732 : * placeholder for sorting; it carries no data now.
3733 : */
3734 146 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
3735 146 : lodata->objType = DO_LARGE_OBJECT_DATA;
3736 146 : lodata->catId = nilCatalogId;
3737 146 : AssignDumpId(lodata);
3738 146 : lodata->name = pg_strdup(namebuf);
3739 146 : lodata->components |= DUMP_COMPONENT_DATA;
3740 : /* Set up explicit dependency from data to metadata */
3741 146 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3742 146 : lodata->dependencies[0] = loinfo->dobj.dumpId;
3743 146 : lodata->nDeps = lodata->allocDeps = 1;
3744 : }
3745 :
3746 256 : PQclear(res);
3747 256 : destroyPQExpBuffer(loQry);
3748 256 : }
3749 :
3750 : /*
3751 : * dumpLO
3752 : *
3753 : * dump the definition (metadata) of the given large object group
3754 : */
3755 : static void
3756 146 : dumpLO(Archive *fout, const LoInfo *loinfo)
3757 : {
3758 146 : PQExpBuffer cquery = createPQExpBuffer();
3759 :
3760 : /*
3761 : * The "definition" is just a newline-separated list of OIDs. We need to
3762 : * put something into the dropStmt too, but it can just be a comment.
3763 : */
3764 312 : for (int i = 0; i < loinfo->numlos; i++)
3765 166 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
3766 :
3767 146 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
3768 146 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
3769 146 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
3770 : .owner = loinfo->rolname,
3771 : .description = "BLOB METADATA",
3772 : .section = SECTION_DATA,
3773 : .createStmt = cquery->data,
3774 : .dropStmt = "-- dummy"));
3775 :
3776 : /*
3777 : * Dump per-blob comments and seclabels if any. We assume these are rare
3778 : * enough that it's okay to generate retail TOC entries for them.
3779 : */
3780 146 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
3781 : DUMP_COMPONENT_SECLABEL))
3782 : {
3783 176 : for (int i = 0; i < loinfo->numlos; i++)
3784 : {
3785 : CatalogId catId;
3786 : char namebuf[32];
3787 :
3788 : /* Build identifying info for this blob */
3789 98 : catId.tableoid = loinfo->dobj.catId.tableoid;
3790 98 : catId.oid = loinfo->looids[i];
3791 98 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
3792 :
3793 98 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
3794 98 : dumpComment(fout, "LARGE OBJECT", namebuf,
3795 : NULL, loinfo->rolname,
3796 : catId, 0, loinfo->dobj.dumpId);
3797 :
3798 98 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
3799 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
3800 : NULL, loinfo->rolname,
3801 : catId, 0, loinfo->dobj.dumpId);
3802 : }
3803 : }
3804 :
3805 : /*
3806 : * Dump the ACLs if any (remember that all blobs in the group will have
3807 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
3808 : * there's more, make a "LARGE OBJECTS" entry that really contains only
3809 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
3810 : * string to emit a mutated version for each blob.
3811 : */
3812 146 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
3813 : {
3814 : char namebuf[32];
3815 :
3816 : /* Build identifying info for the first blob */
3817 68 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
3818 :
3819 68 : if (loinfo->numlos > 1)
3820 : {
3821 : char tagbuf[64];
3822 :
3823 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
3824 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
3825 :
3826 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3827 : "LARGE OBJECT", namebuf, NULL, NULL,
3828 : tagbuf, loinfo->rolname, &loinfo->dacl);
3829 : }
3830 : else
3831 : {
3832 68 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
3833 : "LARGE OBJECT", namebuf, NULL, NULL,
3834 : NULL, loinfo->rolname, &loinfo->dacl);
3835 : }
3836 : }
3837 :
3838 146 : destroyPQExpBuffer(cquery);
3839 146 : }
3840 :
3841 : /*
3842 : * dumpLOs:
3843 : * dump the data contents of the large objects in the given group
3844 : */
3845 : static int
3846 132 : dumpLOs(Archive *fout, const void *arg)
3847 : {
3848 132 : const LoInfo *loinfo = (const LoInfo *) arg;
3849 132 : PGconn *conn = GetConnection(fout);
3850 : char buf[LOBBUFSIZE];
3851 :
3852 132 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
3853 :
3854 280 : for (int i = 0; i < loinfo->numlos; i++)
3855 : {
3856 148 : Oid loOid = loinfo->looids[i];
3857 : int loFd;
3858 : int cnt;
3859 :
3860 : /* Open the LO */
3861 148 : loFd = lo_open(conn, loOid, INV_READ);
3862 148 : if (loFd == -1)
3863 0 : pg_fatal("could not open large object %u: %s",
3864 : loOid, PQerrorMessage(conn));
3865 :
3866 148 : StartLO(fout, loOid);
3867 :
3868 : /* Now read it in chunks, sending data to archive */
3869 : do
3870 : {
3871 226 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
3872 226 : if (cnt < 0)
3873 0 : pg_fatal("error reading large object %u: %s",
3874 : loOid, PQerrorMessage(conn));
3875 :
3876 226 : WriteData(fout, buf, cnt);
3877 226 : } while (cnt > 0);
3878 :
3879 148 : lo_close(conn, loFd);
3880 :
3881 148 : EndLO(fout, loOid);
3882 : }
3883 :
3884 132 : return 1;
3885 : }
3886 :
3887 : /*
3888 : * getPolicies
3889 : * get information about all RLS policies on dumpable tables.
3890 : */
3891 : void
3892 304 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
3893 : {
3894 : PQExpBuffer query;
3895 : PQExpBuffer tbloids;
3896 : PGresult *res;
3897 : PolicyInfo *polinfo;
3898 : int i_oid;
3899 : int i_tableoid;
3900 : int i_polrelid;
3901 : int i_polname;
3902 : int i_polcmd;
3903 : int i_polpermissive;
3904 : int i_polroles;
3905 : int i_polqual;
3906 : int i_polwithcheck;
3907 : int i,
3908 : j,
3909 : ntups;
3910 :
3911 : /* No policies before 9.5 */
3912 304 : if (fout->remoteVersion < 90500)
3913 0 : return;
3914 :
3915 304 : query = createPQExpBuffer();
3916 304 : tbloids = createPQExpBuffer();
3917 :
3918 : /*
3919 : * Identify tables of interest, and check which ones have RLS enabled.
3920 : */
3921 304 : appendPQExpBufferChar(tbloids, '{');
3922 77210 : for (i = 0; i < numTables; i++)
3923 : {
3924 76906 : TableInfo *tbinfo = &tblinfo[i];
3925 :
3926 : /* Ignore row security on tables not to be dumped */
3927 76906 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
3928 65382 : continue;
3929 :
3930 : /* It can't have RLS or policies if it's not a table */
3931 11524 : if (tbinfo->relkind != RELKIND_RELATION &&
3932 3236 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
3933 2212 : continue;
3934 :
3935 : /* Add it to the list of table OIDs to be probed below */
3936 9312 : if (tbloids->len > 1) /* do we have more than the '{'? */
3937 9120 : appendPQExpBufferChar(tbloids, ',');
3938 9312 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
3939 :
3940 : /* Is RLS enabled? (That's separate from whether it has policies) */
3941 9312 : if (tbinfo->rowsec)
3942 : {
3943 104 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
3944 :
3945 : /*
3946 : * We represent RLS being enabled on a table by creating a
3947 : * PolicyInfo object with null polname.
3948 : *
3949 : * Note: use tableoid 0 so that this object won't be mistaken for
3950 : * something that pg_depend entries apply to.
3951 : */
3952 104 : polinfo = pg_malloc(sizeof(PolicyInfo));
3953 104 : polinfo->dobj.objType = DO_POLICY;
3954 104 : polinfo->dobj.catId.tableoid = 0;
3955 104 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3956 104 : AssignDumpId(&polinfo->dobj);
3957 104 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
3958 104 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
3959 104 : polinfo->poltable = tbinfo;
3960 104 : polinfo->polname = NULL;
3961 104 : polinfo->polcmd = '\0';
3962 104 : polinfo->polpermissive = 0;
3963 104 : polinfo->polroles = NULL;
3964 104 : polinfo->polqual = NULL;
3965 104 : polinfo->polwithcheck = NULL;
3966 : }
3967 : }
3968 304 : appendPQExpBufferChar(tbloids, '}');
3969 :
3970 : /*
3971 : * Now, read all RLS policies belonging to the tables of interest, and
3972 : * create PolicyInfo objects for them. (Note that we must filter the
3973 : * results server-side not locally, because we dare not apply pg_get_expr
3974 : * to tables we don't have lock on.)
3975 : */
3976 304 : pg_log_info("reading row-level security policies");
3977 :
3978 304 : printfPQExpBuffer(query,
3979 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
3980 304 : if (fout->remoteVersion >= 100000)
3981 304 : appendPQExpBufferStr(query, "pol.polpermissive, ");
3982 : else
3983 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
3984 304 : appendPQExpBuffer(query,
3985 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
3986 : " 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, "
3987 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
3988 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
3989 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
3990 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
3991 : tbloids->data);
3992 :
3993 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3994 :
3995 304 : ntups = PQntuples(res);
3996 304 : if (ntups > 0)
3997 : {
3998 84 : i_oid = PQfnumber(res, "oid");
3999 84 : i_tableoid = PQfnumber(res, "tableoid");
4000 84 : i_polrelid = PQfnumber(res, "polrelid");
4001 84 : i_polname = PQfnumber(res, "polname");
4002 84 : i_polcmd = PQfnumber(res, "polcmd");
4003 84 : i_polpermissive = PQfnumber(res, "polpermissive");
4004 84 : i_polroles = PQfnumber(res, "polroles");
4005 84 : i_polqual = PQfnumber(res, "polqual");
4006 84 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4007 :
4008 84 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4009 :
4010 618 : for (j = 0; j < ntups; j++)
4011 : {
4012 534 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4013 534 : TableInfo *tbinfo = findTableByOid(polrelid);
4014 :
4015 534 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4016 :
4017 534 : polinfo[j].dobj.objType = DO_POLICY;
4018 534 : polinfo[j].dobj.catId.tableoid =
4019 534 : atooid(PQgetvalue(res, j, i_tableoid));
4020 534 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4021 534 : AssignDumpId(&polinfo[j].dobj);
4022 534 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4023 534 : polinfo[j].poltable = tbinfo;
4024 534 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4025 534 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4026 :
4027 534 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4028 534 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4029 :
4030 534 : if (PQgetisnull(res, j, i_polroles))
4031 238 : polinfo[j].polroles = NULL;
4032 : else
4033 296 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4034 :
4035 534 : if (PQgetisnull(res, j, i_polqual))
4036 74 : polinfo[j].polqual = NULL;
4037 : else
4038 460 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4039 :
4040 534 : if (PQgetisnull(res, j, i_polwithcheck))
4041 282 : polinfo[j].polwithcheck = NULL;
4042 : else
4043 252 : polinfo[j].polwithcheck
4044 252 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4045 : }
4046 : }
4047 :
4048 304 : PQclear(res);
4049 :
4050 304 : destroyPQExpBuffer(query);
4051 304 : destroyPQExpBuffer(tbloids);
4052 : }
4053 :
4054 : /*
4055 : * dumpPolicy
4056 : * dump the definition of the given policy
4057 : */
4058 : static void
4059 638 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4060 : {
4061 638 : DumpOptions *dopt = fout->dopt;
4062 638 : TableInfo *tbinfo = polinfo->poltable;
4063 : PQExpBuffer query;
4064 : PQExpBuffer delqry;
4065 : PQExpBuffer polprefix;
4066 : char *qtabname;
4067 : const char *cmd;
4068 : char *tag;
4069 :
4070 : /* Do nothing in data-only dump */
4071 638 : if (dopt->dataOnly)
4072 56 : return;
4073 :
4074 : /*
4075 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4076 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4077 : * ROW LEVEL SECURITY.
4078 : */
4079 582 : if (polinfo->polname == NULL)
4080 : {
4081 96 : query = createPQExpBuffer();
4082 :
4083 96 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4084 96 : fmtQualifiedDumpable(tbinfo));
4085 :
4086 : /*
4087 : * We must emit the ROW SECURITY object's dependency on its table
4088 : * explicitly, because it will not match anything in pg_depend (unlike
4089 : * the case for other PolicyInfo objects).
4090 : */
4091 96 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4092 96 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4093 96 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4094 : .namespace = polinfo->dobj.namespace->dobj.name,
4095 : .owner = tbinfo->rolname,
4096 : .description = "ROW SECURITY",
4097 : .section = SECTION_POST_DATA,
4098 : .createStmt = query->data,
4099 : .deps = &(tbinfo->dobj.dumpId),
4100 : .nDeps = 1));
4101 :
4102 96 : destroyPQExpBuffer(query);
4103 96 : return;
4104 : }
4105 :
4106 486 : if (polinfo->polcmd == '*')
4107 162 : cmd = "";
4108 324 : else if (polinfo->polcmd == 'r')
4109 86 : cmd = " FOR SELECT";
4110 238 : else if (polinfo->polcmd == 'a')
4111 66 : cmd = " FOR INSERT";
4112 172 : else if (polinfo->polcmd == 'w')
4113 86 : cmd = " FOR UPDATE";
4114 86 : else if (polinfo->polcmd == 'd')
4115 86 : cmd = " FOR DELETE";
4116 : else
4117 0 : pg_fatal("unexpected policy command type: %c",
4118 : polinfo->polcmd);
4119 :
4120 486 : query = createPQExpBuffer();
4121 486 : delqry = createPQExpBuffer();
4122 486 : polprefix = createPQExpBuffer();
4123 :
4124 486 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4125 :
4126 486 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4127 :
4128 486 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4129 486 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4130 :
4131 486 : if (polinfo->polroles != NULL)
4132 264 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4133 :
4134 486 : if (polinfo->polqual != NULL)
4135 420 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4136 :
4137 486 : if (polinfo->polwithcheck != NULL)
4138 228 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4139 :
4140 486 : appendPQExpBufferStr(query, ";\n");
4141 :
4142 486 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4143 486 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4144 :
4145 486 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4146 486 : fmtId(polinfo->polname));
4147 :
4148 486 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4149 :
4150 486 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4151 486 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4152 486 : ARCHIVE_OPTS(.tag = tag,
4153 : .namespace = polinfo->dobj.namespace->dobj.name,
4154 : .owner = tbinfo->rolname,
4155 : .description = "POLICY",
4156 : .section = SECTION_POST_DATA,
4157 : .createStmt = query->data,
4158 : .dropStmt = delqry->data));
4159 :
4160 486 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4161 0 : dumpComment(fout, polprefix->data, qtabname,
4162 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4163 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4164 :
4165 486 : free(tag);
4166 486 : destroyPQExpBuffer(query);
4167 486 : destroyPQExpBuffer(delqry);
4168 486 : destroyPQExpBuffer(polprefix);
4169 486 : free(qtabname);
4170 : }
4171 :
4172 : /*
4173 : * getPublications
4174 : * get information about publications
4175 : */
4176 : PublicationInfo *
4177 304 : getPublications(Archive *fout, int *numPublications)
4178 : {
4179 304 : DumpOptions *dopt = fout->dopt;
4180 : PQExpBuffer query;
4181 : PGresult *res;
4182 : PublicationInfo *pubinfo;
4183 : int i_tableoid;
4184 : int i_oid;
4185 : int i_pubname;
4186 : int i_pubowner;
4187 : int i_puballtables;
4188 : int i_pubinsert;
4189 : int i_pubupdate;
4190 : int i_pubdelete;
4191 : int i_pubtruncate;
4192 : int i_pubviaroot;
4193 : int i,
4194 : ntups;
4195 :
4196 304 : if (dopt->no_publications || fout->remoteVersion < 100000)
4197 : {
4198 0 : *numPublications = 0;
4199 0 : return NULL;
4200 : }
4201 :
4202 304 : query = createPQExpBuffer();
4203 :
4204 304 : resetPQExpBuffer(query);
4205 :
4206 : /* Get the publications. */
4207 304 : if (fout->remoteVersion >= 130000)
4208 304 : appendPQExpBufferStr(query,
4209 : "SELECT p.tableoid, p.oid, p.pubname, "
4210 : "p.pubowner, "
4211 : "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
4212 : "FROM pg_publication p");
4213 0 : else if (fout->remoteVersion >= 110000)
4214 0 : appendPQExpBufferStr(query,
4215 : "SELECT p.tableoid, p.oid, p.pubname, "
4216 : "p.pubowner, "
4217 : "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
4218 : "FROM pg_publication p");
4219 : else
4220 0 : appendPQExpBufferStr(query,
4221 : "SELECT p.tableoid, p.oid, p.pubname, "
4222 : "p.pubowner, "
4223 : "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
4224 : "FROM pg_publication p");
4225 :
4226 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4227 :
4228 304 : ntups = PQntuples(res);
4229 :
4230 304 : i_tableoid = PQfnumber(res, "tableoid");
4231 304 : i_oid = PQfnumber(res, "oid");
4232 304 : i_pubname = PQfnumber(res, "pubname");
4233 304 : i_pubowner = PQfnumber(res, "pubowner");
4234 304 : i_puballtables = PQfnumber(res, "puballtables");
4235 304 : i_pubinsert = PQfnumber(res, "pubinsert");
4236 304 : i_pubupdate = PQfnumber(res, "pubupdate");
4237 304 : i_pubdelete = PQfnumber(res, "pubdelete");
4238 304 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4239 304 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4240 :
4241 304 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4242 :
4243 650 : for (i = 0; i < ntups; i++)
4244 : {
4245 346 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4246 346 : pubinfo[i].dobj.catId.tableoid =
4247 346 : atooid(PQgetvalue(res, i, i_tableoid));
4248 346 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4249 346 : AssignDumpId(&pubinfo[i].dobj);
4250 346 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4251 346 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4252 346 : pubinfo[i].puballtables =
4253 346 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4254 346 : pubinfo[i].pubinsert =
4255 346 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4256 346 : pubinfo[i].pubupdate =
4257 346 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4258 346 : pubinfo[i].pubdelete =
4259 346 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4260 346 : pubinfo[i].pubtruncate =
4261 346 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4262 346 : pubinfo[i].pubviaroot =
4263 346 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4264 :
4265 : /* Decide whether we want to dump it */
4266 346 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4267 : }
4268 304 : PQclear(res);
4269 :
4270 304 : destroyPQExpBuffer(query);
4271 :
4272 304 : *numPublications = ntups;
4273 304 : return pubinfo;
4274 : }
4275 :
4276 : /*
4277 : * dumpPublication
4278 : * dump the definition of the given publication
4279 : */
4280 : static void
4281 282 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4282 : {
4283 282 : DumpOptions *dopt = fout->dopt;
4284 : PQExpBuffer delq;
4285 : PQExpBuffer query;
4286 : char *qpubname;
4287 282 : bool first = true;
4288 :
4289 : /* Do nothing in data-only dump */
4290 282 : if (dopt->dataOnly)
4291 24 : return;
4292 :
4293 258 : delq = createPQExpBuffer();
4294 258 : query = createPQExpBuffer();
4295 :
4296 258 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4297 :
4298 258 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4299 : qpubname);
4300 :
4301 258 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4302 : qpubname);
4303 :
4304 258 : if (pubinfo->puballtables)
4305 66 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4306 :
4307 258 : appendPQExpBufferStr(query, " WITH (publish = '");
4308 258 : if (pubinfo->pubinsert)
4309 : {
4310 194 : appendPQExpBufferStr(query, "insert");
4311 194 : first = false;
4312 : }
4313 :
4314 258 : if (pubinfo->pubupdate)
4315 : {
4316 194 : if (!first)
4317 194 : appendPQExpBufferStr(query, ", ");
4318 :
4319 194 : appendPQExpBufferStr(query, "update");
4320 194 : first = false;
4321 : }
4322 :
4323 258 : if (pubinfo->pubdelete)
4324 : {
4325 194 : if (!first)
4326 194 : appendPQExpBufferStr(query, ", ");
4327 :
4328 194 : appendPQExpBufferStr(query, "delete");
4329 194 : first = false;
4330 : }
4331 :
4332 258 : if (pubinfo->pubtruncate)
4333 : {
4334 194 : if (!first)
4335 194 : appendPQExpBufferStr(query, ", ");
4336 :
4337 194 : appendPQExpBufferStr(query, "truncate");
4338 194 : first = false;
4339 : }
4340 :
4341 258 : appendPQExpBufferChar(query, '\'');
4342 :
4343 258 : if (pubinfo->pubviaroot)
4344 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4345 :
4346 258 : appendPQExpBufferStr(query, ");\n");
4347 :
4348 258 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4349 258 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4350 258 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4351 : .owner = pubinfo->rolname,
4352 : .description = "PUBLICATION",
4353 : .section = SECTION_POST_DATA,
4354 : .createStmt = query->data,
4355 : .dropStmt = delq->data));
4356 :
4357 258 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4358 64 : dumpComment(fout, "PUBLICATION", qpubname,
4359 : NULL, pubinfo->rolname,
4360 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4361 :
4362 258 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4363 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4364 : NULL, pubinfo->rolname,
4365 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4366 :
4367 258 : destroyPQExpBuffer(delq);
4368 258 : destroyPQExpBuffer(query);
4369 258 : free(qpubname);
4370 : }
4371 :
4372 : /*
4373 : * getPublicationNamespaces
4374 : * get information about publication membership for dumpable schemas.
4375 : */
4376 : void
4377 304 : getPublicationNamespaces(Archive *fout)
4378 : {
4379 : PQExpBuffer query;
4380 : PGresult *res;
4381 : PublicationSchemaInfo *pubsinfo;
4382 304 : DumpOptions *dopt = fout->dopt;
4383 : int i_tableoid;
4384 : int i_oid;
4385 : int i_pnpubid;
4386 : int i_pnnspid;
4387 : int i,
4388 : j,
4389 : ntups;
4390 :
4391 304 : if (dopt->no_publications || fout->remoteVersion < 150000)
4392 0 : return;
4393 :
4394 304 : query = createPQExpBuffer();
4395 :
4396 : /* Collect all publication membership info. */
4397 304 : appendPQExpBufferStr(query,
4398 : "SELECT tableoid, oid, pnpubid, pnnspid "
4399 : "FROM pg_catalog.pg_publication_namespace");
4400 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4401 :
4402 304 : ntups = PQntuples(res);
4403 :
4404 304 : i_tableoid = PQfnumber(res, "tableoid");
4405 304 : i_oid = PQfnumber(res, "oid");
4406 304 : i_pnpubid = PQfnumber(res, "pnpubid");
4407 304 : i_pnnspid = PQfnumber(res, "pnnspid");
4408 :
4409 : /* this allocation may be more than we need */
4410 304 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4411 304 : j = 0;
4412 :
4413 476 : for (i = 0; i < ntups; i++)
4414 : {
4415 172 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4416 172 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4417 : PublicationInfo *pubinfo;
4418 : NamespaceInfo *nspinfo;
4419 :
4420 : /*
4421 : * Ignore any entries for which we aren't interested in either the
4422 : * publication or the rel.
4423 : */
4424 172 : pubinfo = findPublicationByOid(pnpubid);
4425 172 : if (pubinfo == NULL)
4426 0 : continue;
4427 172 : nspinfo = findNamespaceByOid(pnnspid);
4428 172 : if (nspinfo == NULL)
4429 0 : continue;
4430 :
4431 : /*
4432 : * We always dump publication namespaces unless the corresponding
4433 : * namespace is excluded from the dump.
4434 : */
4435 172 : if (nspinfo->dobj.dump == DUMP_COMPONENT_NONE)
4436 30 : continue;
4437 :
4438 : /* OK, make a DumpableObject for this relationship */
4439 142 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4440 142 : pubsinfo[j].dobj.catId.tableoid =
4441 142 : atooid(PQgetvalue(res, i, i_tableoid));
4442 142 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4443 142 : AssignDumpId(&pubsinfo[j].dobj);
4444 142 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4445 142 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4446 142 : pubsinfo[j].publication = pubinfo;
4447 142 : pubsinfo[j].pubschema = nspinfo;
4448 :
4449 : /* Decide whether we want to dump it */
4450 142 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4451 :
4452 142 : j++;
4453 : }
4454 :
4455 304 : PQclear(res);
4456 304 : destroyPQExpBuffer(query);
4457 : }
4458 :
4459 : /*
4460 : * getPublicationTables
4461 : * get information about publication membership for dumpable tables.
4462 : */
4463 : void
4464 304 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4465 : {
4466 : PQExpBuffer query;
4467 : PGresult *res;
4468 : PublicationRelInfo *pubrinfo;
4469 304 : DumpOptions *dopt = fout->dopt;
4470 : int i_tableoid;
4471 : int i_oid;
4472 : int i_prpubid;
4473 : int i_prrelid;
4474 : int i_prrelqual;
4475 : int i_prattrs;
4476 : int i,
4477 : j,
4478 : ntups;
4479 :
4480 304 : if (dopt->no_publications || fout->remoteVersion < 100000)
4481 0 : return;
4482 :
4483 304 : query = createPQExpBuffer();
4484 :
4485 : /* Collect all publication membership info. */
4486 304 : if (fout->remoteVersion >= 150000)
4487 304 : appendPQExpBufferStr(query,
4488 : "SELECT tableoid, oid, prpubid, prrelid, "
4489 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4490 : "(CASE\n"
4491 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4492 : " (SELECT array_agg(attname)\n"
4493 : " FROM\n"
4494 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4495 : " pg_catalog.pg_attribute\n"
4496 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4497 : " ELSE NULL END) prattrs "
4498 : "FROM pg_catalog.pg_publication_rel pr");
4499 : else
4500 0 : appendPQExpBufferStr(query,
4501 : "SELECT tableoid, oid, prpubid, prrelid, "
4502 : "NULL AS prrelqual, NULL AS prattrs "
4503 : "FROM pg_catalog.pg_publication_rel");
4504 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4505 :
4506 304 : ntups = PQntuples(res);
4507 :
4508 304 : i_tableoid = PQfnumber(res, "tableoid");
4509 304 : i_oid = PQfnumber(res, "oid");
4510 304 : i_prpubid = PQfnumber(res, "prpubid");
4511 304 : i_prrelid = PQfnumber(res, "prrelid");
4512 304 : i_prrelqual = PQfnumber(res, "prrelqual");
4513 304 : i_prattrs = PQfnumber(res, "prattrs");
4514 :
4515 : /* this allocation may be more than we need */
4516 304 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4517 304 : j = 0;
4518 :
4519 906 : for (i = 0; i < ntups; i++)
4520 : {
4521 602 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4522 602 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4523 : PublicationInfo *pubinfo;
4524 : TableInfo *tbinfo;
4525 :
4526 : /*
4527 : * Ignore any entries for which we aren't interested in either the
4528 : * publication or the rel.
4529 : */
4530 602 : pubinfo = findPublicationByOid(prpubid);
4531 602 : if (pubinfo == NULL)
4532 0 : continue;
4533 602 : tbinfo = findTableByOid(prrelid);
4534 602 : if (tbinfo == NULL)
4535 0 : continue;
4536 :
4537 : /*
4538 : * Ignore publication membership of tables whose definitions are not
4539 : * to be dumped.
4540 : */
4541 602 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
4542 92 : continue;
4543 :
4544 : /* OK, make a DumpableObject for this relationship */
4545 510 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4546 510 : pubrinfo[j].dobj.catId.tableoid =
4547 510 : atooid(PQgetvalue(res, i, i_tableoid));
4548 510 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4549 510 : AssignDumpId(&pubrinfo[j].dobj);
4550 510 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4551 510 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4552 510 : pubrinfo[j].publication = pubinfo;
4553 510 : pubrinfo[j].pubtable = tbinfo;
4554 510 : if (PQgetisnull(res, i, i_prrelqual))
4555 292 : pubrinfo[j].pubrelqual = NULL;
4556 : else
4557 218 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4558 :
4559 510 : if (!PQgetisnull(res, i, i_prattrs))
4560 : {
4561 : char **attnames;
4562 : int nattnames;
4563 : PQExpBuffer attribs;
4564 :
4565 144 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4566 : &attnames, &nattnames))
4567 0 : pg_fatal("could not parse %s array", "prattrs");
4568 144 : attribs = createPQExpBuffer();
4569 432 : for (int k = 0; k < nattnames; k++)
4570 : {
4571 288 : if (k > 0)
4572 144 : appendPQExpBufferStr(attribs, ", ");
4573 :
4574 288 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4575 : }
4576 144 : pubrinfo[j].pubrattrs = attribs->data;
4577 : }
4578 : else
4579 366 : pubrinfo[j].pubrattrs = NULL;
4580 :
4581 : /* Decide whether we want to dump it */
4582 510 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4583 :
4584 510 : j++;
4585 : }
4586 :
4587 304 : PQclear(res);
4588 304 : destroyPQExpBuffer(query);
4589 : }
4590 :
4591 : /*
4592 : * dumpPublicationNamespace
4593 : * dump the definition of the given publication schema mapping.
4594 : */
4595 : static void
4596 138 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4597 : {
4598 138 : DumpOptions *dopt = fout->dopt;
4599 138 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4600 138 : PublicationInfo *pubinfo = pubsinfo->publication;
4601 : PQExpBuffer query;
4602 : char *tag;
4603 :
4604 : /* Do nothing in data-only dump */
4605 138 : if (dopt->dataOnly)
4606 12 : return;
4607 :
4608 126 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4609 :
4610 126 : query = createPQExpBuffer();
4611 :
4612 126 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4613 126 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4614 :
4615 : /*
4616 : * There is no point in creating drop query as the drop is done by schema
4617 : * drop.
4618 : */
4619 126 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4620 126 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4621 126 : ARCHIVE_OPTS(.tag = tag,
4622 : .namespace = schemainfo->dobj.name,
4623 : .owner = pubinfo->rolname,
4624 : .description = "PUBLICATION TABLES IN SCHEMA",
4625 : .section = SECTION_POST_DATA,
4626 : .createStmt = query->data));
4627 :
4628 : /* These objects can't currently have comments or seclabels */
4629 :
4630 126 : free(tag);
4631 126 : destroyPQExpBuffer(query);
4632 : }
4633 :
4634 : /*
4635 : * dumpPublicationTable
4636 : * dump the definition of the given publication table mapping
4637 : */
4638 : static void
4639 470 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4640 : {
4641 470 : DumpOptions *dopt = fout->dopt;
4642 470 : PublicationInfo *pubinfo = pubrinfo->publication;
4643 470 : TableInfo *tbinfo = pubrinfo->pubtable;
4644 : PQExpBuffer query;
4645 : char *tag;
4646 :
4647 : /* Do nothing in data-only dump */
4648 470 : if (dopt->dataOnly)
4649 42 : return;
4650 :
4651 428 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4652 :
4653 428 : query = createPQExpBuffer();
4654 :
4655 428 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4656 428 : fmtId(pubinfo->dobj.name));
4657 428 : appendPQExpBuffer(query, " %s",
4658 428 : fmtQualifiedDumpable(tbinfo));
4659 :
4660 428 : if (pubrinfo->pubrattrs)
4661 124 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4662 :
4663 428 : if (pubrinfo->pubrelqual)
4664 : {
4665 : /*
4666 : * It's necessary to add parentheses around the expression because
4667 : * pg_get_expr won't supply the parentheses for things like WHERE
4668 : * TRUE.
4669 : */
4670 184 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4671 : }
4672 428 : appendPQExpBufferStr(query, ";\n");
4673 :
4674 : /*
4675 : * There is no point in creating a drop query as the drop is done by table
4676 : * drop. (If you think to change this, see also _printTocEntry().)
4677 : * Although this object doesn't really have ownership as such, set the
4678 : * owner field anyway to ensure that the command is run by the correct
4679 : * role at restore time.
4680 : */
4681 428 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4682 428 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4683 428 : ARCHIVE_OPTS(.tag = tag,
4684 : .namespace = tbinfo->dobj.namespace->dobj.name,
4685 : .owner = pubinfo->rolname,
4686 : .description = "PUBLICATION TABLE",
4687 : .section = SECTION_POST_DATA,
4688 : .createStmt = query->data));
4689 :
4690 : /* These objects can't currently have comments or seclabels */
4691 :
4692 428 : free(tag);
4693 428 : destroyPQExpBuffer(query);
4694 : }
4695 :
4696 : /*
4697 : * Is the currently connected user a superuser?
4698 : */
4699 : static bool
4700 304 : is_superuser(Archive *fout)
4701 : {
4702 304 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4703 : const char *val;
4704 :
4705 304 : val = PQparameterStatus(AH->connection, "is_superuser");
4706 :
4707 304 : if (val && strcmp(val, "on") == 0)
4708 298 : return true;
4709 :
4710 6 : return false;
4711 : }
4712 :
4713 : /*
4714 : * getSubscriptions
4715 : * get information about subscriptions
4716 : */
4717 : void
4718 304 : getSubscriptions(Archive *fout)
4719 : {
4720 304 : DumpOptions *dopt = fout->dopt;
4721 : PQExpBuffer query;
4722 : PGresult *res;
4723 : SubscriptionInfo *subinfo;
4724 : int i_tableoid;
4725 : int i_oid;
4726 : int i_subname;
4727 : int i_subowner;
4728 : int i_subbinary;
4729 : int i_substream;
4730 : int i_subtwophasestate;
4731 : int i_subdisableonerr;
4732 : int i_subpasswordrequired;
4733 : int i_subrunasowner;
4734 : int i_subconninfo;
4735 : int i_subslotname;
4736 : int i_subsynccommit;
4737 : int i_subpublications;
4738 : int i_suborigin;
4739 : int i_suboriginremotelsn;
4740 : int i_subenabled;
4741 : int i_subfailover;
4742 : int i,
4743 : ntups;
4744 :
4745 304 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
4746 0 : return;
4747 :
4748 304 : if (!is_superuser(fout))
4749 : {
4750 : int n;
4751 :
4752 6 : res = ExecuteSqlQuery(fout,
4753 : "SELECT count(*) FROM pg_subscription "
4754 : "WHERE subdbid = (SELECT oid FROM pg_database"
4755 : " WHERE datname = current_database())",
4756 : PGRES_TUPLES_OK);
4757 6 : n = atoi(PQgetvalue(res, 0, 0));
4758 6 : if (n > 0)
4759 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
4760 6 : PQclear(res);
4761 6 : return;
4762 : }
4763 :
4764 298 : query = createPQExpBuffer();
4765 :
4766 : /* Get the subscriptions in current database. */
4767 298 : appendPQExpBufferStr(query,
4768 : "SELECT s.tableoid, s.oid, s.subname,\n"
4769 : " s.subowner,\n"
4770 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
4771 : " s.subpublications,\n");
4772 :
4773 298 : if (fout->remoteVersion >= 140000)
4774 298 : appendPQExpBufferStr(query, " s.subbinary,\n");
4775 : else
4776 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
4777 :
4778 298 : if (fout->remoteVersion >= 140000)
4779 298 : appendPQExpBufferStr(query, " s.substream,\n");
4780 : else
4781 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
4782 :
4783 298 : if (fout->remoteVersion >= 150000)
4784 298 : appendPQExpBufferStr(query,
4785 : " s.subtwophasestate,\n"
4786 : " s.subdisableonerr,\n");
4787 : else
4788 0 : appendPQExpBuffer(query,
4789 : " '%c' AS subtwophasestate,\n"
4790 : " false AS subdisableonerr,\n",
4791 : LOGICALREP_TWOPHASE_STATE_DISABLED);
4792 :
4793 298 : if (fout->remoteVersion >= 160000)
4794 298 : appendPQExpBufferStr(query,
4795 : " s.subpasswordrequired,\n"
4796 : " s.subrunasowner,\n"
4797 : " s.suborigin,\n");
4798 : else
4799 0 : appendPQExpBuffer(query,
4800 : " 't' AS subpasswordrequired,\n"
4801 : " 't' AS subrunasowner,\n"
4802 : " '%s' AS suborigin,\n",
4803 : LOGICALREP_ORIGIN_ANY);
4804 :
4805 298 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4806 28 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
4807 : " s.subenabled,\n");
4808 : else
4809 270 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
4810 : " false AS subenabled,\n");
4811 :
4812 298 : if (fout->remoteVersion >= 170000)
4813 298 : appendPQExpBufferStr(query,
4814 : " s.subfailover\n");
4815 : else
4816 0 : appendPQExpBuffer(query,
4817 : " false AS subfailover\n");
4818 :
4819 298 : appendPQExpBufferStr(query,
4820 : "FROM pg_subscription s\n");
4821 :
4822 298 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
4823 28 : appendPQExpBufferStr(query,
4824 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
4825 : " ON o.external_id = 'pg_' || s.oid::text \n");
4826 :
4827 298 : appendPQExpBufferStr(query,
4828 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
4829 : " WHERE datname = current_database())");
4830 :
4831 298 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4832 :
4833 298 : ntups = PQntuples(res);
4834 :
4835 : /*
4836 : * Get subscription fields. We don't include subskiplsn in the dump as
4837 : * after restoring the dump this value may no longer be relevant.
4838 : */
4839 298 : i_tableoid = PQfnumber(res, "tableoid");
4840 298 : i_oid = PQfnumber(res, "oid");
4841 298 : i_subname = PQfnumber(res, "subname");
4842 298 : i_subowner = PQfnumber(res, "subowner");
4843 298 : i_subbinary = PQfnumber(res, "subbinary");
4844 298 : i_substream = PQfnumber(res, "substream");
4845 298 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
4846 298 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
4847 298 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
4848 298 : i_subrunasowner = PQfnumber(res, "subrunasowner");
4849 298 : i_subconninfo = PQfnumber(res, "subconninfo");
4850 298 : i_subslotname = PQfnumber(res, "subslotname");
4851 298 : i_subsynccommit = PQfnumber(res, "subsynccommit");
4852 298 : i_subpublications = PQfnumber(res, "subpublications");
4853 298 : i_suborigin = PQfnumber(res, "suborigin");
4854 298 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
4855 298 : i_subenabled = PQfnumber(res, "subenabled");
4856 298 : i_subfailover = PQfnumber(res, "subfailover");
4857 :
4858 298 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
4859 :
4860 548 : for (i = 0; i < ntups; i++)
4861 : {
4862 250 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
4863 250 : subinfo[i].dobj.catId.tableoid =
4864 250 : atooid(PQgetvalue(res, i, i_tableoid));
4865 250 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4866 250 : AssignDumpId(&subinfo[i].dobj);
4867 250 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
4868 250 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
4869 :
4870 500 : subinfo[i].subbinary =
4871 250 : pg_strdup(PQgetvalue(res, i, i_subbinary));
4872 500 : subinfo[i].substream =
4873 250 : pg_strdup(PQgetvalue(res, i, i_substream));
4874 500 : subinfo[i].subtwophasestate =
4875 250 : pg_strdup(PQgetvalue(res, i, i_subtwophasestate));
4876 500 : subinfo[i].subdisableonerr =
4877 250 : pg_strdup(PQgetvalue(res, i, i_subdisableonerr));
4878 500 : subinfo[i].subpasswordrequired =
4879 250 : pg_strdup(PQgetvalue(res, i, i_subpasswordrequired));
4880 500 : subinfo[i].subrunasowner =
4881 250 : pg_strdup(PQgetvalue(res, i, i_subrunasowner));
4882 500 : subinfo[i].subconninfo =
4883 250 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
4884 250 : if (PQgetisnull(res, i, i_subslotname))
4885 0 : subinfo[i].subslotname = NULL;
4886 : else
4887 250 : subinfo[i].subslotname =
4888 250 : pg_strdup(PQgetvalue(res, i, i_subslotname));
4889 500 : subinfo[i].subsynccommit =
4890 250 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
4891 500 : subinfo[i].subpublications =
4892 250 : pg_strdup(PQgetvalue(res, i, i_subpublications));
4893 250 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
4894 250 : if (PQgetisnull(res, i, i_suboriginremotelsn))
4895 248 : subinfo[i].suboriginremotelsn = NULL;
4896 : else
4897 2 : subinfo[i].suboriginremotelsn =
4898 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
4899 500 : subinfo[i].subenabled =
4900 250 : pg_strdup(PQgetvalue(res, i, i_subenabled));
4901 500 : subinfo[i].subfailover =
4902 250 : pg_strdup(PQgetvalue(res, i, i_subfailover));
4903 :
4904 : /* Decide whether we want to dump it */
4905 250 : selectDumpableObject(&(subinfo[i].dobj), fout);
4906 : }
4907 298 : PQclear(res);
4908 :
4909 298 : destroyPQExpBuffer(query);
4910 : }
4911 :
4912 : /*
4913 : * getSubscriptionTables
4914 : * Get information about subscription membership for dumpable tables. This
4915 : * will be used only in binary-upgrade mode for PG17 or later versions.
4916 : */
4917 : void
4918 304 : getSubscriptionTables(Archive *fout)
4919 : {
4920 304 : DumpOptions *dopt = fout->dopt;
4921 304 : SubscriptionInfo *subinfo = NULL;
4922 : SubRelInfo *subrinfo;
4923 : PGresult *res;
4924 : int i_srsubid;
4925 : int i_srrelid;
4926 : int i_srsubstate;
4927 : int i_srsublsn;
4928 : int ntups;
4929 304 : Oid last_srsubid = InvalidOid;
4930 :
4931 304 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
4932 28 : fout->remoteVersion < 170000)
4933 276 : return;
4934 :
4935 28 : res = ExecuteSqlQuery(fout,
4936 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
4937 : "FROM pg_catalog.pg_subscription_rel "
4938 : "ORDER BY srsubid",
4939 : PGRES_TUPLES_OK);
4940 28 : ntups = PQntuples(res);
4941 28 : if (ntups == 0)
4942 26 : goto cleanup;
4943 :
4944 : /* Get pg_subscription_rel attributes */
4945 2 : i_srsubid = PQfnumber(res, "srsubid");
4946 2 : i_srrelid = PQfnumber(res, "srrelid");
4947 2 : i_srsubstate = PQfnumber(res, "srsubstate");
4948 2 : i_srsublsn = PQfnumber(res, "srsublsn");
4949 :
4950 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
4951 6 : for (int i = 0; i < ntups; i++)
4952 : {
4953 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
4954 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
4955 : TableInfo *tblinfo;
4956 :
4957 : /*
4958 : * If we switched to a new subscription, check if the subscription
4959 : * exists.
4960 : */
4961 4 : if (cur_srsubid != last_srsubid)
4962 : {
4963 4 : subinfo = findSubscriptionByOid(cur_srsubid);
4964 4 : if (subinfo == NULL)
4965 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
4966 :
4967 4 : last_srsubid = cur_srsubid;
4968 : }
4969 :
4970 4 : tblinfo = findTableByOid(relid);
4971 4 : if (tblinfo == NULL)
4972 0 : pg_fatal("failed sanity check, table with OID %u not found",
4973 : relid);
4974 :
4975 : /* OK, make a DumpableObject for this relationship */
4976 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
4977 4 : subrinfo[i].dobj.catId.tableoid = relid;
4978 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
4979 4 : AssignDumpId(&subrinfo[i].dobj);
4980 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
4981 4 : subrinfo[i].tblinfo = tblinfo;
4982 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
4983 4 : if (PQgetisnull(res, i, i_srsublsn))
4984 2 : subrinfo[i].srsublsn = NULL;
4985 : else
4986 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
4987 :
4988 4 : subrinfo[i].subinfo = subinfo;
4989 :
4990 : /* Decide whether we want to dump it */
4991 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
4992 : }
4993 :
4994 2 : cleanup:
4995 28 : PQclear(res);
4996 : }
4997 :
4998 : /*
4999 : * dumpSubscriptionTable
5000 : * Dump the definition of the given subscription table mapping. This will be
5001 : * used only in binary-upgrade mode for PG17 or later versions.
5002 : */
5003 : static void
5004 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5005 : {
5006 4 : DumpOptions *dopt = fout->dopt;
5007 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5008 : PQExpBuffer query;
5009 : char *tag;
5010 :
5011 : /* Do nothing in data-only dump */
5012 4 : if (dopt->dataOnly)
5013 0 : return;
5014 :
5015 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5016 :
5017 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5018 :
5019 4 : query = createPQExpBuffer();
5020 :
5021 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5022 : {
5023 : /*
5024 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5025 : * to pg_subscription_rel table. This will be used only in
5026 : * binary-upgrade mode.
5027 : */
5028 4 : appendPQExpBufferStr(query,
5029 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5030 4 : appendPQExpBufferStr(query,
5031 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5032 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5033 4 : appendPQExpBuffer(query,
5034 : ", %u, '%c'",
5035 4 : subrinfo->tblinfo->dobj.catId.oid,
5036 4 : subrinfo->srsubstate);
5037 :
5038 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5039 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5040 : else
5041 2 : appendPQExpBuffer(query, ", NULL");
5042 :
5043 4 : appendPQExpBufferStr(query, ");\n");
5044 : }
5045 :
5046 : /*
5047 : * There is no point in creating a drop query as the drop is done by table
5048 : * drop. (If you think to change this, see also _printTocEntry().)
5049 : * Although this object doesn't really have ownership as such, set the
5050 : * owner field anyway to ensure that the command is run by the correct
5051 : * role at restore time.
5052 : */
5053 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5054 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5055 4 : ARCHIVE_OPTS(.tag = tag,
5056 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5057 : .owner = subinfo->rolname,
5058 : .description = "SUBSCRIPTION TABLE",
5059 : .section = SECTION_POST_DATA,
5060 : .createStmt = query->data));
5061 :
5062 : /* These objects can't currently have comments or seclabels */
5063 :
5064 4 : free(tag);
5065 4 : destroyPQExpBuffer(query);
5066 : }
5067 :
5068 : /*
5069 : * dumpSubscription
5070 : * dump the definition of the given subscription
5071 : */
5072 : static void
5073 214 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5074 : {
5075 214 : DumpOptions *dopt = fout->dopt;
5076 : PQExpBuffer delq;
5077 : PQExpBuffer query;
5078 : PQExpBuffer publications;
5079 : char *qsubname;
5080 214 : char **pubnames = NULL;
5081 214 : int npubnames = 0;
5082 : int i;
5083 214 : char two_phase_disabled[] = {LOGICALREP_TWOPHASE_STATE_DISABLED, '\0'};
5084 :
5085 : /* Do nothing in data-only dump */
5086 214 : if (dopt->dataOnly)
5087 18 : return;
5088 :
5089 196 : delq = createPQExpBuffer();
5090 196 : query = createPQExpBuffer();
5091 :
5092 196 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5093 :
5094 196 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5095 : qsubname);
5096 :
5097 196 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5098 : qsubname);
5099 196 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5100 :
5101 : /* Build list of quoted publications and append them to query. */
5102 196 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5103 0 : pg_fatal("could not parse %s array", "subpublications");
5104 :
5105 196 : publications = createPQExpBuffer();
5106 392 : for (i = 0; i < npubnames; i++)
5107 : {
5108 196 : if (i > 0)
5109 0 : appendPQExpBufferStr(publications, ", ");
5110 :
5111 196 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5112 : }
5113 :
5114 196 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5115 196 : if (subinfo->subslotname)
5116 196 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5117 : else
5118 0 : appendPQExpBufferStr(query, "NONE");
5119 :
5120 196 : if (strcmp(subinfo->subbinary, "t") == 0)
5121 0 : appendPQExpBufferStr(query, ", binary = true");
5122 :
5123 196 : if (strcmp(subinfo->substream, "t") == 0)
5124 0 : appendPQExpBufferStr(query, ", streaming = on");
5125 196 : else if (strcmp(subinfo->substream, "p") == 0)
5126 0 : appendPQExpBufferStr(query, ", streaming = parallel");
5127 :
5128 196 : if (strcmp(subinfo->subtwophasestate, two_phase_disabled) != 0)
5129 0 : appendPQExpBufferStr(query, ", two_phase = on");
5130 :
5131 196 : if (strcmp(subinfo->subdisableonerr, "t") == 0)
5132 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5133 :
5134 196 : if (strcmp(subinfo->subpasswordrequired, "t") != 0)
5135 0 : appendPQExpBuffer(query, ", password_required = false");
5136 :
5137 196 : if (strcmp(subinfo->subrunasowner, "t") == 0)
5138 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5139 :
5140 196 : if (strcmp(subinfo->subfailover, "t") == 0)
5141 2 : appendPQExpBufferStr(query, ", failover = true");
5142 :
5143 196 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5144 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5145 :
5146 196 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5147 64 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5148 :
5149 196 : appendPQExpBufferStr(query, ");\n");
5150 :
5151 : /*
5152 : * In binary-upgrade mode, we allow the replication to continue after the
5153 : * upgrade.
5154 : */
5155 196 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5156 : {
5157 10 : if (subinfo->suboriginremotelsn)
5158 : {
5159 : /*
5160 : * Preserve the remote_lsn for the subscriber's replication
5161 : * origin. This value is required to start the replication from
5162 : * the position before the upgrade. This value will be stale if
5163 : * the publisher gets upgraded before the subscriber node.
5164 : * However, this shouldn't be a problem as the upgrade of the
5165 : * publisher ensures that all the transactions were replicated
5166 : * before upgrading it.
5167 : */
5168 2 : appendPQExpBufferStr(query,
5169 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5170 2 : appendPQExpBufferStr(query,
5171 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5172 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5173 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5174 : }
5175 :
5176 10 : if (strcmp(subinfo->subenabled, "t") == 0)
5177 : {
5178 : /*
5179 : * Enable the subscription to allow the replication to continue
5180 : * after the upgrade.
5181 : */
5182 2 : appendPQExpBufferStr(query,
5183 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5184 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5185 : }
5186 : }
5187 :
5188 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5189 196 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5190 196 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5191 : .owner = subinfo->rolname,
5192 : .description = "SUBSCRIPTION",
5193 : .section = SECTION_POST_DATA,
5194 : .createStmt = query->data,
5195 : .dropStmt = delq->data));
5196 :
5197 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5198 64 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5199 : NULL, subinfo->rolname,
5200 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5201 :
5202 196 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5203 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5204 : NULL, subinfo->rolname,
5205 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5206 :
5207 196 : destroyPQExpBuffer(publications);
5208 196 : free(pubnames);
5209 :
5210 196 : destroyPQExpBuffer(delq);
5211 196 : destroyPQExpBuffer(query);
5212 196 : free(qsubname);
5213 : }
5214 :
5215 : /*
5216 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5217 : * the object needs.
5218 : */
5219 : static void
5220 9212 : append_depends_on_extension(Archive *fout,
5221 : PQExpBuffer create,
5222 : const DumpableObject *dobj,
5223 : const char *catalog,
5224 : const char *keyword,
5225 : const char *objname)
5226 : {
5227 9212 : if (dobj->depends_on_ext)
5228 : {
5229 : char *nm;
5230 : PGresult *res;
5231 : PQExpBuffer query;
5232 : int ntups;
5233 : int i_extname;
5234 : int i;
5235 :
5236 : /* dodge fmtId() non-reentrancy */
5237 84 : nm = pg_strdup(objname);
5238 :
5239 84 : query = createPQExpBuffer();
5240 84 : appendPQExpBuffer(query,
5241 : "SELECT e.extname "
5242 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5243 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5244 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5245 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5246 : catalog,
5247 : dobj->catId.oid);
5248 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5249 84 : ntups = PQntuples(res);
5250 84 : i_extname = PQfnumber(res, "extname");
5251 168 : for (i = 0; i < ntups; i++)
5252 : {
5253 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5254 : keyword, nm,
5255 84 : fmtId(PQgetvalue(res, i, i_extname)));
5256 : }
5257 :
5258 84 : PQclear(res);
5259 84 : destroyPQExpBuffer(query);
5260 84 : pg_free(nm);
5261 : }
5262 9212 : }
5263 :
5264 : static Oid
5265 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5266 : {
5267 : /*
5268 : * If the old version didn't assign an array type, but the new version
5269 : * does, we must select an unused type OID to assign. This currently only
5270 : * happens for domains, when upgrading pre-v11 to v11 and up.
5271 : *
5272 : * Note: local state here is kind of ugly, but we must have some, since we
5273 : * mustn't choose the same unused OID more than once.
5274 : */
5275 : static Oid next_possible_free_oid = FirstNormalObjectId;
5276 : PGresult *res;
5277 : bool is_dup;
5278 :
5279 : do
5280 : {
5281 0 : ++next_possible_free_oid;
5282 0 : printfPQExpBuffer(upgrade_query,
5283 : "SELECT EXISTS(SELECT 1 "
5284 : "FROM pg_catalog.pg_type "
5285 : "WHERE oid = '%u'::pg_catalog.oid);",
5286 : next_possible_free_oid);
5287 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5288 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5289 0 : PQclear(res);
5290 0 : } while (is_dup);
5291 :
5292 0 : return next_possible_free_oid;
5293 : }
5294 :
5295 : static void
5296 1644 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5297 : PQExpBuffer upgrade_buffer,
5298 : Oid pg_type_oid,
5299 : bool force_array_type,
5300 : bool include_multirange_type)
5301 : {
5302 1644 : PQExpBuffer upgrade_query = createPQExpBuffer();
5303 : PGresult *res;
5304 : Oid pg_type_array_oid;
5305 : Oid pg_type_multirange_oid;
5306 : Oid pg_type_multirange_array_oid;
5307 :
5308 1644 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5309 1644 : appendPQExpBuffer(upgrade_buffer,
5310 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5311 : pg_type_oid);
5312 :
5313 1644 : appendPQExpBuffer(upgrade_query,
5314 : "SELECT typarray "
5315 : "FROM pg_catalog.pg_type "
5316 : "WHERE oid = '%u'::pg_catalog.oid;",
5317 : pg_type_oid);
5318 :
5319 1644 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5320 :
5321 1644 : pg_type_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5322 :
5323 1644 : PQclear(res);
5324 :
5325 1644 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5326 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5327 :
5328 1644 : if (OidIsValid(pg_type_array_oid))
5329 : {
5330 1640 : appendPQExpBufferStr(upgrade_buffer,
5331 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5332 1640 : appendPQExpBuffer(upgrade_buffer,
5333 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5334 : pg_type_array_oid);
5335 : }
5336 :
5337 : /*
5338 : * Pre-set the multirange type oid and its own array type oid.
5339 : */
5340 1644 : if (include_multirange_type)
5341 : {
5342 12 : if (fout->remoteVersion >= 140000)
5343 : {
5344 12 : printfPQExpBuffer(upgrade_query,
5345 : "SELECT t.oid, t.typarray "
5346 : "FROM pg_catalog.pg_type t "
5347 : "JOIN pg_catalog.pg_range r "
5348 : "ON t.oid = r.rngmultitypid "
5349 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5350 : pg_type_oid);
5351 :
5352 12 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5353 :
5354 12 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5355 12 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5356 :
5357 12 : PQclear(res);
5358 : }
5359 : else
5360 : {
5361 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5362 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5363 : }
5364 :
5365 12 : appendPQExpBufferStr(upgrade_buffer,
5366 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5367 12 : appendPQExpBuffer(upgrade_buffer,
5368 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5369 : pg_type_multirange_oid);
5370 12 : appendPQExpBufferStr(upgrade_buffer,
5371 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5372 12 : appendPQExpBuffer(upgrade_buffer,
5373 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5374 : pg_type_multirange_array_oid);
5375 : }
5376 :
5377 1644 : destroyPQExpBuffer(upgrade_query);
5378 1644 : }
5379 :
5380 : static void
5381 1510 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5382 : PQExpBuffer upgrade_buffer,
5383 : const TableInfo *tbinfo)
5384 : {
5385 1510 : Oid pg_type_oid = tbinfo->reltype;
5386 :
5387 1510 : if (OidIsValid(pg_type_oid))
5388 1510 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5389 : pg_type_oid, false, false);
5390 1510 : }
5391 :
5392 : static void
5393 2206 : binary_upgrade_set_pg_class_oids(Archive *fout,
5394 : PQExpBuffer upgrade_buffer, Oid pg_class_oid,
5395 : bool is_index)
5396 : {
5397 2206 : PQExpBuffer upgrade_query = createPQExpBuffer();
5398 : PGresult *upgrade_res;
5399 : RelFileNumber relfilenumber;
5400 : Oid toast_oid;
5401 : RelFileNumber toast_relfilenumber;
5402 : char relkind;
5403 : Oid toast_index_oid;
5404 : RelFileNumber toast_index_relfilenumber;
5405 :
5406 : /*
5407 : * Preserve the OID and relfilenumber of the table, table's index, table's
5408 : * toast table and toast table's index if any.
5409 : *
5410 : * One complexity is that the current table definition might not require
5411 : * the creation of a TOAST table, but the old database might have a TOAST
5412 : * table that was created earlier, before some wide columns were dropped.
5413 : * By setting the TOAST oid we force creation of the TOAST heap and index
5414 : * by the new backend, so we can copy the files during binary upgrade
5415 : * without worrying about this case.
5416 : */
5417 2206 : appendPQExpBuffer(upgrade_query,
5418 : "SELECT c.relkind, c.relfilenode, c.reltoastrelid, ct.relfilenode AS toast_relfilenode, i.indexrelid, cti.relfilenode AS toast_index_relfilenode "
5419 : "FROM pg_catalog.pg_class c LEFT JOIN "
5420 : "pg_catalog.pg_index i ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5421 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5422 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5423 : "WHERE c.oid = '%u'::pg_catalog.oid;",
5424 : pg_class_oid);
5425 :
5426 2206 : upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5427 :
5428 2206 : relkind = *PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "relkind"));
5429 :
5430 2206 : relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5431 : PQfnumber(upgrade_res, "relfilenode")));
5432 2206 : toast_oid = atooid(PQgetvalue(upgrade_res, 0,
5433 : PQfnumber(upgrade_res, "reltoastrelid")));
5434 2206 : toast_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5435 : PQfnumber(upgrade_res, "toast_relfilenode")));
5436 2206 : toast_index_oid = atooid(PQgetvalue(upgrade_res, 0,
5437 : PQfnumber(upgrade_res, "indexrelid")));
5438 2206 : toast_index_relfilenumber = atooid(PQgetvalue(upgrade_res, 0,
5439 : PQfnumber(upgrade_res, "toast_index_relfilenode")));
5440 :
5441 2206 : appendPQExpBufferStr(upgrade_buffer,
5442 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5443 :
5444 2206 : if (!is_index)
5445 : {
5446 1656 : appendPQExpBuffer(upgrade_buffer,
5447 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5448 : pg_class_oid);
5449 :
5450 : /*
5451 : * Not every relation has storage. Also, in a pre-v12 database,
5452 : * partitioned tables have a relfilenumber, which should not be
5453 : * preserved when upgrading.
5454 : */
5455 1656 : if (RelFileNumberIsValid(relfilenumber) && relkind != RELKIND_PARTITIONED_TABLE)
5456 1356 : appendPQExpBuffer(upgrade_buffer,
5457 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5458 : relfilenumber);
5459 :
5460 : /*
5461 : * In a pre-v12 database, partitioned tables might be marked as having
5462 : * toast tables, but we should ignore them if so.
5463 : */
5464 1656 : if (OidIsValid(toast_oid) &&
5465 : relkind != RELKIND_PARTITIONED_TABLE)
5466 : {
5467 550 : appendPQExpBuffer(upgrade_buffer,
5468 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5469 : toast_oid);
5470 550 : appendPQExpBuffer(upgrade_buffer,
5471 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5472 : toast_relfilenumber);
5473 :
5474 : /* every toast table has an index */
5475 550 : appendPQExpBuffer(upgrade_buffer,
5476 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5477 : toast_index_oid);
5478 550 : appendPQExpBuffer(upgrade_buffer,
5479 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5480 : toast_index_relfilenumber);
5481 : }
5482 :
5483 1656 : PQclear(upgrade_res);
5484 : }
5485 : else
5486 : {
5487 : /* Preserve the OID and relfilenumber of the index */
5488 550 : appendPQExpBuffer(upgrade_buffer,
5489 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5490 : pg_class_oid);
5491 550 : appendPQExpBuffer(upgrade_buffer,
5492 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5493 : relfilenumber);
5494 : }
5495 :
5496 2206 : appendPQExpBufferChar(upgrade_buffer, '\n');
5497 :
5498 2206 : destroyPQExpBuffer(upgrade_query);
5499 2206 : }
5500 :
5501 : /*
5502 : * If the DumpableObject is a member of an extension, add a suitable
5503 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5504 : *
5505 : * For somewhat historical reasons, objname should already be quoted,
5506 : * but not objnamespace (if any).
5507 : */
5508 : static void
5509 2620 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5510 : const DumpableObject *dobj,
5511 : const char *objtype,
5512 : const char *objname,
5513 : const char *objnamespace)
5514 : {
5515 2620 : DumpableObject *extobj = NULL;
5516 : int i;
5517 :
5518 2620 : if (!dobj->ext_member)
5519 2588 : return;
5520 :
5521 : /*
5522 : * Find the parent extension. We could avoid this search if we wanted to
5523 : * add a link field to DumpableObject, but the space costs of that would
5524 : * be considerable. We assume that member objects could only have a
5525 : * direct dependency on their own extension, not any others.
5526 : */
5527 32 : for (i = 0; i < dobj->nDeps; i++)
5528 : {
5529 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5530 32 : if (extobj && extobj->objType == DO_EXTENSION)
5531 32 : break;
5532 0 : extobj = NULL;
5533 : }
5534 32 : if (extobj == NULL)
5535 0 : pg_fatal("could not find parent extension for %s %s",
5536 : objtype, objname);
5537 :
5538 32 : appendPQExpBufferStr(upgrade_buffer,
5539 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5540 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5541 32 : fmtId(extobj->name),
5542 : objtype);
5543 32 : if (objnamespace && *objnamespace)
5544 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5545 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5546 : }
5547 :
5548 : /*
5549 : * getNamespaces:
5550 : * read all namespaces in the system catalogs and return them in the
5551 : * NamespaceInfo* structure
5552 : *
5553 : * numNamespaces is set to the number of namespaces read in
5554 : */
5555 : NamespaceInfo *
5556 306 : getNamespaces(Archive *fout, int *numNamespaces)
5557 : {
5558 : PGresult *res;
5559 : int ntups;
5560 : int i;
5561 : PQExpBuffer query;
5562 : NamespaceInfo *nsinfo;
5563 : int i_tableoid;
5564 : int i_oid;
5565 : int i_nspname;
5566 : int i_nspowner;
5567 : int i_nspacl;
5568 : int i_acldefault;
5569 :
5570 306 : query = createPQExpBuffer();
5571 :
5572 : /*
5573 : * we fetch all namespaces including system ones, so that every object we
5574 : * read in can be linked to a containing namespace.
5575 : */
5576 306 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5577 : "n.nspowner, "
5578 : "n.nspacl, "
5579 : "acldefault('n', n.nspowner) AS acldefault "
5580 : "FROM pg_namespace n");
5581 :
5582 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5583 :
5584 306 : ntups = PQntuples(res);
5585 :
5586 306 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5587 :
5588 306 : i_tableoid = PQfnumber(res, "tableoid");
5589 306 : i_oid = PQfnumber(res, "oid");
5590 306 : i_nspname = PQfnumber(res, "nspname");
5591 306 : i_nspowner = PQfnumber(res, "nspowner");
5592 306 : i_nspacl = PQfnumber(res, "nspacl");
5593 306 : i_acldefault = PQfnumber(res, "acldefault");
5594 :
5595 2582 : for (i = 0; i < ntups; i++)
5596 : {
5597 : const char *nspowner;
5598 :
5599 2276 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5600 2276 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5601 2276 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5602 2276 : AssignDumpId(&nsinfo[i].dobj);
5603 2276 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5604 2276 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5605 2276 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5606 2276 : nsinfo[i].dacl.privtype = 0;
5607 2276 : nsinfo[i].dacl.initprivs = NULL;
5608 2276 : nspowner = PQgetvalue(res, i, i_nspowner);
5609 2276 : nsinfo[i].nspowner = atooid(nspowner);
5610 2276 : nsinfo[i].rolname = getRoleName(nspowner);
5611 :
5612 : /* Decide whether to dump this namespace */
5613 2276 : selectDumpableNamespace(&nsinfo[i], fout);
5614 :
5615 : /* Mark whether namespace has an ACL */
5616 2276 : if (!PQgetisnull(res, i, i_nspacl))
5617 1006 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5618 :
5619 : /*
5620 : * We ignore any pg_init_privs.initprivs entry for the public schema
5621 : * and assume a predetermined default, for several reasons. First,
5622 : * dropping and recreating the schema removes its pg_init_privs entry,
5623 : * but an empty destination database starts with this ACL nonetheless.
5624 : * Second, we support dump/reload of public schema ownership changes.
5625 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5626 : * initprivs continues to reflect the initial owner. Hence,
5627 : * synthesize the value that nspacl will have after the restore's
5628 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5629 : * match the source's ACL, even if the latter was an initdb-default
5630 : * ACL, which changed in v15. An upgrade pulls in changes to most
5631 : * system object ACLs that the DBA had not customized. We've made the
5632 : * public schema depart from that, because changing its ACL so easily
5633 : * breaks applications.
5634 : */
5635 2276 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5636 : {
5637 298 : PQExpBuffer aclarray = createPQExpBuffer();
5638 298 : PQExpBuffer aclitem = createPQExpBuffer();
5639 :
5640 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5641 298 : appendPQExpBufferChar(aclarray, '{');
5642 298 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5643 298 : appendPQExpBufferStr(aclitem, "=UC/");
5644 298 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5645 298 : appendPGArray(aclarray, aclitem->data);
5646 298 : resetPQExpBuffer(aclitem);
5647 298 : appendPQExpBufferStr(aclitem, "=U/");
5648 298 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5649 298 : appendPGArray(aclarray, aclitem->data);
5650 298 : appendPQExpBufferChar(aclarray, '}');
5651 :
5652 298 : nsinfo[i].dacl.privtype = 'i';
5653 298 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5654 298 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5655 :
5656 298 : destroyPQExpBuffer(aclarray);
5657 298 : destroyPQExpBuffer(aclitem);
5658 : }
5659 : }
5660 :
5661 306 : PQclear(res);
5662 306 : destroyPQExpBuffer(query);
5663 :
5664 306 : *numNamespaces = ntups;
5665 :
5666 306 : return nsinfo;
5667 : }
5668 :
5669 : /*
5670 : * findNamespace:
5671 : * given a namespace OID, look up the info read by getNamespaces
5672 : */
5673 : static NamespaceInfo *
5674 940808 : findNamespace(Oid nsoid)
5675 : {
5676 : NamespaceInfo *nsinfo;
5677 :
5678 940808 : nsinfo = findNamespaceByOid(nsoid);
5679 940808 : if (nsinfo == NULL)
5680 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5681 940808 : return nsinfo;
5682 : }
5683 :
5684 : /*
5685 : * getExtensions:
5686 : * read all extensions in the system catalogs and return them in the
5687 : * ExtensionInfo* structure
5688 : *
5689 : * numExtensions is set to the number of extensions read in
5690 : */
5691 : ExtensionInfo *
5692 306 : getExtensions(Archive *fout, int *numExtensions)
5693 : {
5694 306 : DumpOptions *dopt = fout->dopt;
5695 : PGresult *res;
5696 : int ntups;
5697 : int i;
5698 : PQExpBuffer query;
5699 : ExtensionInfo *extinfo;
5700 : int i_tableoid;
5701 : int i_oid;
5702 : int i_extname;
5703 : int i_nspname;
5704 : int i_extrelocatable;
5705 : int i_extversion;
5706 : int i_extconfig;
5707 : int i_extcondition;
5708 :
5709 306 : query = createPQExpBuffer();
5710 :
5711 306 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
5712 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
5713 : "FROM pg_extension x "
5714 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
5715 :
5716 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5717 :
5718 306 : ntups = PQntuples(res);
5719 :
5720 306 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
5721 :
5722 306 : i_tableoid = PQfnumber(res, "tableoid");
5723 306 : i_oid = PQfnumber(res, "oid");
5724 306 : i_extname = PQfnumber(res, "extname");
5725 306 : i_nspname = PQfnumber(res, "nspname");
5726 306 : i_extrelocatable = PQfnumber(res, "extrelocatable");
5727 306 : i_extversion = PQfnumber(res, "extversion");
5728 306 : i_extconfig = PQfnumber(res, "extconfig");
5729 306 : i_extcondition = PQfnumber(res, "extcondition");
5730 :
5731 662 : for (i = 0; i < ntups; i++)
5732 : {
5733 356 : extinfo[i].dobj.objType = DO_EXTENSION;
5734 356 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5735 356 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5736 356 : AssignDumpId(&extinfo[i].dobj);
5737 356 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
5738 356 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
5739 356 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
5740 356 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
5741 356 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
5742 356 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
5743 :
5744 : /* Decide whether we want to dump it */
5745 356 : selectDumpableExtension(&(extinfo[i]), dopt);
5746 : }
5747 :
5748 306 : PQclear(res);
5749 306 : destroyPQExpBuffer(query);
5750 :
5751 306 : *numExtensions = ntups;
5752 :
5753 306 : return extinfo;
5754 : }
5755 :
5756 : /*
5757 : * getTypes:
5758 : * read all types in the system catalogs and return them in the
5759 : * TypeInfo* structure
5760 : *
5761 : * numTypes is set to the number of types read in
5762 : *
5763 : * NB: this must run after getFuncs() because we assume we can do
5764 : * findFuncByOid().
5765 : */
5766 : TypeInfo *
5767 304 : getTypes(Archive *fout, int *numTypes)
5768 : {
5769 : PGresult *res;
5770 : int ntups;
5771 : int i;
5772 304 : PQExpBuffer query = createPQExpBuffer();
5773 : TypeInfo *tyinfo;
5774 : ShellTypeInfo *stinfo;
5775 : int i_tableoid;
5776 : int i_oid;
5777 : int i_typname;
5778 : int i_typnamespace;
5779 : int i_typacl;
5780 : int i_acldefault;
5781 : int i_typowner;
5782 : int i_typelem;
5783 : int i_typrelid;
5784 : int i_typrelkind;
5785 : int i_typtype;
5786 : int i_typisdefined;
5787 : int i_isarray;
5788 :
5789 : /*
5790 : * we include even the built-in types because those may be used as array
5791 : * elements by user-defined types
5792 : *
5793 : * we filter out the built-in types when we dump out the types
5794 : *
5795 : * same approach for undefined (shell) types and array types
5796 : *
5797 : * Note: as of 8.3 we can reliably detect whether a type is an
5798 : * auto-generated array type by checking the element type's typarray.
5799 : * (Before that the test is capable of generating false positives.) We
5800 : * still check for name beginning with '_', though, so as to avoid the
5801 : * cost of the subselect probe for all standard types. This would have to
5802 : * be revisited if the backend ever allows renaming of array types.
5803 : */
5804 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
5805 : "typnamespace, typacl, "
5806 : "acldefault('T', typowner) AS acldefault, "
5807 : "typowner, "
5808 : "typelem, typrelid, "
5809 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
5810 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
5811 : "typtype, typisdefined, "
5812 : "typname[0] = '_' AND typelem != 0 AND "
5813 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
5814 : "FROM pg_type");
5815 :
5816 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5817 :
5818 304 : ntups = PQntuples(res);
5819 :
5820 304 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
5821 :
5822 304 : i_tableoid = PQfnumber(res, "tableoid");
5823 304 : i_oid = PQfnumber(res, "oid");
5824 304 : i_typname = PQfnumber(res, "typname");
5825 304 : i_typnamespace = PQfnumber(res, "typnamespace");
5826 304 : i_typacl = PQfnumber(res, "typacl");
5827 304 : i_acldefault = PQfnumber(res, "acldefault");
5828 304 : i_typowner = PQfnumber(res, "typowner");
5829 304 : i_typelem = PQfnumber(res, "typelem");
5830 304 : i_typrelid = PQfnumber(res, "typrelid");
5831 304 : i_typrelkind = PQfnumber(res, "typrelkind");
5832 304 : i_typtype = PQfnumber(res, "typtype");
5833 304 : i_typisdefined = PQfnumber(res, "typisdefined");
5834 304 : i_isarray = PQfnumber(res, "isarray");
5835 :
5836 213500 : for (i = 0; i < ntups; i++)
5837 : {
5838 213196 : tyinfo[i].dobj.objType = DO_TYPE;
5839 213196 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5840 213196 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5841 213196 : AssignDumpId(&tyinfo[i].dobj);
5842 213196 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
5843 426392 : tyinfo[i].dobj.namespace =
5844 213196 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
5845 213196 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
5846 213196 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5847 213196 : tyinfo[i].dacl.privtype = 0;
5848 213196 : tyinfo[i].dacl.initprivs = NULL;
5849 213196 : tyinfo[i].ftypname = NULL; /* may get filled later */
5850 213196 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
5851 213196 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
5852 213196 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
5853 213196 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
5854 213196 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
5855 213196 : tyinfo[i].shellType = NULL;
5856 :
5857 213196 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
5858 213100 : tyinfo[i].isDefined = true;
5859 : else
5860 96 : tyinfo[i].isDefined = false;
5861 :
5862 213196 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
5863 102142 : tyinfo[i].isArray = true;
5864 : else
5865 111054 : tyinfo[i].isArray = false;
5866 :
5867 213196 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
5868 2036 : tyinfo[i].isMultirange = true;
5869 : else
5870 211160 : tyinfo[i].isMultirange = false;
5871 :
5872 : /* Decide whether we want to dump it */
5873 213196 : selectDumpableType(&tyinfo[i], fout);
5874 :
5875 : /* Mark whether type has an ACL */
5876 213196 : if (!PQgetisnull(res, i, i_typacl))
5877 394 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5878 :
5879 : /*
5880 : * If it's a domain, fetch info about its constraints, if any
5881 : */
5882 213196 : tyinfo[i].nDomChecks = 0;
5883 213196 : tyinfo[i].domChecks = NULL;
5884 213196 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
5885 24314 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
5886 262 : getDomainConstraints(fout, &(tyinfo[i]));
5887 :
5888 : /*
5889 : * If it's a base type, make a DumpableObject representing a shell
5890 : * definition of the type. We will need to dump that ahead of the I/O
5891 : * functions for the type. Similarly, range types need a shell
5892 : * definition in case they have a canonicalize function.
5893 : *
5894 : * Note: the shell type doesn't have a catId. You might think it
5895 : * should copy the base type's catId, but then it might capture the
5896 : * pg_depend entries for the type, which we don't want.
5897 : */
5898 213196 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
5899 24314 : (tyinfo[i].typtype == TYPTYPE_BASE ||
5900 11956 : tyinfo[i].typtype == TYPTYPE_RANGE))
5901 : {
5902 12554 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
5903 12554 : stinfo->dobj.objType = DO_SHELL_TYPE;
5904 12554 : stinfo->dobj.catId = nilCatalogId;
5905 12554 : AssignDumpId(&stinfo->dobj);
5906 12554 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
5907 12554 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
5908 12554 : stinfo->baseType = &(tyinfo[i]);
5909 12554 : tyinfo[i].shellType = stinfo;
5910 :
5911 : /*
5912 : * Initially mark the shell type as not to be dumped. We'll only
5913 : * dump it if the I/O or canonicalize functions need to be dumped;
5914 : * this is taken care of while sorting dependencies.
5915 : */
5916 12554 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
5917 : }
5918 : }
5919 :
5920 304 : *numTypes = ntups;
5921 :
5922 304 : PQclear(res);
5923 :
5924 304 : destroyPQExpBuffer(query);
5925 :
5926 304 : return tyinfo;
5927 : }
5928 :
5929 : /*
5930 : * getOperators:
5931 : * read all operators in the system catalogs and return them in the
5932 : * OprInfo* structure
5933 : *
5934 : * numOprs is set to the number of operators read in
5935 : */
5936 : OprInfo *
5937 304 : getOperators(Archive *fout, int *numOprs)
5938 : {
5939 : PGresult *res;
5940 : int ntups;
5941 : int i;
5942 304 : PQExpBuffer query = createPQExpBuffer();
5943 : OprInfo *oprinfo;
5944 : int i_tableoid;
5945 : int i_oid;
5946 : int i_oprname;
5947 : int i_oprnamespace;
5948 : int i_oprowner;
5949 : int i_oprkind;
5950 : int i_oprcode;
5951 :
5952 : /*
5953 : * find all operators, including builtin operators; we filter out
5954 : * system-defined operators at dump-out time.
5955 : */
5956 :
5957 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
5958 : "oprnamespace, "
5959 : "oprowner, "
5960 : "oprkind, "
5961 : "oprcode::oid AS oprcode "
5962 : "FROM pg_operator");
5963 :
5964 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5965 :
5966 304 : ntups = PQntuples(res);
5967 304 : *numOprs = ntups;
5968 :
5969 304 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
5970 :
5971 304 : i_tableoid = PQfnumber(res, "tableoid");
5972 304 : i_oid = PQfnumber(res, "oid");
5973 304 : i_oprname = PQfnumber(res, "oprname");
5974 304 : i_oprnamespace = PQfnumber(res, "oprnamespace");
5975 304 : i_oprowner = PQfnumber(res, "oprowner");
5976 304 : i_oprkind = PQfnumber(res, "oprkind");
5977 304 : i_oprcode = PQfnumber(res, "oprcode");
5978 :
5979 243424 : for (i = 0; i < ntups; i++)
5980 : {
5981 243120 : oprinfo[i].dobj.objType = DO_OPERATOR;
5982 243120 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5983 243120 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5984 243120 : AssignDumpId(&oprinfo[i].dobj);
5985 243120 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
5986 486240 : oprinfo[i].dobj.namespace =
5987 243120 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
5988 243120 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
5989 243120 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
5990 243120 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
5991 :
5992 : /* Decide whether we want to dump it */
5993 243120 : selectDumpableObject(&(oprinfo[i].dobj), fout);
5994 : }
5995 :
5996 304 : PQclear(res);
5997 :
5998 304 : destroyPQExpBuffer(query);
5999 :
6000 304 : return oprinfo;
6001 : }
6002 :
6003 : /*
6004 : * getCollations:
6005 : * read all collations in the system catalogs and return them in the
6006 : * CollInfo* structure
6007 : *
6008 : * numCollations is set to the number of collations read in
6009 : */
6010 : CollInfo *
6011 304 : getCollations(Archive *fout, int *numCollations)
6012 : {
6013 : PGresult *res;
6014 : int ntups;
6015 : int i;
6016 : PQExpBuffer query;
6017 : CollInfo *collinfo;
6018 : int i_tableoid;
6019 : int i_oid;
6020 : int i_collname;
6021 : int i_collnamespace;
6022 : int i_collowner;
6023 :
6024 304 : query = createPQExpBuffer();
6025 :
6026 : /*
6027 : * find all collations, including builtin collations; we filter out
6028 : * system-defined collations at dump-out time.
6029 : */
6030 :
6031 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6032 : "collnamespace, "
6033 : "collowner "
6034 : "FROM pg_collation");
6035 :
6036 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6037 :
6038 304 : ntups = PQntuples(res);
6039 304 : *numCollations = ntups;
6040 :
6041 304 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6042 :
6043 304 : i_tableoid = PQfnumber(res, "tableoid");
6044 304 : i_oid = PQfnumber(res, "oid");
6045 304 : i_collname = PQfnumber(res, "collname");
6046 304 : i_collnamespace = PQfnumber(res, "collnamespace");
6047 304 : i_collowner = PQfnumber(res, "collowner");
6048 :
6049 241264 : for (i = 0; i < ntups; i++)
6050 : {
6051 240960 : collinfo[i].dobj.objType = DO_COLLATION;
6052 240960 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6053 240960 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6054 240960 : AssignDumpId(&collinfo[i].dobj);
6055 240960 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6056 481920 : collinfo[i].dobj.namespace =
6057 240960 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6058 240960 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6059 :
6060 : /* Decide whether we want to dump it */
6061 240960 : selectDumpableObject(&(collinfo[i].dobj), fout);
6062 : }
6063 :
6064 304 : PQclear(res);
6065 :
6066 304 : destroyPQExpBuffer(query);
6067 :
6068 304 : return collinfo;
6069 : }
6070 :
6071 : /*
6072 : * getConversions:
6073 : * read all conversions in the system catalogs and return them in the
6074 : * ConvInfo* structure
6075 : *
6076 : * numConversions is set to the number of conversions read in
6077 : */
6078 : ConvInfo *
6079 304 : getConversions(Archive *fout, int *numConversions)
6080 : {
6081 : PGresult *res;
6082 : int ntups;
6083 : int i;
6084 : PQExpBuffer query;
6085 : ConvInfo *convinfo;
6086 : int i_tableoid;
6087 : int i_oid;
6088 : int i_conname;
6089 : int i_connamespace;
6090 : int i_conowner;
6091 :
6092 304 : query = createPQExpBuffer();
6093 :
6094 : /*
6095 : * find all conversions, including builtin conversions; we filter out
6096 : * system-defined conversions at dump-out time.
6097 : */
6098 :
6099 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6100 : "connamespace, "
6101 : "conowner "
6102 : "FROM pg_conversion");
6103 :
6104 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6105 :
6106 304 : ntups = PQntuples(res);
6107 304 : *numConversions = ntups;
6108 :
6109 304 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6110 :
6111 304 : i_tableoid = PQfnumber(res, "tableoid");
6112 304 : i_oid = PQfnumber(res, "oid");
6113 304 : i_conname = PQfnumber(res, "conname");
6114 304 : i_connamespace = PQfnumber(res, "connamespace");
6115 304 : i_conowner = PQfnumber(res, "conowner");
6116 :
6117 39302 : for (i = 0; i < ntups; i++)
6118 : {
6119 38998 : convinfo[i].dobj.objType = DO_CONVERSION;
6120 38998 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6121 38998 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6122 38998 : AssignDumpId(&convinfo[i].dobj);
6123 38998 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6124 77996 : convinfo[i].dobj.namespace =
6125 38998 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6126 38998 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6127 :
6128 : /* Decide whether we want to dump it */
6129 38998 : selectDumpableObject(&(convinfo[i].dobj), fout);
6130 : }
6131 :
6132 304 : PQclear(res);
6133 :
6134 304 : destroyPQExpBuffer(query);
6135 :
6136 304 : return convinfo;
6137 : }
6138 :
6139 : /*
6140 : * getAccessMethods:
6141 : * read all user-defined access methods in the system catalogs and return
6142 : * them in the AccessMethodInfo* structure
6143 : *
6144 : * numAccessMethods is set to the number of access methods read in
6145 : */
6146 : AccessMethodInfo *
6147 304 : getAccessMethods(Archive *fout, int *numAccessMethods)
6148 : {
6149 : PGresult *res;
6150 : int ntups;
6151 : int i;
6152 : PQExpBuffer query;
6153 : AccessMethodInfo *aminfo;
6154 : int i_tableoid;
6155 : int i_oid;
6156 : int i_amname;
6157 : int i_amhandler;
6158 : int i_amtype;
6159 :
6160 : /* Before 9.6, there are no user-defined access methods */
6161 304 : if (fout->remoteVersion < 90600)
6162 : {
6163 0 : *numAccessMethods = 0;
6164 0 : return NULL;
6165 : }
6166 :
6167 304 : query = createPQExpBuffer();
6168 :
6169 : /* Select all access methods from pg_am table */
6170 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6171 : "amhandler::pg_catalog.regproc AS amhandler "
6172 : "FROM pg_am");
6173 :
6174 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6175 :
6176 304 : ntups = PQntuples(res);
6177 304 : *numAccessMethods = ntups;
6178 :
6179 304 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6180 :
6181 304 : i_tableoid = PQfnumber(res, "tableoid");
6182 304 : i_oid = PQfnumber(res, "oid");
6183 304 : i_amname = PQfnumber(res, "amname");
6184 304 : i_amhandler = PQfnumber(res, "amhandler");
6185 304 : i_amtype = PQfnumber(res, "amtype");
6186 :
6187 2664 : for (i = 0; i < ntups; i++)
6188 : {
6189 2360 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6190 2360 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6191 2360 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6192 2360 : AssignDumpId(&aminfo[i].dobj);
6193 2360 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6194 2360 : aminfo[i].dobj.namespace = NULL;
6195 2360 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6196 2360 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6197 :
6198 : /* Decide whether we want to dump it */
6199 2360 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6200 : }
6201 :
6202 304 : PQclear(res);
6203 :
6204 304 : destroyPQExpBuffer(query);
6205 :
6206 304 : return aminfo;
6207 : }
6208 :
6209 :
6210 : /*
6211 : * getOpclasses:
6212 : * read all opclasses in the system catalogs and return them in the
6213 : * OpclassInfo* structure
6214 : *
6215 : * numOpclasses is set to the number of opclasses read in
6216 : */
6217 : OpclassInfo *
6218 304 : getOpclasses(Archive *fout, int *numOpclasses)
6219 : {
6220 : PGresult *res;
6221 : int ntups;
6222 : int i;
6223 304 : PQExpBuffer query = createPQExpBuffer();
6224 : OpclassInfo *opcinfo;
6225 : int i_tableoid;
6226 : int i_oid;
6227 : int i_opcname;
6228 : int i_opcnamespace;
6229 : int i_opcowner;
6230 :
6231 : /*
6232 : * find all opclasses, including builtin opclasses; we filter out
6233 : * system-defined opclasses at dump-out time.
6234 : */
6235 :
6236 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6237 : "opcnamespace, "
6238 : "opcowner "
6239 : "FROM pg_opclass");
6240 :
6241 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6242 :
6243 304 : ntups = PQntuples(res);
6244 304 : *numOpclasses = ntups;
6245 :
6246 304 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6247 :
6248 304 : i_tableoid = PQfnumber(res, "tableoid");
6249 304 : i_oid = PQfnumber(res, "oid");
6250 304 : i_opcname = PQfnumber(res, "opcname");
6251 304 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6252 304 : i_opcowner = PQfnumber(res, "opcowner");
6253 :
6254 54400 : for (i = 0; i < ntups; i++)
6255 : {
6256 54096 : opcinfo[i].dobj.objType = DO_OPCLASS;
6257 54096 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6258 54096 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6259 54096 : AssignDumpId(&opcinfo[i].dobj);
6260 54096 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6261 108192 : opcinfo[i].dobj.namespace =
6262 54096 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6263 54096 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6264 :
6265 : /* Decide whether we want to dump it */
6266 54096 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6267 : }
6268 :
6269 304 : PQclear(res);
6270 :
6271 304 : destroyPQExpBuffer(query);
6272 :
6273 304 : return opcinfo;
6274 : }
6275 :
6276 : /*
6277 : * getOpfamilies:
6278 : * read all opfamilies in the system catalogs and return them in the
6279 : * OpfamilyInfo* structure
6280 : *
6281 : * numOpfamilies is set to the number of opfamilies read in
6282 : */
6283 : OpfamilyInfo *
6284 304 : getOpfamilies(Archive *fout, int *numOpfamilies)
6285 : {
6286 : PGresult *res;
6287 : int ntups;
6288 : int i;
6289 : PQExpBuffer query;
6290 : OpfamilyInfo *opfinfo;
6291 : int i_tableoid;
6292 : int i_oid;
6293 : int i_opfname;
6294 : int i_opfnamespace;
6295 : int i_opfowner;
6296 :
6297 304 : query = createPQExpBuffer();
6298 :
6299 : /*
6300 : * find all opfamilies, including builtin opfamilies; we filter out
6301 : * system-defined opfamilies at dump-out time.
6302 : */
6303 :
6304 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6305 : "opfnamespace, "
6306 : "opfowner "
6307 : "FROM pg_opfamily");
6308 :
6309 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6310 :
6311 304 : ntups = PQntuples(res);
6312 304 : *numOpfamilies = ntups;
6313 :
6314 304 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6315 :
6316 304 : i_tableoid = PQfnumber(res, "tableoid");
6317 304 : i_oid = PQfnumber(res, "oid");
6318 304 : i_opfname = PQfnumber(res, "opfname");
6319 304 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6320 304 : i_opfowner = PQfnumber(res, "opfowner");
6321 :
6322 44930 : for (i = 0; i < ntups; i++)
6323 : {
6324 44626 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6325 44626 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6326 44626 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6327 44626 : AssignDumpId(&opfinfo[i].dobj);
6328 44626 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6329 89252 : opfinfo[i].dobj.namespace =
6330 44626 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6331 44626 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6332 :
6333 : /* Decide whether we want to dump it */
6334 44626 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6335 : }
6336 :
6337 304 : PQclear(res);
6338 :
6339 304 : destroyPQExpBuffer(query);
6340 :
6341 304 : return opfinfo;
6342 : }
6343 :
6344 : /*
6345 : * getAggregates:
6346 : * read all the user-defined aggregates in the system catalogs and
6347 : * return them in the AggInfo* structure
6348 : *
6349 : * numAggs is set to the number of aggregates read in
6350 : */
6351 : AggInfo *
6352 304 : getAggregates(Archive *fout, int *numAggs)
6353 : {
6354 304 : DumpOptions *dopt = fout->dopt;
6355 : PGresult *res;
6356 : int ntups;
6357 : int i;
6358 304 : PQExpBuffer query = createPQExpBuffer();
6359 : AggInfo *agginfo;
6360 : int i_tableoid;
6361 : int i_oid;
6362 : int i_aggname;
6363 : int i_aggnamespace;
6364 : int i_pronargs;
6365 : int i_proargtypes;
6366 : int i_proowner;
6367 : int i_aggacl;
6368 : int i_acldefault;
6369 :
6370 : /*
6371 : * Find all interesting aggregates. See comment in getFuncs() for the
6372 : * rationale behind the filtering logic.
6373 : */
6374 304 : if (fout->remoteVersion >= 90600)
6375 : {
6376 : const char *agg_check;
6377 :
6378 608 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6379 304 : : "p.proisagg");
6380 :
6381 304 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6382 : "p.proname AS aggname, "
6383 : "p.pronamespace AS aggnamespace, "
6384 : "p.pronargs, p.proargtypes, "
6385 : "p.proowner, "
6386 : "p.proacl AS aggacl, "
6387 : "acldefault('f', p.proowner) AS acldefault "
6388 : "FROM pg_proc p "
6389 : "LEFT JOIN pg_init_privs pip ON "
6390 : "(p.oid = pip.objoid "
6391 : "AND pip.classoid = 'pg_proc'::regclass "
6392 : "AND pip.objsubid = 0) "
6393 : "WHERE %s AND ("
6394 : "p.pronamespace != "
6395 : "(SELECT oid FROM pg_namespace "
6396 : "WHERE nspname = 'pg_catalog') OR "
6397 : "p.proacl IS DISTINCT FROM pip.initprivs",
6398 : agg_check);
6399 304 : if (dopt->binary_upgrade)
6400 28 : appendPQExpBufferStr(query,
6401 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6402 : "classid = 'pg_proc'::regclass AND "
6403 : "objid = p.oid AND "
6404 : "refclassid = 'pg_extension'::regclass AND "
6405 : "deptype = 'e')");
6406 304 : appendPQExpBufferChar(query, ')');
6407 : }
6408 : else
6409 : {
6410 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6411 : "pronamespace AS aggnamespace, "
6412 : "pronargs, proargtypes, "
6413 : "proowner, "
6414 : "proacl AS aggacl, "
6415 : "acldefault('f', proowner) AS acldefault "
6416 : "FROM pg_proc p "
6417 : "WHERE proisagg AND ("
6418 : "pronamespace != "
6419 : "(SELECT oid FROM pg_namespace "
6420 : "WHERE nspname = 'pg_catalog')");
6421 0 : if (dopt->binary_upgrade)
6422 0 : appendPQExpBufferStr(query,
6423 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6424 : "classid = 'pg_proc'::regclass AND "
6425 : "objid = p.oid AND "
6426 : "refclassid = 'pg_extension'::regclass AND "
6427 : "deptype = 'e')");
6428 0 : appendPQExpBufferChar(query, ')');
6429 : }
6430 :
6431 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6432 :
6433 304 : ntups = PQntuples(res);
6434 304 : *numAggs = ntups;
6435 :
6436 304 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6437 :
6438 304 : i_tableoid = PQfnumber(res, "tableoid");
6439 304 : i_oid = PQfnumber(res, "oid");
6440 304 : i_aggname = PQfnumber(res, "aggname");
6441 304 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6442 304 : i_pronargs = PQfnumber(res, "pronargs");
6443 304 : i_proargtypes = PQfnumber(res, "proargtypes");
6444 304 : i_proowner = PQfnumber(res, "proowner");
6445 304 : i_aggacl = PQfnumber(res, "aggacl");
6446 304 : i_acldefault = PQfnumber(res, "acldefault");
6447 :
6448 910 : for (i = 0; i < ntups; i++)
6449 : {
6450 606 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6451 606 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6452 606 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6453 606 : AssignDumpId(&agginfo[i].aggfn.dobj);
6454 606 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6455 1212 : agginfo[i].aggfn.dobj.namespace =
6456 606 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6457 606 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6458 606 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6459 606 : agginfo[i].aggfn.dacl.privtype = 0;
6460 606 : agginfo[i].aggfn.dacl.initprivs = NULL;
6461 606 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6462 606 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6463 606 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6464 606 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6465 606 : if (agginfo[i].aggfn.nargs == 0)
6466 80 : agginfo[i].aggfn.argtypes = NULL;
6467 : else
6468 : {
6469 526 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6470 526 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6471 526 : agginfo[i].aggfn.argtypes,
6472 526 : agginfo[i].aggfn.nargs);
6473 : }
6474 606 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6475 :
6476 : /* Decide whether we want to dump it */
6477 606 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6478 :
6479 : /* Mark whether aggregate has an ACL */
6480 606 : if (!PQgetisnull(res, i, i_aggacl))
6481 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6482 : }
6483 :
6484 304 : PQclear(res);
6485 :
6486 304 : destroyPQExpBuffer(query);
6487 :
6488 304 : return agginfo;
6489 : }
6490 :
6491 : /*
6492 : * getFuncs:
6493 : * read all the user-defined functions in the system catalogs and
6494 : * return them in the FuncInfo* structure
6495 : *
6496 : * numFuncs is set to the number of functions read in
6497 : */
6498 : FuncInfo *
6499 304 : getFuncs(Archive *fout, int *numFuncs)
6500 : {
6501 304 : DumpOptions *dopt = fout->dopt;
6502 : PGresult *res;
6503 : int ntups;
6504 : int i;
6505 304 : PQExpBuffer query = createPQExpBuffer();
6506 : FuncInfo *finfo;
6507 : int i_tableoid;
6508 : int i_oid;
6509 : int i_proname;
6510 : int i_pronamespace;
6511 : int i_proowner;
6512 : int i_prolang;
6513 : int i_pronargs;
6514 : int i_proargtypes;
6515 : int i_prorettype;
6516 : int i_proacl;
6517 : int i_acldefault;
6518 :
6519 : /*
6520 : * Find all interesting functions. This is a bit complicated:
6521 : *
6522 : * 1. Always exclude aggregates; those are handled elsewhere.
6523 : *
6524 : * 2. Always exclude functions that are internally dependent on something
6525 : * else, since presumably those will be created as a result of creating
6526 : * the something else. This currently acts only to suppress constructor
6527 : * functions for range types. Note this is OK only because the
6528 : * constructors don't have any dependencies the range type doesn't have;
6529 : * otherwise we might not get creation ordering correct.
6530 : *
6531 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6532 : * they're members of extensions and we are in binary-upgrade mode then
6533 : * include them, since we want to dump extension members individually in
6534 : * that mode. Also, if they are used by casts or transforms then we need
6535 : * to gather the information about them, though they won't be dumped if
6536 : * they are built-in. Also, in 9.6 and up, include functions in
6537 : * pg_catalog if they have an ACL different from what's shown in
6538 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6539 : */
6540 304 : if (fout->remoteVersion >= 90600)
6541 : {
6542 : const char *not_agg_check;
6543 :
6544 608 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6545 304 : : "NOT p.proisagg");
6546 :
6547 304 : appendPQExpBuffer(query,
6548 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6549 : "p.pronargs, p.proargtypes, p.prorettype, "
6550 : "p.proacl, "
6551 : "acldefault('f', p.proowner) AS acldefault, "
6552 : "p.pronamespace, "
6553 : "p.proowner "
6554 : "FROM pg_proc p "
6555 : "LEFT JOIN pg_init_privs pip ON "
6556 : "(p.oid = pip.objoid "
6557 : "AND pip.classoid = 'pg_proc'::regclass "
6558 : "AND pip.objsubid = 0) "
6559 : "WHERE %s"
6560 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6561 : "WHERE classid = 'pg_proc'::regclass AND "
6562 : "objid = p.oid AND deptype = 'i')"
6563 : "\n AND ("
6564 : "\n pronamespace != "
6565 : "(SELECT oid FROM pg_namespace "
6566 : "WHERE nspname = 'pg_catalog')"
6567 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6568 : "\n WHERE pg_cast.oid > %u "
6569 : "\n AND p.oid = pg_cast.castfunc)"
6570 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6571 : "\n WHERE pg_transform.oid > %u AND "
6572 : "\n (p.oid = pg_transform.trffromsql"
6573 : "\n OR p.oid = pg_transform.trftosql))",
6574 : not_agg_check,
6575 : g_last_builtin_oid,
6576 : g_last_builtin_oid);
6577 304 : if (dopt->binary_upgrade)
6578 28 : appendPQExpBufferStr(query,
6579 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6580 : "classid = 'pg_proc'::regclass AND "
6581 : "objid = p.oid AND "
6582 : "refclassid = 'pg_extension'::regclass AND "
6583 : "deptype = 'e')");
6584 304 : appendPQExpBufferStr(query,
6585 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6586 304 : appendPQExpBufferChar(query, ')');
6587 : }
6588 : else
6589 : {
6590 0 : appendPQExpBuffer(query,
6591 : "SELECT tableoid, oid, proname, prolang, "
6592 : "pronargs, proargtypes, prorettype, proacl, "
6593 : "acldefault('f', proowner) AS acldefault, "
6594 : "pronamespace, "
6595 : "proowner "
6596 : "FROM pg_proc p "
6597 : "WHERE NOT proisagg"
6598 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6599 : "WHERE classid = 'pg_proc'::regclass AND "
6600 : "objid = p.oid AND deptype = 'i')"
6601 : "\n AND ("
6602 : "\n pronamespace != "
6603 : "(SELECT oid FROM pg_namespace "
6604 : "WHERE nspname = 'pg_catalog')"
6605 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6606 : "\n WHERE pg_cast.oid > '%u'::oid"
6607 : "\n AND p.oid = pg_cast.castfunc)",
6608 : g_last_builtin_oid);
6609 :
6610 0 : if (fout->remoteVersion >= 90500)
6611 0 : appendPQExpBuffer(query,
6612 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6613 : "\n WHERE pg_transform.oid > '%u'::oid"
6614 : "\n AND (p.oid = pg_transform.trffromsql"
6615 : "\n OR p.oid = pg_transform.trftosql))",
6616 : g_last_builtin_oid);
6617 :
6618 0 : if (dopt->binary_upgrade)
6619 0 : appendPQExpBufferStr(query,
6620 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6621 : "classid = 'pg_proc'::regclass AND "
6622 : "objid = p.oid AND "
6623 : "refclassid = 'pg_extension'::regclass AND "
6624 : "deptype = 'e')");
6625 0 : appendPQExpBufferChar(query, ')');
6626 : }
6627 :
6628 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6629 :
6630 304 : ntups = PQntuples(res);
6631 :
6632 304 : *numFuncs = ntups;
6633 :
6634 304 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6635 :
6636 304 : i_tableoid = PQfnumber(res, "tableoid");
6637 304 : i_oid = PQfnumber(res, "oid");
6638 304 : i_proname = PQfnumber(res, "proname");
6639 304 : i_pronamespace = PQfnumber(res, "pronamespace");
6640 304 : i_proowner = PQfnumber(res, "proowner");
6641 304 : i_prolang = PQfnumber(res, "prolang");
6642 304 : i_pronargs = PQfnumber(res, "pronargs");
6643 304 : i_proargtypes = PQfnumber(res, "proargtypes");
6644 304 : i_prorettype = PQfnumber(res, "prorettype");
6645 304 : i_proacl = PQfnumber(res, "proacl");
6646 304 : i_acldefault = PQfnumber(res, "acldefault");
6647 :
6648 7686 : for (i = 0; i < ntups; i++)
6649 : {
6650 7382 : finfo[i].dobj.objType = DO_FUNC;
6651 7382 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6652 7382 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6653 7382 : AssignDumpId(&finfo[i].dobj);
6654 7382 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6655 14764 : finfo[i].dobj.namespace =
6656 7382 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6657 7382 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6658 7382 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6659 7382 : finfo[i].dacl.privtype = 0;
6660 7382 : finfo[i].dacl.initprivs = NULL;
6661 7382 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6662 7382 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6663 7382 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6664 7382 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6665 7382 : if (finfo[i].nargs == 0)
6666 1618 : finfo[i].argtypes = NULL;
6667 : else
6668 : {
6669 5764 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6670 5764 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6671 5764 : finfo[i].argtypes, finfo[i].nargs);
6672 : }
6673 7382 : finfo[i].postponed_def = false; /* might get set during sort */
6674 :
6675 : /* Decide whether we want to dump it */
6676 7382 : selectDumpableObject(&(finfo[i].dobj), fout);
6677 :
6678 : /* Mark whether function has an ACL */
6679 7382 : if (!PQgetisnull(res, i, i_proacl))
6680 272 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6681 : }
6682 :
6683 304 : PQclear(res);
6684 :
6685 304 : destroyPQExpBuffer(query);
6686 :
6687 304 : return finfo;
6688 : }
6689 :
6690 : /*
6691 : * getTables
6692 : * read all the tables (no indexes) in the system catalogs,
6693 : * and return them as an array of TableInfo structures
6694 : *
6695 : * *numTables is set to the number of tables read in
6696 : */
6697 : TableInfo *
6698 306 : getTables(Archive *fout, int *numTables)
6699 : {
6700 306 : DumpOptions *dopt = fout->dopt;
6701 : PGresult *res;
6702 : int ntups;
6703 : int i;
6704 306 : PQExpBuffer query = createPQExpBuffer();
6705 : TableInfo *tblinfo;
6706 : int i_reltableoid;
6707 : int i_reloid;
6708 : int i_relname;
6709 : int i_relnamespace;
6710 : int i_relkind;
6711 : int i_reltype;
6712 : int i_relowner;
6713 : int i_relchecks;
6714 : int i_relhasindex;
6715 : int i_relhasrules;
6716 : int i_relpages;
6717 : int i_toastpages;
6718 : int i_owning_tab;
6719 : int i_owning_col;
6720 : int i_reltablespace;
6721 : int i_relhasoids;
6722 : int i_relhastriggers;
6723 : int i_relpersistence;
6724 : int i_relispopulated;
6725 : int i_relreplident;
6726 : int i_relrowsec;
6727 : int i_relforcerowsec;
6728 : int i_relfrozenxid;
6729 : int i_toastfrozenxid;
6730 : int i_toastoid;
6731 : int i_relminmxid;
6732 : int i_toastminmxid;
6733 : int i_reloptions;
6734 : int i_checkoption;
6735 : int i_toastreloptions;
6736 : int i_reloftype;
6737 : int i_foreignserver;
6738 : int i_amname;
6739 : int i_is_identity_sequence;
6740 : int i_relacl;
6741 : int i_acldefault;
6742 : int i_ispartition;
6743 :
6744 : /*
6745 : * Find all the tables and table-like objects.
6746 : *
6747 : * We must fetch all tables in this phase because otherwise we cannot
6748 : * correctly identify inherited columns, owned sequences, etc.
6749 : *
6750 : * We include system catalogs, so that we can work if a user table is
6751 : * defined to inherit from a system catalog (pretty weird, but...)
6752 : *
6753 : * Note: in this phase we should collect only a minimal amount of
6754 : * information about each table, basically just enough to decide if it is
6755 : * interesting. In particular, since we do not yet have lock on any user
6756 : * table, we MUST NOT invoke any server-side data collection functions
6757 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
6758 : * wrong answers if any concurrent DDL is happening.
6759 : */
6760 :
6761 306 : appendPQExpBufferStr(query,
6762 : "SELECT c.tableoid, c.oid, c.relname, "
6763 : "c.relnamespace, c.relkind, c.reltype, "
6764 : "c.relowner, "
6765 : "c.relchecks, "
6766 : "c.relhasindex, c.relhasrules, c.relpages, "
6767 : "c.relhastriggers, "
6768 : "c.relpersistence, "
6769 : "c.reloftype, "
6770 : "c.relacl, "
6771 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
6772 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
6773 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
6774 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6775 : "ELSE 0 END AS foreignserver, "
6776 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
6777 : "tc.oid AS toid, "
6778 : "tc.relpages AS toastpages, "
6779 : "tc.reloptions AS toast_reloptions, "
6780 : "d.refobjid AS owning_tab, "
6781 : "d.refobjsubid AS owning_col, "
6782 : "tsp.spcname AS reltablespace, ");
6783 :
6784 306 : if (fout->remoteVersion >= 120000)
6785 306 : appendPQExpBufferStr(query,
6786 : "false AS relhasoids, ");
6787 : else
6788 0 : appendPQExpBufferStr(query,
6789 : "c.relhasoids, ");
6790 :
6791 306 : if (fout->remoteVersion >= 90300)
6792 306 : appendPQExpBufferStr(query,
6793 : "c.relispopulated, ");
6794 : else
6795 0 : appendPQExpBufferStr(query,
6796 : "'t' as relispopulated, ");
6797 :
6798 306 : if (fout->remoteVersion >= 90400)
6799 306 : appendPQExpBufferStr(query,
6800 : "c.relreplident, ");
6801 : else
6802 0 : appendPQExpBufferStr(query,
6803 : "'d' AS relreplident, ");
6804 :
6805 306 : if (fout->remoteVersion >= 90500)
6806 306 : appendPQExpBufferStr(query,
6807 : "c.relrowsecurity, c.relforcerowsecurity, ");
6808 : else
6809 0 : appendPQExpBufferStr(query,
6810 : "false AS relrowsecurity, "
6811 : "false AS relforcerowsecurity, ");
6812 :
6813 306 : if (fout->remoteVersion >= 90300)
6814 306 : appendPQExpBufferStr(query,
6815 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
6816 : else
6817 0 : appendPQExpBufferStr(query,
6818 : "0 AS relminmxid, 0 AS tminmxid, ");
6819 :
6820 306 : if (fout->remoteVersion >= 90300)
6821 306 : appendPQExpBufferStr(query,
6822 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
6823 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
6824 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
6825 : else
6826 0 : appendPQExpBufferStr(query,
6827 : "c.reloptions, NULL AS checkoption, ");
6828 :
6829 306 : if (fout->remoteVersion >= 90600)
6830 306 : appendPQExpBufferStr(query,
6831 : "am.amname, ");
6832 : else
6833 0 : appendPQExpBufferStr(query,
6834 : "NULL AS amname, ");
6835 :
6836 306 : if (fout->remoteVersion >= 90600)
6837 306 : appendPQExpBufferStr(query,
6838 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
6839 : else
6840 0 : appendPQExpBufferStr(query,
6841 : "false AS is_identity_sequence, ");
6842 :
6843 306 : if (fout->remoteVersion >= 100000)
6844 306 : appendPQExpBufferStr(query,
6845 : "c.relispartition AS ispartition ");
6846 : else
6847 0 : appendPQExpBufferStr(query,
6848 : "false AS ispartition ");
6849 :
6850 : /*
6851 : * Left join to pg_depend to pick up dependency info linking sequences to
6852 : * their owning column, if any (note this dependency is AUTO except for
6853 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
6854 : * collect the spcname.
6855 : */
6856 306 : appendPQExpBufferStr(query,
6857 : "\nFROM pg_class c\n"
6858 : "LEFT JOIN pg_depend d ON "
6859 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
6860 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
6861 : "d.objsubid = 0 AND "
6862 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
6863 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
6864 :
6865 : /*
6866 : * In 9.6 and up, left join to pg_am to pick up the amname.
6867 : */
6868 306 : if (fout->remoteVersion >= 90600)
6869 306 : appendPQExpBufferStr(query,
6870 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
6871 :
6872 : /*
6873 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
6874 : * that versions 10 and 11 have them, but later versions do not, so
6875 : * emitting them causes the upgrade to fail.
6876 : */
6877 306 : appendPQExpBufferStr(query,
6878 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
6879 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
6880 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
6881 :
6882 : /*
6883 : * Restrict to interesting relkinds (in particular, not indexes). Not all
6884 : * relkinds are possible in older servers, but it's not worth the trouble
6885 : * to emit a version-dependent list.
6886 : *
6887 : * Composite-type table entries won't be dumped as such, but we have to
6888 : * make a DumpableObject for them so that we can track dependencies of the
6889 : * composite type (pg_depend entries for columns of the composite type
6890 : * link to the pg_class entry not the pg_type entry).
6891 : */
6892 306 : appendPQExpBufferStr(query,
6893 : "WHERE c.relkind IN ("
6894 : CppAsString2(RELKIND_RELATION) ", "
6895 : CppAsString2(RELKIND_SEQUENCE) ", "
6896 : CppAsString2(RELKIND_VIEW) ", "
6897 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
6898 : CppAsString2(RELKIND_MATVIEW) ", "
6899 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
6900 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
6901 : "ORDER BY c.oid");
6902 :
6903 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6904 :
6905 306 : ntups = PQntuples(res);
6906 :
6907 306 : *numTables = ntups;
6908 :
6909 : /*
6910 : * Extract data from result and lock dumpable tables. We do the locking
6911 : * before anything else, to minimize the window wherein a table could
6912 : * disappear under us.
6913 : *
6914 : * Note that we have to save info about all tables here, even when dumping
6915 : * only one, because we don't yet know which tables might be inheritance
6916 : * ancestors of the target table.
6917 : */
6918 306 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
6919 :
6920 306 : i_reltableoid = PQfnumber(res, "tableoid");
6921 306 : i_reloid = PQfnumber(res, "oid");
6922 306 : i_relname = PQfnumber(res, "relname");
6923 306 : i_relnamespace = PQfnumber(res, "relnamespace");
6924 306 : i_relkind = PQfnumber(res, "relkind");
6925 306 : i_reltype = PQfnumber(res, "reltype");
6926 306 : i_relowner = PQfnumber(res, "relowner");
6927 306 : i_relchecks = PQfnumber(res, "relchecks");
6928 306 : i_relhasindex = PQfnumber(res, "relhasindex");
6929 306 : i_relhasrules = PQfnumber(res, "relhasrules");
6930 306 : i_relpages = PQfnumber(res, "relpages");
6931 306 : i_toastpages = PQfnumber(res, "toastpages");
6932 306 : i_owning_tab = PQfnumber(res, "owning_tab");
6933 306 : i_owning_col = PQfnumber(res, "owning_col");
6934 306 : i_reltablespace = PQfnumber(res, "reltablespace");
6935 306 : i_relhasoids = PQfnumber(res, "relhasoids");
6936 306 : i_relhastriggers = PQfnumber(res, "relhastriggers");
6937 306 : i_relpersistence = PQfnumber(res, "relpersistence");
6938 306 : i_relispopulated = PQfnumber(res, "relispopulated");
6939 306 : i_relreplident = PQfnumber(res, "relreplident");
6940 306 : i_relrowsec = PQfnumber(res, "relrowsecurity");
6941 306 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
6942 306 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
6943 306 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
6944 306 : i_toastoid = PQfnumber(res, "toid");
6945 306 : i_relminmxid = PQfnumber(res, "relminmxid");
6946 306 : i_toastminmxid = PQfnumber(res, "tminmxid");
6947 306 : i_reloptions = PQfnumber(res, "reloptions");
6948 306 : i_checkoption = PQfnumber(res, "checkoption");
6949 306 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
6950 306 : i_reloftype = PQfnumber(res, "reloftype");
6951 306 : i_foreignserver = PQfnumber(res, "foreignserver");
6952 306 : i_amname = PQfnumber(res, "amname");
6953 306 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
6954 306 : i_relacl = PQfnumber(res, "relacl");
6955 306 : i_acldefault = PQfnumber(res, "acldefault");
6956 306 : i_ispartition = PQfnumber(res, "ispartition");
6957 :
6958 306 : if (dopt->lockWaitTimeout)
6959 : {
6960 : /*
6961 : * Arrange to fail instead of waiting forever for a table lock.
6962 : *
6963 : * NB: this coding assumes that the only queries issued within the
6964 : * following loop are LOCK TABLEs; else the timeout may be undesirably
6965 : * applied to other things too.
6966 : */
6967 4 : resetPQExpBuffer(query);
6968 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
6969 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
6970 4 : ExecuteSqlStatement(fout, query->data);
6971 : }
6972 :
6973 306 : resetPQExpBuffer(query);
6974 :
6975 77730 : for (i = 0; i < ntups; i++)
6976 : {
6977 77424 : tblinfo[i].dobj.objType = DO_TABLE;
6978 77424 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
6979 77424 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
6980 77424 : AssignDumpId(&tblinfo[i].dobj);
6981 77424 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
6982 154848 : tblinfo[i].dobj.namespace =
6983 77424 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
6984 77424 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
6985 77424 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6986 77424 : tblinfo[i].dacl.privtype = 0;
6987 77424 : tblinfo[i].dacl.initprivs = NULL;
6988 77424 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
6989 77424 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
6990 77424 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
6991 77424 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
6992 77424 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
6993 77424 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
6994 77424 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
6995 77424 : if (PQgetisnull(res, i, i_toastpages))
6996 61380 : tblinfo[i].toastpages = 0;
6997 : else
6998 16044 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
6999 77424 : if (PQgetisnull(res, i, i_owning_tab))
7000 : {
7001 76810 : tblinfo[i].owning_tab = InvalidOid;
7002 76810 : tblinfo[i].owning_col = 0;
7003 : }
7004 : else
7005 : {
7006 614 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7007 614 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7008 : }
7009 77424 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7010 77424 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7011 77424 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7012 77424 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7013 77424 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7014 77424 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7015 77424 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7016 77424 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7017 77424 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7018 77424 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7019 77424 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7020 77424 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7021 77424 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7022 77424 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7023 77424 : if (PQgetisnull(res, i, i_checkoption))
7024 77336 : tblinfo[i].checkoption = NULL;
7025 : else
7026 88 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7027 77424 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7028 77424 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7029 77424 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7030 77424 : if (PQgetisnull(res, i, i_amname))
7031 46682 : tblinfo[i].amname = NULL;
7032 : else
7033 30742 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7034 77424 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7035 77424 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7036 :
7037 : /* other fields were zeroed above */
7038 :
7039 : /*
7040 : * Decide whether we want to dump this table.
7041 : */
7042 77424 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7043 298 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7044 : else
7045 77126 : selectDumpableTable(&tblinfo[i], fout);
7046 :
7047 : /*
7048 : * Now, consider the table "interesting" if we need to dump its
7049 : * definition or its data. Later on, we'll skip a lot of data
7050 : * collection for uninteresting tables.
7051 : *
7052 : * Note: the "interesting" flag will also be set by flagInhTables for
7053 : * parents of interesting tables, so that we collect necessary
7054 : * inheritance info even when the parents are not themselves being
7055 : * dumped. This is the main reason why we need an "interesting" flag
7056 : * that's separate from the components-to-dump bitmask.
7057 : */
7058 77424 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7059 : (DUMP_COMPONENT_DEFINITION |
7060 77424 : DUMP_COMPONENT_DATA)) != 0;
7061 :
7062 77424 : tblinfo[i].dummy_view = false; /* might get set during sort */
7063 77424 : tblinfo[i].postponed_def = false; /* might get set during sort */
7064 :
7065 : /* Tables have data */
7066 77424 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7067 :
7068 : /* Mark whether table has an ACL */
7069 77424 : if (!PQgetisnull(res, i, i_relacl))
7070 63496 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7071 77424 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7072 :
7073 : /*
7074 : * Read-lock target tables to make sure they aren't DROPPED or altered
7075 : * in schema before we get around to dumping them.
7076 : *
7077 : * Note that we don't explicitly lock parents of the target tables; we
7078 : * assume our lock on the child is enough to prevent schema
7079 : * alterations to parent tables.
7080 : *
7081 : * NOTE: it'd be kinda nice to lock other relations too, not only
7082 : * plain or partitioned tables, but the backend doesn't presently
7083 : * allow that.
7084 : *
7085 : * We only need to lock the table for certain components; see
7086 : * pg_dump.h
7087 : */
7088 77424 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7089 11612 : (tblinfo[i].relkind == RELKIND_RELATION ||
7090 3264 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7091 : {
7092 : /*
7093 : * Tables are locked in batches. When dumping from a remote
7094 : * server this can save a significant amount of time by reducing
7095 : * the number of round trips.
7096 : */
7097 9378 : if (query->len == 0)
7098 194 : appendPQExpBuffer(query, "LOCK TABLE %s",
7099 194 : fmtQualifiedDumpable(&tblinfo[i]));
7100 : else
7101 : {
7102 9184 : appendPQExpBuffer(query, ", %s",
7103 9184 : fmtQualifiedDumpable(&tblinfo[i]));
7104 :
7105 : /* Arbitrarily end a batch when query length reaches 100K. */
7106 9184 : if (query->len >= 100000)
7107 : {
7108 : /* Lock another batch of tables. */
7109 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7110 0 : ExecuteSqlStatement(fout, query->data);
7111 0 : resetPQExpBuffer(query);
7112 : }
7113 : }
7114 : }
7115 : }
7116 :
7117 306 : if (query->len != 0)
7118 : {
7119 : /* Lock the tables in the last batch. */
7120 194 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7121 194 : ExecuteSqlStatement(fout, query->data);
7122 : }
7123 :
7124 304 : if (dopt->lockWaitTimeout)
7125 : {
7126 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7127 : }
7128 :
7129 304 : PQclear(res);
7130 :
7131 304 : destroyPQExpBuffer(query);
7132 :
7133 304 : return tblinfo;
7134 : }
7135 :
7136 : /*
7137 : * getOwnedSeqs
7138 : * identify owned sequences and mark them as dumpable if owning table is
7139 : *
7140 : * We used to do this in getTables(), but it's better to do it after the
7141 : * index used by findTableByOid() has been set up.
7142 : */
7143 : void
7144 304 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7145 : {
7146 : int i;
7147 :
7148 : /*
7149 : * Force sequences that are "owned" by table columns to be dumped whenever
7150 : * their owning table is being dumped.
7151 : */
7152 77210 : for (i = 0; i < numTables; i++)
7153 : {
7154 76906 : TableInfo *seqinfo = &tblinfo[i];
7155 : TableInfo *owning_tab;
7156 :
7157 76906 : if (!OidIsValid(seqinfo->owning_tab))
7158 76298 : continue; /* not an owned sequence */
7159 :
7160 608 : owning_tab = findTableByOid(seqinfo->owning_tab);
7161 608 : if (owning_tab == NULL)
7162 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7163 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7164 :
7165 : /*
7166 : * Only dump identity sequences if we're going to dump the table that
7167 : * it belongs to.
7168 : */
7169 608 : if (owning_tab->dobj.dump == DUMP_COMPONENT_NONE &&
7170 52 : seqinfo->is_identity_sequence)
7171 : {
7172 14 : seqinfo->dobj.dump = DUMP_COMPONENT_NONE;
7173 14 : continue;
7174 : }
7175 :
7176 : /*
7177 : * Otherwise we need to dump the components that are being dumped for
7178 : * the table and any components which the sequence is explicitly
7179 : * marked with.
7180 : *
7181 : * We can't simply use the set of components which are being dumped
7182 : * for the table as the table might be in an extension (and only the
7183 : * non-extension components, eg: ACLs if changed, security labels, and
7184 : * policies, are being dumped) while the sequence is not (and
7185 : * therefore the definition and other components should also be
7186 : * dumped).
7187 : *
7188 : * If the sequence is part of the extension then it should be properly
7189 : * marked by checkExtensionMembership() and this will be a no-op as
7190 : * the table will be equivalently marked.
7191 : */
7192 594 : seqinfo->dobj.dump = seqinfo->dobj.dump | owning_tab->dobj.dump;
7193 :
7194 594 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7195 560 : seqinfo->interesting = true;
7196 : }
7197 304 : }
7198 :
7199 : /*
7200 : * getInherits
7201 : * read all the inheritance information
7202 : * from the system catalogs return them in the InhInfo* structure
7203 : *
7204 : * numInherits is set to the number of pairs read in
7205 : */
7206 : InhInfo *
7207 304 : getInherits(Archive *fout, int *numInherits)
7208 : {
7209 : PGresult *res;
7210 : int ntups;
7211 : int i;
7212 304 : PQExpBuffer query = createPQExpBuffer();
7213 : InhInfo *inhinfo;
7214 :
7215 : int i_inhrelid;
7216 : int i_inhparent;
7217 :
7218 : /* find all the inheritance information */
7219 304 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7220 :
7221 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7222 :
7223 304 : ntups = PQntuples(res);
7224 :
7225 304 : *numInherits = ntups;
7226 :
7227 304 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7228 :
7229 304 : i_inhrelid = PQfnumber(res, "inhrelid");
7230 304 : i_inhparent = PQfnumber(res, "inhparent");
7231 :
7232 4914 : for (i = 0; i < ntups; i++)
7233 : {
7234 4610 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7235 4610 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7236 : }
7237 :
7238 304 : PQclear(res);
7239 :
7240 304 : destroyPQExpBuffer(query);
7241 :
7242 304 : return inhinfo;
7243 : }
7244 :
7245 : /*
7246 : * getPartitioningInfo
7247 : * get information about partitioning
7248 : *
7249 : * For the most part, we only collect partitioning info about tables we
7250 : * intend to dump. However, this function has to consider all partitioned
7251 : * tables in the database, because we need to know about parents of partitions
7252 : * we are going to dump even if the parents themselves won't be dumped.
7253 : *
7254 : * Specifically, what we need to know is whether each partitioned table
7255 : * has an "unsafe" partitioning scheme that requires us to force
7256 : * load-via-partition-root mode for its children. Currently the only case
7257 : * for which we force that is hash partitioning on enum columns, since the
7258 : * hash codes depend on enum value OIDs which won't be replicated across
7259 : * dump-and-reload. There are other cases in which load-via-partition-root
7260 : * might be necessary, but we expect users to cope with them.
7261 : */
7262 : void
7263 304 : getPartitioningInfo(Archive *fout)
7264 : {
7265 : PQExpBuffer query;
7266 : PGresult *res;
7267 : int ntups;
7268 :
7269 : /* hash partitioning didn't exist before v11 */
7270 304 : if (fout->remoteVersion < 110000)
7271 0 : return;
7272 : /* needn't bother if schema-only dump */
7273 304 : if (fout->dopt->schemaOnly)
7274 32 : return;
7275 :
7276 272 : query = createPQExpBuffer();
7277 :
7278 : /*
7279 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7280 : * appears among the partition opclasses. We needn't check partstrat.
7281 : *
7282 : * Note that this query may well retrieve info about tables we aren't
7283 : * going to dump and hence have no lock on. That's okay since we need not
7284 : * invoke any unsafe server-side functions.
7285 : */
7286 272 : appendPQExpBufferStr(query,
7287 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7288 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7289 : "ON c.opcmethod = a.oid\n"
7290 : "WHERE opcname = 'enum_ops' "
7291 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7292 : "AND amname = 'hash') = ANY(partclass)");
7293 :
7294 272 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7295 :
7296 272 : ntups = PQntuples(res);
7297 :
7298 276 : for (int i = 0; i < ntups; i++)
7299 : {
7300 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7301 : TableInfo *tbinfo;
7302 :
7303 4 : tbinfo = findTableByOid(tabrelid);
7304 4 : if (tbinfo == NULL)
7305 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7306 : tabrelid);
7307 4 : tbinfo->unsafe_partitions = true;
7308 : }
7309 :
7310 272 : PQclear(res);
7311 :
7312 272 : destroyPQExpBuffer(query);
7313 : }
7314 :
7315 : /*
7316 : * getIndexes
7317 : * get information about every index on a dumpable table
7318 : *
7319 : * Note: index data is not returned directly to the caller, but it
7320 : * does get entered into the DumpableObject tables.
7321 : */
7322 : void
7323 304 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7324 : {
7325 304 : PQExpBuffer query = createPQExpBuffer();
7326 304 : PQExpBuffer tbloids = createPQExpBuffer();
7327 : PGresult *res;
7328 : int ntups;
7329 : int curtblindx;
7330 : IndxInfo *indxinfo;
7331 : int i_tableoid,
7332 : i_oid,
7333 : i_indrelid,
7334 : i_indexname,
7335 : i_parentidx,
7336 : i_indexdef,
7337 : i_indnkeyatts,
7338 : i_indnatts,
7339 : i_indkey,
7340 : i_indisclustered,
7341 : i_indisreplident,
7342 : i_indnullsnotdistinct,
7343 : i_contype,
7344 : i_conname,
7345 : i_condeferrable,
7346 : i_condeferred,
7347 : i_conperiod,
7348 : i_contableoid,
7349 : i_conoid,
7350 : i_condef,
7351 : i_tablespace,
7352 : i_indreloptions,
7353 : i_indstatcols,
7354 : i_indstatvals;
7355 :
7356 : /*
7357 : * We want to perform just one query against pg_index. However, we
7358 : * mustn't try to select every row of the catalog and then sort it out on
7359 : * the client side, because some of the server-side functions we need
7360 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7361 : * build an array of the OIDs of tables we care about (and now have lock
7362 : * on!), and use a WHERE clause to constrain which rows are selected.
7363 : */
7364 304 : appendPQExpBufferChar(tbloids, '{');
7365 77210 : for (int i = 0; i < numTables; i++)
7366 : {
7367 76906 : TableInfo *tbinfo = &tblinfo[i];
7368 :
7369 76906 : if (!tbinfo->hasindex)
7370 54084 : continue;
7371 :
7372 : /*
7373 : * We can ignore indexes of uninteresting tables.
7374 : */
7375 22822 : if (!tbinfo->interesting)
7376 19500 : continue;
7377 :
7378 : /* OK, we need info for this table */
7379 3322 : if (tbloids->len > 1) /* do we have more than the '{'? */
7380 3174 : appendPQExpBufferChar(tbloids, ',');
7381 3322 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7382 : }
7383 304 : appendPQExpBufferChar(tbloids, '}');
7384 :
7385 304 : appendPQExpBufferStr(query,
7386 : "SELECT t.tableoid, t.oid, i.indrelid, "
7387 : "t.relname AS indexname, "
7388 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7389 : "i.indkey, i.indisclustered, "
7390 : "c.contype, c.conname, "
7391 : "c.condeferrable, c.condeferred, "
7392 : "c.tableoid AS contableoid, "
7393 : "c.oid AS conoid, "
7394 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7395 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7396 : "t.reloptions AS indreloptions, ");
7397 :
7398 :
7399 304 : if (fout->remoteVersion >= 90400)
7400 304 : appendPQExpBufferStr(query,
7401 : "i.indisreplident, ");
7402 : else
7403 0 : appendPQExpBufferStr(query,
7404 : "false AS indisreplident, ");
7405 :
7406 304 : if (fout->remoteVersion >= 110000)
7407 304 : appendPQExpBufferStr(query,
7408 : "inh.inhparent AS parentidx, "
7409 : "i.indnkeyatts AS indnkeyatts, "
7410 : "i.indnatts AS indnatts, "
7411 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7412 : " FROM pg_catalog.pg_attribute "
7413 : " WHERE attrelid = i.indexrelid AND "
7414 : " attstattarget >= 0) AS indstatcols, "
7415 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7416 : " FROM pg_catalog.pg_attribute "
7417 : " WHERE attrelid = i.indexrelid AND "
7418 : " attstattarget >= 0) AS indstatvals, ");
7419 : else
7420 0 : appendPQExpBufferStr(query,
7421 : "0 AS parentidx, "
7422 : "i.indnatts AS indnkeyatts, "
7423 : "i.indnatts AS indnatts, "
7424 : "'' AS indstatcols, "
7425 : "'' AS indstatvals, ");
7426 :
7427 304 : if (fout->remoteVersion >= 150000)
7428 304 : appendPQExpBufferStr(query,
7429 : "i.indnullsnotdistinct, ");
7430 : else
7431 0 : appendPQExpBufferStr(query,
7432 : "false AS indnullsnotdistinct, ");
7433 :
7434 304 : if (fout->remoteVersion >= 170000)
7435 304 : appendPQExpBufferStr(query,
7436 : "c.conperiod ");
7437 : else
7438 0 : appendPQExpBufferStr(query,
7439 : "NULL AS conperiod ");
7440 :
7441 : /*
7442 : * The point of the messy-looking outer join is to find a constraint that
7443 : * is related by an internal dependency link to the index. If we find one,
7444 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7445 : * index won't have more than one internal dependency.
7446 : *
7447 : * Note: the check on conrelid is redundant, but useful because that
7448 : * column is indexed while conindid is not.
7449 : */
7450 304 : if (fout->remoteVersion >= 110000)
7451 : {
7452 304 : appendPQExpBuffer(query,
7453 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7454 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7455 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7456 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7457 : "LEFT JOIN pg_catalog.pg_constraint c "
7458 : "ON (i.indrelid = c.conrelid AND "
7459 : "i.indexrelid = c.conindid AND "
7460 : "c.contype IN ('p','u','x')) "
7461 : "LEFT JOIN pg_catalog.pg_inherits inh "
7462 : "ON (inh.inhrelid = indexrelid) "
7463 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7464 : "AND i.indisready "
7465 : "ORDER BY i.indrelid, indexname",
7466 : tbloids->data);
7467 : }
7468 : else
7469 : {
7470 : /*
7471 : * the test on indisready is necessary in 9.2, and harmless in
7472 : * earlier/later versions
7473 : */
7474 0 : appendPQExpBuffer(query,
7475 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7476 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7477 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7478 : "LEFT JOIN pg_catalog.pg_constraint c "
7479 : "ON (i.indrelid = c.conrelid AND "
7480 : "i.indexrelid = c.conindid AND "
7481 : "c.contype IN ('p','u','x')) "
7482 : "WHERE i.indisvalid AND i.indisready "
7483 : "ORDER BY i.indrelid, indexname",
7484 : tbloids->data);
7485 : }
7486 :
7487 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7488 :
7489 304 : ntups = PQntuples(res);
7490 :
7491 304 : i_tableoid = PQfnumber(res, "tableoid");
7492 304 : i_oid = PQfnumber(res, "oid");
7493 304 : i_indrelid = PQfnumber(res, "indrelid");
7494 304 : i_indexname = PQfnumber(res, "indexname");
7495 304 : i_parentidx = PQfnumber(res, "parentidx");
7496 304 : i_indexdef = PQfnumber(res, "indexdef");
7497 304 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7498 304 : i_indnatts = PQfnumber(res, "indnatts");
7499 304 : i_indkey = PQfnumber(res, "indkey");
7500 304 : i_indisclustered = PQfnumber(res, "indisclustered");
7501 304 : i_indisreplident = PQfnumber(res, "indisreplident");
7502 304 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7503 304 : i_contype = PQfnumber(res, "contype");
7504 304 : i_conname = PQfnumber(res, "conname");
7505 304 : i_condeferrable = PQfnumber(res, "condeferrable");
7506 304 : i_condeferred = PQfnumber(res, "condeferred");
7507 304 : i_conperiod = PQfnumber(res, "conperiod");
7508 304 : i_contableoid = PQfnumber(res, "contableoid");
7509 304 : i_conoid = PQfnumber(res, "conoid");
7510 304 : i_condef = PQfnumber(res, "condef");
7511 304 : i_tablespace = PQfnumber(res, "tablespace");
7512 304 : i_indreloptions = PQfnumber(res, "indreloptions");
7513 304 : i_indstatcols = PQfnumber(res, "indstatcols");
7514 304 : i_indstatvals = PQfnumber(res, "indstatvals");
7515 :
7516 304 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7517 :
7518 : /*
7519 : * Outer loop iterates once per table, not once per row. Incrementing of
7520 : * j is handled by the inner loop.
7521 : */
7522 304 : curtblindx = -1;
7523 3618 : for (int j = 0; j < ntups;)
7524 : {
7525 3314 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7526 3314 : TableInfo *tbinfo = NULL;
7527 : int numinds;
7528 :
7529 : /* Count rows for this table */
7530 4256 : for (numinds = 1; numinds < ntups - j; numinds++)
7531 4108 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7532 3166 : break;
7533 :
7534 : /*
7535 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7536 : * order.
7537 : */
7538 42884 : while (++curtblindx < numTables)
7539 : {
7540 42884 : tbinfo = &tblinfo[curtblindx];
7541 42884 : if (tbinfo->dobj.catId.oid == indrelid)
7542 3314 : break;
7543 : }
7544 3314 : if (curtblindx >= numTables)
7545 0 : pg_fatal("unrecognized table OID %u", indrelid);
7546 : /* cross-check that we only got requested tables */
7547 3314 : if (!tbinfo->hasindex ||
7548 3314 : !tbinfo->interesting)
7549 0 : pg_fatal("unexpected index data for table \"%s\"",
7550 : tbinfo->dobj.name);
7551 :
7552 : /* Save data for this table */
7553 3314 : tbinfo->indexes = indxinfo + j;
7554 3314 : tbinfo->numIndexes = numinds;
7555 :
7556 7570 : for (int c = 0; c < numinds; c++, j++)
7557 : {
7558 : char contype;
7559 :
7560 4256 : indxinfo[j].dobj.objType = DO_INDEX;
7561 4256 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7562 4256 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7563 4256 : AssignDumpId(&indxinfo[j].dobj);
7564 4256 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7565 4256 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7566 4256 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7567 4256 : indxinfo[j].indextable = tbinfo;
7568 4256 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7569 4256 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7570 4256 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7571 4256 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7572 4256 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7573 4256 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7574 4256 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7575 4256 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7576 4256 : parseOidArray(PQgetvalue(res, j, i_indkey),
7577 4256 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7578 4256 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7579 4256 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7580 4256 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7581 4256 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7582 4256 : indxinfo[j].partattaches = (SimplePtrList)
7583 : {
7584 : NULL, NULL
7585 : };
7586 4256 : contype = *(PQgetvalue(res, j, i_contype));
7587 :
7588 4256 : if (contype == 'p' || contype == 'u' || contype == 'x')
7589 2320 : {
7590 : /*
7591 : * If we found a constraint matching the index, create an
7592 : * entry for it.
7593 : */
7594 : ConstraintInfo *constrinfo;
7595 :
7596 2320 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
7597 2320 : constrinfo->dobj.objType = DO_CONSTRAINT;
7598 2320 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7599 2320 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7600 2320 : AssignDumpId(&constrinfo->dobj);
7601 2320 : constrinfo->dobj.dump = tbinfo->dobj.dump;
7602 2320 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7603 2320 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
7604 2320 : constrinfo->contable = tbinfo;
7605 2320 : constrinfo->condomain = NULL;
7606 2320 : constrinfo->contype = contype;
7607 2320 : if (contype == 'x')
7608 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
7609 : else
7610 2300 : constrinfo->condef = NULL;
7611 2320 : constrinfo->confrelid = InvalidOid;
7612 2320 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
7613 2320 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
7614 2320 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
7615 2320 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
7616 2320 : constrinfo->conislocal = true;
7617 2320 : constrinfo->separate = true;
7618 :
7619 2320 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
7620 : }
7621 : else
7622 : {
7623 : /* Plain secondary index */
7624 1936 : indxinfo[j].indexconstraint = 0;
7625 : }
7626 : }
7627 : }
7628 :
7629 304 : PQclear(res);
7630 :
7631 304 : destroyPQExpBuffer(query);
7632 304 : destroyPQExpBuffer(tbloids);
7633 304 : }
7634 :
7635 : /*
7636 : * getExtendedStatistics
7637 : * get information about extended-statistics objects.
7638 : *
7639 : * Note: extended statistics data is not returned directly to the caller, but
7640 : * it does get entered into the DumpableObject tables.
7641 : */
7642 : void
7643 304 : getExtendedStatistics(Archive *fout)
7644 : {
7645 : PQExpBuffer query;
7646 : PGresult *res;
7647 : StatsExtInfo *statsextinfo;
7648 : int ntups;
7649 : int i_tableoid;
7650 : int i_oid;
7651 : int i_stxname;
7652 : int i_stxnamespace;
7653 : int i_stxowner;
7654 : int i_stxrelid;
7655 : int i_stattarget;
7656 : int i;
7657 :
7658 : /* Extended statistics were new in v10 */
7659 304 : if (fout->remoteVersion < 100000)
7660 0 : return;
7661 :
7662 304 : query = createPQExpBuffer();
7663 :
7664 304 : if (fout->remoteVersion < 130000)
7665 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7666 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
7667 : "FROM pg_catalog.pg_statistic_ext");
7668 : else
7669 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
7670 : "stxnamespace, stxowner, stxrelid, stxstattarget "
7671 : "FROM pg_catalog.pg_statistic_ext");
7672 :
7673 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7674 :
7675 304 : ntups = PQntuples(res);
7676 :
7677 304 : i_tableoid = PQfnumber(res, "tableoid");
7678 304 : i_oid = PQfnumber(res, "oid");
7679 304 : i_stxname = PQfnumber(res, "stxname");
7680 304 : i_stxnamespace = PQfnumber(res, "stxnamespace");
7681 304 : i_stxowner = PQfnumber(res, "stxowner");
7682 304 : i_stxrelid = PQfnumber(res, "stxrelid");
7683 304 : i_stattarget = PQfnumber(res, "stxstattarget");
7684 :
7685 304 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
7686 :
7687 602 : for (i = 0; i < ntups; i++)
7688 : {
7689 298 : statsextinfo[i].dobj.objType = DO_STATSEXT;
7690 298 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7691 298 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7692 298 : AssignDumpId(&statsextinfo[i].dobj);
7693 298 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
7694 596 : statsextinfo[i].dobj.namespace =
7695 298 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
7696 298 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
7697 596 : statsextinfo[i].stattable =
7698 298 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
7699 298 : if (PQgetisnull(res, i, i_stattarget))
7700 212 : statsextinfo[i].stattarget = -1;
7701 : else
7702 86 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
7703 :
7704 : /* Decide whether we want to dump it */
7705 298 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
7706 : }
7707 :
7708 304 : PQclear(res);
7709 304 : destroyPQExpBuffer(query);
7710 : }
7711 :
7712 : /*
7713 : * getConstraints
7714 : *
7715 : * Get info about constraints on dumpable tables.
7716 : *
7717 : * Currently handles foreign keys only.
7718 : * Unique and primary key constraints are handled with indexes,
7719 : * while check constraints are processed in getTableAttrs().
7720 : */
7721 : void
7722 304 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
7723 : {
7724 304 : PQExpBuffer query = createPQExpBuffer();
7725 304 : PQExpBuffer tbloids = createPQExpBuffer();
7726 : PGresult *res;
7727 : int ntups;
7728 : int curtblindx;
7729 304 : TableInfo *tbinfo = NULL;
7730 : ConstraintInfo *constrinfo;
7731 : int i_contableoid,
7732 : i_conoid,
7733 : i_conrelid,
7734 : i_conname,
7735 : i_confrelid,
7736 : i_conindid,
7737 : i_condef;
7738 :
7739 : /*
7740 : * We want to perform just one query against pg_constraint. However, we
7741 : * mustn't try to select every row of the catalog and then sort it out on
7742 : * the client side, because some of the server-side functions we need
7743 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7744 : * build an array of the OIDs of tables we care about (and now have lock
7745 : * on!), and use a WHERE clause to constrain which rows are selected.
7746 : */
7747 304 : appendPQExpBufferChar(tbloids, '{');
7748 77210 : for (int i = 0; i < numTables; i++)
7749 : {
7750 76906 : TableInfo *tinfo = &tblinfo[i];
7751 :
7752 : /*
7753 : * For partitioned tables, foreign keys have no triggers so they must
7754 : * be included anyway in case some foreign keys are defined.
7755 : */
7756 76906 : if ((!tinfo->hastriggers &&
7757 75128 : tinfo->relkind != RELKIND_PARTITIONED_TABLE) ||
7758 2456 : !(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
7759 74580 : continue;
7760 :
7761 : /* OK, we need info for this table */
7762 2326 : if (tbloids->len > 1) /* do we have more than the '{'? */
7763 2224 : appendPQExpBufferChar(tbloids, ',');
7764 2326 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
7765 : }
7766 304 : appendPQExpBufferChar(tbloids, '}');
7767 :
7768 304 : appendPQExpBufferStr(query,
7769 : "SELECT c.tableoid, c.oid, "
7770 : "conrelid, conname, confrelid, ");
7771 304 : if (fout->remoteVersion >= 110000)
7772 304 : appendPQExpBufferStr(query, "conindid, ");
7773 : else
7774 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
7775 304 : appendPQExpBuffer(query,
7776 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
7777 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7778 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
7779 : "WHERE contype = 'f' ",
7780 : tbloids->data);
7781 304 : if (fout->remoteVersion >= 110000)
7782 304 : appendPQExpBufferStr(query,
7783 : "AND conparentid = 0 ");
7784 304 : appendPQExpBufferStr(query,
7785 : "ORDER BY conrelid, conname");
7786 :
7787 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7788 :
7789 304 : ntups = PQntuples(res);
7790 :
7791 304 : i_contableoid = PQfnumber(res, "tableoid");
7792 304 : i_conoid = PQfnumber(res, "oid");
7793 304 : i_conrelid = PQfnumber(res, "conrelid");
7794 304 : i_conname = PQfnumber(res, "conname");
7795 304 : i_confrelid = PQfnumber(res, "confrelid");
7796 304 : i_conindid = PQfnumber(res, "conindid");
7797 304 : i_condef = PQfnumber(res, "condef");
7798 :
7799 304 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7800 :
7801 304 : curtblindx = -1;
7802 648 : for (int j = 0; j < ntups; j++)
7803 : {
7804 344 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
7805 : TableInfo *reftable;
7806 :
7807 : /*
7808 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7809 : * order.
7810 : */
7811 344 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
7812 : {
7813 25048 : while (++curtblindx < numTables)
7814 : {
7815 25048 : tbinfo = &tblinfo[curtblindx];
7816 25048 : if (tbinfo->dobj.catId.oid == conrelid)
7817 324 : break;
7818 : }
7819 324 : if (curtblindx >= numTables)
7820 0 : pg_fatal("unrecognized table OID %u", conrelid);
7821 : }
7822 :
7823 344 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
7824 344 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
7825 344 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
7826 344 : AssignDumpId(&constrinfo[j].dobj);
7827 344 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
7828 344 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7829 344 : constrinfo[j].contable = tbinfo;
7830 344 : constrinfo[j].condomain = NULL;
7831 344 : constrinfo[j].contype = 'f';
7832 344 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
7833 344 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
7834 344 : constrinfo[j].conindex = 0;
7835 344 : constrinfo[j].condeferrable = false;
7836 344 : constrinfo[j].condeferred = false;
7837 344 : constrinfo[j].conislocal = true;
7838 344 : constrinfo[j].separate = true;
7839 :
7840 : /*
7841 : * Restoring an FK that points to a partitioned table requires that
7842 : * all partition indexes have been attached beforehand. Ensure that
7843 : * happens by making the constraint depend on each index partition
7844 : * attach object.
7845 : */
7846 344 : reftable = findTableByOid(constrinfo[j].confrelid);
7847 344 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
7848 : {
7849 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
7850 :
7851 40 : if (indexOid != InvalidOid)
7852 : {
7853 40 : for (int k = 0; k < reftable->numIndexes; k++)
7854 : {
7855 : IndxInfo *refidx;
7856 :
7857 : /* not our index? */
7858 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
7859 0 : continue;
7860 :
7861 40 : refidx = &reftable->indexes[k];
7862 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
7863 40 : break;
7864 : }
7865 : }
7866 : }
7867 : }
7868 :
7869 304 : PQclear(res);
7870 :
7871 304 : destroyPQExpBuffer(query);
7872 304 : destroyPQExpBuffer(tbloids);
7873 304 : }
7874 :
7875 : /*
7876 : * addConstrChildIdxDeps
7877 : *
7878 : * Recursive subroutine for getConstraints
7879 : *
7880 : * Given an object representing a foreign key constraint and an index on the
7881 : * partitioned table it references, mark the constraint object as dependent
7882 : * on the DO_INDEX_ATTACH object of each index partition, recursively
7883 : * drilling down to their partitions if any. This ensures that the FK is not
7884 : * restored until the index is fully marked valid.
7885 : */
7886 : static void
7887 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
7888 : {
7889 : SimplePtrListCell *cell;
7890 :
7891 : Assert(dobj->objType == DO_FK_CONSTRAINT);
7892 :
7893 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
7894 : {
7895 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
7896 :
7897 220 : addObjectDependency(dobj, attach->dobj.dumpId);
7898 :
7899 220 : if (attach->partitionIdx->partattaches.head != NULL)
7900 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
7901 : }
7902 90 : }
7903 :
7904 : /*
7905 : * getDomainConstraints
7906 : *
7907 : * Get info about constraints on a domain.
7908 : */
7909 : static void
7910 262 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
7911 : {
7912 : int i;
7913 : ConstraintInfo *constrinfo;
7914 262 : PQExpBuffer query = createPQExpBuffer();
7915 : PGresult *res;
7916 : int i_tableoid,
7917 : i_oid,
7918 : i_conname,
7919 : i_consrc;
7920 : int ntups;
7921 :
7922 262 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
7923 : {
7924 : /* Set up query for constraint-specific details */
7925 82 : appendPQExpBufferStr(query,
7926 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
7927 : "SELECT tableoid, oid, conname, "
7928 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
7929 : "convalidated "
7930 : "FROM pg_catalog.pg_constraint "
7931 : "WHERE contypid = $1 AND contype = 'c' "
7932 : "ORDER BY conname");
7933 :
7934 82 : ExecuteSqlStatement(fout, query->data);
7935 :
7936 82 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
7937 : }
7938 :
7939 262 : printfPQExpBuffer(query,
7940 : "EXECUTE getDomainConstraints('%u')",
7941 : tyinfo->dobj.catId.oid);
7942 :
7943 262 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7944 :
7945 262 : ntups = PQntuples(res);
7946 :
7947 262 : i_tableoid = PQfnumber(res, "tableoid");
7948 262 : i_oid = PQfnumber(res, "oid");
7949 262 : i_conname = PQfnumber(res, "conname");
7950 262 : i_consrc = PQfnumber(res, "consrc");
7951 :
7952 262 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
7953 :
7954 262 : tyinfo->nDomChecks = ntups;
7955 262 : tyinfo->domChecks = constrinfo;
7956 :
7957 434 : for (i = 0; i < ntups; i++)
7958 : {
7959 172 : bool validated = PQgetvalue(res, i, 4)[0] == 't';
7960 :
7961 172 : constrinfo[i].dobj.objType = DO_CONSTRAINT;
7962 172 : constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7963 172 : constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7964 172 : AssignDumpId(&constrinfo[i].dobj);
7965 172 : constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
7966 172 : constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
7967 172 : constrinfo[i].contable = NULL;
7968 172 : constrinfo[i].condomain = tyinfo;
7969 172 : constrinfo[i].contype = 'c';
7970 172 : constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
7971 172 : constrinfo[i].confrelid = InvalidOid;
7972 172 : constrinfo[i].conindex = 0;
7973 172 : constrinfo[i].condeferrable = false;
7974 172 : constrinfo[i].condeferred = false;
7975 172 : constrinfo[i].conislocal = true;
7976 :
7977 172 : constrinfo[i].separate = !validated;
7978 :
7979 : /*
7980 : * Make the domain depend on the constraint, ensuring it won't be
7981 : * output till any constraint dependencies are OK. If the constraint
7982 : * has not been validated, it's going to be dumped after the domain
7983 : * anyway, so this doesn't matter.
7984 : */
7985 172 : if (validated)
7986 172 : addObjectDependency(&tyinfo->dobj,
7987 172 : constrinfo[i].dobj.dumpId);
7988 : }
7989 :
7990 262 : PQclear(res);
7991 :
7992 262 : destroyPQExpBuffer(query);
7993 262 : }
7994 :
7995 : /*
7996 : * getRules
7997 : * get basic information about every rule in the system
7998 : *
7999 : * numRules is set to the number of rules read in
8000 : */
8001 : RuleInfo *
8002 304 : getRules(Archive *fout, int *numRules)
8003 : {
8004 : PGresult *res;
8005 : int ntups;
8006 : int i;
8007 304 : PQExpBuffer query = createPQExpBuffer();
8008 : RuleInfo *ruleinfo;
8009 : int i_tableoid;
8010 : int i_oid;
8011 : int i_rulename;
8012 : int i_ruletable;
8013 : int i_ev_type;
8014 : int i_is_instead;
8015 : int i_ev_enabled;
8016 :
8017 304 : appendPQExpBufferStr(query, "SELECT "
8018 : "tableoid, oid, rulename, "
8019 : "ev_class AS ruletable, ev_type, is_instead, "
8020 : "ev_enabled "
8021 : "FROM pg_rewrite "
8022 : "ORDER BY oid");
8023 :
8024 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8025 :
8026 304 : ntups = PQntuples(res);
8027 :
8028 304 : *numRules = ntups;
8029 :
8030 304 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8031 :
8032 304 : i_tableoid = PQfnumber(res, "tableoid");
8033 304 : i_oid = PQfnumber(res, "oid");
8034 304 : i_rulename = PQfnumber(res, "rulename");
8035 304 : i_ruletable = PQfnumber(res, "ruletable");
8036 304 : i_ev_type = PQfnumber(res, "ev_type");
8037 304 : i_is_instead = PQfnumber(res, "is_instead");
8038 304 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8039 :
8040 46184 : for (i = 0; i < ntups; i++)
8041 : {
8042 : Oid ruletableoid;
8043 :
8044 45880 : ruleinfo[i].dobj.objType = DO_RULE;
8045 45880 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8046 45880 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8047 45880 : AssignDumpId(&ruleinfo[i].dobj);
8048 45880 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8049 45880 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8050 45880 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8051 45880 : if (ruleinfo[i].ruletable == NULL)
8052 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8053 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8054 45880 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8055 45880 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8056 45880 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8057 45880 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8058 45880 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8059 45880 : if (ruleinfo[i].ruletable)
8060 : {
8061 : /*
8062 : * If the table is a view or materialized view, force its ON
8063 : * SELECT rule to be sorted before the view itself --- this
8064 : * ensures that any dependencies for the rule affect the table's
8065 : * positioning. Other rules are forced to appear after their
8066 : * table.
8067 : */
8068 45880 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8069 1184 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8070 45550 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8071 : {
8072 44882 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8073 44882 : ruleinfo[i].dobj.dumpId);
8074 : /* We'll merge the rule into CREATE VIEW, if possible */
8075 44882 : ruleinfo[i].separate = false;
8076 : }
8077 : else
8078 : {
8079 998 : addObjectDependency(&ruleinfo[i].dobj,
8080 998 : ruleinfo[i].ruletable->dobj.dumpId);
8081 998 : ruleinfo[i].separate = true;
8082 : }
8083 : }
8084 : else
8085 0 : ruleinfo[i].separate = true;
8086 : }
8087 :
8088 304 : PQclear(res);
8089 :
8090 304 : destroyPQExpBuffer(query);
8091 :
8092 304 : return ruleinfo;
8093 : }
8094 :
8095 : /*
8096 : * getTriggers
8097 : * get information about every trigger on a dumpable table
8098 : *
8099 : * Note: trigger data is not returned directly to the caller, but it
8100 : * does get entered into the DumpableObject tables.
8101 : */
8102 : void
8103 304 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8104 : {
8105 304 : PQExpBuffer query = createPQExpBuffer();
8106 304 : PQExpBuffer tbloids = createPQExpBuffer();
8107 : PGresult *res;
8108 : int ntups;
8109 : int curtblindx;
8110 : TriggerInfo *tginfo;
8111 : int i_tableoid,
8112 : i_oid,
8113 : i_tgrelid,
8114 : i_tgname,
8115 : i_tgenabled,
8116 : i_tgispartition,
8117 : i_tgdef;
8118 :
8119 : /*
8120 : * We want to perform just one query against pg_trigger. However, we
8121 : * mustn't try to select every row of the catalog and then sort it out on
8122 : * the client side, because some of the server-side functions we need
8123 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8124 : * build an array of the OIDs of tables we care about (and now have lock
8125 : * on!), and use a WHERE clause to constrain which rows are selected.
8126 : */
8127 304 : appendPQExpBufferChar(tbloids, '{');
8128 77210 : for (int i = 0; i < numTables; i++)
8129 : {
8130 76906 : TableInfo *tbinfo = &tblinfo[i];
8131 :
8132 76906 : if (!tbinfo->hastriggers ||
8133 1778 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8134 75244 : continue;
8135 :
8136 : /* OK, we need info for this table */
8137 1662 : if (tbloids->len > 1) /* do we have more than the '{'? */
8138 1564 : appendPQExpBufferChar(tbloids, ',');
8139 1662 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8140 : }
8141 304 : appendPQExpBufferChar(tbloids, '}');
8142 :
8143 304 : if (fout->remoteVersion >= 150000)
8144 : {
8145 : /*
8146 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8147 : * result in non-forward-compatible dumps of WHEN clauses due to
8148 : * under-parenthesization.
8149 : *
8150 : * NB: We need to see partition triggers in case the tgenabled flag
8151 : * has been changed from the parent.
8152 : */
8153 304 : appendPQExpBuffer(query,
8154 : "SELECT t.tgrelid, t.tgname, "
8155 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8156 : "t.tgenabled, t.tableoid, t.oid, "
8157 : "t.tgparentid <> 0 AS tgispartition\n"
8158 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8159 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8160 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8161 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8162 : "OR t.tgenabled != u.tgenabled) "
8163 : "ORDER BY t.tgrelid, t.tgname",
8164 : tbloids->data);
8165 : }
8166 0 : else if (fout->remoteVersion >= 130000)
8167 : {
8168 : /*
8169 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8170 : * result in non-forward-compatible dumps of WHEN clauses due to
8171 : * under-parenthesization.
8172 : *
8173 : * NB: We need to see tgisinternal triggers in partitions, in case the
8174 : * tgenabled flag has been changed from the parent.
8175 : */
8176 0 : appendPQExpBuffer(query,
8177 : "SELECT t.tgrelid, t.tgname, "
8178 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8179 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8180 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8181 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8182 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8183 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8184 : "ORDER BY t.tgrelid, t.tgname",
8185 : tbloids->data);
8186 : }
8187 0 : else if (fout->remoteVersion >= 110000)
8188 : {
8189 : /*
8190 : * NB: We need to see tgisinternal triggers in partitions, in case the
8191 : * tgenabled flag has been changed from the parent. No tgparentid in
8192 : * version 11-12, so we have to match them via pg_depend.
8193 : *
8194 : * See above about pretty=true in pg_get_triggerdef.
8195 : */
8196 0 : appendPQExpBuffer(query,
8197 : "SELECT t.tgrelid, t.tgname, "
8198 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8199 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8200 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8201 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8202 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8203 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8204 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8205 : " d.objid = t.oid "
8206 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8207 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8208 : "ORDER BY t.tgrelid, t.tgname",
8209 : tbloids->data);
8210 : }
8211 : else
8212 : {
8213 : /* See above about pretty=true in pg_get_triggerdef */
8214 0 : appendPQExpBuffer(query,
8215 : "SELECT t.tgrelid, t.tgname, "
8216 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8217 : "t.tgenabled, false as tgispartition, "
8218 : "t.tableoid, t.oid "
8219 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8220 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8221 : "WHERE NOT tgisinternal "
8222 : "ORDER BY t.tgrelid, t.tgname",
8223 : tbloids->data);
8224 : }
8225 :
8226 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8227 :
8228 304 : ntups = PQntuples(res);
8229 :
8230 304 : i_tableoid = PQfnumber(res, "tableoid");
8231 304 : i_oid = PQfnumber(res, "oid");
8232 304 : i_tgrelid = PQfnumber(res, "tgrelid");
8233 304 : i_tgname = PQfnumber(res, "tgname");
8234 304 : i_tgenabled = PQfnumber(res, "tgenabled");
8235 304 : i_tgispartition = PQfnumber(res, "tgispartition");
8236 304 : i_tgdef = PQfnumber(res, "tgdef");
8237 :
8238 304 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8239 :
8240 : /*
8241 : * Outer loop iterates once per table, not once per row. Incrementing of
8242 : * j is handled by the inner loop.
8243 : */
8244 304 : curtblindx = -1;
8245 886 : for (int j = 0; j < ntups;)
8246 : {
8247 582 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8248 582 : TableInfo *tbinfo = NULL;
8249 : int numtrigs;
8250 :
8251 : /* Count rows for this table */
8252 986 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8253 888 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8254 484 : break;
8255 :
8256 : /*
8257 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8258 : * order.
8259 : */
8260 29950 : while (++curtblindx < numTables)
8261 : {
8262 29950 : tbinfo = &tblinfo[curtblindx];
8263 29950 : if (tbinfo->dobj.catId.oid == tgrelid)
8264 582 : break;
8265 : }
8266 582 : if (curtblindx >= numTables)
8267 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8268 :
8269 : /* Save data for this table */
8270 582 : tbinfo->triggers = tginfo + j;
8271 582 : tbinfo->numTriggers = numtrigs;
8272 :
8273 1568 : for (int c = 0; c < numtrigs; c++, j++)
8274 : {
8275 986 : tginfo[j].dobj.objType = DO_TRIGGER;
8276 986 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8277 986 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8278 986 : AssignDumpId(&tginfo[j].dobj);
8279 986 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8280 986 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8281 986 : tginfo[j].tgtable = tbinfo;
8282 986 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8283 986 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8284 986 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8285 : }
8286 : }
8287 :
8288 304 : PQclear(res);
8289 :
8290 304 : destroyPQExpBuffer(query);
8291 304 : destroyPQExpBuffer(tbloids);
8292 304 : }
8293 :
8294 : /*
8295 : * getEventTriggers
8296 : * get information about event triggers
8297 : */
8298 : EventTriggerInfo *
8299 304 : getEventTriggers(Archive *fout, int *numEventTriggers)
8300 : {
8301 : int i;
8302 : PQExpBuffer query;
8303 : PGresult *res;
8304 : EventTriggerInfo *evtinfo;
8305 : int i_tableoid,
8306 : i_oid,
8307 : i_evtname,
8308 : i_evtevent,
8309 : i_evtowner,
8310 : i_evttags,
8311 : i_evtfname,
8312 : i_evtenabled;
8313 : int ntups;
8314 :
8315 : /* Before 9.3, there are no event triggers */
8316 304 : if (fout->remoteVersion < 90300)
8317 : {
8318 0 : *numEventTriggers = 0;
8319 0 : return NULL;
8320 : }
8321 :
8322 304 : query = createPQExpBuffer();
8323 :
8324 304 : appendPQExpBufferStr(query,
8325 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8326 : "evtevent, evtowner, "
8327 : "array_to_string(array("
8328 : "select quote_literal(x) "
8329 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8330 : "e.evtfoid::regproc as evtfname "
8331 : "FROM pg_event_trigger e "
8332 : "ORDER BY e.oid");
8333 :
8334 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8335 :
8336 304 : ntups = PQntuples(res);
8337 :
8338 304 : *numEventTriggers = ntups;
8339 :
8340 304 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8341 :
8342 304 : i_tableoid = PQfnumber(res, "tableoid");
8343 304 : i_oid = PQfnumber(res, "oid");
8344 304 : i_evtname = PQfnumber(res, "evtname");
8345 304 : i_evtevent = PQfnumber(res, "evtevent");
8346 304 : i_evtowner = PQfnumber(res, "evtowner");
8347 304 : i_evttags = PQfnumber(res, "evttags");
8348 304 : i_evtfname = PQfnumber(res, "evtfname");
8349 304 : i_evtenabled = PQfnumber(res, "evtenabled");
8350 :
8351 400 : for (i = 0; i < ntups; i++)
8352 : {
8353 96 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8354 96 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8355 96 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8356 96 : AssignDumpId(&evtinfo[i].dobj);
8357 96 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8358 96 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8359 96 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8360 96 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8361 96 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8362 96 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8363 96 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8364 :
8365 : /* Decide whether we want to dump it */
8366 96 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8367 : }
8368 :
8369 304 : PQclear(res);
8370 :
8371 304 : destroyPQExpBuffer(query);
8372 :
8373 304 : return evtinfo;
8374 : }
8375 :
8376 : /*
8377 : * getProcLangs
8378 : * get basic information about every procedural language in the system
8379 : *
8380 : * numProcLangs is set to the number of langs read in
8381 : *
8382 : * NB: this must run after getFuncs() because we assume we can do
8383 : * findFuncByOid().
8384 : */
8385 : ProcLangInfo *
8386 304 : getProcLangs(Archive *fout, int *numProcLangs)
8387 : {
8388 : PGresult *res;
8389 : int ntups;
8390 : int i;
8391 304 : PQExpBuffer query = createPQExpBuffer();
8392 : ProcLangInfo *planginfo;
8393 : int i_tableoid;
8394 : int i_oid;
8395 : int i_lanname;
8396 : int i_lanpltrusted;
8397 : int i_lanplcallfoid;
8398 : int i_laninline;
8399 : int i_lanvalidator;
8400 : int i_lanacl;
8401 : int i_acldefault;
8402 : int i_lanowner;
8403 :
8404 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8405 : "lanname, lanpltrusted, lanplcallfoid, "
8406 : "laninline, lanvalidator, "
8407 : "lanacl, "
8408 : "acldefault('l', lanowner) AS acldefault, "
8409 : "lanowner "
8410 : "FROM pg_language "
8411 : "WHERE lanispl "
8412 : "ORDER BY oid");
8413 :
8414 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8415 :
8416 304 : ntups = PQntuples(res);
8417 :
8418 304 : *numProcLangs = ntups;
8419 :
8420 304 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8421 :
8422 304 : i_tableoid = PQfnumber(res, "tableoid");
8423 304 : i_oid = PQfnumber(res, "oid");
8424 304 : i_lanname = PQfnumber(res, "lanname");
8425 304 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8426 304 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8427 304 : i_laninline = PQfnumber(res, "laninline");
8428 304 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8429 304 : i_lanacl = PQfnumber(res, "lanacl");
8430 304 : i_acldefault = PQfnumber(res, "acldefault");
8431 304 : i_lanowner = PQfnumber(res, "lanowner");
8432 :
8433 694 : for (i = 0; i < ntups; i++)
8434 : {
8435 390 : planginfo[i].dobj.objType = DO_PROCLANG;
8436 390 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8437 390 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8438 390 : AssignDumpId(&planginfo[i].dobj);
8439 :
8440 390 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8441 390 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8442 390 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8443 390 : planginfo[i].dacl.privtype = 0;
8444 390 : planginfo[i].dacl.initprivs = NULL;
8445 390 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8446 390 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8447 390 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8448 390 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8449 390 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8450 :
8451 : /* Decide whether we want to dump it */
8452 390 : selectDumpableProcLang(&(planginfo[i]), fout);
8453 :
8454 : /* Mark whether language has an ACL */
8455 390 : if (!PQgetisnull(res, i, i_lanacl))
8456 86 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8457 : }
8458 :
8459 304 : PQclear(res);
8460 :
8461 304 : destroyPQExpBuffer(query);
8462 :
8463 304 : return planginfo;
8464 : }
8465 :
8466 : /*
8467 : * getCasts
8468 : * get basic information about most casts in the system
8469 : *
8470 : * numCasts is set to the number of casts read in
8471 : *
8472 : * Skip casts from a range to its multirange, since we'll create those
8473 : * automatically.
8474 : */
8475 : CastInfo *
8476 304 : getCasts(Archive *fout, int *numCasts)
8477 : {
8478 : PGresult *res;
8479 : int ntups;
8480 : int i;
8481 304 : PQExpBuffer query = createPQExpBuffer();
8482 : CastInfo *castinfo;
8483 : int i_tableoid;
8484 : int i_oid;
8485 : int i_castsource;
8486 : int i_casttarget;
8487 : int i_castfunc;
8488 : int i_castcontext;
8489 : int i_castmethod;
8490 :
8491 304 : if (fout->remoteVersion >= 140000)
8492 : {
8493 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8494 : "castsource, casttarget, castfunc, castcontext, "
8495 : "castmethod "
8496 : "FROM pg_cast c "
8497 : "WHERE NOT EXISTS ( "
8498 : "SELECT 1 FROM pg_range r "
8499 : "WHERE c.castsource = r.rngtypid "
8500 : "AND c.casttarget = r.rngmultitypid "
8501 : ") "
8502 : "ORDER BY 3,4");
8503 : }
8504 : else
8505 : {
8506 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8507 : "castsource, casttarget, castfunc, castcontext, "
8508 : "castmethod "
8509 : "FROM pg_cast ORDER BY 3,4");
8510 : }
8511 :
8512 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8513 :
8514 304 : ntups = PQntuples(res);
8515 :
8516 304 : *numCasts = ntups;
8517 :
8518 304 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8519 :
8520 304 : i_tableoid = PQfnumber(res, "tableoid");
8521 304 : i_oid = PQfnumber(res, "oid");
8522 304 : i_castsource = PQfnumber(res, "castsource");
8523 304 : i_casttarget = PQfnumber(res, "casttarget");
8524 304 : i_castfunc = PQfnumber(res, "castfunc");
8525 304 : i_castcontext = PQfnumber(res, "castcontext");
8526 304 : i_castmethod = PQfnumber(res, "castmethod");
8527 :
8528 68242 : for (i = 0; i < ntups; i++)
8529 : {
8530 : PQExpBufferData namebuf;
8531 : TypeInfo *sTypeInfo;
8532 : TypeInfo *tTypeInfo;
8533 :
8534 67938 : castinfo[i].dobj.objType = DO_CAST;
8535 67938 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8536 67938 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8537 67938 : AssignDumpId(&castinfo[i].dobj);
8538 67938 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8539 67938 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8540 67938 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8541 67938 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8542 67938 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8543 :
8544 : /*
8545 : * Try to name cast as concatenation of typnames. This is only used
8546 : * for purposes of sorting. If we fail to find either type, the name
8547 : * will be an empty string.
8548 : */
8549 67938 : initPQExpBuffer(&namebuf);
8550 67938 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8551 67938 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8552 67938 : if (sTypeInfo && tTypeInfo)
8553 67938 : appendPQExpBuffer(&namebuf, "%s %s",
8554 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8555 67938 : castinfo[i].dobj.name = namebuf.data;
8556 :
8557 : /* Decide whether we want to dump it */
8558 67938 : selectDumpableCast(&(castinfo[i]), fout);
8559 : }
8560 :
8561 304 : PQclear(res);
8562 :
8563 304 : destroyPQExpBuffer(query);
8564 :
8565 304 : return castinfo;
8566 : }
8567 :
8568 : static char *
8569 170 : get_language_name(Archive *fout, Oid langid)
8570 : {
8571 : PQExpBuffer query;
8572 : PGresult *res;
8573 : char *lanname;
8574 :
8575 170 : query = createPQExpBuffer();
8576 170 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8577 170 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8578 170 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8579 170 : destroyPQExpBuffer(query);
8580 170 : PQclear(res);
8581 :
8582 170 : return lanname;
8583 : }
8584 :
8585 : /*
8586 : * getTransforms
8587 : * get basic information about every transform in the system
8588 : *
8589 : * numTransforms is set to the number of transforms read in
8590 : */
8591 : TransformInfo *
8592 304 : getTransforms(Archive *fout, int *numTransforms)
8593 : {
8594 : PGresult *res;
8595 : int ntups;
8596 : int i;
8597 : PQExpBuffer query;
8598 : TransformInfo *transforminfo;
8599 : int i_tableoid;
8600 : int i_oid;
8601 : int i_trftype;
8602 : int i_trflang;
8603 : int i_trffromsql;
8604 : int i_trftosql;
8605 :
8606 : /* Transforms didn't exist pre-9.5 */
8607 304 : if (fout->remoteVersion < 90500)
8608 : {
8609 0 : *numTransforms = 0;
8610 0 : return NULL;
8611 : }
8612 :
8613 304 : query = createPQExpBuffer();
8614 :
8615 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8616 : "trftype, trflang, trffromsql::oid, trftosql::oid "
8617 : "FROM pg_transform "
8618 : "ORDER BY 3,4");
8619 :
8620 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8621 :
8622 304 : ntups = PQntuples(res);
8623 :
8624 304 : *numTransforms = ntups;
8625 :
8626 304 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
8627 :
8628 304 : i_tableoid = PQfnumber(res, "tableoid");
8629 304 : i_oid = PQfnumber(res, "oid");
8630 304 : i_trftype = PQfnumber(res, "trftype");
8631 304 : i_trflang = PQfnumber(res, "trflang");
8632 304 : i_trffromsql = PQfnumber(res, "trffromsql");
8633 304 : i_trftosql = PQfnumber(res, "trftosql");
8634 :
8635 400 : for (i = 0; i < ntups; i++)
8636 : {
8637 : PQExpBufferData namebuf;
8638 : TypeInfo *typeInfo;
8639 : char *lanname;
8640 :
8641 96 : transforminfo[i].dobj.objType = DO_TRANSFORM;
8642 96 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8643 96 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8644 96 : AssignDumpId(&transforminfo[i].dobj);
8645 96 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
8646 96 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
8647 96 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
8648 96 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
8649 :
8650 : /*
8651 : * Try to name transform as concatenation of type and language name.
8652 : * This is only used for purposes of sorting. If we fail to find
8653 : * either, the name will be an empty string.
8654 : */
8655 96 : initPQExpBuffer(&namebuf);
8656 96 : typeInfo = findTypeByOid(transforminfo[i].trftype);
8657 96 : lanname = get_language_name(fout, transforminfo[i].trflang);
8658 96 : if (typeInfo && lanname)
8659 96 : appendPQExpBuffer(&namebuf, "%s %s",
8660 : typeInfo->dobj.name, lanname);
8661 96 : transforminfo[i].dobj.name = namebuf.data;
8662 96 : free(lanname);
8663 :
8664 : /* Decide whether we want to dump it */
8665 96 : selectDumpableObject(&(transforminfo[i].dobj), fout);
8666 : }
8667 :
8668 304 : PQclear(res);
8669 :
8670 304 : destroyPQExpBuffer(query);
8671 :
8672 304 : return transforminfo;
8673 : }
8674 :
8675 : /*
8676 : * getTableAttrs -
8677 : * for each interesting table, read info about its attributes
8678 : * (names, types, default values, CHECK constraints, etc)
8679 : *
8680 : * modifies tblinfo
8681 : */
8682 : void
8683 304 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
8684 : {
8685 304 : DumpOptions *dopt = fout->dopt;
8686 304 : PQExpBuffer q = createPQExpBuffer();
8687 304 : PQExpBuffer tbloids = createPQExpBuffer();
8688 304 : PQExpBuffer checkoids = createPQExpBuffer();
8689 : PGresult *res;
8690 : int ntups;
8691 : int curtblindx;
8692 : int i_attrelid;
8693 : int i_attnum;
8694 : int i_attname;
8695 : int i_atttypname;
8696 : int i_attstattarget;
8697 : int i_attstorage;
8698 : int i_typstorage;
8699 : int i_attidentity;
8700 : int i_attgenerated;
8701 : int i_attisdropped;
8702 : int i_attlen;
8703 : int i_attalign;
8704 : int i_attislocal;
8705 : int i_notnull_name;
8706 : int i_notnull_noinherit;
8707 : int i_notnull_is_pk;
8708 : int i_notnull_inh;
8709 : int i_attoptions;
8710 : int i_attcollation;
8711 : int i_attcompression;
8712 : int i_attfdwoptions;
8713 : int i_attmissingval;
8714 : int i_atthasdef;
8715 :
8716 : /*
8717 : * We want to perform just one query against pg_attribute, and then just
8718 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
8719 : * (for CHECK constraints and for NOT NULL constraints). However, we
8720 : * mustn't try to select every row of those catalogs and then sort it out
8721 : * on the client side, because some of the server-side functions we need
8722 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8723 : * build an array of the OIDs of tables we care about (and now have lock
8724 : * on!), and use a WHERE clause to constrain which rows are selected.
8725 : */
8726 304 : appendPQExpBufferChar(tbloids, '{');
8727 304 : appendPQExpBufferChar(checkoids, '{');
8728 77210 : for (int i = 0; i < numTables; i++)
8729 : {
8730 76906 : TableInfo *tbinfo = &tblinfo[i];
8731 :
8732 : /* Don't bother to collect info for sequences */
8733 76906 : if (tbinfo->relkind == RELKIND_SEQUENCE)
8734 982 : continue;
8735 :
8736 : /* Don't bother with uninteresting tables, either */
8737 75924 : if (!tbinfo->interesting)
8738 65064 : continue;
8739 :
8740 : /* OK, we need info for this table */
8741 10860 : if (tbloids->len > 1) /* do we have more than the '{'? */
8742 10660 : appendPQExpBufferChar(tbloids, ',');
8743 10860 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8744 :
8745 10860 : if (tbinfo->ncheck > 0)
8746 : {
8747 : /* Also make a list of the ones with check constraints */
8748 884 : if (checkoids->len > 1) /* do we have more than the '{'? */
8749 752 : appendPQExpBufferChar(checkoids, ',');
8750 884 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
8751 : }
8752 : }
8753 304 : appendPQExpBufferChar(tbloids, '}');
8754 304 : appendPQExpBufferChar(checkoids, '}');
8755 :
8756 : /*
8757 : * Find all the user attributes and their types.
8758 : *
8759 : * Since we only want to dump COLLATE clauses for attributes whose
8760 : * collation is different from their type's default, we use a CASE here to
8761 : * suppress uninteresting attcollations cheaply.
8762 : */
8763 304 : appendPQExpBufferStr(q,
8764 : "SELECT\n"
8765 : "a.attrelid,\n"
8766 : "a.attnum,\n"
8767 : "a.attname,\n"
8768 : "a.attstattarget,\n"
8769 : "a.attstorage,\n"
8770 : "t.typstorage,\n"
8771 : "a.atthasdef,\n"
8772 : "a.attisdropped,\n"
8773 : "a.attlen,\n"
8774 : "a.attalign,\n"
8775 : "a.attislocal,\n"
8776 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
8777 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
8778 : "CASE WHEN a.attcollation <> t.typcollation "
8779 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
8780 : "pg_catalog.array_to_string(ARRAY("
8781 : "SELECT pg_catalog.quote_ident(option_name) || "
8782 : "' ' || pg_catalog.quote_literal(option_value) "
8783 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
8784 : "ORDER BY option_name"
8785 : "), E',\n ') AS attfdwoptions,\n");
8786 :
8787 : /*
8788 : * Find out any NOT NULL markings for each column. In 17 and up we read
8789 : * pg_constraint to obtain the constraint name. notnull_noinherit is set
8790 : * according to the NO INHERIT property. For versions prior to 17, we
8791 : * store an empty string as the name when a constraint is marked as
8792 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
8793 : * without a name); also, such cases are never NO INHERIT.
8794 : *
8795 : * We track in notnull_inh whether the constraint was defined directly in
8796 : * this table or via an ancestor, for binary upgrade.
8797 : *
8798 : * Lastly, we need to know if the PK for the table involves each column;
8799 : * for columns that are there we need a NOT NULL marking even if there's
8800 : * no explicit constraint, to avoid the table having to be scanned for
8801 : * NULLs after the data is loaded when the PK is created, later in the
8802 : * dump; for this case we add throwaway constraints that are dropped once
8803 : * the PK is created.
8804 : *
8805 : * Another complication arises from columns that have attnotnull set, but
8806 : * for which no corresponding not-null nor PK constraint exists. This can
8807 : * happen if, for example, a primary key is dropped indirectly -- say,
8808 : * because one of its columns is dropped. This is an irregular condition,
8809 : * so we don't work hard to preserve it, and instead act as though an
8810 : * unnamed not-null constraint exists.
8811 : */
8812 304 : if (fout->remoteVersion >= 170000)
8813 304 : appendPQExpBufferStr(q,
8814 : "CASE WHEN co.conname IS NOT NULL THEN co.conname "
8815 : " WHEN a.attnotnull AND copk.conname IS NULL THEN '' ELSE NULL END AS notnull_name,\n"
8816 : "CASE WHEN co.conname IS NOT NULL THEN co.connoinherit "
8817 : " WHEN a.attnotnull THEN false ELSE NULL END AS notnull_noinherit,\n"
8818 : "copk.conname IS NOT NULL as notnull_is_pk,\n"
8819 : "CASE WHEN co.conname IS NOT NULL THEN "
8820 : " coalesce(NOT co.conislocal, true) "
8821 : "ELSE false END as notnull_inh,\n");
8822 : else
8823 0 : appendPQExpBufferStr(q,
8824 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
8825 : "false AS notnull_noinherit,\n"
8826 : "copk.conname IS NOT NULL AS notnull_is_pk,\n"
8827 : "NOT a.attislocal AS notnull_inh,\n");
8828 :
8829 304 : if (fout->remoteVersion >= 140000)
8830 304 : appendPQExpBufferStr(q,
8831 : "a.attcompression AS attcompression,\n");
8832 : else
8833 0 : appendPQExpBufferStr(q,
8834 : "'' AS attcompression,\n");
8835 :
8836 304 : if (fout->remoteVersion >= 100000)
8837 304 : appendPQExpBufferStr(q,
8838 : "a.attidentity,\n");
8839 : else
8840 0 : appendPQExpBufferStr(q,
8841 : "'' AS attidentity,\n");
8842 :
8843 304 : if (fout->remoteVersion >= 110000)
8844 304 : appendPQExpBufferStr(q,
8845 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
8846 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
8847 : else
8848 0 : appendPQExpBufferStr(q,
8849 : "NULL AS attmissingval,\n");
8850 :
8851 304 : if (fout->remoteVersion >= 120000)
8852 304 : appendPQExpBufferStr(q,
8853 : "a.attgenerated\n");
8854 : else
8855 0 : appendPQExpBufferStr(q,
8856 : "'' AS attgenerated\n");
8857 :
8858 : /* need left join to pg_type to not fail on dropped columns ... */
8859 304 : appendPQExpBuffer(q,
8860 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8861 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
8862 : "LEFT JOIN pg_catalog.pg_type t "
8863 : "ON (a.atttypid = t.oid)\n",
8864 : tbloids->data);
8865 :
8866 : /*
8867 : * In versions 17 and up, we need pg_constraint for explicit NOT NULL
8868 : * entries. Also, we need to know if the NOT NULL for each column is
8869 : * backing a primary key.
8870 : */
8871 304 : if (fout->remoteVersion >= 170000)
8872 304 : appendPQExpBufferStr(q,
8873 : " LEFT JOIN pg_catalog.pg_constraint co ON "
8874 : "(a.attrelid = co.conrelid\n"
8875 : " AND co.contype = 'n' AND "
8876 : "co.conkey = array[a.attnum])\n");
8877 :
8878 304 : appendPQExpBufferStr(q,
8879 : "LEFT JOIN pg_catalog.pg_constraint copk ON "
8880 : "(copk.conrelid = src.tbloid\n"
8881 : " AND copk.contype = 'p' AND "
8882 : "copk.conkey @> array[a.attnum])\n"
8883 : "WHERE a.attnum > 0::pg_catalog.int2\n"
8884 : "ORDER BY a.attrelid, a.attnum");
8885 :
8886 304 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
8887 :
8888 304 : ntups = PQntuples(res);
8889 :
8890 304 : i_attrelid = PQfnumber(res, "attrelid");
8891 304 : i_attnum = PQfnumber(res, "attnum");
8892 304 : i_attname = PQfnumber(res, "attname");
8893 304 : i_atttypname = PQfnumber(res, "atttypname");
8894 304 : i_attstattarget = PQfnumber(res, "attstattarget");
8895 304 : i_attstorage = PQfnumber(res, "attstorage");
8896 304 : i_typstorage = PQfnumber(res, "typstorage");
8897 304 : i_attidentity = PQfnumber(res, "attidentity");
8898 304 : i_attgenerated = PQfnumber(res, "attgenerated");
8899 304 : i_attisdropped = PQfnumber(res, "attisdropped");
8900 304 : i_attlen = PQfnumber(res, "attlen");
8901 304 : i_attalign = PQfnumber(res, "attalign");
8902 304 : i_attislocal = PQfnumber(res, "attislocal");
8903 304 : i_notnull_name = PQfnumber(res, "notnull_name");
8904 304 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
8905 304 : i_notnull_is_pk = PQfnumber(res, "notnull_is_pk");
8906 304 : i_notnull_inh = PQfnumber(res, "notnull_inh");
8907 304 : i_attoptions = PQfnumber(res, "attoptions");
8908 304 : i_attcollation = PQfnumber(res, "attcollation");
8909 304 : i_attcompression = PQfnumber(res, "attcompression");
8910 304 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
8911 304 : i_attmissingval = PQfnumber(res, "attmissingval");
8912 304 : i_atthasdef = PQfnumber(res, "atthasdef");
8913 :
8914 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
8915 304 : resetPQExpBuffer(tbloids);
8916 304 : appendPQExpBufferChar(tbloids, '{');
8917 :
8918 : /*
8919 : * Outer loop iterates once per table, not once per row. Incrementing of
8920 : * r is handled by the inner loop.
8921 : */
8922 304 : curtblindx = -1;
8923 10910 : for (int r = 0; r < ntups;)
8924 : {
8925 10606 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
8926 10606 : TableInfo *tbinfo = NULL;
8927 : int numatts;
8928 : bool hasdefaults;
8929 : int notnullcount;
8930 :
8931 : /* Count rows for this table */
8932 39122 : for (numatts = 1; numatts < ntups - r; numatts++)
8933 38928 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
8934 10412 : break;
8935 :
8936 : /*
8937 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8938 : * order.
8939 : */
8940 53110 : while (++curtblindx < numTables)
8941 : {
8942 53110 : tbinfo = &tblinfo[curtblindx];
8943 53110 : if (tbinfo->dobj.catId.oid == attrelid)
8944 10606 : break;
8945 : }
8946 10606 : if (curtblindx >= numTables)
8947 0 : pg_fatal("unrecognized table OID %u", attrelid);
8948 : /* cross-check that we only got requested tables */
8949 10606 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
8950 10606 : !tbinfo->interesting)
8951 0 : pg_fatal("unexpected column data for table \"%s\"",
8952 : tbinfo->dobj.name);
8953 :
8954 10606 : notnullcount = 0;
8955 :
8956 : /* Save data for this table */
8957 10606 : tbinfo->numatts = numatts;
8958 10606 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
8959 10606 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
8960 10606 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
8961 10606 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
8962 10606 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
8963 10606 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
8964 10606 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
8965 10606 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
8966 10606 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
8967 10606 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
8968 10606 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
8969 10606 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
8970 10606 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
8971 10606 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
8972 10606 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
8973 10606 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
8974 10606 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
8975 10606 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
8976 10606 : tbinfo->notnull_throwaway = (bool *) pg_malloc(numatts * sizeof(bool));
8977 10606 : tbinfo->notnull_inh = (bool *) pg_malloc(numatts * sizeof(bool));
8978 10606 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
8979 10606 : hasdefaults = false;
8980 :
8981 49728 : for (int j = 0; j < numatts; j++, r++)
8982 : {
8983 39122 : bool use_named_notnull = false;
8984 39122 : bool use_unnamed_notnull = false;
8985 39122 : bool use_throwaway_notnull = false;
8986 :
8987 39122 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
8988 0 : pg_fatal("invalid column numbering in table \"%s\"",
8989 : tbinfo->dobj.name);
8990 39122 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
8991 39122 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
8992 39122 : if (PQgetisnull(res, r, i_attstattarget))
8993 39048 : tbinfo->attstattarget[j] = -1;
8994 : else
8995 74 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
8996 39122 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
8997 39122 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
8998 39122 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
8999 39122 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9000 39122 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9001 39122 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9002 39122 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9003 39122 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9004 39122 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9005 :
9006 : /*
9007 : * Not-null constraints require a jumping through a few hoops.
9008 : * First, if the user has specified a constraint name that's not
9009 : * the system-assigned default name, then we need to preserve
9010 : * that. But if they haven't, then we don't want to use the
9011 : * verbose syntax in the dump output. (Also, in versions prior to
9012 : * 17, there was no constraint name at all.)
9013 : *
9014 : * (XXX Comparing the name this way to a supposed default name is
9015 : * a bit of a hack, but it beats having to store a boolean flag in
9016 : * pg_constraint just for this, or having to compute the knowledge
9017 : * at pg_dump time from the server.)
9018 : *
9019 : * We also need to know if a column is part of the primary key. In
9020 : * that case, we want to mark the column as not-null at table
9021 : * creation time, so that the table doesn't have to be scanned to
9022 : * check for nulls when the PK is created afterwards; this is
9023 : * especially critical during pg_upgrade (where the data would not
9024 : * be scanned at all otherwise.) If the column is part of the PK
9025 : * and does not have any other not-null constraint, then we
9026 : * fabricate a throwaway constraint name that we later use to
9027 : * remove the constraint after the PK has been created.
9028 : *
9029 : * For inheritance child tables, we don't want to print not-null
9030 : * when the constraint was defined at the parent level instead of
9031 : * locally.
9032 : */
9033 :
9034 : /*
9035 : * We use notnull_inh to suppress unwanted not-null constraints in
9036 : * inheritance children, when said constraints come from the
9037 : * parent(s).
9038 : */
9039 39122 : tbinfo->notnull_inh[j] = PQgetvalue(res, r, i_notnull_inh)[0] == 't';
9040 :
9041 39122 : if (fout->remoteVersion < 170000)
9042 : {
9043 0 : if (!PQgetisnull(res, r, i_notnull_name) &&
9044 0 : dopt->binary_upgrade &&
9045 0 : !tbinfo->ispartition &&
9046 0 : tbinfo->notnull_inh[j])
9047 : {
9048 0 : use_named_notnull = true;
9049 : /* XXX should match ChooseConstraintName better */
9050 0 : tbinfo->notnull_constrs[j] =
9051 0 : psprintf("%s_%s_not_null", tbinfo->dobj.name,
9052 0 : tbinfo->attnames[j]);
9053 : }
9054 0 : else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
9055 : {
9056 : /*
9057 : * We want this flag to be set for columns of a primary
9058 : * key in which data is going to be loaded by the dump we
9059 : * produce; thus a partitioned table doesn't need it.
9060 : */
9061 0 : if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
9062 0 : use_throwaway_notnull = true;
9063 : }
9064 0 : else if (!PQgetisnull(res, r, i_notnull_name))
9065 0 : use_unnamed_notnull = true;
9066 : }
9067 : else
9068 : {
9069 39122 : if (!PQgetisnull(res, r, i_notnull_name))
9070 : {
9071 : /*
9072 : * In binary upgrade of inheritance child tables, must
9073 : * have a constraint name that we can UPDATE later.
9074 : */
9075 3172 : if (dopt->binary_upgrade &&
9076 262 : !tbinfo->ispartition &&
9077 172 : tbinfo->notnull_inh[j])
9078 : {
9079 32 : use_named_notnull = true;
9080 32 : tbinfo->notnull_constrs[j] =
9081 32 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9082 :
9083 : }
9084 : else
9085 : {
9086 : char *default_name;
9087 :
9088 : /* XXX should match ChooseConstraintName better */
9089 3140 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9090 3140 : tbinfo->attnames[j]);
9091 3140 : if (strcmp(default_name,
9092 3140 : PQgetvalue(res, r, i_notnull_name)) == 0)
9093 1388 : use_unnamed_notnull = true;
9094 : else
9095 : {
9096 1752 : use_named_notnull = true;
9097 1752 : tbinfo->notnull_constrs[j] =
9098 1752 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9099 : }
9100 : }
9101 : }
9102 35950 : else if (PQgetvalue(res, r, i_notnull_is_pk)[0] == 't')
9103 : {
9104 : /* see above */
9105 1598 : if (tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
9106 1468 : use_throwaway_notnull = true;
9107 : }
9108 : }
9109 :
9110 39122 : if (use_unnamed_notnull)
9111 : {
9112 1388 : tbinfo->notnull_constrs[j] = "";
9113 1388 : tbinfo->notnull_throwaway[j] = false;
9114 : }
9115 37734 : else if (use_named_notnull)
9116 : {
9117 : /* The name itself has already been determined */
9118 1784 : tbinfo->notnull_throwaway[j] = false;
9119 : }
9120 35950 : else if (use_throwaway_notnull)
9121 : {
9122 : /*
9123 : * Give this constraint a throwaway name.
9124 : */
9125 2936 : tbinfo->notnull_constrs[j] =
9126 1468 : psprintf("pgdump_throwaway_notnull_%d", notnullcount++);
9127 1468 : tbinfo->notnull_throwaway[j] = true;
9128 1468 : tbinfo->notnull_inh[j] = false;
9129 : }
9130 : else
9131 : {
9132 34482 : tbinfo->notnull_constrs[j] = NULL;
9133 34482 : tbinfo->notnull_throwaway[j] = false;
9134 : }
9135 :
9136 : /*
9137 : * Throwaway constraints must always be NO INHERIT; otherwise do
9138 : * what the catalog says.
9139 : */
9140 76776 : tbinfo->notnull_noinh[j] = use_throwaway_notnull ||
9141 37654 : PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9142 :
9143 39122 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9144 39122 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9145 39122 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9146 39122 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9147 39122 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9148 39122 : tbinfo->attrdefs[j] = NULL; /* fix below */
9149 39122 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9150 1686 : hasdefaults = true;
9151 : }
9152 :
9153 10606 : if (hasdefaults)
9154 : {
9155 : /* Collect OIDs of interesting tables that have defaults */
9156 1390 : if (tbloids->len > 1) /* do we have more than the '{'? */
9157 1294 : appendPQExpBufferChar(tbloids, ',');
9158 1390 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9159 : }
9160 : }
9161 :
9162 304 : PQclear(res);
9163 :
9164 : /*
9165 : * Now get info about column defaults. This is skipped for a data-only
9166 : * dump, as it is only needed for table schemas.
9167 : */
9168 304 : if (!dopt->dataOnly && tbloids->len > 1)
9169 : {
9170 : AttrDefInfo *attrdefs;
9171 : int numDefaults;
9172 88 : TableInfo *tbinfo = NULL;
9173 :
9174 88 : pg_log_info("finding table default expressions");
9175 :
9176 88 : appendPQExpBufferChar(tbloids, '}');
9177 :
9178 88 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9179 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9180 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9181 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9182 : "ORDER BY a.adrelid, a.adnum",
9183 : tbloids->data);
9184 :
9185 88 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9186 :
9187 88 : numDefaults = PQntuples(res);
9188 88 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9189 :
9190 88 : curtblindx = -1;
9191 1694 : for (int j = 0; j < numDefaults; j++)
9192 : {
9193 1606 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9194 1606 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9195 1606 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9196 1606 : int adnum = atoi(PQgetvalue(res, j, 3));
9197 1606 : char *adsrc = PQgetvalue(res, j, 4);
9198 :
9199 : /*
9200 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9201 : * OID order.
9202 : */
9203 1606 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9204 : {
9205 29442 : while (++curtblindx < numTables)
9206 : {
9207 29442 : tbinfo = &tblinfo[curtblindx];
9208 29442 : if (tbinfo->dobj.catId.oid == adrelid)
9209 1322 : break;
9210 : }
9211 1322 : if (curtblindx >= numTables)
9212 0 : pg_fatal("unrecognized table OID %u", adrelid);
9213 : }
9214 :
9215 1606 : if (adnum <= 0 || adnum > tbinfo->numatts)
9216 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9217 : adnum, tbinfo->dobj.name);
9218 :
9219 : /*
9220 : * dropped columns shouldn't have defaults, but just in case,
9221 : * ignore 'em
9222 : */
9223 1606 : if (tbinfo->attisdropped[adnum - 1])
9224 0 : continue;
9225 :
9226 1606 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9227 1606 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9228 1606 : attrdefs[j].dobj.catId.oid = adoid;
9229 1606 : AssignDumpId(&attrdefs[j].dobj);
9230 1606 : attrdefs[j].adtable = tbinfo;
9231 1606 : attrdefs[j].adnum = adnum;
9232 1606 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9233 :
9234 1606 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9235 1606 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9236 :
9237 1606 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9238 :
9239 : /*
9240 : * Figure out whether the default/generation expression should be
9241 : * dumped as part of the main CREATE TABLE (or similar) command or
9242 : * as a separate ALTER TABLE (or similar) command. The preference
9243 : * is to put it into the CREATE command, but in some cases that's
9244 : * not possible.
9245 : */
9246 1606 : if (tbinfo->attgenerated[adnum - 1])
9247 : {
9248 : /*
9249 : * Column generation expressions cannot be dumped separately,
9250 : * because there is no syntax for it. By setting separate to
9251 : * false here we prevent the "default" from being processed as
9252 : * its own dumpable object. Later, flagInhAttrs() will mark
9253 : * it as not to be dumped at all, if possible (that is, if it
9254 : * can be inherited from a parent).
9255 : */
9256 690 : attrdefs[j].separate = false;
9257 : }
9258 916 : else if (tbinfo->relkind == RELKIND_VIEW)
9259 : {
9260 : /*
9261 : * Defaults on a VIEW must always be dumped as separate ALTER
9262 : * TABLE commands.
9263 : */
9264 66 : attrdefs[j].separate = true;
9265 : }
9266 850 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9267 : {
9268 : /* column will be suppressed, print default separately */
9269 8 : attrdefs[j].separate = true;
9270 : }
9271 : else
9272 : {
9273 842 : attrdefs[j].separate = false;
9274 : }
9275 :
9276 1606 : if (!attrdefs[j].separate)
9277 : {
9278 : /*
9279 : * Mark the default as needing to appear before the table, so
9280 : * that any dependencies it has must be emitted before the
9281 : * CREATE TABLE. If this is not possible, we'll change to
9282 : * "separate" mode while sorting dependencies.
9283 : */
9284 1532 : addObjectDependency(&tbinfo->dobj,
9285 1532 : attrdefs[j].dobj.dumpId);
9286 : }
9287 :
9288 1606 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9289 : }
9290 :
9291 88 : PQclear(res);
9292 : }
9293 :
9294 : /*
9295 : * Get info about table CHECK constraints. This is skipped for a
9296 : * data-only dump, as it is only needed for table schemas.
9297 : */
9298 304 : if (!dopt->dataOnly && checkoids->len > 2)
9299 : {
9300 : ConstraintInfo *constrs;
9301 : int numConstrs;
9302 : int i_tableoid;
9303 : int i_oid;
9304 : int i_conrelid;
9305 : int i_conname;
9306 : int i_consrc;
9307 : int i_conislocal;
9308 : int i_convalidated;
9309 :
9310 122 : pg_log_info("finding table check constraints");
9311 :
9312 122 : resetPQExpBuffer(q);
9313 122 : appendPQExpBuffer(q,
9314 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9315 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9316 : "conislocal, convalidated "
9317 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9318 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9319 : "WHERE contype = 'c' "
9320 : "ORDER BY c.conrelid, c.conname",
9321 : checkoids->data);
9322 :
9323 122 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9324 :
9325 122 : numConstrs = PQntuples(res);
9326 122 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9327 :
9328 122 : i_tableoid = PQfnumber(res, "tableoid");
9329 122 : i_oid = PQfnumber(res, "oid");
9330 122 : i_conrelid = PQfnumber(res, "conrelid");
9331 122 : i_conname = PQfnumber(res, "conname");
9332 122 : i_consrc = PQfnumber(res, "consrc");
9333 122 : i_conislocal = PQfnumber(res, "conislocal");
9334 122 : i_convalidated = PQfnumber(res, "convalidated");
9335 :
9336 : /* As above, this loop iterates once per table, not once per row */
9337 122 : curtblindx = -1;
9338 954 : for (int j = 0; j < numConstrs;)
9339 : {
9340 832 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9341 832 : TableInfo *tbinfo = NULL;
9342 : int numcons;
9343 :
9344 : /* Count rows for this table */
9345 1096 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9346 974 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9347 710 : break;
9348 :
9349 : /*
9350 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9351 : * OID order.
9352 : */
9353 34960 : while (++curtblindx < numTables)
9354 : {
9355 34960 : tbinfo = &tblinfo[curtblindx];
9356 34960 : if (tbinfo->dobj.catId.oid == conrelid)
9357 832 : break;
9358 : }
9359 832 : if (curtblindx >= numTables)
9360 0 : pg_fatal("unrecognized table OID %u", conrelid);
9361 :
9362 832 : if (numcons != tbinfo->ncheck)
9363 : {
9364 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9365 : "expected %d check constraints on table \"%s\" but found %d",
9366 : tbinfo->ncheck),
9367 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9368 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9369 0 : exit_nicely(1);
9370 : }
9371 :
9372 832 : tbinfo->checkexprs = constrs + j;
9373 :
9374 1928 : for (int c = 0; c < numcons; c++, j++)
9375 : {
9376 1096 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9377 :
9378 1096 : constrs[j].dobj.objType = DO_CONSTRAINT;
9379 1096 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9380 1096 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9381 1096 : AssignDumpId(&constrs[j].dobj);
9382 1096 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9383 1096 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9384 1096 : constrs[j].contable = tbinfo;
9385 1096 : constrs[j].condomain = NULL;
9386 1096 : constrs[j].contype = 'c';
9387 1096 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9388 1096 : constrs[j].confrelid = InvalidOid;
9389 1096 : constrs[j].conindex = 0;
9390 1096 : constrs[j].condeferrable = false;
9391 1096 : constrs[j].condeferred = false;
9392 1096 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9393 :
9394 : /*
9395 : * An unvalidated constraint needs to be dumped separately, so
9396 : * that potentially-violating existing data is loaded before
9397 : * the constraint.
9398 : */
9399 1096 : constrs[j].separate = !validated;
9400 :
9401 1096 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9402 :
9403 : /*
9404 : * Mark the constraint as needing to appear before the table
9405 : * --- this is so that any other dependencies of the
9406 : * constraint will be emitted before we try to create the
9407 : * table. If the constraint is to be dumped separately, it
9408 : * will be dumped after data is loaded anyway, so don't do it.
9409 : * (There's an automatic dependency in the opposite direction
9410 : * anyway, so don't need to add one manually here.)
9411 : */
9412 1096 : if (!constrs[j].separate)
9413 1026 : addObjectDependency(&tbinfo->dobj,
9414 1026 : constrs[j].dobj.dumpId);
9415 :
9416 : /*
9417 : * We will detect later whether the constraint must be split
9418 : * out from the table definition.
9419 : */
9420 : }
9421 : }
9422 :
9423 122 : PQclear(res);
9424 : }
9425 :
9426 304 : destroyPQExpBuffer(q);
9427 304 : destroyPQExpBuffer(tbloids);
9428 304 : destroyPQExpBuffer(checkoids);
9429 304 : }
9430 :
9431 : /*
9432 : * Test whether a column should be printed as part of table's CREATE TABLE.
9433 : * Column number is zero-based.
9434 : *
9435 : * Normally this is always true, but it's false for dropped columns, as well
9436 : * as those that were inherited without any local definition. (If we print
9437 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9438 : * For partitions, it's always true, because we want the partitions to be
9439 : * created independently and ATTACH PARTITION used afterwards.
9440 : *
9441 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9442 : * attisdropped state later, so as to keep control of the physical column
9443 : * order.
9444 : *
9445 : * This function exists because there are scattered nonobvious places that
9446 : * must be kept in sync with this decision.
9447 : */
9448 : bool
9449 70538 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9450 : {
9451 70538 : if (dopt->binary_upgrade)
9452 11650 : return true;
9453 58888 : if (tbinfo->attisdropped[colno])
9454 696 : return false;
9455 58192 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9456 : }
9457 :
9458 :
9459 : /*
9460 : * getTSParsers:
9461 : * read all text search parsers in the system catalogs and return them
9462 : * in the TSParserInfo* structure
9463 : *
9464 : * numTSParsers is set to the number of parsers read in
9465 : */
9466 : TSParserInfo *
9467 304 : getTSParsers(Archive *fout, int *numTSParsers)
9468 : {
9469 : PGresult *res;
9470 : int ntups;
9471 : int i;
9472 : PQExpBuffer query;
9473 : TSParserInfo *prsinfo;
9474 : int i_tableoid;
9475 : int i_oid;
9476 : int i_prsname;
9477 : int i_prsnamespace;
9478 : int i_prsstart;
9479 : int i_prstoken;
9480 : int i_prsend;
9481 : int i_prsheadline;
9482 : int i_prslextype;
9483 :
9484 304 : query = createPQExpBuffer();
9485 :
9486 : /*
9487 : * find all text search objects, including builtin ones; we filter out
9488 : * system-defined objects at dump-out time.
9489 : */
9490 :
9491 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
9492 : "prsstart::oid, prstoken::oid, "
9493 : "prsend::oid, prsheadline::oid, prslextype::oid "
9494 : "FROM pg_ts_parser");
9495 :
9496 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9497 :
9498 304 : ntups = PQntuples(res);
9499 304 : *numTSParsers = ntups;
9500 :
9501 304 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
9502 :
9503 304 : i_tableoid = PQfnumber(res, "tableoid");
9504 304 : i_oid = PQfnumber(res, "oid");
9505 304 : i_prsname = PQfnumber(res, "prsname");
9506 304 : i_prsnamespace = PQfnumber(res, "prsnamespace");
9507 304 : i_prsstart = PQfnumber(res, "prsstart");
9508 304 : i_prstoken = PQfnumber(res, "prstoken");
9509 304 : i_prsend = PQfnumber(res, "prsend");
9510 304 : i_prsheadline = PQfnumber(res, "prsheadline");
9511 304 : i_prslextype = PQfnumber(res, "prslextype");
9512 :
9513 694 : for (i = 0; i < ntups; i++)
9514 : {
9515 390 : prsinfo[i].dobj.objType = DO_TSPARSER;
9516 390 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9517 390 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9518 390 : AssignDumpId(&prsinfo[i].dobj);
9519 390 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
9520 780 : prsinfo[i].dobj.namespace =
9521 390 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
9522 390 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
9523 390 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
9524 390 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
9525 390 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
9526 390 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
9527 :
9528 : /* Decide whether we want to dump it */
9529 390 : selectDumpableObject(&(prsinfo[i].dobj), fout);
9530 : }
9531 :
9532 304 : PQclear(res);
9533 :
9534 304 : destroyPQExpBuffer(query);
9535 :
9536 304 : return prsinfo;
9537 : }
9538 :
9539 : /*
9540 : * getTSDictionaries:
9541 : * read all text search dictionaries in the system catalogs and return them
9542 : * in the TSDictInfo* structure
9543 : *
9544 : * numTSDicts is set to the number of dictionaries read in
9545 : */
9546 : TSDictInfo *
9547 304 : getTSDictionaries(Archive *fout, int *numTSDicts)
9548 : {
9549 : PGresult *res;
9550 : int ntups;
9551 : int i;
9552 : PQExpBuffer query;
9553 : TSDictInfo *dictinfo;
9554 : int i_tableoid;
9555 : int i_oid;
9556 : int i_dictname;
9557 : int i_dictnamespace;
9558 : int i_dictowner;
9559 : int i_dicttemplate;
9560 : int i_dictinitoption;
9561 :
9562 304 : query = createPQExpBuffer();
9563 :
9564 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
9565 : "dictnamespace, dictowner, "
9566 : "dicttemplate, dictinitoption "
9567 : "FROM pg_ts_dict");
9568 :
9569 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9570 :
9571 304 : ntups = PQntuples(res);
9572 304 : *numTSDicts = ntups;
9573 :
9574 304 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
9575 :
9576 304 : i_tableoid = PQfnumber(res, "tableoid");
9577 304 : i_oid = PQfnumber(res, "oid");
9578 304 : i_dictname = PQfnumber(res, "dictname");
9579 304 : i_dictnamespace = PQfnumber(res, "dictnamespace");
9580 304 : i_dictowner = PQfnumber(res, "dictowner");
9581 304 : i_dictinitoption = PQfnumber(res, "dictinitoption");
9582 304 : i_dicttemplate = PQfnumber(res, "dicttemplate");
9583 :
9584 9296 : for (i = 0; i < ntups; i++)
9585 : {
9586 8992 : dictinfo[i].dobj.objType = DO_TSDICT;
9587 8992 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9588 8992 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9589 8992 : AssignDumpId(&dictinfo[i].dobj);
9590 8992 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
9591 17984 : dictinfo[i].dobj.namespace =
9592 8992 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
9593 8992 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
9594 8992 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
9595 8992 : if (PQgetisnull(res, i, i_dictinitoption))
9596 390 : dictinfo[i].dictinitoption = NULL;
9597 : else
9598 8602 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
9599 :
9600 : /* Decide whether we want to dump it */
9601 8992 : selectDumpableObject(&(dictinfo[i].dobj), fout);
9602 : }
9603 :
9604 304 : PQclear(res);
9605 :
9606 304 : destroyPQExpBuffer(query);
9607 :
9608 304 : return dictinfo;
9609 : }
9610 :
9611 : /*
9612 : * getTSTemplates:
9613 : * read all text search templates in the system catalogs and return them
9614 : * in the TSTemplateInfo* structure
9615 : *
9616 : * numTSTemplates is set to the number of templates read in
9617 : */
9618 : TSTemplateInfo *
9619 304 : getTSTemplates(Archive *fout, int *numTSTemplates)
9620 : {
9621 : PGresult *res;
9622 : int ntups;
9623 : int i;
9624 : PQExpBuffer query;
9625 : TSTemplateInfo *tmplinfo;
9626 : int i_tableoid;
9627 : int i_oid;
9628 : int i_tmplname;
9629 : int i_tmplnamespace;
9630 : int i_tmplinit;
9631 : int i_tmpllexize;
9632 :
9633 304 : query = createPQExpBuffer();
9634 :
9635 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
9636 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
9637 : "FROM pg_ts_template");
9638 :
9639 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9640 :
9641 304 : ntups = PQntuples(res);
9642 304 : *numTSTemplates = ntups;
9643 :
9644 304 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
9645 :
9646 304 : i_tableoid = PQfnumber(res, "tableoid");
9647 304 : i_oid = PQfnumber(res, "oid");
9648 304 : i_tmplname = PQfnumber(res, "tmplname");
9649 304 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
9650 304 : i_tmplinit = PQfnumber(res, "tmplinit");
9651 304 : i_tmpllexize = PQfnumber(res, "tmpllexize");
9652 :
9653 1910 : for (i = 0; i < ntups; i++)
9654 : {
9655 1606 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
9656 1606 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9657 1606 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9658 1606 : AssignDumpId(&tmplinfo[i].dobj);
9659 1606 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
9660 3212 : tmplinfo[i].dobj.namespace =
9661 1606 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
9662 1606 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
9663 1606 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
9664 :
9665 : /* Decide whether we want to dump it */
9666 1606 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
9667 : }
9668 :
9669 304 : PQclear(res);
9670 :
9671 304 : destroyPQExpBuffer(query);
9672 :
9673 304 : return tmplinfo;
9674 : }
9675 :
9676 : /*
9677 : * getTSConfigurations:
9678 : * read all text search configurations in the system catalogs and return
9679 : * them in the TSConfigInfo* structure
9680 : *
9681 : * numTSConfigs is set to the number of configurations read in
9682 : */
9683 : TSConfigInfo *
9684 304 : getTSConfigurations(Archive *fout, int *numTSConfigs)
9685 : {
9686 : PGresult *res;
9687 : int ntups;
9688 : int i;
9689 : PQExpBuffer query;
9690 : TSConfigInfo *cfginfo;
9691 : int i_tableoid;
9692 : int i_oid;
9693 : int i_cfgname;
9694 : int i_cfgnamespace;
9695 : int i_cfgowner;
9696 : int i_cfgparser;
9697 :
9698 304 : query = createPQExpBuffer();
9699 :
9700 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
9701 : "cfgnamespace, cfgowner, cfgparser "
9702 : "FROM pg_ts_config");
9703 :
9704 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9705 :
9706 304 : ntups = PQntuples(res);
9707 304 : *numTSConfigs = ntups;
9708 :
9709 304 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
9710 :
9711 304 : i_tableoid = PQfnumber(res, "tableoid");
9712 304 : i_oid = PQfnumber(res, "oid");
9713 304 : i_cfgname = PQfnumber(res, "cfgname");
9714 304 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
9715 304 : i_cfgowner = PQfnumber(res, "cfgowner");
9716 304 : i_cfgparser = PQfnumber(res, "cfgparser");
9717 :
9718 9246 : for (i = 0; i < ntups; i++)
9719 : {
9720 8942 : cfginfo[i].dobj.objType = DO_TSCONFIG;
9721 8942 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9722 8942 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9723 8942 : AssignDumpId(&cfginfo[i].dobj);
9724 8942 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
9725 17884 : cfginfo[i].dobj.namespace =
9726 8942 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
9727 8942 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
9728 8942 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
9729 :
9730 : /* Decide whether we want to dump it */
9731 8942 : selectDumpableObject(&(cfginfo[i].dobj), fout);
9732 : }
9733 :
9734 304 : PQclear(res);
9735 :
9736 304 : destroyPQExpBuffer(query);
9737 :
9738 304 : return cfginfo;
9739 : }
9740 :
9741 : /*
9742 : * getForeignDataWrappers:
9743 : * read all foreign-data wrappers in the system catalogs and return
9744 : * them in the FdwInfo* structure
9745 : *
9746 : * numForeignDataWrappers is set to the number of fdws read in
9747 : */
9748 : FdwInfo *
9749 304 : getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
9750 : {
9751 : PGresult *res;
9752 : int ntups;
9753 : int i;
9754 : PQExpBuffer query;
9755 : FdwInfo *fdwinfo;
9756 : int i_tableoid;
9757 : int i_oid;
9758 : int i_fdwname;
9759 : int i_fdwowner;
9760 : int i_fdwhandler;
9761 : int i_fdwvalidator;
9762 : int i_fdwacl;
9763 : int i_acldefault;
9764 : int i_fdwoptions;
9765 :
9766 304 : query = createPQExpBuffer();
9767 :
9768 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
9769 : "fdwowner, "
9770 : "fdwhandler::pg_catalog.regproc, "
9771 : "fdwvalidator::pg_catalog.regproc, "
9772 : "fdwacl, "
9773 : "acldefault('F', fdwowner) AS acldefault, "
9774 : "array_to_string(ARRAY("
9775 : "SELECT quote_ident(option_name) || ' ' || "
9776 : "quote_literal(option_value) "
9777 : "FROM pg_options_to_table(fdwoptions) "
9778 : "ORDER BY option_name"
9779 : "), E',\n ') AS fdwoptions "
9780 : "FROM pg_foreign_data_wrapper");
9781 :
9782 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9783 :
9784 304 : ntups = PQntuples(res);
9785 304 : *numForeignDataWrappers = ntups;
9786 :
9787 304 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
9788 :
9789 304 : i_tableoid = PQfnumber(res, "tableoid");
9790 304 : i_oid = PQfnumber(res, "oid");
9791 304 : i_fdwname = PQfnumber(res, "fdwname");
9792 304 : i_fdwowner = PQfnumber(res, "fdwowner");
9793 304 : i_fdwhandler = PQfnumber(res, "fdwhandler");
9794 304 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
9795 304 : i_fdwacl = PQfnumber(res, "fdwacl");
9796 304 : i_acldefault = PQfnumber(res, "acldefault");
9797 304 : i_fdwoptions = PQfnumber(res, "fdwoptions");
9798 :
9799 438 : for (i = 0; i < ntups; i++)
9800 : {
9801 134 : fdwinfo[i].dobj.objType = DO_FDW;
9802 134 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9803 134 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9804 134 : AssignDumpId(&fdwinfo[i].dobj);
9805 134 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
9806 134 : fdwinfo[i].dobj.namespace = NULL;
9807 134 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
9808 134 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9809 134 : fdwinfo[i].dacl.privtype = 0;
9810 134 : fdwinfo[i].dacl.initprivs = NULL;
9811 134 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
9812 134 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
9813 134 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
9814 134 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
9815 :
9816 : /* Decide whether we want to dump it */
9817 134 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
9818 :
9819 : /* Mark whether FDW has an ACL */
9820 134 : if (!PQgetisnull(res, i, i_fdwacl))
9821 86 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9822 : }
9823 :
9824 304 : PQclear(res);
9825 :
9826 304 : destroyPQExpBuffer(query);
9827 :
9828 304 : return fdwinfo;
9829 : }
9830 :
9831 : /*
9832 : * getForeignServers:
9833 : * read all foreign servers in the system catalogs and return
9834 : * them in the ForeignServerInfo * structure
9835 : *
9836 : * numForeignServers is set to the number of servers read in
9837 : */
9838 : ForeignServerInfo *
9839 304 : getForeignServers(Archive *fout, int *numForeignServers)
9840 : {
9841 : PGresult *res;
9842 : int ntups;
9843 : int i;
9844 : PQExpBuffer query;
9845 : ForeignServerInfo *srvinfo;
9846 : int i_tableoid;
9847 : int i_oid;
9848 : int i_srvname;
9849 : int i_srvowner;
9850 : int i_srvfdw;
9851 : int i_srvtype;
9852 : int i_srvversion;
9853 : int i_srvacl;
9854 : int i_acldefault;
9855 : int i_srvoptions;
9856 :
9857 304 : query = createPQExpBuffer();
9858 :
9859 304 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
9860 : "srvowner, "
9861 : "srvfdw, srvtype, srvversion, srvacl, "
9862 : "acldefault('S', srvowner) AS acldefault, "
9863 : "array_to_string(ARRAY("
9864 : "SELECT quote_ident(option_name) || ' ' || "
9865 : "quote_literal(option_value) "
9866 : "FROM pg_options_to_table(srvoptions) "
9867 : "ORDER BY option_name"
9868 : "), E',\n ') AS srvoptions "
9869 : "FROM pg_foreign_server");
9870 :
9871 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9872 :
9873 304 : ntups = PQntuples(res);
9874 304 : *numForeignServers = ntups;
9875 :
9876 304 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
9877 :
9878 304 : i_tableoid = PQfnumber(res, "tableoid");
9879 304 : i_oid = PQfnumber(res, "oid");
9880 304 : i_srvname = PQfnumber(res, "srvname");
9881 304 : i_srvowner = PQfnumber(res, "srvowner");
9882 304 : i_srvfdw = PQfnumber(res, "srvfdw");
9883 304 : i_srvtype = PQfnumber(res, "srvtype");
9884 304 : i_srvversion = PQfnumber(res, "srvversion");
9885 304 : i_srvacl = PQfnumber(res, "srvacl");
9886 304 : i_acldefault = PQfnumber(res, "acldefault");
9887 304 : i_srvoptions = PQfnumber(res, "srvoptions");
9888 :
9889 446 : for (i = 0; i < ntups; i++)
9890 : {
9891 142 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
9892 142 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9893 142 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9894 142 : AssignDumpId(&srvinfo[i].dobj);
9895 142 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
9896 142 : srvinfo[i].dobj.namespace = NULL;
9897 142 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
9898 142 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9899 142 : srvinfo[i].dacl.privtype = 0;
9900 142 : srvinfo[i].dacl.initprivs = NULL;
9901 142 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
9902 142 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
9903 142 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
9904 142 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
9905 142 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
9906 :
9907 : /* Decide whether we want to dump it */
9908 142 : selectDumpableObject(&(srvinfo[i].dobj), fout);
9909 :
9910 : /* Servers have user mappings */
9911 142 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
9912 :
9913 : /* Mark whether server has an ACL */
9914 142 : if (!PQgetisnull(res, i, i_srvacl))
9915 86 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9916 : }
9917 :
9918 304 : PQclear(res);
9919 :
9920 304 : destroyPQExpBuffer(query);
9921 :
9922 304 : return srvinfo;
9923 : }
9924 :
9925 : /*
9926 : * getDefaultACLs:
9927 : * read all default ACL information in the system catalogs and return
9928 : * them in the DefaultACLInfo structure
9929 : *
9930 : * numDefaultACLs is set to the number of ACLs read in
9931 : */
9932 : DefaultACLInfo *
9933 304 : getDefaultACLs(Archive *fout, int *numDefaultACLs)
9934 : {
9935 304 : DumpOptions *dopt = fout->dopt;
9936 : DefaultACLInfo *daclinfo;
9937 : PQExpBuffer query;
9938 : PGresult *res;
9939 : int i_oid;
9940 : int i_tableoid;
9941 : int i_defaclrole;
9942 : int i_defaclnamespace;
9943 : int i_defaclobjtype;
9944 : int i_defaclacl;
9945 : int i_acldefault;
9946 : int i,
9947 : ntups;
9948 :
9949 304 : query = createPQExpBuffer();
9950 :
9951 : /*
9952 : * Global entries (with defaclnamespace=0) replace the hard-wired default
9953 : * ACL for their object type. We should dump them as deltas from the
9954 : * default ACL, since that will be used as a starting point for
9955 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
9956 : * non-global entries can only add privileges not revoke them. We must
9957 : * dump those as-is (i.e., as deltas from an empty ACL).
9958 : *
9959 : * We can use defaclobjtype as the object type for acldefault(), except
9960 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
9961 : * 's'.
9962 : */
9963 304 : appendPQExpBufferStr(query,
9964 : "SELECT oid, tableoid, "
9965 : "defaclrole, "
9966 : "defaclnamespace, "
9967 : "defaclobjtype, "
9968 : "defaclacl, "
9969 : "CASE WHEN defaclnamespace = 0 THEN "
9970 : "acldefault(CASE WHEN defaclobjtype = 'S' "
9971 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
9972 : "defaclrole) ELSE '{}' END AS acldefault "
9973 : "FROM pg_default_acl");
9974 :
9975 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9976 :
9977 304 : ntups = PQntuples(res);
9978 304 : *numDefaultACLs = ntups;
9979 :
9980 304 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
9981 :
9982 304 : i_oid = PQfnumber(res, "oid");
9983 304 : i_tableoid = PQfnumber(res, "tableoid");
9984 304 : i_defaclrole = PQfnumber(res, "defaclrole");
9985 304 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
9986 304 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
9987 304 : i_defaclacl = PQfnumber(res, "defaclacl");
9988 304 : i_acldefault = PQfnumber(res, "acldefault");
9989 :
9990 648 : for (i = 0; i < ntups; i++)
9991 : {
9992 344 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
9993 :
9994 344 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
9995 344 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9996 344 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9997 344 : AssignDumpId(&daclinfo[i].dobj);
9998 : /* cheesy ... is it worth coming up with a better object name? */
9999 344 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10000 :
10001 344 : if (nspid != InvalidOid)
10002 172 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10003 : else
10004 172 : daclinfo[i].dobj.namespace = NULL;
10005 :
10006 344 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10007 344 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10008 344 : daclinfo[i].dacl.privtype = 0;
10009 344 : daclinfo[i].dacl.initprivs = NULL;
10010 344 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10011 344 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10012 :
10013 : /* Default ACLs are ACLs, of course */
10014 344 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10015 :
10016 : /* Decide whether we want to dump it */
10017 344 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10018 : }
10019 :
10020 304 : PQclear(res);
10021 :
10022 304 : destroyPQExpBuffer(query);
10023 :
10024 304 : return daclinfo;
10025 : }
10026 :
10027 : /*
10028 : * getRoleName -- look up the name of a role, given its OID
10029 : *
10030 : * In current usage, we don't expect failures, so error out for a bad OID.
10031 : */
10032 : static const char *
10033 942884 : getRoleName(const char *roleoid_str)
10034 : {
10035 942884 : Oid roleoid = atooid(roleoid_str);
10036 :
10037 : /*
10038 : * Do binary search to find the appropriate item.
10039 : */
10040 942884 : if (nrolenames > 0)
10041 : {
10042 942884 : RoleNameItem *low = &rolenames[0];
10043 942884 : RoleNameItem *high = &rolenames[nrolenames - 1];
10044 :
10045 3771646 : while (low <= high)
10046 : {
10047 3771646 : RoleNameItem *middle = low + (high - low) / 2;
10048 :
10049 3771646 : if (roleoid < middle->roleoid)
10050 2826458 : high = middle - 1;
10051 945188 : else if (roleoid > middle->roleoid)
10052 2304 : low = middle + 1;
10053 : else
10054 942884 : return middle->rolename; /* found a match */
10055 : }
10056 : }
10057 :
10058 0 : pg_fatal("role with OID %u does not exist", roleoid);
10059 : return NULL; /* keep compiler quiet */
10060 : }
10061 :
10062 : /*
10063 : * collectRoleNames --
10064 : *
10065 : * Construct a table of all known roles.
10066 : * The table is sorted by OID for speed in lookup.
10067 : */
10068 : static void
10069 306 : collectRoleNames(Archive *fout)
10070 : {
10071 : PGresult *res;
10072 : const char *query;
10073 : int i;
10074 :
10075 306 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10076 :
10077 306 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10078 :
10079 306 : nrolenames = PQntuples(res);
10080 :
10081 306 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10082 :
10083 5602 : for (i = 0; i < nrolenames; i++)
10084 : {
10085 5296 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10086 5296 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10087 : }
10088 :
10089 306 : PQclear(res);
10090 306 : }
10091 :
10092 : /*
10093 : * getAdditionalACLs
10094 : *
10095 : * We have now created all the DumpableObjects, and collected the ACL data
10096 : * that appears in the directly-associated catalog entries. However, there's
10097 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10098 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10099 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10100 : * Also, in versions having the pg_init_privs catalog, read that and load the
10101 : * information into the relevant DumpableObjects.
10102 : */
10103 : static void
10104 300 : getAdditionalACLs(Archive *fout)
10105 : {
10106 300 : PQExpBuffer query = createPQExpBuffer();
10107 : PGresult *res;
10108 : int ntups,
10109 : i;
10110 :
10111 : /* Check for per-column ACLs */
10112 300 : appendPQExpBufferStr(query,
10113 : "SELECT DISTINCT attrelid FROM pg_attribute "
10114 : "WHERE attacl IS NOT NULL");
10115 :
10116 300 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10117 :
10118 300 : ntups = PQntuples(res);
10119 920 : for (i = 0; i < ntups; i++)
10120 : {
10121 620 : Oid relid = atooid(PQgetvalue(res, i, 0));
10122 : TableInfo *tblinfo;
10123 :
10124 620 : tblinfo = findTableByOid(relid);
10125 : /* OK to ignore tables we haven't got a DumpableObject for */
10126 620 : if (tblinfo)
10127 : {
10128 620 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10129 620 : tblinfo->hascolumnACLs = true;
10130 : }
10131 : }
10132 300 : PQclear(res);
10133 :
10134 : /* Fetch initial-privileges data */
10135 300 : if (fout->remoteVersion >= 90600)
10136 : {
10137 300 : printfPQExpBuffer(query,
10138 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10139 : "FROM pg_init_privs");
10140 :
10141 300 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10142 :
10143 300 : ntups = PQntuples(res);
10144 67816 : for (i = 0; i < ntups; i++)
10145 : {
10146 67516 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10147 67516 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10148 67516 : int objsubid = atoi(PQgetvalue(res, i, 2));
10149 67516 : char privtype = *(PQgetvalue(res, i, 3));
10150 67516 : char *initprivs = PQgetvalue(res, i, 4);
10151 : CatalogId objId;
10152 : DumpableObject *dobj;
10153 :
10154 67516 : objId.tableoid = classoid;
10155 67516 : objId.oid = objoid;
10156 67516 : dobj = findObjectByCatalogId(objId);
10157 : /* OK to ignore entries we haven't got a DumpableObject for */
10158 67516 : if (dobj)
10159 : {
10160 : /* Cope with sub-object initprivs */
10161 49000 : if (objsubid != 0)
10162 : {
10163 5148 : if (dobj->objType == DO_TABLE)
10164 : {
10165 : /* For a column initprivs, set the table's ACL flags */
10166 5148 : dobj->components |= DUMP_COMPONENT_ACL;
10167 5148 : ((TableInfo *) dobj)->hascolumnACLs = true;
10168 : }
10169 : else
10170 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10171 : classoid, objoid, objsubid);
10172 5440 : continue;
10173 : }
10174 :
10175 : /*
10176 : * We ignore any pg_init_privs.initprivs entry for the public
10177 : * schema, as explained in getNamespaces().
10178 : */
10179 43852 : if (dobj->objType == DO_NAMESPACE &&
10180 592 : strcmp(dobj->name, "public") == 0)
10181 292 : continue;
10182 :
10183 : /* Else it had better be of a type we think has ACLs */
10184 43560 : if (dobj->objType == DO_NAMESPACE ||
10185 43260 : dobj->objType == DO_TYPE ||
10186 43212 : dobj->objType == DO_FUNC ||
10187 43032 : dobj->objType == DO_AGG ||
10188 42984 : dobj->objType == DO_TABLE ||
10189 0 : dobj->objType == DO_PROCLANG ||
10190 0 : dobj->objType == DO_FDW ||
10191 0 : dobj->objType == DO_FOREIGN_SERVER)
10192 43560 : {
10193 43560 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10194 :
10195 43560 : daobj->dacl.privtype = privtype;
10196 43560 : daobj->dacl.initprivs = pstrdup(initprivs);
10197 : }
10198 : else
10199 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10200 : classoid, objoid, objsubid);
10201 : }
10202 : }
10203 300 : PQclear(res);
10204 : }
10205 :
10206 300 : destroyPQExpBuffer(query);
10207 300 : }
10208 :
10209 : /*
10210 : * dumpCommentExtended --
10211 : *
10212 : * This routine is used to dump any comments associated with the
10213 : * object handed to this routine. The routine takes the object type
10214 : * and object name (ready to print, except for schema decoration), plus
10215 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10216 : * plus catalog ID and subid which are the lookup key for pg_description,
10217 : * plus the dump ID for the object (for setting a dependency).
10218 : * If a matching pg_description entry is found, it is dumped.
10219 : *
10220 : * Note: in some cases, such as comments for triggers and rules, the "type"
10221 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10222 : * but it doesn't seem worth complicating the API for all callers to make
10223 : * it cleaner.
10224 : *
10225 : * Note: although this routine takes a dumpId for dependency purposes,
10226 : * that purpose is just to mark the dependency in the emitted dump file
10227 : * for possible future use by pg_restore. We do NOT use it for determining
10228 : * ordering of the comment in the dump file, because this routine is called
10229 : * after dependency sorting occurs. This routine should be called just after
10230 : * calling ArchiveEntry() for the specified object.
10231 : */
10232 : static void
10233 5148 : dumpCommentExtended(Archive *fout, const char *type,
10234 : const char *name, const char *namespace,
10235 : const char *owner, CatalogId catalogId,
10236 : int subid, DumpId dumpId,
10237 : const char *initdb_comment)
10238 : {
10239 5148 : DumpOptions *dopt = fout->dopt;
10240 : CommentItem *comments;
10241 : int ncomments;
10242 :
10243 : /* do nothing, if --no-comments is supplied */
10244 5148 : if (dopt->no_comments)
10245 0 : return;
10246 :
10247 : /* Comments are schema not data ... except LO comments are data */
10248 5148 : if (strcmp(type, "LARGE OBJECT") != 0)
10249 : {
10250 5050 : if (dopt->dataOnly)
10251 0 : return;
10252 : }
10253 : else
10254 : {
10255 : /* We do dump LO comments in binary-upgrade mode */
10256 98 : if (dopt->schemaOnly && !dopt->binary_upgrade)
10257 0 : return;
10258 : }
10259 :
10260 : /* Search for comments associated with catalogId, using table */
10261 5148 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10262 : &comments);
10263 :
10264 : /* Is there one matching the subid? */
10265 5148 : while (ncomments > 0)
10266 : {
10267 5064 : if (comments->objsubid == subid)
10268 5064 : break;
10269 0 : comments++;
10270 0 : ncomments--;
10271 : }
10272 :
10273 5148 : if (initdb_comment != NULL)
10274 : {
10275 : static CommentItem empty_comment = {.descr = ""};
10276 :
10277 : /*
10278 : * initdb creates this object with a comment. Skip dumping the
10279 : * initdb-provided comment, which would complicate matters for
10280 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10281 : * comment, replicate that.
10282 : */
10283 220 : if (ncomments == 0)
10284 : {
10285 8 : comments = &empty_comment;
10286 8 : ncomments = 1;
10287 : }
10288 212 : else if (strcmp(comments->descr, initdb_comment) == 0)
10289 212 : ncomments = 0;
10290 : }
10291 :
10292 : /* If a comment exists, build COMMENT ON statement */
10293 5148 : if (ncomments > 0)
10294 : {
10295 4860 : PQExpBuffer query = createPQExpBuffer();
10296 4860 : PQExpBuffer tag = createPQExpBuffer();
10297 :
10298 4860 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10299 4860 : if (namespace && *namespace)
10300 4570 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10301 4860 : appendPQExpBuffer(query, "%s IS ", name);
10302 4860 : appendStringLiteralAH(query, comments->descr, fout);
10303 4860 : appendPQExpBufferStr(query, ";\n");
10304 :
10305 4860 : appendPQExpBuffer(tag, "%s %s", type, name);
10306 :
10307 : /*
10308 : * We mark comments as SECTION_NONE because they really belong in the
10309 : * same section as their parent, whether that is pre-data or
10310 : * post-data.
10311 : */
10312 4860 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10313 4860 : ARCHIVE_OPTS(.tag = tag->data,
10314 : .namespace = namespace,
10315 : .owner = owner,
10316 : .description = "COMMENT",
10317 : .section = SECTION_NONE,
10318 : .createStmt = query->data,
10319 : .deps = &dumpId,
10320 : .nDeps = 1));
10321 :
10322 4860 : destroyPQExpBuffer(query);
10323 4860 : destroyPQExpBuffer(tag);
10324 : }
10325 : }
10326 :
10327 : /*
10328 : * dumpComment --
10329 : *
10330 : * Typical simplification of the above function.
10331 : */
10332 : static inline void
10333 4898 : dumpComment(Archive *fout, const char *type,
10334 : const char *name, const char *namespace,
10335 : const char *owner, CatalogId catalogId,
10336 : int subid, DumpId dumpId)
10337 : {
10338 4898 : dumpCommentExtended(fout, type, name, namespace, owner,
10339 : catalogId, subid, dumpId, NULL);
10340 4898 : }
10341 :
10342 : /*
10343 : * dumpTableComment --
10344 : *
10345 : * As above, but dump comments for both the specified table (or view)
10346 : * and its columns.
10347 : */
10348 : static void
10349 152 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
10350 : const char *reltypename)
10351 : {
10352 152 : DumpOptions *dopt = fout->dopt;
10353 : CommentItem *comments;
10354 : int ncomments;
10355 : PQExpBuffer query;
10356 : PQExpBuffer tag;
10357 :
10358 : /* do nothing, if --no-comments is supplied */
10359 152 : if (dopt->no_comments)
10360 0 : return;
10361 :
10362 : /* Comments are SCHEMA not data */
10363 152 : if (dopt->dataOnly)
10364 0 : return;
10365 :
10366 : /* Search for comments associated with relation, using table */
10367 152 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
10368 : tbinfo->dobj.catId.oid,
10369 : &comments);
10370 :
10371 : /* If comments exist, build COMMENT ON statements */
10372 152 : if (ncomments <= 0)
10373 0 : return;
10374 :
10375 152 : query = createPQExpBuffer();
10376 152 : tag = createPQExpBuffer();
10377 :
10378 436 : while (ncomments > 0)
10379 : {
10380 284 : const char *descr = comments->descr;
10381 284 : int objsubid = comments->objsubid;
10382 :
10383 284 : if (objsubid == 0)
10384 : {
10385 66 : resetPQExpBuffer(tag);
10386 66 : appendPQExpBuffer(tag, "%s %s", reltypename,
10387 66 : fmtId(tbinfo->dobj.name));
10388 :
10389 66 : resetPQExpBuffer(query);
10390 66 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
10391 66 : fmtQualifiedDumpable(tbinfo));
10392 66 : appendStringLiteralAH(query, descr, fout);
10393 66 : appendPQExpBufferStr(query, ";\n");
10394 :
10395 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10396 66 : ARCHIVE_OPTS(.tag = tag->data,
10397 : .namespace = tbinfo->dobj.namespace->dobj.name,
10398 : .owner = tbinfo->rolname,
10399 : .description = "COMMENT",
10400 : .section = SECTION_NONE,
10401 : .createStmt = query->data,
10402 : .deps = &(tbinfo->dobj.dumpId),
10403 : .nDeps = 1));
10404 : }
10405 218 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
10406 : {
10407 218 : resetPQExpBuffer(tag);
10408 218 : appendPQExpBuffer(tag, "COLUMN %s.",
10409 218 : fmtId(tbinfo->dobj.name));
10410 218 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
10411 :
10412 218 : resetPQExpBuffer(query);
10413 218 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
10414 218 : fmtQualifiedDumpable(tbinfo));
10415 218 : appendPQExpBuffer(query, "%s IS ",
10416 218 : fmtId(tbinfo->attnames[objsubid - 1]));
10417 218 : appendStringLiteralAH(query, descr, fout);
10418 218 : appendPQExpBufferStr(query, ";\n");
10419 :
10420 218 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10421 218 : ARCHIVE_OPTS(.tag = tag->data,
10422 : .namespace = tbinfo->dobj.namespace->dobj.name,
10423 : .owner = tbinfo->rolname,
10424 : .description = "COMMENT",
10425 : .section = SECTION_NONE,
10426 : .createStmt = query->data,
10427 : .deps = &(tbinfo->dobj.dumpId),
10428 : .nDeps = 1));
10429 : }
10430 :
10431 284 : comments++;
10432 284 : ncomments--;
10433 : }
10434 :
10435 152 : destroyPQExpBuffer(query);
10436 152 : destroyPQExpBuffer(tag);
10437 : }
10438 :
10439 : /*
10440 : * findComments --
10441 : *
10442 : * Find the comment(s), if any, associated with the given object. All the
10443 : * objsubid values associated with the given classoid/objoid are found with
10444 : * one search.
10445 : */
10446 : static int
10447 5366 : findComments(Oid classoid, Oid objoid, CommentItem **items)
10448 : {
10449 5366 : CommentItem *middle = NULL;
10450 : CommentItem *low;
10451 : CommentItem *high;
10452 : int nmatch;
10453 :
10454 : /*
10455 : * Do binary search to find some item matching the object.
10456 : */
10457 5366 : low = &comments[0];
10458 5366 : high = &comments[ncomments - 1];
10459 53562 : while (low <= high)
10460 : {
10461 53478 : middle = low + (high - low) / 2;
10462 :
10463 53478 : if (classoid < middle->classoid)
10464 9080 : high = middle - 1;
10465 44398 : else if (classoid > middle->classoid)
10466 7812 : low = middle + 1;
10467 36586 : else if (objoid < middle->objoid)
10468 14324 : high = middle - 1;
10469 22262 : else if (objoid > middle->objoid)
10470 16980 : low = middle + 1;
10471 : else
10472 5282 : break; /* found a match */
10473 : }
10474 :
10475 5366 : if (low > high) /* no matches */
10476 : {
10477 84 : *items = NULL;
10478 84 : return 0;
10479 : }
10480 :
10481 : /*
10482 : * Now determine how many items match the object. The search loop
10483 : * invariant still holds: only items between low and high inclusive could
10484 : * match.
10485 : */
10486 5282 : nmatch = 1;
10487 5282 : while (middle > low)
10488 : {
10489 2420 : if (classoid != middle[-1].classoid ||
10490 2248 : objoid != middle[-1].objoid)
10491 : break;
10492 0 : middle--;
10493 0 : nmatch++;
10494 : }
10495 :
10496 5282 : *items = middle;
10497 :
10498 5282 : middle += nmatch;
10499 5414 : while (middle <= high)
10500 : {
10501 2762 : if (classoid != middle->classoid ||
10502 2302 : objoid != middle->objoid)
10503 : break;
10504 132 : middle++;
10505 132 : nmatch++;
10506 : }
10507 :
10508 5282 : return nmatch;
10509 : }
10510 :
10511 : /*
10512 : * collectComments --
10513 : *
10514 : * Construct a table of all comments available for database objects;
10515 : * also set the has-comment component flag for each relevant object.
10516 : *
10517 : * We used to do per-object queries for the comments, but it's much faster
10518 : * to pull them all over at once, and on most databases the memory cost
10519 : * isn't high.
10520 : *
10521 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
10522 : */
10523 : static void
10524 304 : collectComments(Archive *fout)
10525 : {
10526 : PGresult *res;
10527 : PQExpBuffer query;
10528 : int i_description;
10529 : int i_classoid;
10530 : int i_objoid;
10531 : int i_objsubid;
10532 : int ntups;
10533 : int i;
10534 : DumpableObject *dobj;
10535 :
10536 304 : query = createPQExpBuffer();
10537 :
10538 304 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
10539 : "FROM pg_catalog.pg_description "
10540 : "ORDER BY classoid, objoid, objsubid");
10541 :
10542 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10543 :
10544 : /* Construct lookup table containing OIDs in numeric form */
10545 :
10546 304 : i_description = PQfnumber(res, "description");
10547 304 : i_classoid = PQfnumber(res, "classoid");
10548 304 : i_objoid = PQfnumber(res, "objoid");
10549 304 : i_objsubid = PQfnumber(res, "objsubid");
10550 :
10551 304 : ntups = PQntuples(res);
10552 :
10553 304 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
10554 304 : ncomments = 0;
10555 304 : dobj = NULL;
10556 :
10557 1584810 : for (i = 0; i < ntups; i++)
10558 : {
10559 : CatalogId objId;
10560 : int subid;
10561 :
10562 1584506 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
10563 1584506 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
10564 1584506 : subid = atoi(PQgetvalue(res, i, i_objsubid));
10565 :
10566 : /* We needn't remember comments that don't match any dumpable object */
10567 1584506 : if (dobj == NULL ||
10568 575924 : dobj->catId.tableoid != objId.tableoid ||
10569 572138 : dobj->catId.oid != objId.oid)
10570 1584334 : dobj = findObjectByCatalogId(objId);
10571 1584506 : if (dobj == NULL)
10572 1008286 : continue;
10573 :
10574 : /*
10575 : * Comments on columns of composite types are linked to the type's
10576 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
10577 : * in the type's own DumpableObject.
10578 : */
10579 576220 : if (subid != 0 && dobj->objType == DO_TABLE &&
10580 364 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
10581 86 : {
10582 : TypeInfo *cTypeInfo;
10583 :
10584 86 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
10585 86 : if (cTypeInfo)
10586 86 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
10587 : }
10588 : else
10589 576134 : dobj->components |= DUMP_COMPONENT_COMMENT;
10590 :
10591 576220 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
10592 576220 : comments[ncomments].classoid = objId.tableoid;
10593 576220 : comments[ncomments].objoid = objId.oid;
10594 576220 : comments[ncomments].objsubid = subid;
10595 576220 : ncomments++;
10596 : }
10597 :
10598 304 : PQclear(res);
10599 304 : destroyPQExpBuffer(query);
10600 304 : }
10601 :
10602 : /*
10603 : * dumpDumpableObject
10604 : *
10605 : * This routine and its subsidiaries are responsible for creating
10606 : * ArchiveEntries (TOC objects) for each object to be dumped.
10607 : */
10608 : static void
10609 1098304 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
10610 : {
10611 : /*
10612 : * Clear any dump-request bits for components that don't exist for this
10613 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
10614 : * request for every kind of object.)
10615 : */
10616 1098304 : dobj->dump &= dobj->components;
10617 :
10618 : /* Now, short-circuit if there's nothing to be done here. */
10619 1098304 : if (dobj->dump == 0)
10620 985608 : return;
10621 :
10622 112696 : switch (dobj->objType)
10623 : {
10624 770 : case DO_NAMESPACE:
10625 770 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
10626 770 : break;
10627 38 : case DO_EXTENSION:
10628 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
10629 38 : break;
10630 1258 : case DO_TYPE:
10631 1258 : dumpType(fout, (const TypeInfo *) dobj);
10632 1258 : break;
10633 142 : case DO_SHELL_TYPE:
10634 142 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
10635 142 : break;
10636 3518 : case DO_FUNC:
10637 3518 : dumpFunc(fout, (const FuncInfo *) dobj);
10638 3518 : break;
10639 580 : case DO_AGG:
10640 580 : dumpAgg(fout, (const AggInfo *) dobj);
10641 580 : break;
10642 1808 : case DO_OPERATOR:
10643 1808 : dumpOpr(fout, (const OprInfo *) dobj);
10644 1808 : break;
10645 152 : case DO_ACCESS_METHOD:
10646 152 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
10647 152 : break;
10648 600 : case DO_OPCLASS:
10649 600 : dumpOpclass(fout, (const OpclassInfo *) dobj);
10650 600 : break;
10651 506 : case DO_OPFAMILY:
10652 506 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
10653 506 : break;
10654 1744 : case DO_COLLATION:
10655 1744 : dumpCollation(fout, (const CollInfo *) dobj);
10656 1744 : break;
10657 328 : case DO_CONVERSION:
10658 328 : dumpConversion(fout, (const ConvInfo *) dobj);
10659 328 : break;
10660 49336 : case DO_TABLE:
10661 49336 : dumpTable(fout, (const TableInfo *) dobj);
10662 49336 : break;
10663 2476 : case DO_TABLE_ATTACH:
10664 2476 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
10665 2476 : break;
10666 1520 : case DO_ATTRDEF:
10667 1520 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
10668 1520 : break;
10669 4244 : case DO_INDEX:
10670 4244 : dumpIndex(fout, (const IndxInfo *) dobj);
10671 4244 : break;
10672 1086 : case DO_INDEX_ATTACH:
10673 1086 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
10674 1086 : break;
10675 254 : case DO_STATSEXT:
10676 254 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
10677 254 : break;
10678 676 : case DO_REFRESH_MATVIEW:
10679 676 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
10680 676 : break;
10681 1820 : case DO_RULE:
10682 1820 : dumpRule(fout, (const RuleInfo *) dobj);
10683 1820 : break;
10684 986 : case DO_TRIGGER:
10685 986 : dumpTrigger(fout, (const TriggerInfo *) dobj);
10686 986 : break;
10687 80 : case DO_EVENT_TRIGGER:
10688 80 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
10689 80 : break;
10690 3542 : case DO_CONSTRAINT:
10691 3542 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
10692 3542 : break;
10693 344 : case DO_FK_CONSTRAINT:
10694 344 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
10695 344 : break;
10696 156 : case DO_PROCLANG:
10697 156 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
10698 156 : break;
10699 130 : case DO_CAST:
10700 130 : dumpCast(fout, (const CastInfo *) dobj);
10701 130 : break;
10702 80 : case DO_TRANSFORM:
10703 80 : dumpTransform(fout, (const TransformInfo *) dobj);
10704 80 : break;
10705 722 : case DO_SEQUENCE_SET:
10706 722 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
10707 722 : break;
10708 7024 : case DO_TABLE_DATA:
10709 7024 : dumpTableData(fout, (const TableDataInfo *) dobj);
10710 7024 : break;
10711 23092 : case DO_DUMMY_TYPE:
10712 : /* table rowtypes and array types are never dumped separately */
10713 23092 : break;
10714 74 : case DO_TSPARSER:
10715 74 : dumpTSParser(fout, (const TSParserInfo *) dobj);
10716 74 : break;
10717 220 : case DO_TSDICT:
10718 220 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
10719 220 : break;
10720 82 : case DO_TSTEMPLATE:
10721 82 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
10722 82 : break;
10723 170 : case DO_TSCONFIG:
10724 170 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
10725 170 : break;
10726 100 : case DO_FDW:
10727 100 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
10728 100 : break;
10729 108 : case DO_FOREIGN_SERVER:
10730 108 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
10731 108 : break;
10732 284 : case DO_DEFAULT_ACL:
10733 284 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
10734 284 : break;
10735 146 : case DO_LARGE_OBJECT:
10736 146 : dumpLO(fout, (const LoInfo *) dobj);
10737 146 : break;
10738 146 : case DO_LARGE_OBJECT_DATA:
10739 146 : if (dobj->dump & DUMP_COMPONENT_DATA)
10740 : {
10741 : LoInfo *loinfo;
10742 : TocEntry *te;
10743 :
10744 146 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
10745 146 : if (loinfo == NULL)
10746 0 : pg_fatal("missing metadata for large objects \"%s\"",
10747 : dobj->name);
10748 :
10749 146 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
10750 146 : ARCHIVE_OPTS(.tag = dobj->name,
10751 : .owner = loinfo->rolname,
10752 : .description = "BLOBS",
10753 : .section = SECTION_DATA,
10754 : .deps = dobj->dependencies,
10755 : .nDeps = dobj->nDeps,
10756 : .dumpFn = dumpLOs,
10757 : .dumpArg = loinfo));
10758 :
10759 : /*
10760 : * Set the TocEntry's dataLength in case we are doing a
10761 : * parallel dump and want to order dump jobs by table size.
10762 : * (We need some size estimate for every TocEntry with a
10763 : * DataDumper function.) We don't currently have any cheap
10764 : * way to estimate the size of LOs, but fortunately it doesn't
10765 : * matter too much as long as we get large batches of LOs
10766 : * processed reasonably early. Assume 8K per blob.
10767 : */
10768 146 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
10769 : }
10770 146 : break;
10771 638 : case DO_POLICY:
10772 638 : dumpPolicy(fout, (const PolicyInfo *) dobj);
10773 638 : break;
10774 282 : case DO_PUBLICATION:
10775 282 : dumpPublication(fout, (const PublicationInfo *) dobj);
10776 282 : break;
10777 470 : case DO_PUBLICATION_REL:
10778 470 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
10779 470 : break;
10780 138 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
10781 138 : dumpPublicationNamespace(fout,
10782 : (const PublicationSchemaInfo *) dobj);
10783 138 : break;
10784 214 : case DO_SUBSCRIPTION:
10785 214 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
10786 214 : break;
10787 4 : case DO_SUBSCRIPTION_REL:
10788 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
10789 4 : break;
10790 608 : case DO_PRE_DATA_BOUNDARY:
10791 : case DO_POST_DATA_BOUNDARY:
10792 : /* never dumped, nothing to do */
10793 608 : break;
10794 : }
10795 : }
10796 :
10797 : /*
10798 : * dumpNamespace
10799 : * writes out to fout the queries to recreate a user-defined namespace
10800 : */
10801 : static void
10802 770 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
10803 : {
10804 770 : DumpOptions *dopt = fout->dopt;
10805 : PQExpBuffer q;
10806 : PQExpBuffer delq;
10807 : char *qnspname;
10808 :
10809 : /* Do nothing in data-only dump */
10810 770 : if (dopt->dataOnly)
10811 32 : return;
10812 :
10813 738 : q = createPQExpBuffer();
10814 738 : delq = createPQExpBuffer();
10815 :
10816 738 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
10817 :
10818 738 : if (nspinfo->create)
10819 : {
10820 494 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
10821 494 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
10822 : }
10823 : else
10824 : {
10825 : /* see selectDumpableNamespace() */
10826 244 : appendPQExpBufferStr(delq,
10827 : "-- *not* dropping schema, since initdb creates it\n");
10828 244 : appendPQExpBufferStr(q,
10829 : "-- *not* creating schema, since initdb creates it\n");
10830 : }
10831 :
10832 738 : if (dopt->binary_upgrade)
10833 80 : binary_upgrade_extension_member(q, &nspinfo->dobj,
10834 : "SCHEMA", qnspname, NULL);
10835 :
10836 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10837 312 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
10838 312 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
10839 : .owner = nspinfo->rolname,
10840 : .description = "SCHEMA",
10841 : .section = SECTION_PRE_DATA,
10842 : .createStmt = q->data,
10843 : .dropStmt = delq->data));
10844 :
10845 : /* Dump Schema Comments and Security Labels */
10846 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10847 : {
10848 250 : const char *initdb_comment = NULL;
10849 :
10850 250 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
10851 220 : initdb_comment = "standard public schema";
10852 250 : dumpCommentExtended(fout, "SCHEMA", qnspname,
10853 : NULL, nspinfo->rolname,
10854 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
10855 : initdb_comment);
10856 : }
10857 :
10858 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10859 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
10860 : NULL, nspinfo->rolname,
10861 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
10862 :
10863 738 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
10864 572 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
10865 : qnspname, NULL, NULL,
10866 : NULL, nspinfo->rolname, &nspinfo->dacl);
10867 :
10868 738 : free(qnspname);
10869 :
10870 738 : destroyPQExpBuffer(q);
10871 738 : destroyPQExpBuffer(delq);
10872 : }
10873 :
10874 : /*
10875 : * dumpExtension
10876 : * writes out to fout the queries to recreate an extension
10877 : */
10878 : static void
10879 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
10880 : {
10881 38 : DumpOptions *dopt = fout->dopt;
10882 : PQExpBuffer q;
10883 : PQExpBuffer delq;
10884 : char *qextname;
10885 :
10886 : /* Do nothing in data-only dump */
10887 38 : if (dopt->dataOnly)
10888 2 : return;
10889 :
10890 36 : q = createPQExpBuffer();
10891 36 : delq = createPQExpBuffer();
10892 :
10893 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
10894 :
10895 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
10896 :
10897 36 : if (!dopt->binary_upgrade)
10898 : {
10899 : /*
10900 : * In a regular dump, we simply create the extension, intentionally
10901 : * not specifying a version, so that the destination installation's
10902 : * default version is used.
10903 : *
10904 : * Use of IF NOT EXISTS here is unlike our behavior for other object
10905 : * types; but there are various scenarios in which it's convenient to
10906 : * manually create the desired extension before restoring, so we
10907 : * prefer to allow it to exist already.
10908 : */
10909 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
10910 34 : qextname, fmtId(extinfo->namespace));
10911 : }
10912 : else
10913 : {
10914 : /*
10915 : * In binary-upgrade mode, it's critical to reproduce the state of the
10916 : * database exactly, so our procedure is to create an empty extension,
10917 : * restore all the contained objects normally, and add them to the
10918 : * extension one by one. This function performs just the first of
10919 : * those steps. binary_upgrade_extension_member() takes care of
10920 : * adding member objects as they're created.
10921 : */
10922 : int i;
10923 : int n;
10924 :
10925 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
10926 :
10927 : /*
10928 : * We unconditionally create the extension, so we must drop it if it
10929 : * exists. This could happen if the user deleted 'plpgsql' and then
10930 : * readded it, causing its oid to be greater than g_last_builtin_oid.
10931 : */
10932 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
10933 :
10934 2 : appendPQExpBufferStr(q,
10935 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
10936 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
10937 2 : appendPQExpBufferStr(q, ", ");
10938 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
10939 2 : appendPQExpBufferStr(q, ", ");
10940 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
10941 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
10942 2 : appendPQExpBufferStr(q, ", ");
10943 :
10944 : /*
10945 : * Note that we're pushing extconfig (an OID array) back into
10946 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
10947 : * preserved in binary upgrade.
10948 : */
10949 2 : if (strlen(extinfo->extconfig) > 2)
10950 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
10951 : else
10952 0 : appendPQExpBufferStr(q, "NULL");
10953 2 : appendPQExpBufferStr(q, ", ");
10954 2 : if (strlen(extinfo->extcondition) > 2)
10955 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
10956 : else
10957 0 : appendPQExpBufferStr(q, "NULL");
10958 2 : appendPQExpBufferStr(q, ", ");
10959 2 : appendPQExpBufferStr(q, "ARRAY[");
10960 2 : n = 0;
10961 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
10962 : {
10963 : DumpableObject *extobj;
10964 :
10965 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
10966 2 : if (extobj && extobj->objType == DO_EXTENSION)
10967 : {
10968 0 : if (n++ > 0)
10969 0 : appendPQExpBufferChar(q, ',');
10970 0 : appendStringLiteralAH(q, extobj->name, fout);
10971 : }
10972 : }
10973 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
10974 2 : appendPQExpBufferStr(q, ");\n");
10975 : }
10976 :
10977 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
10978 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
10979 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
10980 : .description = "EXTENSION",
10981 : .section = SECTION_PRE_DATA,
10982 : .createStmt = q->data,
10983 : .dropStmt = delq->data));
10984 :
10985 : /* Dump Extension Comments and Security Labels */
10986 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
10987 36 : dumpComment(fout, "EXTENSION", qextname,
10988 : NULL, "",
10989 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10990 :
10991 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
10992 0 : dumpSecLabel(fout, "EXTENSION", qextname,
10993 : NULL, "",
10994 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
10995 :
10996 36 : free(qextname);
10997 :
10998 36 : destroyPQExpBuffer(q);
10999 36 : destroyPQExpBuffer(delq);
11000 : }
11001 :
11002 : /*
11003 : * dumpType
11004 : * writes out to fout the queries to recreate a user-defined type
11005 : */
11006 : static void
11007 1258 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11008 : {
11009 1258 : DumpOptions *dopt = fout->dopt;
11010 :
11011 : /* Do nothing in data-only dump */
11012 1258 : if (dopt->dataOnly)
11013 44 : return;
11014 :
11015 : /* Dump out in proper style */
11016 1214 : if (tyinfo->typtype == TYPTYPE_BASE)
11017 276 : dumpBaseType(fout, tyinfo);
11018 938 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11019 256 : dumpDomain(fout, tyinfo);
11020 682 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11021 262 : dumpCompositeType(fout, tyinfo);
11022 420 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11023 110 : dumpEnumType(fout, tyinfo);
11024 310 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11025 184 : dumpRangeType(fout, tyinfo);
11026 126 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11027 76 : dumpUndefinedType(fout, tyinfo);
11028 : else
11029 50 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11030 : tyinfo->dobj.name);
11031 : }
11032 :
11033 : /*
11034 : * dumpEnumType
11035 : * writes out to fout the queries to recreate a user-defined enum type
11036 : */
11037 : static void
11038 110 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11039 : {
11040 110 : DumpOptions *dopt = fout->dopt;
11041 110 : PQExpBuffer q = createPQExpBuffer();
11042 110 : PQExpBuffer delq = createPQExpBuffer();
11043 110 : PQExpBuffer query = createPQExpBuffer();
11044 : PGresult *res;
11045 : int num,
11046 : i;
11047 : Oid enum_oid;
11048 : char *qtypname;
11049 : char *qualtypname;
11050 : char *label;
11051 : int i_enumlabel;
11052 : int i_oid;
11053 :
11054 110 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11055 : {
11056 : /* Set up query for enum-specific details */
11057 80 : appendPQExpBufferStr(query,
11058 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11059 : "SELECT oid, enumlabel "
11060 : "FROM pg_catalog.pg_enum "
11061 : "WHERE enumtypid = $1 "
11062 : "ORDER BY enumsortorder");
11063 :
11064 80 : ExecuteSqlStatement(fout, query->data);
11065 :
11066 80 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11067 : }
11068 :
11069 110 : printfPQExpBuffer(query,
11070 : "EXECUTE dumpEnumType('%u')",
11071 : tyinfo->dobj.catId.oid);
11072 :
11073 110 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11074 :
11075 110 : num = PQntuples(res);
11076 :
11077 110 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11078 110 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11079 :
11080 : /*
11081 : * CASCADE shouldn't be required here as for normal types since the I/O
11082 : * functions are generic and do not get dropped.
11083 : */
11084 110 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11085 :
11086 110 : if (dopt->binary_upgrade)
11087 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11088 : tyinfo->dobj.catId.oid,
11089 : false, false);
11090 :
11091 110 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
11092 : qualtypname);
11093 :
11094 110 : if (!dopt->binary_upgrade)
11095 : {
11096 100 : i_enumlabel = PQfnumber(res, "enumlabel");
11097 :
11098 : /* Labels with server-assigned oids */
11099 732 : for (i = 0; i < num; i++)
11100 : {
11101 632 : label = PQgetvalue(res, i, i_enumlabel);
11102 632 : if (i > 0)
11103 532 : appendPQExpBufferChar(q, ',');
11104 632 : appendPQExpBufferStr(q, "\n ");
11105 632 : appendStringLiteralAH(q, label, fout);
11106 : }
11107 : }
11108 :
11109 110 : appendPQExpBufferStr(q, "\n);\n");
11110 :
11111 110 : if (dopt->binary_upgrade)
11112 : {
11113 10 : i_oid = PQfnumber(res, "oid");
11114 10 : i_enumlabel = PQfnumber(res, "enumlabel");
11115 :
11116 : /* Labels with dump-assigned (preserved) oids */
11117 116 : for (i = 0; i < num; i++)
11118 : {
11119 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
11120 106 : label = PQgetvalue(res, i, i_enumlabel);
11121 :
11122 106 : if (i == 0)
11123 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
11124 106 : appendPQExpBuffer(q,
11125 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
11126 : enum_oid);
11127 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
11128 106 : appendStringLiteralAH(q, label, fout);
11129 106 : appendPQExpBufferStr(q, ";\n\n");
11130 : }
11131 : }
11132 :
11133 110 : if (dopt->binary_upgrade)
11134 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11135 : "TYPE", qtypname,
11136 10 : tyinfo->dobj.namespace->dobj.name);
11137 :
11138 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11139 110 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11140 110 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11141 : .namespace = tyinfo->dobj.namespace->dobj.name,
11142 : .owner = tyinfo->rolname,
11143 : .description = "TYPE",
11144 : .section = SECTION_PRE_DATA,
11145 : .createStmt = q->data,
11146 : .dropStmt = delq->data));
11147 :
11148 : /* Dump Type Comments and Security Labels */
11149 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11150 66 : dumpComment(fout, "TYPE", qtypname,
11151 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11152 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11153 :
11154 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11155 0 : dumpSecLabel(fout, "TYPE", qtypname,
11156 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11157 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11158 :
11159 110 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11160 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11161 : qtypname, NULL,
11162 66 : tyinfo->dobj.namespace->dobj.name,
11163 : NULL, tyinfo->rolname, &tyinfo->dacl);
11164 :
11165 110 : PQclear(res);
11166 110 : destroyPQExpBuffer(q);
11167 110 : destroyPQExpBuffer(delq);
11168 110 : destroyPQExpBuffer(query);
11169 110 : free(qtypname);
11170 110 : free(qualtypname);
11171 110 : }
11172 :
11173 : /*
11174 : * dumpRangeType
11175 : * writes out to fout the queries to recreate a user-defined range type
11176 : */
11177 : static void
11178 184 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
11179 : {
11180 184 : DumpOptions *dopt = fout->dopt;
11181 184 : PQExpBuffer q = createPQExpBuffer();
11182 184 : PQExpBuffer delq = createPQExpBuffer();
11183 184 : PQExpBuffer query = createPQExpBuffer();
11184 : PGresult *res;
11185 : Oid collationOid;
11186 : char *qtypname;
11187 : char *qualtypname;
11188 : char *procname;
11189 :
11190 184 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
11191 : {
11192 : /* Set up query for range-specific details */
11193 78 : appendPQExpBufferStr(query,
11194 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
11195 :
11196 78 : appendPQExpBufferStr(query,
11197 : "SELECT ");
11198 :
11199 78 : if (fout->remoteVersion >= 140000)
11200 78 : appendPQExpBufferStr(query,
11201 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
11202 : else
11203 0 : appendPQExpBufferStr(query,
11204 : "NULL AS rngmultitype, ");
11205 :
11206 78 : appendPQExpBufferStr(query,
11207 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
11208 : "opc.opcname AS opcname, "
11209 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
11210 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
11211 : "opc.opcdefault, "
11212 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
11213 : " ELSE rngcollation END AS collation, "
11214 : "rngcanonical, rngsubdiff "
11215 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
11216 : " pg_catalog.pg_opclass opc "
11217 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
11218 : "rngtypid = $1");
11219 :
11220 78 : ExecuteSqlStatement(fout, query->data);
11221 :
11222 78 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
11223 : }
11224 :
11225 184 : printfPQExpBuffer(query,
11226 : "EXECUTE dumpRangeType('%u')",
11227 : tyinfo->dobj.catId.oid);
11228 :
11229 184 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11230 :
11231 184 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11232 184 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11233 :
11234 : /*
11235 : * CASCADE shouldn't be required here as for normal types since the I/O
11236 : * functions are generic and do not get dropped.
11237 : */
11238 184 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11239 :
11240 184 : if (dopt->binary_upgrade)
11241 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11242 : tyinfo->dobj.catId.oid,
11243 : false, true);
11244 :
11245 184 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
11246 : qualtypname);
11247 :
11248 184 : appendPQExpBuffer(q, "\n subtype = %s",
11249 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
11250 :
11251 184 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
11252 184 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
11253 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
11254 :
11255 : /* print subtype_opclass only if not default for subtype */
11256 184 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
11257 : {
11258 66 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
11259 66 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
11260 :
11261 66 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
11262 : fmtId(nspname));
11263 66 : appendPQExpBufferStr(q, fmtId(opcname));
11264 : }
11265 :
11266 184 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
11267 184 : if (OidIsValid(collationOid))
11268 : {
11269 76 : CollInfo *coll = findCollationByOid(collationOid);
11270 :
11271 76 : if (coll)
11272 76 : appendPQExpBuffer(q, ",\n collation = %s",
11273 76 : fmtQualifiedDumpable(coll));
11274 : }
11275 :
11276 184 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
11277 184 : if (strcmp(procname, "-") != 0)
11278 6 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
11279 :
11280 184 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
11281 184 : if (strcmp(procname, "-") != 0)
11282 22 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
11283 :
11284 184 : appendPQExpBufferStr(q, "\n);\n");
11285 :
11286 184 : if (dopt->binary_upgrade)
11287 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11288 : "TYPE", qtypname,
11289 12 : tyinfo->dobj.namespace->dobj.name);
11290 :
11291 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11292 184 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11293 184 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11294 : .namespace = tyinfo->dobj.namespace->dobj.name,
11295 : .owner = tyinfo->rolname,
11296 : .description = "TYPE",
11297 : .section = SECTION_PRE_DATA,
11298 : .createStmt = q->data,
11299 : .dropStmt = delq->data));
11300 :
11301 : /* Dump Type Comments and Security Labels */
11302 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11303 78 : dumpComment(fout, "TYPE", qtypname,
11304 78 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11305 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11306 :
11307 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11308 0 : dumpSecLabel(fout, "TYPE", qtypname,
11309 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11310 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11311 :
11312 184 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11313 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11314 : qtypname, NULL,
11315 66 : tyinfo->dobj.namespace->dobj.name,
11316 : NULL, tyinfo->rolname, &tyinfo->dacl);
11317 :
11318 184 : PQclear(res);
11319 184 : destroyPQExpBuffer(q);
11320 184 : destroyPQExpBuffer(delq);
11321 184 : destroyPQExpBuffer(query);
11322 184 : free(qtypname);
11323 184 : free(qualtypname);
11324 184 : }
11325 :
11326 : /*
11327 : * dumpUndefinedType
11328 : * writes out to fout the queries to recreate a !typisdefined type
11329 : *
11330 : * This is a shell type, but we use different terminology to distinguish
11331 : * this case from where we have to emit a shell type definition to break
11332 : * circular dependencies. An undefined type shouldn't ever have anything
11333 : * depending on it.
11334 : */
11335 : static void
11336 76 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
11337 : {
11338 76 : DumpOptions *dopt = fout->dopt;
11339 76 : PQExpBuffer q = createPQExpBuffer();
11340 76 : PQExpBuffer delq = createPQExpBuffer();
11341 : char *qtypname;
11342 : char *qualtypname;
11343 :
11344 76 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11345 76 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11346 :
11347 76 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11348 :
11349 76 : if (dopt->binary_upgrade)
11350 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11351 : tyinfo->dobj.catId.oid,
11352 : false, false);
11353 :
11354 76 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
11355 : qualtypname);
11356 :
11357 76 : if (dopt->binary_upgrade)
11358 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11359 : "TYPE", qtypname,
11360 4 : tyinfo->dobj.namespace->dobj.name);
11361 :
11362 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11363 76 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11364 76 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11365 : .namespace = tyinfo->dobj.namespace->dobj.name,
11366 : .owner = tyinfo->rolname,
11367 : .description = "TYPE",
11368 : .section = SECTION_PRE_DATA,
11369 : .createStmt = q->data,
11370 : .dropStmt = delq->data));
11371 :
11372 : /* Dump Type Comments and Security Labels */
11373 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11374 66 : dumpComment(fout, "TYPE", qtypname,
11375 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11376 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11377 :
11378 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11379 0 : dumpSecLabel(fout, "TYPE", qtypname,
11380 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11381 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11382 :
11383 76 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11384 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11385 : qtypname, NULL,
11386 0 : tyinfo->dobj.namespace->dobj.name,
11387 : NULL, tyinfo->rolname, &tyinfo->dacl);
11388 :
11389 76 : destroyPQExpBuffer(q);
11390 76 : destroyPQExpBuffer(delq);
11391 76 : free(qtypname);
11392 76 : free(qualtypname);
11393 76 : }
11394 :
11395 : /*
11396 : * dumpBaseType
11397 : * writes out to fout the queries to recreate a user-defined base type
11398 : */
11399 : static void
11400 276 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
11401 : {
11402 276 : DumpOptions *dopt = fout->dopt;
11403 276 : PQExpBuffer q = createPQExpBuffer();
11404 276 : PQExpBuffer delq = createPQExpBuffer();
11405 276 : PQExpBuffer query = createPQExpBuffer();
11406 : PGresult *res;
11407 : char *qtypname;
11408 : char *qualtypname;
11409 : char *typlen;
11410 : char *typinput;
11411 : char *typoutput;
11412 : char *typreceive;
11413 : char *typsend;
11414 : char *typmodin;
11415 : char *typmodout;
11416 : char *typanalyze;
11417 : char *typsubscript;
11418 : Oid typreceiveoid;
11419 : Oid typsendoid;
11420 : Oid typmodinoid;
11421 : Oid typmodoutoid;
11422 : Oid typanalyzeoid;
11423 : Oid typsubscriptoid;
11424 : char *typcategory;
11425 : char *typispreferred;
11426 : char *typdelim;
11427 : char *typbyval;
11428 : char *typalign;
11429 : char *typstorage;
11430 : char *typcollatable;
11431 : char *typdefault;
11432 276 : bool typdefault_is_literal = false;
11433 :
11434 276 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
11435 : {
11436 : /* Set up query for type-specific details */
11437 78 : appendPQExpBufferStr(query,
11438 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
11439 : "SELECT typlen, "
11440 : "typinput, typoutput, typreceive, typsend, "
11441 : "typreceive::pg_catalog.oid AS typreceiveoid, "
11442 : "typsend::pg_catalog.oid AS typsendoid, "
11443 : "typanalyze, "
11444 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
11445 : "typdelim, typbyval, typalign, typstorage, "
11446 : "typmodin, typmodout, "
11447 : "typmodin::pg_catalog.oid AS typmodinoid, "
11448 : "typmodout::pg_catalog.oid AS typmodoutoid, "
11449 : "typcategory, typispreferred, "
11450 : "(typcollation <> 0) AS typcollatable, "
11451 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
11452 :
11453 78 : if (fout->remoteVersion >= 140000)
11454 78 : appendPQExpBufferStr(query,
11455 : "typsubscript, "
11456 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
11457 : else
11458 0 : appendPQExpBufferStr(query,
11459 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
11460 :
11461 78 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
11462 : "WHERE oid = $1");
11463 :
11464 78 : ExecuteSqlStatement(fout, query->data);
11465 :
11466 78 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
11467 : }
11468 :
11469 276 : printfPQExpBuffer(query,
11470 : "EXECUTE dumpBaseType('%u')",
11471 : tyinfo->dobj.catId.oid);
11472 :
11473 276 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11474 :
11475 276 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
11476 276 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
11477 276 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
11478 276 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
11479 276 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
11480 276 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
11481 276 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
11482 276 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
11483 276 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
11484 276 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
11485 276 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
11486 276 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
11487 276 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
11488 276 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
11489 276 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
11490 276 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
11491 276 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
11492 276 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
11493 276 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
11494 276 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
11495 276 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
11496 276 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
11497 276 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11498 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11499 276 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11500 : {
11501 86 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11502 86 : typdefault_is_literal = true; /* it needs quotes */
11503 : }
11504 : else
11505 190 : typdefault = NULL;
11506 :
11507 276 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11508 276 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11509 :
11510 : /*
11511 : * The reason we include CASCADE is that the circular dependency between
11512 : * the type and its I/O functions makes it impossible to drop the type any
11513 : * other way.
11514 : */
11515 276 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
11516 :
11517 : /*
11518 : * We might already have a shell type, but setting pg_type_oid is
11519 : * harmless, and in any case we'd better set the array type OID.
11520 : */
11521 276 : if (dopt->binary_upgrade)
11522 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11523 : tyinfo->dobj.catId.oid,
11524 : false, false);
11525 :
11526 276 : appendPQExpBuffer(q,
11527 : "CREATE TYPE %s (\n"
11528 : " INTERNALLENGTH = %s",
11529 : qualtypname,
11530 276 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
11531 :
11532 : /* regproc result is sufficiently quoted already */
11533 276 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
11534 276 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
11535 276 : if (OidIsValid(typreceiveoid))
11536 136 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
11537 276 : if (OidIsValid(typsendoid))
11538 136 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
11539 276 : if (OidIsValid(typmodinoid))
11540 30 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
11541 276 : if (OidIsValid(typmodoutoid))
11542 30 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
11543 276 : if (OidIsValid(typanalyzeoid))
11544 2 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
11545 :
11546 276 : if (strcmp(typcollatable, "t") == 0)
11547 20 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
11548 :
11549 276 : if (typdefault != NULL)
11550 : {
11551 86 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
11552 86 : if (typdefault_is_literal)
11553 86 : appendStringLiteralAH(q, typdefault, fout);
11554 : else
11555 0 : appendPQExpBufferStr(q, typdefault);
11556 : }
11557 :
11558 276 : if (OidIsValid(typsubscriptoid))
11559 26 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
11560 :
11561 276 : if (OidIsValid(tyinfo->typelem))
11562 24 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
11563 : getFormattedTypeName(fout, tyinfo->typelem,
11564 : zeroIsError));
11565 :
11566 276 : if (strcmp(typcategory, "U") != 0)
11567 : {
11568 110 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
11569 110 : appendStringLiteralAH(q, typcategory, fout);
11570 : }
11571 :
11572 276 : if (strcmp(typispreferred, "t") == 0)
11573 26 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
11574 :
11575 276 : if (typdelim && strcmp(typdelim, ",") != 0)
11576 : {
11577 2 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
11578 2 : appendStringLiteralAH(q, typdelim, fout);
11579 : }
11580 :
11581 276 : if (*typalign == TYPALIGN_CHAR)
11582 8 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
11583 268 : else if (*typalign == TYPALIGN_SHORT)
11584 4 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
11585 264 : else if (*typalign == TYPALIGN_INT)
11586 194 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
11587 70 : else if (*typalign == TYPALIGN_DOUBLE)
11588 70 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
11589 :
11590 276 : if (*typstorage == TYPSTORAGE_PLAIN)
11591 226 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
11592 50 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
11593 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
11594 50 : else if (*typstorage == TYPSTORAGE_EXTENDED)
11595 44 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
11596 6 : else if (*typstorage == TYPSTORAGE_MAIN)
11597 6 : appendPQExpBufferStr(q, ",\n STORAGE = main");
11598 :
11599 276 : if (strcmp(typbyval, "t") == 0)
11600 152 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
11601 :
11602 276 : appendPQExpBufferStr(q, "\n);\n");
11603 :
11604 276 : if (dopt->binary_upgrade)
11605 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11606 : "TYPE", qtypname,
11607 16 : tyinfo->dobj.namespace->dobj.name);
11608 :
11609 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11610 276 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11611 276 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11612 : .namespace = tyinfo->dobj.namespace->dobj.name,
11613 : .owner = tyinfo->rolname,
11614 : .description = "TYPE",
11615 : .section = SECTION_PRE_DATA,
11616 : .createStmt = q->data,
11617 : .dropStmt = delq->data));
11618 :
11619 : /* Dump Type Comments and Security Labels */
11620 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11621 206 : dumpComment(fout, "TYPE", qtypname,
11622 206 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11623 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11624 :
11625 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11626 0 : dumpSecLabel(fout, "TYPE", qtypname,
11627 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11628 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11629 :
11630 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11631 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11632 : qtypname, NULL,
11633 66 : tyinfo->dobj.namespace->dobj.name,
11634 : NULL, tyinfo->rolname, &tyinfo->dacl);
11635 :
11636 276 : PQclear(res);
11637 276 : destroyPQExpBuffer(q);
11638 276 : destroyPQExpBuffer(delq);
11639 276 : destroyPQExpBuffer(query);
11640 276 : free(qtypname);
11641 276 : free(qualtypname);
11642 276 : }
11643 :
11644 : /*
11645 : * dumpDomain
11646 : * writes out to fout the queries to recreate a user-defined domain
11647 : */
11648 : static void
11649 256 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
11650 : {
11651 256 : DumpOptions *dopt = fout->dopt;
11652 256 : PQExpBuffer q = createPQExpBuffer();
11653 256 : PQExpBuffer delq = createPQExpBuffer();
11654 256 : PQExpBuffer query = createPQExpBuffer();
11655 : PGresult *res;
11656 : int i;
11657 : char *qtypname;
11658 : char *qualtypname;
11659 : char *typnotnull;
11660 : char *typdefn;
11661 : char *typdefault;
11662 : Oid typcollation;
11663 256 : bool typdefault_is_literal = false;
11664 :
11665 256 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
11666 : {
11667 : /* Set up query for domain-specific details */
11668 76 : appendPQExpBufferStr(query,
11669 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
11670 :
11671 76 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
11672 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
11673 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
11674 : "t.typdefault, "
11675 : "CASE WHEN t.typcollation <> u.typcollation "
11676 : "THEN t.typcollation ELSE 0 END AS typcollation "
11677 : "FROM pg_catalog.pg_type t "
11678 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
11679 : "WHERE t.oid = $1");
11680 :
11681 76 : ExecuteSqlStatement(fout, query->data);
11682 :
11683 76 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
11684 : }
11685 :
11686 256 : printfPQExpBuffer(query,
11687 : "EXECUTE dumpDomain('%u')",
11688 : tyinfo->dobj.catId.oid);
11689 :
11690 256 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
11691 :
11692 256 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
11693 256 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
11694 256 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
11695 76 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
11696 180 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
11697 : {
11698 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
11699 0 : typdefault_is_literal = true; /* it needs quotes */
11700 : }
11701 : else
11702 180 : typdefault = NULL;
11703 256 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
11704 :
11705 256 : if (dopt->binary_upgrade)
11706 40 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11707 : tyinfo->dobj.catId.oid,
11708 : true, /* force array type */
11709 : false); /* force multirange type */
11710 :
11711 256 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11712 256 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11713 :
11714 256 : appendPQExpBuffer(q,
11715 : "CREATE DOMAIN %s AS %s",
11716 : qualtypname,
11717 : typdefn);
11718 :
11719 : /* Print collation only if different from base type's collation */
11720 256 : if (OidIsValid(typcollation))
11721 : {
11722 : CollInfo *coll;
11723 :
11724 66 : coll = findCollationByOid(typcollation);
11725 66 : if (coll)
11726 66 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
11727 : }
11728 :
11729 256 : if (typnotnull[0] == 't')
11730 30 : appendPQExpBufferStr(q, " NOT NULL");
11731 :
11732 256 : if (typdefault != NULL)
11733 : {
11734 76 : appendPQExpBufferStr(q, " DEFAULT ");
11735 76 : if (typdefault_is_literal)
11736 0 : appendStringLiteralAH(q, typdefault, fout);
11737 : else
11738 76 : appendPQExpBufferStr(q, typdefault);
11739 : }
11740 :
11741 256 : PQclear(res);
11742 :
11743 : /*
11744 : * Add any CHECK constraints for the domain
11745 : */
11746 422 : for (i = 0; i < tyinfo->nDomChecks; i++)
11747 : {
11748 166 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11749 :
11750 166 : if (!domcheck->separate)
11751 166 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
11752 166 : fmtId(domcheck->dobj.name), domcheck->condef);
11753 : }
11754 :
11755 256 : appendPQExpBufferStr(q, ";\n");
11756 :
11757 256 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
11758 :
11759 256 : if (dopt->binary_upgrade)
11760 40 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11761 : "DOMAIN", qtypname,
11762 40 : tyinfo->dobj.namespace->dobj.name);
11763 :
11764 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11765 256 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11766 256 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11767 : .namespace = tyinfo->dobj.namespace->dobj.name,
11768 : .owner = tyinfo->rolname,
11769 : .description = "DOMAIN",
11770 : .section = SECTION_PRE_DATA,
11771 : .createStmt = q->data,
11772 : .dropStmt = delq->data));
11773 :
11774 : /* Dump Domain Comments and Security Labels */
11775 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11776 0 : dumpComment(fout, "DOMAIN", qtypname,
11777 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11778 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11779 :
11780 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11781 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
11782 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11783 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11784 :
11785 256 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
11786 66 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
11787 : qtypname, NULL,
11788 66 : tyinfo->dobj.namespace->dobj.name,
11789 : NULL, tyinfo->rolname, &tyinfo->dacl);
11790 :
11791 : /* Dump any per-constraint comments */
11792 422 : for (i = 0; i < tyinfo->nDomChecks; i++)
11793 : {
11794 166 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
11795 166 : PQExpBuffer conprefix = createPQExpBuffer();
11796 :
11797 166 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
11798 166 : fmtId(domcheck->dobj.name));
11799 :
11800 166 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
11801 66 : dumpComment(fout, conprefix->data, qtypname,
11802 66 : tyinfo->dobj.namespace->dobj.name,
11803 : tyinfo->rolname,
11804 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
11805 :
11806 166 : destroyPQExpBuffer(conprefix);
11807 : }
11808 :
11809 256 : destroyPQExpBuffer(q);
11810 256 : destroyPQExpBuffer(delq);
11811 256 : destroyPQExpBuffer(query);
11812 256 : free(qtypname);
11813 256 : free(qualtypname);
11814 256 : }
11815 :
11816 : /*
11817 : * dumpCompositeType
11818 : * writes out to fout the queries to recreate a user-defined stand-alone
11819 : * composite type
11820 : */
11821 : static void
11822 262 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
11823 : {
11824 262 : DumpOptions *dopt = fout->dopt;
11825 262 : PQExpBuffer q = createPQExpBuffer();
11826 262 : PQExpBuffer dropped = createPQExpBuffer();
11827 262 : PQExpBuffer delq = createPQExpBuffer();
11828 262 : PQExpBuffer query = createPQExpBuffer();
11829 : PGresult *res;
11830 : char *qtypname;
11831 : char *qualtypname;
11832 : int ntups;
11833 : int i_attname;
11834 : int i_atttypdefn;
11835 : int i_attlen;
11836 : int i_attalign;
11837 : int i_attisdropped;
11838 : int i_attcollation;
11839 : int i;
11840 : int actual_atts;
11841 :
11842 262 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
11843 : {
11844 : /*
11845 : * Set up query for type-specific details.
11846 : *
11847 : * Since we only want to dump COLLATE clauses for attributes whose
11848 : * collation is different from their type's default, we use a CASE
11849 : * here to suppress uninteresting attcollations cheaply. atttypid
11850 : * will be 0 for dropped columns; collation does not matter for those.
11851 : */
11852 112 : appendPQExpBufferStr(query,
11853 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
11854 : "SELECT a.attname, a.attnum, "
11855 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
11856 : "a.attlen, a.attalign, a.attisdropped, "
11857 : "CASE WHEN a.attcollation <> at.typcollation "
11858 : "THEN a.attcollation ELSE 0 END AS attcollation "
11859 : "FROM pg_catalog.pg_type ct "
11860 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
11861 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
11862 : "WHERE ct.oid = $1 "
11863 : "ORDER BY a.attnum");
11864 :
11865 112 : ExecuteSqlStatement(fout, query->data);
11866 :
11867 112 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
11868 : }
11869 :
11870 262 : printfPQExpBuffer(query,
11871 : "EXECUTE dumpCompositeType('%u')",
11872 : tyinfo->dobj.catId.oid);
11873 :
11874 262 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11875 :
11876 262 : ntups = PQntuples(res);
11877 :
11878 262 : i_attname = PQfnumber(res, "attname");
11879 262 : i_atttypdefn = PQfnumber(res, "atttypdefn");
11880 262 : i_attlen = PQfnumber(res, "attlen");
11881 262 : i_attalign = PQfnumber(res, "attalign");
11882 262 : i_attisdropped = PQfnumber(res, "attisdropped");
11883 262 : i_attcollation = PQfnumber(res, "attcollation");
11884 :
11885 262 : if (dopt->binary_upgrade)
11886 : {
11887 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11888 : tyinfo->dobj.catId.oid,
11889 : false, false);
11890 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
11891 : }
11892 :
11893 262 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11894 262 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11895 :
11896 262 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
11897 : qualtypname);
11898 :
11899 262 : actual_atts = 0;
11900 830 : for (i = 0; i < ntups; i++)
11901 : {
11902 : char *attname;
11903 : char *atttypdefn;
11904 : char *attlen;
11905 : char *attalign;
11906 : bool attisdropped;
11907 : Oid attcollation;
11908 :
11909 568 : attname = PQgetvalue(res, i, i_attname);
11910 568 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
11911 568 : attlen = PQgetvalue(res, i, i_attlen);
11912 568 : attalign = PQgetvalue(res, i, i_attalign);
11913 568 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
11914 568 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
11915 :
11916 568 : if (attisdropped && !dopt->binary_upgrade)
11917 16 : continue;
11918 :
11919 : /* Format properly if not first attr */
11920 552 : if (actual_atts++ > 0)
11921 290 : appendPQExpBufferChar(q, ',');
11922 552 : appendPQExpBufferStr(q, "\n\t");
11923 :
11924 552 : if (!attisdropped)
11925 : {
11926 548 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
11927 :
11928 : /* Add collation if not default for the column type */
11929 548 : if (OidIsValid(attcollation))
11930 : {
11931 : CollInfo *coll;
11932 :
11933 0 : coll = findCollationByOid(attcollation);
11934 0 : if (coll)
11935 0 : appendPQExpBuffer(q, " COLLATE %s",
11936 0 : fmtQualifiedDumpable(coll));
11937 : }
11938 : }
11939 : else
11940 : {
11941 : /*
11942 : * This is a dropped attribute and we're in binary_upgrade mode.
11943 : * Insert a placeholder for it in the CREATE TYPE command, and set
11944 : * length and alignment with direct UPDATE to the catalogs
11945 : * afterwards. See similar code in dumpTableSchema().
11946 : */
11947 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
11948 :
11949 : /* stash separately for insertion after the CREATE TYPE */
11950 4 : appendPQExpBufferStr(dropped,
11951 : "\n-- For binary upgrade, recreate dropped column.\n");
11952 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
11953 : "SET attlen = %s, "
11954 : "attalign = '%s', attbyval = false\n"
11955 : "WHERE attname = ", attlen, attalign);
11956 4 : appendStringLiteralAH(dropped, attname, fout);
11957 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
11958 4 : appendStringLiteralAH(dropped, qualtypname, fout);
11959 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
11960 :
11961 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
11962 : qualtypname);
11963 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
11964 : fmtId(attname));
11965 : }
11966 : }
11967 262 : appendPQExpBufferStr(q, "\n);\n");
11968 262 : appendPQExpBufferStr(q, dropped->data);
11969 :
11970 262 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11971 :
11972 262 : if (dopt->binary_upgrade)
11973 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
11974 : "TYPE", qtypname,
11975 36 : tyinfo->dobj.namespace->dobj.name);
11976 :
11977 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11978 228 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
11979 228 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
11980 : .namespace = tyinfo->dobj.namespace->dobj.name,
11981 : .owner = tyinfo->rolname,
11982 : .description = "TYPE",
11983 : .section = SECTION_PRE_DATA,
11984 : .createStmt = q->data,
11985 : .dropStmt = delq->data));
11986 :
11987 :
11988 : /* Dump Type Comments and Security Labels */
11989 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11990 66 : dumpComment(fout, "TYPE", qtypname,
11991 66 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11992 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11993 :
11994 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11995 0 : dumpSecLabel(fout, "TYPE", qtypname,
11996 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
11997 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
11998 :
11999 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12000 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12001 : qtypname, NULL,
12002 36 : tyinfo->dobj.namespace->dobj.name,
12003 : NULL, tyinfo->rolname, &tyinfo->dacl);
12004 :
12005 : /* Dump any per-column comments */
12006 262 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12007 66 : dumpCompositeTypeColComments(fout, tyinfo, res);
12008 :
12009 262 : PQclear(res);
12010 262 : destroyPQExpBuffer(q);
12011 262 : destroyPQExpBuffer(dropped);
12012 262 : destroyPQExpBuffer(delq);
12013 262 : destroyPQExpBuffer(query);
12014 262 : free(qtypname);
12015 262 : free(qualtypname);
12016 262 : }
12017 :
12018 : /*
12019 : * dumpCompositeTypeColComments
12020 : * writes out to fout the queries to recreate comments on the columns of
12021 : * a user-defined stand-alone composite type.
12022 : *
12023 : * The caller has already made a query to collect the names and attnums
12024 : * of the type's columns, so we just pass that result into here rather
12025 : * than reading them again.
12026 : */
12027 : static void
12028 66 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
12029 : PGresult *res)
12030 : {
12031 : CommentItem *comments;
12032 : int ncomments;
12033 : PQExpBuffer query;
12034 : PQExpBuffer target;
12035 : int i;
12036 : int ntups;
12037 : int i_attname;
12038 : int i_attnum;
12039 : int i_attisdropped;
12040 :
12041 : /* do nothing, if --no-comments is supplied */
12042 66 : if (fout->dopt->no_comments)
12043 0 : return;
12044 :
12045 : /* Search for comments associated with type's pg_class OID */
12046 66 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
12047 : &comments);
12048 :
12049 : /* If no comments exist, we're done */
12050 66 : if (ncomments <= 0)
12051 0 : return;
12052 :
12053 : /* Build COMMENT ON statements */
12054 66 : query = createPQExpBuffer();
12055 66 : target = createPQExpBuffer();
12056 :
12057 66 : ntups = PQntuples(res);
12058 66 : i_attnum = PQfnumber(res, "attnum");
12059 66 : i_attname = PQfnumber(res, "attname");
12060 66 : i_attisdropped = PQfnumber(res, "attisdropped");
12061 132 : while (ncomments > 0)
12062 : {
12063 : const char *attname;
12064 :
12065 66 : attname = NULL;
12066 66 : for (i = 0; i < ntups; i++)
12067 : {
12068 66 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
12069 66 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
12070 : {
12071 66 : attname = PQgetvalue(res, i, i_attname);
12072 66 : break;
12073 : }
12074 : }
12075 66 : if (attname) /* just in case we don't find it */
12076 : {
12077 66 : const char *descr = comments->descr;
12078 :
12079 66 : resetPQExpBuffer(target);
12080 66 : appendPQExpBuffer(target, "COLUMN %s.",
12081 66 : fmtId(tyinfo->dobj.name));
12082 66 : appendPQExpBufferStr(target, fmtId(attname));
12083 :
12084 66 : resetPQExpBuffer(query);
12085 66 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
12086 66 : fmtQualifiedDumpable(tyinfo));
12087 66 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
12088 66 : appendStringLiteralAH(query, descr, fout);
12089 66 : appendPQExpBufferStr(query, ";\n");
12090 :
12091 66 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
12092 66 : ARCHIVE_OPTS(.tag = target->data,
12093 : .namespace = tyinfo->dobj.namespace->dobj.name,
12094 : .owner = tyinfo->rolname,
12095 : .description = "COMMENT",
12096 : .section = SECTION_NONE,
12097 : .createStmt = query->data,
12098 : .deps = &(tyinfo->dobj.dumpId),
12099 : .nDeps = 1));
12100 : }
12101 :
12102 66 : comments++;
12103 66 : ncomments--;
12104 : }
12105 :
12106 66 : destroyPQExpBuffer(query);
12107 66 : destroyPQExpBuffer(target);
12108 : }
12109 :
12110 : /*
12111 : * dumpShellType
12112 : * writes out to fout the queries to create a shell type
12113 : *
12114 : * We dump a shell definition in advance of the I/O functions for the type.
12115 : */
12116 : static void
12117 142 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
12118 : {
12119 142 : DumpOptions *dopt = fout->dopt;
12120 : PQExpBuffer q;
12121 :
12122 : /* Do nothing in data-only dump */
12123 142 : if (dopt->dataOnly)
12124 6 : return;
12125 :
12126 136 : q = createPQExpBuffer();
12127 :
12128 : /*
12129 : * Note the lack of a DROP command for the shell type; any required DROP
12130 : * is driven off the base type entry, instead. This interacts with
12131 : * _printTocEntry()'s use of the presence of a DROP command to decide
12132 : * whether an entry needs an ALTER OWNER command. We don't want to alter
12133 : * the shell type's owner immediately on creation; that should happen only
12134 : * after it's filled in, otherwise the backend complains.
12135 : */
12136 :
12137 136 : if (dopt->binary_upgrade)
12138 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12139 16 : stinfo->baseType->dobj.catId.oid,
12140 : false, false);
12141 :
12142 136 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12143 136 : fmtQualifiedDumpable(stinfo));
12144 :
12145 136 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12146 136 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
12147 136 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
12148 : .namespace = stinfo->dobj.namespace->dobj.name,
12149 : .owner = stinfo->baseType->rolname,
12150 : .description = "SHELL TYPE",
12151 : .section = SECTION_PRE_DATA,
12152 : .createStmt = q->data));
12153 :
12154 136 : destroyPQExpBuffer(q);
12155 : }
12156 :
12157 : /*
12158 : * dumpProcLang
12159 : * writes out to fout the queries to recreate a user-defined
12160 : * procedural language
12161 : */
12162 : static void
12163 156 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
12164 : {
12165 156 : DumpOptions *dopt = fout->dopt;
12166 : PQExpBuffer defqry;
12167 : PQExpBuffer delqry;
12168 : bool useParams;
12169 : char *qlanname;
12170 : FuncInfo *funcInfo;
12171 156 : FuncInfo *inlineInfo = NULL;
12172 156 : FuncInfo *validatorInfo = NULL;
12173 :
12174 : /* Do nothing in data-only dump */
12175 156 : if (dopt->dataOnly)
12176 14 : return;
12177 :
12178 : /*
12179 : * Try to find the support function(s). It is not an error if we don't
12180 : * find them --- if the functions are in the pg_catalog schema, as is
12181 : * standard in 8.1 and up, then we won't have loaded them. (In this case
12182 : * we will emit a parameterless CREATE LANGUAGE command, which will
12183 : * require PL template knowledge in the backend to reload.)
12184 : */
12185 :
12186 142 : funcInfo = findFuncByOid(plang->lanplcallfoid);
12187 142 : if (funcInfo != NULL && !funcInfo->dobj.dump)
12188 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
12189 :
12190 142 : if (OidIsValid(plang->laninline))
12191 : {
12192 78 : inlineInfo = findFuncByOid(plang->laninline);
12193 78 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
12194 2 : inlineInfo = NULL;
12195 : }
12196 :
12197 142 : if (OidIsValid(plang->lanvalidator))
12198 : {
12199 78 : validatorInfo = findFuncByOid(plang->lanvalidator);
12200 78 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
12201 2 : validatorInfo = NULL;
12202 : }
12203 :
12204 : /*
12205 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
12206 : * parameters. Otherwise, we'll write a parameterless command, which will
12207 : * be interpreted as CREATE EXTENSION.
12208 : */
12209 62 : useParams = (funcInfo != NULL &&
12210 266 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
12211 62 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
12212 :
12213 142 : defqry = createPQExpBuffer();
12214 142 : delqry = createPQExpBuffer();
12215 :
12216 142 : qlanname = pg_strdup(fmtId(plang->dobj.name));
12217 :
12218 142 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
12219 : qlanname);
12220 :
12221 142 : if (useParams)
12222 : {
12223 62 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
12224 62 : plang->lanpltrusted ? "TRUSTED " : "",
12225 : qlanname);
12226 62 : appendPQExpBuffer(defqry, " HANDLER %s",
12227 62 : fmtQualifiedDumpable(funcInfo));
12228 62 : if (OidIsValid(plang->laninline))
12229 0 : appendPQExpBuffer(defqry, " INLINE %s",
12230 0 : fmtQualifiedDumpable(inlineInfo));
12231 62 : if (OidIsValid(plang->lanvalidator))
12232 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
12233 0 : fmtQualifiedDumpable(validatorInfo));
12234 : }
12235 : else
12236 : {
12237 : /*
12238 : * If not dumping parameters, then use CREATE OR REPLACE so that the
12239 : * command will not fail if the language is preinstalled in the target
12240 : * database.
12241 : *
12242 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
12243 : * EXISTS; perhaps we should emit that instead? But it might just add
12244 : * confusion.
12245 : */
12246 80 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
12247 : qlanname);
12248 : }
12249 142 : appendPQExpBufferStr(defqry, ";\n");
12250 :
12251 142 : if (dopt->binary_upgrade)
12252 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
12253 : "LANGUAGE", qlanname, NULL);
12254 :
12255 142 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
12256 64 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
12257 64 : ARCHIVE_OPTS(.tag = plang->dobj.name,
12258 : .owner = plang->lanowner,
12259 : .description = "PROCEDURAL LANGUAGE",
12260 : .section = SECTION_PRE_DATA,
12261 : .createStmt = defqry->data,
12262 : .dropStmt = delqry->data,
12263 : ));
12264 :
12265 : /* Dump Proc Lang Comments and Security Labels */
12266 142 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
12267 0 : dumpComment(fout, "LANGUAGE", qlanname,
12268 : NULL, plang->lanowner,
12269 : plang->dobj.catId, 0, plang->dobj.dumpId);
12270 :
12271 142 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
12272 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
12273 : NULL, plang->lanowner,
12274 : plang->dobj.catId, 0, plang->dobj.dumpId);
12275 :
12276 142 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
12277 78 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
12278 : qlanname, NULL, NULL,
12279 : NULL, plang->lanowner, &plang->dacl);
12280 :
12281 142 : free(qlanname);
12282 :
12283 142 : destroyPQExpBuffer(defqry);
12284 142 : destroyPQExpBuffer(delqry);
12285 : }
12286 :
12287 : /*
12288 : * format_function_arguments: generate function name and argument list
12289 : *
12290 : * This is used when we can rely on pg_get_function_arguments to format
12291 : * the argument list. Note, however, that pg_get_function_arguments
12292 : * does not special-case zero-argument aggregates.
12293 : */
12294 : static char *
12295 8052 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
12296 : {
12297 : PQExpBufferData fn;
12298 :
12299 8052 : initPQExpBuffer(&fn);
12300 8052 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
12301 8052 : if (is_agg && finfo->nargs == 0)
12302 160 : appendPQExpBufferStr(&fn, "(*)");
12303 : else
12304 7892 : appendPQExpBuffer(&fn, "(%s)", funcargs);
12305 8052 : return fn.data;
12306 : }
12307 :
12308 : /*
12309 : * format_function_signature: generate function name and argument list
12310 : *
12311 : * Only a minimal list of input argument types is generated; this is
12312 : * sufficient to reference the function, but not to define it.
12313 : *
12314 : * If honor_quotes is false then the function name is never quoted.
12315 : * This is appropriate for use in TOC tags, but not in SQL commands.
12316 : */
12317 : static char *
12318 4248 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
12319 : {
12320 : PQExpBufferData fn;
12321 : int j;
12322 :
12323 4248 : initPQExpBuffer(&fn);
12324 4248 : if (honor_quotes)
12325 794 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
12326 : else
12327 3454 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
12328 7808 : for (j = 0; j < finfo->nargs; j++)
12329 : {
12330 3560 : if (j > 0)
12331 834 : appendPQExpBufferStr(&fn, ", ");
12332 :
12333 3560 : appendPQExpBufferStr(&fn,
12334 3560 : getFormattedTypeName(fout, finfo->argtypes[j],
12335 : zeroIsError));
12336 : }
12337 4248 : appendPQExpBufferChar(&fn, ')');
12338 4248 : return fn.data;
12339 : }
12340 :
12341 :
12342 : /*
12343 : * dumpFunc:
12344 : * dump out one function
12345 : */
12346 : static void
12347 3518 : dumpFunc(Archive *fout, const FuncInfo *finfo)
12348 : {
12349 3518 : DumpOptions *dopt = fout->dopt;
12350 : PQExpBuffer query;
12351 : PQExpBuffer q;
12352 : PQExpBuffer delqry;
12353 : PQExpBuffer asPart;
12354 : PGresult *res;
12355 : char *funcsig; /* identity signature */
12356 3518 : char *funcfullsig = NULL; /* full signature */
12357 : char *funcsig_tag;
12358 : char *qual_funcsig;
12359 : char *proretset;
12360 : char *prosrc;
12361 : char *probin;
12362 : char *prosqlbody;
12363 : char *funcargs;
12364 : char *funciargs;
12365 : char *funcresult;
12366 : char *protrftypes;
12367 : char *prokind;
12368 : char *provolatile;
12369 : char *proisstrict;
12370 : char *prosecdef;
12371 : char *proleakproof;
12372 : char *proconfig;
12373 : char *procost;
12374 : char *prorows;
12375 : char *prosupport;
12376 : char *proparallel;
12377 : char *lanname;
12378 3518 : char **configitems = NULL;
12379 3518 : int nconfigitems = 0;
12380 : const char *keyword;
12381 :
12382 : /* Do nothing in data-only dump */
12383 3518 : if (dopt->dataOnly)
12384 64 : return;
12385 :
12386 3454 : query = createPQExpBuffer();
12387 3454 : q = createPQExpBuffer();
12388 3454 : delqry = createPQExpBuffer();
12389 3454 : asPart = createPQExpBuffer();
12390 :
12391 3454 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
12392 : {
12393 : /* Set up query for function-specific details */
12394 120 : appendPQExpBufferStr(query,
12395 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
12396 :
12397 120 : appendPQExpBufferStr(query,
12398 : "SELECT\n"
12399 : "proretset,\n"
12400 : "prosrc,\n"
12401 : "probin,\n"
12402 : "provolatile,\n"
12403 : "proisstrict,\n"
12404 : "prosecdef,\n"
12405 : "lanname,\n"
12406 : "proconfig,\n"
12407 : "procost,\n"
12408 : "prorows,\n"
12409 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
12410 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
12411 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
12412 : "proleakproof,\n");
12413 :
12414 120 : if (fout->remoteVersion >= 90500)
12415 120 : appendPQExpBufferStr(query,
12416 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
12417 : else
12418 0 : appendPQExpBufferStr(query,
12419 : "NULL AS protrftypes,\n");
12420 :
12421 120 : if (fout->remoteVersion >= 90600)
12422 120 : appendPQExpBufferStr(query,
12423 : "proparallel,\n");
12424 : else
12425 0 : appendPQExpBufferStr(query,
12426 : "'u' AS proparallel,\n");
12427 :
12428 120 : if (fout->remoteVersion >= 110000)
12429 120 : appendPQExpBufferStr(query,
12430 : "prokind,\n");
12431 : else
12432 0 : appendPQExpBufferStr(query,
12433 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
12434 :
12435 120 : if (fout->remoteVersion >= 120000)
12436 120 : appendPQExpBufferStr(query,
12437 : "prosupport,\n");
12438 : else
12439 0 : appendPQExpBufferStr(query,
12440 : "'-' AS prosupport,\n");
12441 :
12442 120 : if (fout->remoteVersion >= 140000)
12443 120 : appendPQExpBufferStr(query,
12444 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
12445 : else
12446 0 : appendPQExpBufferStr(query,
12447 : "NULL AS prosqlbody\n");
12448 :
12449 120 : appendPQExpBufferStr(query,
12450 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
12451 : "WHERE p.oid = $1 "
12452 : "AND l.oid = p.prolang");
12453 :
12454 120 : ExecuteSqlStatement(fout, query->data);
12455 :
12456 120 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
12457 : }
12458 :
12459 3454 : printfPQExpBuffer(query,
12460 : "EXECUTE dumpFunc('%u')",
12461 : finfo->dobj.catId.oid);
12462 :
12463 3454 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12464 :
12465 3454 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
12466 3454 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
12467 : {
12468 3356 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
12469 3356 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
12470 3356 : prosqlbody = NULL;
12471 : }
12472 : else
12473 : {
12474 98 : prosrc = NULL;
12475 98 : probin = NULL;
12476 98 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
12477 : }
12478 3454 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
12479 3454 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
12480 3454 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
12481 3454 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
12482 3454 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
12483 3454 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
12484 3454 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
12485 3454 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
12486 3454 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
12487 3454 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
12488 3454 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
12489 3454 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
12490 3454 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
12491 3454 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
12492 3454 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
12493 :
12494 : /*
12495 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
12496 : * is used.
12497 : */
12498 3454 : if (prosqlbody)
12499 : {
12500 98 : appendPQExpBufferStr(asPart, prosqlbody);
12501 : }
12502 3356 : else if (probin[0] != '\0')
12503 : {
12504 286 : appendPQExpBufferStr(asPart, "AS ");
12505 286 : appendStringLiteralAH(asPart, probin, fout);
12506 286 : if (prosrc[0] != '\0')
12507 : {
12508 286 : appendPQExpBufferStr(asPart, ", ");
12509 :
12510 : /*
12511 : * where we have bin, use dollar quoting if allowed and src
12512 : * contains quote or backslash; else use regular quoting.
12513 : */
12514 286 : if (dopt->disable_dollar_quoting ||
12515 286 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
12516 286 : appendStringLiteralAH(asPart, prosrc, fout);
12517 : else
12518 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
12519 : }
12520 : }
12521 : else
12522 : {
12523 3070 : appendPQExpBufferStr(asPart, "AS ");
12524 : /* with no bin, dollar quote src unconditionally if allowed */
12525 3070 : if (dopt->disable_dollar_quoting)
12526 0 : appendStringLiteralAH(asPart, prosrc, fout);
12527 : else
12528 3070 : appendStringLiteralDQ(asPart, prosrc, NULL);
12529 : }
12530 :
12531 3454 : if (*proconfig)
12532 : {
12533 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
12534 0 : pg_fatal("could not parse %s array", "proconfig");
12535 : }
12536 : else
12537 : {
12538 3424 : configitems = NULL;
12539 3424 : nconfigitems = 0;
12540 : }
12541 :
12542 3454 : funcfullsig = format_function_arguments(finfo, funcargs, false);
12543 3454 : funcsig = format_function_arguments(finfo, funciargs, false);
12544 :
12545 3454 : funcsig_tag = format_function_signature(fout, finfo, false);
12546 :
12547 3454 : qual_funcsig = psprintf("%s.%s",
12548 3454 : fmtId(finfo->dobj.namespace->dobj.name),
12549 : funcsig);
12550 :
12551 3454 : if (prokind[0] == PROKIND_PROCEDURE)
12552 156 : keyword = "PROCEDURE";
12553 : else
12554 3298 : keyword = "FUNCTION"; /* works for window functions too */
12555 :
12556 3454 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
12557 : keyword, qual_funcsig);
12558 :
12559 6908 : appendPQExpBuffer(q, "CREATE %s %s.%s",
12560 : keyword,
12561 3454 : fmtId(finfo->dobj.namespace->dobj.name),
12562 : funcfullsig ? funcfullsig :
12563 : funcsig);
12564 :
12565 3454 : if (prokind[0] == PROKIND_PROCEDURE)
12566 : /* no result type to output */ ;
12567 3298 : else if (funcresult)
12568 3298 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
12569 : else
12570 0 : appendPQExpBuffer(q, " RETURNS %s%s",
12571 0 : (proretset[0] == 't') ? "SETOF " : "",
12572 : getFormattedTypeName(fout, finfo->prorettype,
12573 : zeroIsError));
12574 :
12575 3454 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
12576 :
12577 3454 : if (*protrftypes)
12578 : {
12579 0 : Oid *typeids = palloc(FUNC_MAX_ARGS * sizeof(Oid));
12580 : int i;
12581 :
12582 0 : appendPQExpBufferStr(q, " TRANSFORM ");
12583 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
12584 0 : for (i = 0; typeids[i]; i++)
12585 : {
12586 0 : if (i != 0)
12587 0 : appendPQExpBufferStr(q, ", ");
12588 0 : appendPQExpBuffer(q, "FOR TYPE %s",
12589 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
12590 : }
12591 : }
12592 :
12593 3454 : if (prokind[0] == PROKIND_WINDOW)
12594 10 : appendPQExpBufferStr(q, " WINDOW");
12595 :
12596 3454 : if (provolatile[0] != PROVOLATILE_VOLATILE)
12597 : {
12598 688 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
12599 656 : appendPQExpBufferStr(q, " IMMUTABLE");
12600 32 : else if (provolatile[0] == PROVOLATILE_STABLE)
12601 32 : appendPQExpBufferStr(q, " STABLE");
12602 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
12603 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
12604 : finfo->dobj.name);
12605 : }
12606 :
12607 3454 : if (proisstrict[0] == 't')
12608 694 : appendPQExpBufferStr(q, " STRICT");
12609 :
12610 3454 : if (prosecdef[0] == 't')
12611 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
12612 :
12613 3454 : if (proleakproof[0] == 't')
12614 20 : appendPQExpBufferStr(q, " LEAKPROOF");
12615 :
12616 : /*
12617 : * COST and ROWS are emitted only if present and not default, so as not to
12618 : * break backwards-compatibility of the dump without need. Keep this code
12619 : * in sync with the defaults in functioncmds.c.
12620 : */
12621 3454 : if (strcmp(procost, "0") != 0)
12622 : {
12623 3454 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
12624 : {
12625 : /* default cost is 1 */
12626 744 : if (strcmp(procost, "1") != 0)
12627 0 : appendPQExpBuffer(q, " COST %s", procost);
12628 : }
12629 : else
12630 : {
12631 : /* default cost is 100 */
12632 2710 : if (strcmp(procost, "100") != 0)
12633 12 : appendPQExpBuffer(q, " COST %s", procost);
12634 : }
12635 : }
12636 3454 : if (proretset[0] == 't' &&
12637 376 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
12638 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
12639 :
12640 3454 : if (strcmp(prosupport, "-") != 0)
12641 : {
12642 : /* We rely on regprocout to provide quoting and qualification */
12643 86 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
12644 : }
12645 :
12646 3454 : if (proparallel[0] != PROPARALLEL_UNSAFE)
12647 : {
12648 228 : if (proparallel[0] == PROPARALLEL_SAFE)
12649 218 : appendPQExpBufferStr(q, " PARALLEL SAFE");
12650 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
12651 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
12652 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
12653 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
12654 : finfo->dobj.name);
12655 : }
12656 :
12657 3524 : for (int i = 0; i < nconfigitems; i++)
12658 : {
12659 : /* we feel free to scribble on configitems[] here */
12660 70 : char *configitem = configitems[i];
12661 : char *pos;
12662 :
12663 70 : pos = strchr(configitem, '=');
12664 70 : if (pos == NULL)
12665 0 : continue;
12666 70 : *pos++ = '\0';
12667 70 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
12668 :
12669 : /*
12670 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
12671 : * by flatten_set_variable_args() before they were put into the
12672 : * proconfig array. However, because the quoting rules used there
12673 : * aren't exactly like SQL's, we have to break the list value apart
12674 : * and then quote the elements as string literals. (The elements may
12675 : * be double-quoted as-is, but we can't just feed them to the SQL
12676 : * parser; it would do the wrong thing with elements that are
12677 : * zero-length or longer than NAMEDATALEN.)
12678 : *
12679 : * Variables that are not so marked should just be emitted as simple
12680 : * string literals. If the variable is not known to
12681 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
12682 : * to use GUC_LIST_QUOTE for extension variables.
12683 : */
12684 70 : if (variable_is_guc_list_quote(configitem))
12685 : {
12686 : char **namelist;
12687 : char **nameptr;
12688 :
12689 : /* Parse string into list of identifiers */
12690 : /* this shouldn't fail really */
12691 20 : if (SplitGUCList(pos, ',', &namelist))
12692 : {
12693 70 : for (nameptr = namelist; *nameptr; nameptr++)
12694 : {
12695 50 : if (nameptr != namelist)
12696 30 : appendPQExpBufferStr(q, ", ");
12697 50 : appendStringLiteralAH(q, *nameptr, fout);
12698 : }
12699 : }
12700 20 : pg_free(namelist);
12701 : }
12702 : else
12703 50 : appendStringLiteralAH(q, pos, fout);
12704 : }
12705 :
12706 3454 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
12707 :
12708 3454 : append_depends_on_extension(fout, q, &finfo->dobj,
12709 : "pg_catalog.pg_proc", keyword,
12710 : qual_funcsig);
12711 :
12712 3454 : if (dopt->binary_upgrade)
12713 564 : binary_upgrade_extension_member(q, &finfo->dobj,
12714 : keyword, funcsig,
12715 564 : finfo->dobj.namespace->dobj.name);
12716 :
12717 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12718 3258 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
12719 3258 : ARCHIVE_OPTS(.tag = funcsig_tag,
12720 : .namespace = finfo->dobj.namespace->dobj.name,
12721 : .owner = finfo->rolname,
12722 : .description = keyword,
12723 : .section = finfo->postponed_def ?
12724 : SECTION_POST_DATA : SECTION_PRE_DATA,
12725 : .createStmt = q->data,
12726 : .dropStmt = delqry->data));
12727 :
12728 : /* Dump Function Comments and Security Labels */
12729 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12730 10 : dumpComment(fout, keyword, funcsig,
12731 10 : finfo->dobj.namespace->dobj.name, finfo->rolname,
12732 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
12733 :
12734 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12735 0 : dumpSecLabel(fout, keyword, funcsig,
12736 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
12737 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
12738 :
12739 3454 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
12740 204 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
12741 : funcsig, NULL,
12742 204 : finfo->dobj.namespace->dobj.name,
12743 : NULL, finfo->rolname, &finfo->dacl);
12744 :
12745 3454 : PQclear(res);
12746 :
12747 3454 : destroyPQExpBuffer(query);
12748 3454 : destroyPQExpBuffer(q);
12749 3454 : destroyPQExpBuffer(delqry);
12750 3454 : destroyPQExpBuffer(asPart);
12751 3454 : free(funcsig);
12752 3454 : free(funcfullsig);
12753 3454 : free(funcsig_tag);
12754 3454 : free(qual_funcsig);
12755 3454 : free(configitems);
12756 : }
12757 :
12758 :
12759 : /*
12760 : * Dump a user-defined cast
12761 : */
12762 : static void
12763 130 : dumpCast(Archive *fout, const CastInfo *cast)
12764 : {
12765 130 : DumpOptions *dopt = fout->dopt;
12766 : PQExpBuffer defqry;
12767 : PQExpBuffer delqry;
12768 : PQExpBuffer labelq;
12769 : PQExpBuffer castargs;
12770 130 : FuncInfo *funcInfo = NULL;
12771 : const char *sourceType;
12772 : const char *targetType;
12773 :
12774 : /* Do nothing in data-only dump */
12775 130 : if (dopt->dataOnly)
12776 6 : return;
12777 :
12778 : /* Cannot dump if we don't have the cast function's info */
12779 124 : if (OidIsValid(cast->castfunc))
12780 : {
12781 74 : funcInfo = findFuncByOid(cast->castfunc);
12782 74 : if (funcInfo == NULL)
12783 0 : pg_fatal("could not find function definition for function with OID %u",
12784 : cast->castfunc);
12785 : }
12786 :
12787 124 : defqry = createPQExpBuffer();
12788 124 : delqry = createPQExpBuffer();
12789 124 : labelq = createPQExpBuffer();
12790 124 : castargs = createPQExpBuffer();
12791 :
12792 124 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
12793 124 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
12794 124 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
12795 : sourceType, targetType);
12796 :
12797 124 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
12798 : sourceType, targetType);
12799 :
12800 124 : switch (cast->castmethod)
12801 : {
12802 50 : case COERCION_METHOD_BINARY:
12803 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
12804 50 : break;
12805 0 : case COERCION_METHOD_INOUT:
12806 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
12807 0 : break;
12808 74 : case COERCION_METHOD_FUNCTION:
12809 74 : if (funcInfo)
12810 : {
12811 74 : char *fsig = format_function_signature(fout, funcInfo, true);
12812 :
12813 : /*
12814 : * Always qualify the function name (format_function_signature
12815 : * won't qualify it).
12816 : */
12817 74 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
12818 74 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
12819 74 : free(fsig);
12820 : }
12821 : else
12822 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
12823 74 : break;
12824 0 : default:
12825 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
12826 : }
12827 :
12828 124 : if (cast->castcontext == 'a')
12829 64 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
12830 60 : else if (cast->castcontext == 'i')
12831 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
12832 124 : appendPQExpBufferStr(defqry, ";\n");
12833 :
12834 124 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
12835 : sourceType, targetType);
12836 :
12837 124 : appendPQExpBuffer(castargs, "(%s AS %s)",
12838 : sourceType, targetType);
12839 :
12840 124 : if (dopt->binary_upgrade)
12841 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
12842 14 : "CAST", castargs->data, NULL);
12843 :
12844 124 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
12845 124 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
12846 124 : ARCHIVE_OPTS(.tag = labelq->data,
12847 : .description = "CAST",
12848 : .section = SECTION_PRE_DATA,
12849 : .createStmt = defqry->data,
12850 : .dropStmt = delqry->data));
12851 :
12852 : /* Dump Cast Comments */
12853 124 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
12854 0 : dumpComment(fout, "CAST", castargs->data,
12855 : NULL, "",
12856 : cast->dobj.catId, 0, cast->dobj.dumpId);
12857 :
12858 124 : destroyPQExpBuffer(defqry);
12859 124 : destroyPQExpBuffer(delqry);
12860 124 : destroyPQExpBuffer(labelq);
12861 124 : destroyPQExpBuffer(castargs);
12862 : }
12863 :
12864 : /*
12865 : * Dump a transform
12866 : */
12867 : static void
12868 80 : dumpTransform(Archive *fout, const TransformInfo *transform)
12869 : {
12870 80 : DumpOptions *dopt = fout->dopt;
12871 : PQExpBuffer defqry;
12872 : PQExpBuffer delqry;
12873 : PQExpBuffer labelq;
12874 : PQExpBuffer transformargs;
12875 80 : FuncInfo *fromsqlFuncInfo = NULL;
12876 80 : FuncInfo *tosqlFuncInfo = NULL;
12877 : char *lanname;
12878 : const char *transformType;
12879 :
12880 : /* Do nothing in data-only dump */
12881 80 : if (dopt->dataOnly)
12882 6 : return;
12883 :
12884 : /* Cannot dump if we don't have the transform functions' info */
12885 74 : if (OidIsValid(transform->trffromsql))
12886 : {
12887 74 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
12888 74 : if (fromsqlFuncInfo == NULL)
12889 0 : pg_fatal("could not find function definition for function with OID %u",
12890 : transform->trffromsql);
12891 : }
12892 74 : if (OidIsValid(transform->trftosql))
12893 : {
12894 74 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
12895 74 : if (tosqlFuncInfo == NULL)
12896 0 : pg_fatal("could not find function definition for function with OID %u",
12897 : transform->trftosql);
12898 : }
12899 :
12900 74 : defqry = createPQExpBuffer();
12901 74 : delqry = createPQExpBuffer();
12902 74 : labelq = createPQExpBuffer();
12903 74 : transformargs = createPQExpBuffer();
12904 :
12905 74 : lanname = get_language_name(fout, transform->trflang);
12906 74 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
12907 :
12908 74 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
12909 : transformType, lanname);
12910 :
12911 74 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
12912 : transformType, lanname);
12913 :
12914 74 : if (!transform->trffromsql && !transform->trftosql)
12915 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
12916 :
12917 74 : if (transform->trffromsql)
12918 : {
12919 74 : if (fromsqlFuncInfo)
12920 : {
12921 74 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
12922 :
12923 : /*
12924 : * Always qualify the function name (format_function_signature
12925 : * won't qualify it).
12926 : */
12927 74 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
12928 74 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
12929 74 : free(fsig);
12930 : }
12931 : else
12932 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
12933 : }
12934 :
12935 74 : if (transform->trftosql)
12936 : {
12937 74 : if (transform->trffromsql)
12938 74 : appendPQExpBufferStr(defqry, ", ");
12939 :
12940 74 : if (tosqlFuncInfo)
12941 : {
12942 74 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
12943 :
12944 : /*
12945 : * Always qualify the function name (format_function_signature
12946 : * won't qualify it).
12947 : */
12948 74 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
12949 74 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
12950 74 : free(fsig);
12951 : }
12952 : else
12953 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
12954 : }
12955 :
12956 74 : appendPQExpBufferStr(defqry, ");\n");
12957 :
12958 74 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
12959 : transformType, lanname);
12960 :
12961 74 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
12962 : transformType, lanname);
12963 :
12964 74 : if (dopt->binary_upgrade)
12965 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
12966 4 : "TRANSFORM", transformargs->data, NULL);
12967 :
12968 74 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
12969 74 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
12970 74 : ARCHIVE_OPTS(.tag = labelq->data,
12971 : .description = "TRANSFORM",
12972 : .section = SECTION_PRE_DATA,
12973 : .createStmt = defqry->data,
12974 : .dropStmt = delqry->data,
12975 : .deps = transform->dobj.dependencies,
12976 : .nDeps = transform->dobj.nDeps));
12977 :
12978 : /* Dump Transform Comments */
12979 74 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
12980 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
12981 : NULL, "",
12982 : transform->dobj.catId, 0, transform->dobj.dumpId);
12983 :
12984 74 : free(lanname);
12985 74 : destroyPQExpBuffer(defqry);
12986 74 : destroyPQExpBuffer(delqry);
12987 74 : destroyPQExpBuffer(labelq);
12988 74 : destroyPQExpBuffer(transformargs);
12989 : }
12990 :
12991 :
12992 : /*
12993 : * dumpOpr
12994 : * write out a single operator definition
12995 : */
12996 : static void
12997 1808 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
12998 : {
12999 1808 : DumpOptions *dopt = fout->dopt;
13000 : PQExpBuffer query;
13001 : PQExpBuffer q;
13002 : PQExpBuffer delq;
13003 : PQExpBuffer oprid;
13004 : PQExpBuffer details;
13005 : PGresult *res;
13006 : int i_oprkind;
13007 : int i_oprcode;
13008 : int i_oprleft;
13009 : int i_oprright;
13010 : int i_oprcom;
13011 : int i_oprnegate;
13012 : int i_oprrest;
13013 : int i_oprjoin;
13014 : int i_oprcanmerge;
13015 : int i_oprcanhash;
13016 : char *oprkind;
13017 : char *oprcode;
13018 : char *oprleft;
13019 : char *oprright;
13020 : char *oprcom;
13021 : char *oprnegate;
13022 : char *oprrest;
13023 : char *oprjoin;
13024 : char *oprcanmerge;
13025 : char *oprcanhash;
13026 : char *oprregproc;
13027 : char *oprref;
13028 :
13029 : /* Do nothing in data-only dump */
13030 1808 : if (dopt->dataOnly)
13031 6 : return;
13032 :
13033 : /*
13034 : * some operators are invalid because they were the result of user
13035 : * defining operators before commutators exist
13036 : */
13037 1802 : if (!OidIsValid(oprinfo->oprcode))
13038 28 : return;
13039 :
13040 1774 : query = createPQExpBuffer();
13041 1774 : q = createPQExpBuffer();
13042 1774 : delq = createPQExpBuffer();
13043 1774 : oprid = createPQExpBuffer();
13044 1774 : details = createPQExpBuffer();
13045 :
13046 1774 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
13047 : {
13048 : /* Set up query for operator-specific details */
13049 78 : appendPQExpBufferStr(query,
13050 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
13051 : "SELECT oprkind, "
13052 : "oprcode::pg_catalog.regprocedure, "
13053 : "oprleft::pg_catalog.regtype, "
13054 : "oprright::pg_catalog.regtype, "
13055 : "oprcom, "
13056 : "oprnegate, "
13057 : "oprrest::pg_catalog.regprocedure, "
13058 : "oprjoin::pg_catalog.regprocedure, "
13059 : "oprcanmerge, oprcanhash "
13060 : "FROM pg_catalog.pg_operator "
13061 : "WHERE oid = $1");
13062 :
13063 78 : ExecuteSqlStatement(fout, query->data);
13064 :
13065 78 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
13066 : }
13067 :
13068 1774 : printfPQExpBuffer(query,
13069 : "EXECUTE dumpOpr('%u')",
13070 : oprinfo->dobj.catId.oid);
13071 :
13072 1774 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13073 :
13074 1774 : i_oprkind = PQfnumber(res, "oprkind");
13075 1774 : i_oprcode = PQfnumber(res, "oprcode");
13076 1774 : i_oprleft = PQfnumber(res, "oprleft");
13077 1774 : i_oprright = PQfnumber(res, "oprright");
13078 1774 : i_oprcom = PQfnumber(res, "oprcom");
13079 1774 : i_oprnegate = PQfnumber(res, "oprnegate");
13080 1774 : i_oprrest = PQfnumber(res, "oprrest");
13081 1774 : i_oprjoin = PQfnumber(res, "oprjoin");
13082 1774 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
13083 1774 : i_oprcanhash = PQfnumber(res, "oprcanhash");
13084 :
13085 1774 : oprkind = PQgetvalue(res, 0, i_oprkind);
13086 1774 : oprcode = PQgetvalue(res, 0, i_oprcode);
13087 1774 : oprleft = PQgetvalue(res, 0, i_oprleft);
13088 1774 : oprright = PQgetvalue(res, 0, i_oprright);
13089 1774 : oprcom = PQgetvalue(res, 0, i_oprcom);
13090 1774 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
13091 1774 : oprrest = PQgetvalue(res, 0, i_oprrest);
13092 1774 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
13093 1774 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
13094 1774 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
13095 :
13096 : /* In PG14 upwards postfix operator support does not exist anymore. */
13097 1774 : if (strcmp(oprkind, "r") == 0)
13098 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
13099 : oprcode);
13100 :
13101 1774 : oprregproc = convertRegProcReference(oprcode);
13102 1774 : if (oprregproc)
13103 : {
13104 1774 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
13105 1774 : free(oprregproc);
13106 : }
13107 :
13108 1774 : appendPQExpBuffer(oprid, "%s (",
13109 : oprinfo->dobj.name);
13110 :
13111 : /*
13112 : * right unary means there's a left arg and left unary means there's a
13113 : * right arg. (Although the "r" case is dead code for PG14 and later,
13114 : * continue to support it in case we're dumping from an old server.)
13115 : */
13116 1774 : if (strcmp(oprkind, "r") == 0 ||
13117 1774 : strcmp(oprkind, "b") == 0)
13118 : {
13119 1652 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
13120 1652 : appendPQExpBufferStr(oprid, oprleft);
13121 : }
13122 : else
13123 122 : appendPQExpBufferStr(oprid, "NONE");
13124 :
13125 1774 : if (strcmp(oprkind, "l") == 0 ||
13126 1652 : strcmp(oprkind, "b") == 0)
13127 : {
13128 1774 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
13129 1774 : appendPQExpBuffer(oprid, ", %s)", oprright);
13130 : }
13131 : else
13132 0 : appendPQExpBufferStr(oprid, ", NONE)");
13133 :
13134 1774 : oprref = getFormattedOperatorName(oprcom);
13135 1774 : if (oprref)
13136 : {
13137 1134 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
13138 1134 : free(oprref);
13139 : }
13140 :
13141 1774 : oprref = getFormattedOperatorName(oprnegate);
13142 1774 : if (oprref)
13143 : {
13144 782 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
13145 782 : free(oprref);
13146 : }
13147 :
13148 1774 : if (strcmp(oprcanmerge, "t") == 0)
13149 150 : appendPQExpBufferStr(details, ",\n MERGES");
13150 :
13151 1774 : if (strcmp(oprcanhash, "t") == 0)
13152 92 : appendPQExpBufferStr(details, ",\n HASHES");
13153 :
13154 1774 : oprregproc = convertRegProcReference(oprrest);
13155 1774 : if (oprregproc)
13156 : {
13157 1036 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
13158 1036 : free(oprregproc);
13159 : }
13160 :
13161 1774 : oprregproc = convertRegProcReference(oprjoin);
13162 1774 : if (oprregproc)
13163 : {
13164 1036 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
13165 1036 : free(oprregproc);
13166 : }
13167 :
13168 1774 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
13169 1774 : fmtId(oprinfo->dobj.namespace->dobj.name),
13170 : oprid->data);
13171 :
13172 1774 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
13173 1774 : fmtId(oprinfo->dobj.namespace->dobj.name),
13174 : oprinfo->dobj.name, details->data);
13175 :
13176 1774 : if (dopt->binary_upgrade)
13177 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
13178 24 : "OPERATOR", oprid->data,
13179 24 : oprinfo->dobj.namespace->dobj.name);
13180 :
13181 1774 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13182 1774 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
13183 1774 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
13184 : .namespace = oprinfo->dobj.namespace->dobj.name,
13185 : .owner = oprinfo->rolname,
13186 : .description = "OPERATOR",
13187 : .section = SECTION_PRE_DATA,
13188 : .createStmt = q->data,
13189 : .dropStmt = delq->data));
13190 :
13191 : /* Dump Operator Comments */
13192 1774 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13193 1598 : dumpComment(fout, "OPERATOR", oprid->data,
13194 1598 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
13195 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
13196 :
13197 1774 : PQclear(res);
13198 :
13199 1774 : destroyPQExpBuffer(query);
13200 1774 : destroyPQExpBuffer(q);
13201 1774 : destroyPQExpBuffer(delq);
13202 1774 : destroyPQExpBuffer(oprid);
13203 1774 : destroyPQExpBuffer(details);
13204 : }
13205 :
13206 : /*
13207 : * Convert a function reference obtained from pg_operator
13208 : *
13209 : * Returns allocated string of what to print, or NULL if function references
13210 : * is InvalidOid. Returned string is expected to be free'd by the caller.
13211 : *
13212 : * The input is a REGPROCEDURE display; we have to strip the argument-types
13213 : * part.
13214 : */
13215 : static char *
13216 5322 : convertRegProcReference(const char *proc)
13217 : {
13218 : char *name;
13219 : char *paren;
13220 : bool inquote;
13221 :
13222 : /* In all cases "-" means a null reference */
13223 5322 : if (strcmp(proc, "-") == 0)
13224 1476 : return NULL;
13225 :
13226 3846 : name = pg_strdup(proc);
13227 : /* find non-double-quoted left paren */
13228 3846 : inquote = false;
13229 46214 : for (paren = name; *paren; paren++)
13230 : {
13231 46214 : if (*paren == '(' && !inquote)
13232 : {
13233 3846 : *paren = '\0';
13234 3846 : break;
13235 : }
13236 42368 : if (*paren == '"')
13237 100 : inquote = !inquote;
13238 : }
13239 3846 : return name;
13240 : }
13241 :
13242 : /*
13243 : * getFormattedOperatorName - retrieve the operator name for the
13244 : * given operator OID (presented in string form).
13245 : *
13246 : * Returns an allocated string, or NULL if the given OID is invalid.
13247 : * Caller is responsible for free'ing result string.
13248 : *
13249 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
13250 : * useful in commands where the operator's argument types can be inferred from
13251 : * context. We always schema-qualify the name, though. The predecessor to
13252 : * this code tried to skip the schema qualification if possible, but that led
13253 : * to wrong results in corner cases, such as if an operator and its negator
13254 : * are in different schemas.
13255 : */
13256 : static char *
13257 4120 : getFormattedOperatorName(const char *oproid)
13258 : {
13259 : OprInfo *oprInfo;
13260 :
13261 : /* In all cases "0" means a null reference */
13262 4120 : if (strcmp(oproid, "0") == 0)
13263 2204 : return NULL;
13264 :
13265 1916 : oprInfo = findOprByOid(atooid(oproid));
13266 1916 : if (oprInfo == NULL)
13267 : {
13268 0 : pg_log_warning("could not find operator with OID %s",
13269 : oproid);
13270 0 : return NULL;
13271 : }
13272 :
13273 1916 : return psprintf("OPERATOR(%s.%s)",
13274 1916 : fmtId(oprInfo->dobj.namespace->dobj.name),
13275 : oprInfo->dobj.name);
13276 : }
13277 :
13278 : /*
13279 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
13280 : *
13281 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
13282 : * argument lists of these functions are predetermined. Note that the
13283 : * caller should ensure we are in the proper schema, because the results
13284 : * are search path dependent!
13285 : */
13286 : static char *
13287 360 : convertTSFunction(Archive *fout, Oid funcOid)
13288 : {
13289 : char *result;
13290 : char query[128];
13291 : PGresult *res;
13292 :
13293 360 : snprintf(query, sizeof(query),
13294 : "SELECT '%u'::pg_catalog.regproc", funcOid);
13295 360 : res = ExecuteSqlQueryForSingleRow(fout, query);
13296 :
13297 360 : result = pg_strdup(PQgetvalue(res, 0, 0));
13298 :
13299 360 : PQclear(res);
13300 :
13301 360 : return result;
13302 : }
13303 :
13304 : /*
13305 : * dumpAccessMethod
13306 : * write out a single access method definition
13307 : */
13308 : static void
13309 152 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
13310 : {
13311 152 : DumpOptions *dopt = fout->dopt;
13312 : PQExpBuffer q;
13313 : PQExpBuffer delq;
13314 : char *qamname;
13315 :
13316 : /* Do nothing in data-only dump */
13317 152 : if (dopt->dataOnly)
13318 12 : return;
13319 :
13320 140 : q = createPQExpBuffer();
13321 140 : delq = createPQExpBuffer();
13322 :
13323 140 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
13324 :
13325 140 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
13326 :
13327 140 : switch (aminfo->amtype)
13328 : {
13329 66 : case AMTYPE_INDEX:
13330 66 : appendPQExpBufferStr(q, "TYPE INDEX ");
13331 66 : break;
13332 74 : case AMTYPE_TABLE:
13333 74 : appendPQExpBufferStr(q, "TYPE TABLE ");
13334 74 : break;
13335 0 : default:
13336 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
13337 : aminfo->amtype, qamname);
13338 0 : destroyPQExpBuffer(q);
13339 0 : destroyPQExpBuffer(delq);
13340 0 : free(qamname);
13341 0 : return;
13342 : }
13343 :
13344 140 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
13345 :
13346 140 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
13347 : qamname);
13348 :
13349 140 : if (dopt->binary_upgrade)
13350 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
13351 : "ACCESS METHOD", qamname, NULL);
13352 :
13353 140 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13354 140 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
13355 140 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
13356 : .description = "ACCESS METHOD",
13357 : .section = SECTION_PRE_DATA,
13358 : .createStmt = q->data,
13359 : .dropStmt = delq->data));
13360 :
13361 : /* Dump Access Method Comments */
13362 140 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13363 0 : dumpComment(fout, "ACCESS METHOD", qamname,
13364 : NULL, "",
13365 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
13366 :
13367 140 : destroyPQExpBuffer(q);
13368 140 : destroyPQExpBuffer(delq);
13369 140 : free(qamname);
13370 : }
13371 :
13372 : /*
13373 : * dumpOpclass
13374 : * write out a single operator class definition
13375 : */
13376 : static void
13377 600 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
13378 : {
13379 600 : DumpOptions *dopt = fout->dopt;
13380 : PQExpBuffer query;
13381 : PQExpBuffer q;
13382 : PQExpBuffer delq;
13383 : PQExpBuffer nameusing;
13384 : PGresult *res;
13385 : int ntups;
13386 : int i_opcintype;
13387 : int i_opckeytype;
13388 : int i_opcdefault;
13389 : int i_opcfamily;
13390 : int i_opcfamilyname;
13391 : int i_opcfamilynsp;
13392 : int i_amname;
13393 : int i_amopstrategy;
13394 : int i_amopopr;
13395 : int i_sortfamily;
13396 : int i_sortfamilynsp;
13397 : int i_amprocnum;
13398 : int i_amproc;
13399 : int i_amproclefttype;
13400 : int i_amprocrighttype;
13401 : char *opcintype;
13402 : char *opckeytype;
13403 : char *opcdefault;
13404 : char *opcfamily;
13405 : char *opcfamilyname;
13406 : char *opcfamilynsp;
13407 : char *amname;
13408 : char *amopstrategy;
13409 : char *amopopr;
13410 : char *sortfamily;
13411 : char *sortfamilynsp;
13412 : char *amprocnum;
13413 : char *amproc;
13414 : char *amproclefttype;
13415 : char *amprocrighttype;
13416 : bool needComma;
13417 : int i;
13418 :
13419 : /* Do nothing in data-only dump */
13420 600 : if (dopt->dataOnly)
13421 18 : return;
13422 :
13423 582 : query = createPQExpBuffer();
13424 582 : q = createPQExpBuffer();
13425 582 : delq = createPQExpBuffer();
13426 582 : nameusing = createPQExpBuffer();
13427 :
13428 : /* Get additional fields from the pg_opclass row */
13429 582 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
13430 : "opckeytype::pg_catalog.regtype, "
13431 : "opcdefault, opcfamily, "
13432 : "opfname AS opcfamilyname, "
13433 : "nspname AS opcfamilynsp, "
13434 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
13435 : "FROM pg_catalog.pg_opclass c "
13436 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
13437 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13438 : "WHERE c.oid = '%u'::pg_catalog.oid",
13439 : opcinfo->dobj.catId.oid);
13440 :
13441 582 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13442 :
13443 582 : i_opcintype = PQfnumber(res, "opcintype");
13444 582 : i_opckeytype = PQfnumber(res, "opckeytype");
13445 582 : i_opcdefault = PQfnumber(res, "opcdefault");
13446 582 : i_opcfamily = PQfnumber(res, "opcfamily");
13447 582 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
13448 582 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
13449 582 : i_amname = PQfnumber(res, "amname");
13450 :
13451 : /* opcintype may still be needed after we PQclear res */
13452 582 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
13453 582 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
13454 582 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
13455 : /* opcfamily will still be needed after we PQclear res */
13456 582 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
13457 582 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
13458 582 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
13459 : /* amname will still be needed after we PQclear res */
13460 582 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13461 :
13462 582 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
13463 582 : fmtQualifiedDumpable(opcinfo));
13464 582 : appendPQExpBuffer(delq, " USING %s;\n",
13465 : fmtId(amname));
13466 :
13467 : /* Build the fixed portion of the CREATE command */
13468 582 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
13469 582 : fmtQualifiedDumpable(opcinfo));
13470 582 : if (strcmp(opcdefault, "t") == 0)
13471 238 : appendPQExpBufferStr(q, "DEFAULT ");
13472 582 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
13473 : opcintype,
13474 : fmtId(amname));
13475 582 : if (strlen(opcfamilyname) > 0)
13476 : {
13477 582 : appendPQExpBufferStr(q, " FAMILY ");
13478 582 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
13479 582 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
13480 : }
13481 582 : appendPQExpBufferStr(q, " AS\n ");
13482 :
13483 582 : needComma = false;
13484 :
13485 582 : if (strcmp(opckeytype, "-") != 0)
13486 : {
13487 168 : appendPQExpBuffer(q, "STORAGE %s",
13488 : opckeytype);
13489 168 : needComma = true;
13490 : }
13491 :
13492 582 : PQclear(res);
13493 :
13494 : /*
13495 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13496 : *
13497 : * Print only those opfamily members that are tied to the opclass by
13498 : * pg_depend entries.
13499 : */
13500 582 : resetPQExpBuffer(query);
13501 582 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13502 : "amopopr::pg_catalog.regoperator, "
13503 : "opfname AS sortfamily, "
13504 : "nspname AS sortfamilynsp "
13505 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13506 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13507 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13508 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13509 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13510 : "AND refobjid = '%u'::pg_catalog.oid "
13511 : "AND amopfamily = '%s'::pg_catalog.oid "
13512 : "ORDER BY amopstrategy",
13513 : opcinfo->dobj.catId.oid,
13514 : opcfamily);
13515 :
13516 582 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13517 :
13518 582 : ntups = PQntuples(res);
13519 :
13520 582 : i_amopstrategy = PQfnumber(res, "amopstrategy");
13521 582 : i_amopopr = PQfnumber(res, "amopopr");
13522 582 : i_sortfamily = PQfnumber(res, "sortfamily");
13523 582 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
13524 :
13525 998 : for (i = 0; i < ntups; i++)
13526 : {
13527 416 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
13528 416 : amopopr = PQgetvalue(res, i, i_amopopr);
13529 416 : sortfamily = PQgetvalue(res, i, i_sortfamily);
13530 416 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
13531 :
13532 416 : if (needComma)
13533 264 : appendPQExpBufferStr(q, " ,\n ");
13534 :
13535 416 : appendPQExpBuffer(q, "OPERATOR %s %s",
13536 : amopstrategy, amopopr);
13537 :
13538 416 : if (strlen(sortfamily) > 0)
13539 : {
13540 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13541 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13542 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13543 : }
13544 :
13545 416 : needComma = true;
13546 : }
13547 :
13548 582 : PQclear(res);
13549 :
13550 : /*
13551 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13552 : *
13553 : * Print only those opfamily members that are tied to the opclass by
13554 : * pg_depend entries.
13555 : *
13556 : * We print the amproclefttype/amprocrighttype even though in most cases
13557 : * the backend could deduce the right values, because of the corner case
13558 : * of a btree sort support function for a cross-type comparison.
13559 : */
13560 582 : resetPQExpBuffer(query);
13561 :
13562 582 : appendPQExpBuffer(query, "SELECT amprocnum, "
13563 : "amproc::pg_catalog.regprocedure, "
13564 : "amproclefttype::pg_catalog.regtype, "
13565 : "amprocrighttype::pg_catalog.regtype "
13566 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13567 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
13568 : "AND refobjid = '%u'::pg_catalog.oid "
13569 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13570 : "AND objid = ap.oid "
13571 : "ORDER BY amprocnum",
13572 : opcinfo->dobj.catId.oid);
13573 :
13574 582 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13575 :
13576 582 : ntups = PQntuples(res);
13577 :
13578 582 : i_amprocnum = PQfnumber(res, "amprocnum");
13579 582 : i_amproc = PQfnumber(res, "amproc");
13580 582 : i_amproclefttype = PQfnumber(res, "amproclefttype");
13581 582 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
13582 :
13583 648 : for (i = 0; i < ntups; i++)
13584 : {
13585 66 : amprocnum = PQgetvalue(res, i, i_amprocnum);
13586 66 : amproc = PQgetvalue(res, i, i_amproc);
13587 66 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
13588 66 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
13589 :
13590 66 : if (needComma)
13591 66 : appendPQExpBufferStr(q, " ,\n ");
13592 :
13593 66 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
13594 :
13595 66 : if (*amproclefttype && *amprocrighttype)
13596 66 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
13597 :
13598 66 : appendPQExpBuffer(q, " %s", amproc);
13599 :
13600 66 : needComma = true;
13601 : }
13602 :
13603 582 : PQclear(res);
13604 :
13605 : /*
13606 : * If needComma is still false it means we haven't added anything after
13607 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
13608 : * clause with the same datatype. This isn't sanctioned by the
13609 : * documentation, but actually DefineOpClass will treat it as a no-op.
13610 : */
13611 582 : if (!needComma)
13612 262 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
13613 :
13614 582 : appendPQExpBufferStr(q, ";\n");
13615 :
13616 582 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
13617 582 : appendPQExpBuffer(nameusing, " USING %s",
13618 : fmtId(amname));
13619 :
13620 582 : if (dopt->binary_upgrade)
13621 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
13622 12 : "OPERATOR CLASS", nameusing->data,
13623 12 : opcinfo->dobj.namespace->dobj.name);
13624 :
13625 582 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13626 582 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
13627 582 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
13628 : .namespace = opcinfo->dobj.namespace->dobj.name,
13629 : .owner = opcinfo->rolname,
13630 : .description = "OPERATOR CLASS",
13631 : .section = SECTION_PRE_DATA,
13632 : .createStmt = q->data,
13633 : .dropStmt = delq->data));
13634 :
13635 : /* Dump Operator Class Comments */
13636 582 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13637 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
13638 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
13639 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
13640 :
13641 582 : free(opcintype);
13642 582 : free(opcfamily);
13643 582 : free(amname);
13644 582 : destroyPQExpBuffer(query);
13645 582 : destroyPQExpBuffer(q);
13646 582 : destroyPQExpBuffer(delq);
13647 582 : destroyPQExpBuffer(nameusing);
13648 : }
13649 :
13650 : /*
13651 : * dumpOpfamily
13652 : * write out a single operator family definition
13653 : *
13654 : * Note: this also dumps any "loose" operator members that aren't bound to a
13655 : * specific opclass within the opfamily.
13656 : */
13657 : static void
13658 506 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
13659 : {
13660 506 : DumpOptions *dopt = fout->dopt;
13661 : PQExpBuffer query;
13662 : PQExpBuffer q;
13663 : PQExpBuffer delq;
13664 : PQExpBuffer nameusing;
13665 : PGresult *res;
13666 : PGresult *res_ops;
13667 : PGresult *res_procs;
13668 : int ntups;
13669 : int i_amname;
13670 : int i_amopstrategy;
13671 : int i_amopopr;
13672 : int i_sortfamily;
13673 : int i_sortfamilynsp;
13674 : int i_amprocnum;
13675 : int i_amproc;
13676 : int i_amproclefttype;
13677 : int i_amprocrighttype;
13678 : char *amname;
13679 : char *amopstrategy;
13680 : char *amopopr;
13681 : char *sortfamily;
13682 : char *sortfamilynsp;
13683 : char *amprocnum;
13684 : char *amproc;
13685 : char *amproclefttype;
13686 : char *amprocrighttype;
13687 : bool needComma;
13688 : int i;
13689 :
13690 : /* Do nothing in data-only dump */
13691 506 : if (dopt->dataOnly)
13692 12 : return;
13693 :
13694 494 : query = createPQExpBuffer();
13695 494 : q = createPQExpBuffer();
13696 494 : delq = createPQExpBuffer();
13697 494 : nameusing = createPQExpBuffer();
13698 :
13699 : /*
13700 : * Fetch only those opfamily members that are tied directly to the
13701 : * opfamily by pg_depend entries.
13702 : */
13703 494 : appendPQExpBuffer(query, "SELECT amopstrategy, "
13704 : "amopopr::pg_catalog.regoperator, "
13705 : "opfname AS sortfamily, "
13706 : "nspname AS sortfamilynsp "
13707 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
13708 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
13709 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
13710 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
13711 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13712 : "AND refobjid = '%u'::pg_catalog.oid "
13713 : "AND amopfamily = '%u'::pg_catalog.oid "
13714 : "ORDER BY amopstrategy",
13715 : opfinfo->dobj.catId.oid,
13716 : opfinfo->dobj.catId.oid);
13717 :
13718 494 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13719 :
13720 494 : resetPQExpBuffer(query);
13721 :
13722 494 : appendPQExpBuffer(query, "SELECT amprocnum, "
13723 : "amproc::pg_catalog.regprocedure, "
13724 : "amproclefttype::pg_catalog.regtype, "
13725 : "amprocrighttype::pg_catalog.regtype "
13726 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
13727 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
13728 : "AND refobjid = '%u'::pg_catalog.oid "
13729 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
13730 : "AND objid = ap.oid "
13731 : "ORDER BY amprocnum",
13732 : opfinfo->dobj.catId.oid);
13733 :
13734 494 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
13735 :
13736 : /* Get additional fields from the pg_opfamily row */
13737 494 : resetPQExpBuffer(query);
13738 :
13739 494 : appendPQExpBuffer(query, "SELECT "
13740 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
13741 : "FROM pg_catalog.pg_opfamily "
13742 : "WHERE oid = '%u'::pg_catalog.oid",
13743 : opfinfo->dobj.catId.oid);
13744 :
13745 494 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13746 :
13747 494 : i_amname = PQfnumber(res, "amname");
13748 :
13749 : /* amname will still be needed after we PQclear res */
13750 494 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
13751 :
13752 494 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
13753 494 : fmtQualifiedDumpable(opfinfo));
13754 494 : appendPQExpBuffer(delq, " USING %s;\n",
13755 : fmtId(amname));
13756 :
13757 : /* Build the fixed portion of the CREATE command */
13758 494 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
13759 494 : fmtQualifiedDumpable(opfinfo));
13760 494 : appendPQExpBuffer(q, " USING %s;\n",
13761 : fmtId(amname));
13762 :
13763 494 : PQclear(res);
13764 :
13765 : /* Do we need an ALTER to add loose members? */
13766 494 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
13767 : {
13768 96 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
13769 96 : fmtQualifiedDumpable(opfinfo));
13770 96 : appendPQExpBuffer(q, " USING %s ADD\n ",
13771 : fmtId(amname));
13772 :
13773 96 : needComma = false;
13774 :
13775 : /*
13776 : * Now fetch and print the OPERATOR entries (pg_amop rows).
13777 : */
13778 96 : ntups = PQntuples(res_ops);
13779 :
13780 96 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
13781 96 : i_amopopr = PQfnumber(res_ops, "amopopr");
13782 96 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
13783 96 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
13784 :
13785 426 : for (i = 0; i < ntups; i++)
13786 : {
13787 330 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
13788 330 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
13789 330 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
13790 330 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
13791 :
13792 330 : if (needComma)
13793 264 : appendPQExpBufferStr(q, " ,\n ");
13794 :
13795 330 : appendPQExpBuffer(q, "OPERATOR %s %s",
13796 : amopstrategy, amopopr);
13797 :
13798 330 : if (strlen(sortfamily) > 0)
13799 : {
13800 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
13801 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
13802 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
13803 : }
13804 :
13805 330 : needComma = true;
13806 : }
13807 :
13808 : /*
13809 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
13810 : */
13811 96 : ntups = PQntuples(res_procs);
13812 :
13813 96 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
13814 96 : i_amproc = PQfnumber(res_procs, "amproc");
13815 96 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
13816 96 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
13817 :
13818 456 : for (i = 0; i < ntups; i++)
13819 : {
13820 360 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
13821 360 : amproc = PQgetvalue(res_procs, i, i_amproc);
13822 360 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
13823 360 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
13824 :
13825 360 : if (needComma)
13826 330 : appendPQExpBufferStr(q, " ,\n ");
13827 :
13828 360 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
13829 : amprocnum, amproclefttype, amprocrighttype,
13830 : amproc);
13831 :
13832 360 : needComma = true;
13833 : }
13834 :
13835 96 : appendPQExpBufferStr(q, ";\n");
13836 : }
13837 :
13838 494 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
13839 494 : appendPQExpBuffer(nameusing, " USING %s",
13840 : fmtId(amname));
13841 :
13842 494 : if (dopt->binary_upgrade)
13843 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
13844 18 : "OPERATOR FAMILY", nameusing->data,
13845 18 : opfinfo->dobj.namespace->dobj.name);
13846 :
13847 494 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13848 494 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
13849 494 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
13850 : .namespace = opfinfo->dobj.namespace->dobj.name,
13851 : .owner = opfinfo->rolname,
13852 : .description = "OPERATOR FAMILY",
13853 : .section = SECTION_PRE_DATA,
13854 : .createStmt = q->data,
13855 : .dropStmt = delq->data));
13856 :
13857 : /* Dump Operator Family Comments */
13858 494 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13859 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
13860 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
13861 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
13862 :
13863 494 : free(amname);
13864 494 : PQclear(res_ops);
13865 494 : PQclear(res_procs);
13866 494 : destroyPQExpBuffer(query);
13867 494 : destroyPQExpBuffer(q);
13868 494 : destroyPQExpBuffer(delq);
13869 494 : destroyPQExpBuffer(nameusing);
13870 : }
13871 :
13872 : /*
13873 : * dumpCollation
13874 : * write out a single collation definition
13875 : */
13876 : static void
13877 1744 : dumpCollation(Archive *fout, const CollInfo *collinfo)
13878 : {
13879 1744 : DumpOptions *dopt = fout->dopt;
13880 : PQExpBuffer query;
13881 : PQExpBuffer q;
13882 : PQExpBuffer delq;
13883 : char *qcollname;
13884 : PGresult *res;
13885 : int i_collprovider;
13886 : int i_collisdeterministic;
13887 : int i_collcollate;
13888 : int i_collctype;
13889 : int i_colllocale;
13890 : int i_collicurules;
13891 : const char *collprovider;
13892 : const char *collcollate;
13893 : const char *collctype;
13894 : const char *colllocale;
13895 : const char *collicurules;
13896 :
13897 : /* Do nothing in data-only dump */
13898 1744 : if (dopt->dataOnly)
13899 12 : return;
13900 :
13901 1732 : query = createPQExpBuffer();
13902 1732 : q = createPQExpBuffer();
13903 1732 : delq = createPQExpBuffer();
13904 :
13905 1732 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
13906 :
13907 : /* Get collation-specific details */
13908 1732 : appendPQExpBufferStr(query, "SELECT ");
13909 :
13910 1732 : if (fout->remoteVersion >= 100000)
13911 1732 : appendPQExpBufferStr(query,
13912 : "collprovider, "
13913 : "collversion, ");
13914 : else
13915 0 : appendPQExpBufferStr(query,
13916 : "'c' AS collprovider, "
13917 : "NULL AS collversion, ");
13918 :
13919 1732 : if (fout->remoteVersion >= 120000)
13920 1732 : appendPQExpBufferStr(query,
13921 : "collisdeterministic, ");
13922 : else
13923 0 : appendPQExpBufferStr(query,
13924 : "true AS collisdeterministic, ");
13925 :
13926 1732 : if (fout->remoteVersion >= 170000)
13927 1732 : appendPQExpBufferStr(query,
13928 : "colllocale, ");
13929 0 : else if (fout->remoteVersion >= 150000)
13930 0 : appendPQExpBufferStr(query,
13931 : "colliculocale AS colllocale, ");
13932 : else
13933 0 : appendPQExpBufferStr(query,
13934 : "NULL AS colllocale, ");
13935 :
13936 1732 : if (fout->remoteVersion >= 160000)
13937 1732 : appendPQExpBufferStr(query,
13938 : "collicurules, ");
13939 : else
13940 0 : appendPQExpBufferStr(query,
13941 : "NULL AS collicurules, ");
13942 :
13943 1732 : appendPQExpBuffer(query,
13944 : "collcollate, "
13945 : "collctype "
13946 : "FROM pg_catalog.pg_collation c "
13947 : "WHERE c.oid = '%u'::pg_catalog.oid",
13948 : collinfo->dobj.catId.oid);
13949 :
13950 1732 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13951 :
13952 1732 : i_collprovider = PQfnumber(res, "collprovider");
13953 1732 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
13954 1732 : i_collcollate = PQfnumber(res, "collcollate");
13955 1732 : i_collctype = PQfnumber(res, "collctype");
13956 1732 : i_colllocale = PQfnumber(res, "colllocale");
13957 1732 : i_collicurules = PQfnumber(res, "collicurules");
13958 :
13959 1732 : collprovider = PQgetvalue(res, 0, i_collprovider);
13960 :
13961 1732 : if (!PQgetisnull(res, 0, i_collcollate))
13962 74 : collcollate = PQgetvalue(res, 0, i_collcollate);
13963 : else
13964 1658 : collcollate = NULL;
13965 :
13966 1732 : if (!PQgetisnull(res, 0, i_collctype))
13967 74 : collctype = PQgetvalue(res, 0, i_collctype);
13968 : else
13969 1658 : collctype = NULL;
13970 :
13971 : /*
13972 : * Before version 15, collcollate and collctype were of type NAME and
13973 : * non-nullable. Treat empty strings as NULL for consistency.
13974 : */
13975 1732 : if (fout->remoteVersion < 150000)
13976 : {
13977 0 : if (collcollate[0] == '\0')
13978 0 : collcollate = NULL;
13979 0 : if (collctype[0] == '\0')
13980 0 : collctype = NULL;
13981 : }
13982 :
13983 1732 : if (!PQgetisnull(res, 0, i_colllocale))
13984 1656 : colllocale = PQgetvalue(res, 0, i_colllocale);
13985 : else
13986 76 : colllocale = NULL;
13987 :
13988 1732 : if (!PQgetisnull(res, 0, i_collicurules))
13989 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
13990 : else
13991 1732 : collicurules = NULL;
13992 :
13993 1732 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
13994 1732 : fmtQualifiedDumpable(collinfo));
13995 :
13996 1732 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
13997 1732 : fmtQualifiedDumpable(collinfo));
13998 :
13999 1732 : appendPQExpBufferStr(q, "provider = ");
14000 1732 : if (collprovider[0] == 'b')
14001 14 : appendPQExpBufferStr(q, "builtin");
14002 1718 : else if (collprovider[0] == 'c')
14003 74 : appendPQExpBufferStr(q, "libc");
14004 1644 : else if (collprovider[0] == 'i')
14005 1642 : appendPQExpBufferStr(q, "icu");
14006 2 : else if (collprovider[0] == 'd')
14007 : /* to allow dumping pg_catalog; not accepted on input */
14008 2 : appendPQExpBufferStr(q, "default");
14009 : else
14010 0 : pg_fatal("unrecognized collation provider: %s",
14011 : collprovider);
14012 :
14013 1732 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
14014 0 : appendPQExpBufferStr(q, ", deterministic = false");
14015 :
14016 1732 : if (collprovider[0] == 'd')
14017 : {
14018 2 : if (collcollate || collctype || colllocale || collicurules)
14019 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14020 :
14021 : /* no locale -- the default collation cannot be reloaded anyway */
14022 : }
14023 1730 : else if (collprovider[0] == 'b')
14024 : {
14025 14 : if (collcollate || collctype || !colllocale || collicurules)
14026 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14027 :
14028 14 : appendPQExpBufferStr(q, ", locale = ");
14029 14 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14030 : fout);
14031 : }
14032 1716 : else if (collprovider[0] == 'i')
14033 : {
14034 1642 : if (fout->remoteVersion >= 150000)
14035 : {
14036 1642 : if (collcollate || collctype || !colllocale)
14037 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14038 :
14039 1642 : appendPQExpBufferStr(q, ", locale = ");
14040 1642 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14041 : fout);
14042 : }
14043 : else
14044 : {
14045 0 : if (!collcollate || !collctype || colllocale ||
14046 0 : strcmp(collcollate, collctype) != 0)
14047 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14048 :
14049 0 : appendPQExpBufferStr(q, ", locale = ");
14050 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14051 : }
14052 :
14053 1642 : if (collicurules)
14054 : {
14055 0 : appendPQExpBufferStr(q, ", rules = ");
14056 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
14057 : }
14058 : }
14059 74 : else if (collprovider[0] == 'c')
14060 : {
14061 74 : if (colllocale || collicurules || !collcollate || !collctype)
14062 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14063 :
14064 74 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
14065 : {
14066 74 : appendPQExpBufferStr(q, ", locale = ");
14067 74 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14068 : }
14069 : else
14070 : {
14071 0 : appendPQExpBufferStr(q, ", lc_collate = ");
14072 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
14073 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
14074 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
14075 : }
14076 : }
14077 : else
14078 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
14079 :
14080 : /*
14081 : * For binary upgrade, carry over the collation version. For normal
14082 : * dump/restore, omit the version, so that it is computed upon restore.
14083 : */
14084 1732 : if (dopt->binary_upgrade)
14085 : {
14086 : int i_collversion;
14087 :
14088 8 : i_collversion = PQfnumber(res, "collversion");
14089 8 : if (!PQgetisnull(res, 0, i_collversion))
14090 : {
14091 6 : appendPQExpBufferStr(q, ", version = ");
14092 6 : appendStringLiteralAH(q,
14093 : PQgetvalue(res, 0, i_collversion),
14094 : fout);
14095 : }
14096 : }
14097 :
14098 1732 : appendPQExpBufferStr(q, ");\n");
14099 :
14100 1732 : if (dopt->binary_upgrade)
14101 8 : binary_upgrade_extension_member(q, &collinfo->dobj,
14102 : "COLLATION", qcollname,
14103 8 : collinfo->dobj.namespace->dobj.name);
14104 :
14105 1732 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14106 1732 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
14107 1732 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
14108 : .namespace = collinfo->dobj.namespace->dobj.name,
14109 : .owner = collinfo->rolname,
14110 : .description = "COLLATION",
14111 : .section = SECTION_PRE_DATA,
14112 : .createStmt = q->data,
14113 : .dropStmt = delq->data));
14114 :
14115 : /* Dump Collation Comments */
14116 1732 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14117 1620 : dumpComment(fout, "COLLATION", qcollname,
14118 1620 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
14119 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
14120 :
14121 1732 : PQclear(res);
14122 :
14123 1732 : destroyPQExpBuffer(query);
14124 1732 : destroyPQExpBuffer(q);
14125 1732 : destroyPQExpBuffer(delq);
14126 1732 : free(qcollname);
14127 : }
14128 :
14129 : /*
14130 : * dumpConversion
14131 : * write out a single conversion definition
14132 : */
14133 : static void
14134 328 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
14135 : {
14136 328 : DumpOptions *dopt = fout->dopt;
14137 : PQExpBuffer query;
14138 : PQExpBuffer q;
14139 : PQExpBuffer delq;
14140 : char *qconvname;
14141 : PGresult *res;
14142 : int i_conforencoding;
14143 : int i_contoencoding;
14144 : int i_conproc;
14145 : int i_condefault;
14146 : const char *conforencoding;
14147 : const char *contoencoding;
14148 : const char *conproc;
14149 : bool condefault;
14150 :
14151 : /* Do nothing in data-only dump */
14152 328 : if (dopt->dataOnly)
14153 6 : return;
14154 :
14155 322 : query = createPQExpBuffer();
14156 322 : q = createPQExpBuffer();
14157 322 : delq = createPQExpBuffer();
14158 :
14159 322 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
14160 :
14161 : /* Get conversion-specific details */
14162 322 : appendPQExpBuffer(query, "SELECT "
14163 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
14164 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
14165 : "conproc, condefault "
14166 : "FROM pg_catalog.pg_conversion c "
14167 : "WHERE c.oid = '%u'::pg_catalog.oid",
14168 : convinfo->dobj.catId.oid);
14169 :
14170 322 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14171 :
14172 322 : i_conforencoding = PQfnumber(res, "conforencoding");
14173 322 : i_contoencoding = PQfnumber(res, "contoencoding");
14174 322 : i_conproc = PQfnumber(res, "conproc");
14175 322 : i_condefault = PQfnumber(res, "condefault");
14176 :
14177 322 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
14178 322 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
14179 322 : conproc = PQgetvalue(res, 0, i_conproc);
14180 322 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
14181 :
14182 322 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
14183 322 : fmtQualifiedDumpable(convinfo));
14184 :
14185 322 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
14186 : (condefault) ? "DEFAULT " : "",
14187 322 : fmtQualifiedDumpable(convinfo));
14188 322 : appendStringLiteralAH(q, conforencoding, fout);
14189 322 : appendPQExpBufferStr(q, " TO ");
14190 322 : appendStringLiteralAH(q, contoencoding, fout);
14191 : /* regproc output is already sufficiently quoted */
14192 322 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
14193 :
14194 322 : if (dopt->binary_upgrade)
14195 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
14196 : "CONVERSION", qconvname,
14197 2 : convinfo->dobj.namespace->dobj.name);
14198 :
14199 322 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14200 322 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
14201 322 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
14202 : .namespace = convinfo->dobj.namespace->dobj.name,
14203 : .owner = convinfo->rolname,
14204 : .description = "CONVERSION",
14205 : .section = SECTION_PRE_DATA,
14206 : .createStmt = q->data,
14207 : .dropStmt = delq->data));
14208 :
14209 : /* Dump Conversion Comments */
14210 322 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14211 322 : dumpComment(fout, "CONVERSION", qconvname,
14212 322 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
14213 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
14214 :
14215 322 : PQclear(res);
14216 :
14217 322 : destroyPQExpBuffer(query);
14218 322 : destroyPQExpBuffer(q);
14219 322 : destroyPQExpBuffer(delq);
14220 322 : free(qconvname);
14221 : }
14222 :
14223 : /*
14224 : * format_aggregate_signature: generate aggregate name and argument list
14225 : *
14226 : * The argument type names are qualified if needed. The aggregate name
14227 : * is never qualified.
14228 : */
14229 : static char *
14230 572 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
14231 : {
14232 : PQExpBufferData buf;
14233 : int j;
14234 :
14235 572 : initPQExpBuffer(&buf);
14236 572 : if (honor_quotes)
14237 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
14238 : else
14239 572 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
14240 :
14241 572 : if (agginfo->aggfn.nargs == 0)
14242 80 : appendPQExpBufferStr(&buf, "(*)");
14243 : else
14244 : {
14245 492 : appendPQExpBufferChar(&buf, '(');
14246 1074 : for (j = 0; j < agginfo->aggfn.nargs; j++)
14247 582 : appendPQExpBuffer(&buf, "%s%s",
14248 : (j > 0) ? ", " : "",
14249 : getFormattedTypeName(fout,
14250 582 : agginfo->aggfn.argtypes[j],
14251 : zeroIsError));
14252 492 : appendPQExpBufferChar(&buf, ')');
14253 : }
14254 572 : return buf.data;
14255 : }
14256 :
14257 : /*
14258 : * dumpAgg
14259 : * write out a single aggregate definition
14260 : */
14261 : static void
14262 580 : dumpAgg(Archive *fout, const AggInfo *agginfo)
14263 : {
14264 580 : DumpOptions *dopt = fout->dopt;
14265 : PQExpBuffer query;
14266 : PQExpBuffer q;
14267 : PQExpBuffer delq;
14268 : PQExpBuffer details;
14269 : char *aggsig; /* identity signature */
14270 580 : char *aggfullsig = NULL; /* full signature */
14271 : char *aggsig_tag;
14272 : PGresult *res;
14273 : int i_agginitval;
14274 : int i_aggminitval;
14275 : const char *aggtransfn;
14276 : const char *aggfinalfn;
14277 : const char *aggcombinefn;
14278 : const char *aggserialfn;
14279 : const char *aggdeserialfn;
14280 : const char *aggmtransfn;
14281 : const char *aggminvtransfn;
14282 : const char *aggmfinalfn;
14283 : bool aggfinalextra;
14284 : bool aggmfinalextra;
14285 : char aggfinalmodify;
14286 : char aggmfinalmodify;
14287 : const char *aggsortop;
14288 : char *aggsortconvop;
14289 : char aggkind;
14290 : const char *aggtranstype;
14291 : const char *aggtransspace;
14292 : const char *aggmtranstype;
14293 : const char *aggmtransspace;
14294 : const char *agginitval;
14295 : const char *aggminitval;
14296 : const char *proparallel;
14297 : char defaultfinalmodify;
14298 :
14299 : /* Do nothing in data-only dump */
14300 580 : if (dopt->dataOnly)
14301 8 : return;
14302 :
14303 572 : query = createPQExpBuffer();
14304 572 : q = createPQExpBuffer();
14305 572 : delq = createPQExpBuffer();
14306 572 : details = createPQExpBuffer();
14307 :
14308 572 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
14309 : {
14310 : /* Set up query for aggregate-specific details */
14311 112 : appendPQExpBufferStr(query,
14312 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
14313 :
14314 112 : appendPQExpBufferStr(query,
14315 : "SELECT "
14316 : "aggtransfn,\n"
14317 : "aggfinalfn,\n"
14318 : "aggtranstype::pg_catalog.regtype,\n"
14319 : "agginitval,\n"
14320 : "aggsortop,\n"
14321 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
14322 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
14323 :
14324 112 : if (fout->remoteVersion >= 90400)
14325 112 : appendPQExpBufferStr(query,
14326 : "aggkind,\n"
14327 : "aggmtransfn,\n"
14328 : "aggminvtransfn,\n"
14329 : "aggmfinalfn,\n"
14330 : "aggmtranstype::pg_catalog.regtype,\n"
14331 : "aggfinalextra,\n"
14332 : "aggmfinalextra,\n"
14333 : "aggtransspace,\n"
14334 : "aggmtransspace,\n"
14335 : "aggminitval,\n");
14336 : else
14337 0 : appendPQExpBufferStr(query,
14338 : "'n' AS aggkind,\n"
14339 : "'-' AS aggmtransfn,\n"
14340 : "'-' AS aggminvtransfn,\n"
14341 : "'-' AS aggmfinalfn,\n"
14342 : "0 AS aggmtranstype,\n"
14343 : "false AS aggfinalextra,\n"
14344 : "false AS aggmfinalextra,\n"
14345 : "0 AS aggtransspace,\n"
14346 : "0 AS aggmtransspace,\n"
14347 : "NULL AS aggminitval,\n");
14348 :
14349 112 : if (fout->remoteVersion >= 90600)
14350 112 : appendPQExpBufferStr(query,
14351 : "aggcombinefn,\n"
14352 : "aggserialfn,\n"
14353 : "aggdeserialfn,\n"
14354 : "proparallel,\n");
14355 : else
14356 0 : appendPQExpBufferStr(query,
14357 : "'-' AS aggcombinefn,\n"
14358 : "'-' AS aggserialfn,\n"
14359 : "'-' AS aggdeserialfn,\n"
14360 : "'u' AS proparallel,\n");
14361 :
14362 112 : if (fout->remoteVersion >= 110000)
14363 112 : appendPQExpBufferStr(query,
14364 : "aggfinalmodify,\n"
14365 : "aggmfinalmodify\n");
14366 : else
14367 0 : appendPQExpBufferStr(query,
14368 : "'0' AS aggfinalmodify,\n"
14369 : "'0' AS aggmfinalmodify\n");
14370 :
14371 112 : appendPQExpBufferStr(query,
14372 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
14373 : "WHERE a.aggfnoid = p.oid "
14374 : "AND p.oid = $1");
14375 :
14376 112 : ExecuteSqlStatement(fout, query->data);
14377 :
14378 112 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
14379 : }
14380 :
14381 572 : printfPQExpBuffer(query,
14382 : "EXECUTE dumpAgg('%u')",
14383 : agginfo->aggfn.dobj.catId.oid);
14384 :
14385 572 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14386 :
14387 572 : i_agginitval = PQfnumber(res, "agginitval");
14388 572 : i_aggminitval = PQfnumber(res, "aggminitval");
14389 :
14390 572 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
14391 572 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
14392 572 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
14393 572 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
14394 572 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
14395 572 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
14396 572 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
14397 572 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
14398 572 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
14399 572 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
14400 572 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
14401 572 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
14402 572 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
14403 572 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
14404 572 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
14405 572 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
14406 572 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
14407 572 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
14408 572 : agginitval = PQgetvalue(res, 0, i_agginitval);
14409 572 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
14410 572 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
14411 :
14412 : {
14413 : char *funcargs;
14414 : char *funciargs;
14415 :
14416 572 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
14417 572 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
14418 572 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
14419 572 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
14420 : }
14421 :
14422 572 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
14423 :
14424 : /* identify default modify flag for aggkind (must match DefineAggregate) */
14425 572 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
14426 : /* replace omitted flags for old versions */
14427 572 : if (aggfinalmodify == '0')
14428 0 : aggfinalmodify = defaultfinalmodify;
14429 572 : if (aggmfinalmodify == '0')
14430 0 : aggmfinalmodify = defaultfinalmodify;
14431 :
14432 : /* regproc and regtype output is already sufficiently quoted */
14433 572 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
14434 : aggtransfn, aggtranstype);
14435 :
14436 572 : if (strcmp(aggtransspace, "0") != 0)
14437 : {
14438 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
14439 : aggtransspace);
14440 : }
14441 :
14442 572 : if (!PQgetisnull(res, 0, i_agginitval))
14443 : {
14444 416 : appendPQExpBufferStr(details, ",\n INITCOND = ");
14445 416 : appendStringLiteralAH(details, agginitval, fout);
14446 : }
14447 :
14448 572 : if (strcmp(aggfinalfn, "-") != 0)
14449 : {
14450 266 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
14451 : aggfinalfn);
14452 266 : if (aggfinalextra)
14453 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
14454 266 : if (aggfinalmodify != defaultfinalmodify)
14455 : {
14456 66 : switch (aggfinalmodify)
14457 : {
14458 0 : case AGGMODIFY_READ_ONLY:
14459 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
14460 0 : break;
14461 66 : case AGGMODIFY_SHAREABLE:
14462 66 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
14463 66 : break;
14464 0 : case AGGMODIFY_READ_WRITE:
14465 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
14466 0 : break;
14467 0 : default:
14468 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
14469 : agginfo->aggfn.dobj.name);
14470 : break;
14471 : }
14472 506 : }
14473 : }
14474 :
14475 572 : if (strcmp(aggcombinefn, "-") != 0)
14476 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
14477 :
14478 572 : if (strcmp(aggserialfn, "-") != 0)
14479 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
14480 :
14481 572 : if (strcmp(aggdeserialfn, "-") != 0)
14482 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
14483 :
14484 572 : if (strcmp(aggmtransfn, "-") != 0)
14485 : {
14486 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
14487 : aggmtransfn,
14488 : aggminvtransfn,
14489 : aggmtranstype);
14490 : }
14491 :
14492 572 : if (strcmp(aggmtransspace, "0") != 0)
14493 : {
14494 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
14495 : aggmtransspace);
14496 : }
14497 :
14498 572 : if (!PQgetisnull(res, 0, i_aggminitval))
14499 : {
14500 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
14501 20 : appendStringLiteralAH(details, aggminitval, fout);
14502 : }
14503 :
14504 572 : if (strcmp(aggmfinalfn, "-") != 0)
14505 : {
14506 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
14507 : aggmfinalfn);
14508 0 : if (aggmfinalextra)
14509 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
14510 0 : if (aggmfinalmodify != defaultfinalmodify)
14511 : {
14512 0 : switch (aggmfinalmodify)
14513 : {
14514 0 : case AGGMODIFY_READ_ONLY:
14515 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
14516 0 : break;
14517 0 : case AGGMODIFY_SHAREABLE:
14518 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
14519 0 : break;
14520 0 : case AGGMODIFY_READ_WRITE:
14521 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
14522 0 : break;
14523 0 : default:
14524 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
14525 : agginfo->aggfn.dobj.name);
14526 : break;
14527 : }
14528 572 : }
14529 : }
14530 :
14531 572 : aggsortconvop = getFormattedOperatorName(aggsortop);
14532 572 : if (aggsortconvop)
14533 : {
14534 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
14535 : aggsortconvop);
14536 0 : free(aggsortconvop);
14537 : }
14538 :
14539 572 : if (aggkind == AGGKIND_HYPOTHETICAL)
14540 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
14541 :
14542 572 : if (proparallel[0] != PROPARALLEL_UNSAFE)
14543 : {
14544 10 : if (proparallel[0] == PROPARALLEL_SAFE)
14545 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
14546 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
14547 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
14548 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
14549 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
14550 : agginfo->aggfn.dobj.name);
14551 : }
14552 :
14553 572 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
14554 572 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14555 : aggsig);
14556 :
14557 1144 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
14558 572 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
14559 : aggfullsig ? aggfullsig : aggsig, details->data);
14560 :
14561 572 : if (dopt->binary_upgrade)
14562 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
14563 : "AGGREGATE", aggsig,
14564 98 : agginfo->aggfn.dobj.namespace->dobj.name);
14565 :
14566 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
14567 538 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
14568 : agginfo->aggfn.dobj.dumpId,
14569 538 : ARCHIVE_OPTS(.tag = aggsig_tag,
14570 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
14571 : .owner = agginfo->aggfn.rolname,
14572 : .description = "AGGREGATE",
14573 : .section = SECTION_PRE_DATA,
14574 : .createStmt = q->data,
14575 : .dropStmt = delq->data));
14576 :
14577 : /* Dump Aggregate Comments */
14578 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
14579 20 : dumpComment(fout, "AGGREGATE", aggsig,
14580 20 : agginfo->aggfn.dobj.namespace->dobj.name,
14581 : agginfo->aggfn.rolname,
14582 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14583 :
14584 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
14585 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
14586 0 : agginfo->aggfn.dobj.namespace->dobj.name,
14587 : agginfo->aggfn.rolname,
14588 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
14589 :
14590 : /*
14591 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
14592 : * command look like a function's GRANT; in particular this affects the
14593 : * syntax for zero-argument aggregates and ordered-set aggregates.
14594 : */
14595 572 : free(aggsig);
14596 :
14597 572 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
14598 :
14599 572 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
14600 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
14601 : "FUNCTION", aggsig, NULL,
14602 36 : agginfo->aggfn.dobj.namespace->dobj.name,
14603 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
14604 :
14605 572 : free(aggsig);
14606 572 : free(aggfullsig);
14607 572 : free(aggsig_tag);
14608 :
14609 572 : PQclear(res);
14610 :
14611 572 : destroyPQExpBuffer(query);
14612 572 : destroyPQExpBuffer(q);
14613 572 : destroyPQExpBuffer(delq);
14614 572 : destroyPQExpBuffer(details);
14615 : }
14616 :
14617 : /*
14618 : * dumpTSParser
14619 : * write out a single text search parser
14620 : */
14621 : static void
14622 74 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
14623 : {
14624 74 : DumpOptions *dopt = fout->dopt;
14625 : PQExpBuffer q;
14626 : PQExpBuffer delq;
14627 : char *qprsname;
14628 :
14629 : /* Do nothing in data-only dump */
14630 74 : if (dopt->dataOnly)
14631 6 : return;
14632 :
14633 68 : q = createPQExpBuffer();
14634 68 : delq = createPQExpBuffer();
14635 :
14636 68 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
14637 :
14638 68 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
14639 68 : fmtQualifiedDumpable(prsinfo));
14640 :
14641 68 : appendPQExpBuffer(q, " START = %s,\n",
14642 : convertTSFunction(fout, prsinfo->prsstart));
14643 68 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
14644 : convertTSFunction(fout, prsinfo->prstoken));
14645 68 : appendPQExpBuffer(q, " END = %s,\n",
14646 : convertTSFunction(fout, prsinfo->prsend));
14647 68 : if (prsinfo->prsheadline != InvalidOid)
14648 2 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
14649 : convertTSFunction(fout, prsinfo->prsheadline));
14650 68 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
14651 : convertTSFunction(fout, prsinfo->prslextype));
14652 :
14653 68 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
14654 68 : fmtQualifiedDumpable(prsinfo));
14655 :
14656 68 : if (dopt->binary_upgrade)
14657 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
14658 : "TEXT SEARCH PARSER", qprsname,
14659 2 : prsinfo->dobj.namespace->dobj.name);
14660 :
14661 68 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14662 68 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
14663 68 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
14664 : .namespace = prsinfo->dobj.namespace->dobj.name,
14665 : .description = "TEXT SEARCH PARSER",
14666 : .section = SECTION_PRE_DATA,
14667 : .createStmt = q->data,
14668 : .dropStmt = delq->data));
14669 :
14670 : /* Dump Parser Comments */
14671 68 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14672 68 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
14673 68 : prsinfo->dobj.namespace->dobj.name, "",
14674 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
14675 :
14676 68 : destroyPQExpBuffer(q);
14677 68 : destroyPQExpBuffer(delq);
14678 68 : free(qprsname);
14679 : }
14680 :
14681 : /*
14682 : * dumpTSDictionary
14683 : * write out a single text search dictionary
14684 : */
14685 : static void
14686 220 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
14687 : {
14688 220 : DumpOptions *dopt = fout->dopt;
14689 : PQExpBuffer q;
14690 : PQExpBuffer delq;
14691 : PQExpBuffer query;
14692 : char *qdictname;
14693 : PGresult *res;
14694 : char *nspname;
14695 : char *tmplname;
14696 :
14697 : /* Do nothing in data-only dump */
14698 220 : if (dopt->dataOnly)
14699 6 : return;
14700 :
14701 214 : q = createPQExpBuffer();
14702 214 : delq = createPQExpBuffer();
14703 214 : query = createPQExpBuffer();
14704 :
14705 214 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
14706 :
14707 : /* Fetch name and namespace of the dictionary's template */
14708 214 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
14709 : "FROM pg_ts_template p, pg_namespace n "
14710 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
14711 : dictinfo->dicttemplate);
14712 214 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14713 214 : nspname = PQgetvalue(res, 0, 0);
14714 214 : tmplname = PQgetvalue(res, 0, 1);
14715 :
14716 214 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
14717 214 : fmtQualifiedDumpable(dictinfo));
14718 :
14719 214 : appendPQExpBufferStr(q, " TEMPLATE = ");
14720 214 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
14721 214 : appendPQExpBufferStr(q, fmtId(tmplname));
14722 :
14723 214 : PQclear(res);
14724 :
14725 : /* the dictinitoption can be dumped straight into the command */
14726 214 : if (dictinfo->dictinitoption)
14727 146 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
14728 :
14729 214 : appendPQExpBufferStr(q, " );\n");
14730 :
14731 214 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
14732 214 : fmtQualifiedDumpable(dictinfo));
14733 :
14734 214 : if (dopt->binary_upgrade)
14735 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
14736 : "TEXT SEARCH DICTIONARY", qdictname,
14737 20 : dictinfo->dobj.namespace->dobj.name);
14738 :
14739 214 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14740 214 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
14741 214 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
14742 : .namespace = dictinfo->dobj.namespace->dobj.name,
14743 : .owner = dictinfo->rolname,
14744 : .description = "TEXT SEARCH DICTIONARY",
14745 : .section = SECTION_PRE_DATA,
14746 : .createStmt = q->data,
14747 : .dropStmt = delq->data));
14748 :
14749 : /* Dump Dictionary Comments */
14750 214 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14751 124 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
14752 124 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
14753 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
14754 :
14755 214 : destroyPQExpBuffer(q);
14756 214 : destroyPQExpBuffer(delq);
14757 214 : destroyPQExpBuffer(query);
14758 214 : free(qdictname);
14759 : }
14760 :
14761 : /*
14762 : * dumpTSTemplate
14763 : * write out a single text search template
14764 : */
14765 : static void
14766 82 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
14767 : {
14768 82 : DumpOptions *dopt = fout->dopt;
14769 : PQExpBuffer q;
14770 : PQExpBuffer delq;
14771 : char *qtmplname;
14772 :
14773 : /* Do nothing in data-only dump */
14774 82 : if (dopt->dataOnly)
14775 6 : return;
14776 :
14777 76 : q = createPQExpBuffer();
14778 76 : delq = createPQExpBuffer();
14779 :
14780 76 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
14781 :
14782 76 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
14783 76 : fmtQualifiedDumpable(tmplinfo));
14784 :
14785 76 : if (tmplinfo->tmplinit != InvalidOid)
14786 10 : appendPQExpBuffer(q, " INIT = %s,\n",
14787 : convertTSFunction(fout, tmplinfo->tmplinit));
14788 76 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
14789 : convertTSFunction(fout, tmplinfo->tmpllexize));
14790 :
14791 76 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
14792 76 : fmtQualifiedDumpable(tmplinfo));
14793 :
14794 76 : if (dopt->binary_upgrade)
14795 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
14796 : "TEXT SEARCH TEMPLATE", qtmplname,
14797 2 : tmplinfo->dobj.namespace->dobj.name);
14798 :
14799 76 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14800 76 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
14801 76 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
14802 : .namespace = tmplinfo->dobj.namespace->dobj.name,
14803 : .description = "TEXT SEARCH TEMPLATE",
14804 : .section = SECTION_PRE_DATA,
14805 : .createStmt = q->data,
14806 : .dropStmt = delq->data));
14807 :
14808 : /* Dump Template Comments */
14809 76 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14810 76 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
14811 76 : tmplinfo->dobj.namespace->dobj.name, "",
14812 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
14813 :
14814 76 : destroyPQExpBuffer(q);
14815 76 : destroyPQExpBuffer(delq);
14816 76 : free(qtmplname);
14817 : }
14818 :
14819 : /*
14820 : * dumpTSConfig
14821 : * write out a single text search configuration
14822 : */
14823 : static void
14824 170 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
14825 : {
14826 170 : DumpOptions *dopt = fout->dopt;
14827 : PQExpBuffer q;
14828 : PQExpBuffer delq;
14829 : PQExpBuffer query;
14830 : char *qcfgname;
14831 : PGresult *res;
14832 : char *nspname;
14833 : char *prsname;
14834 : int ntups,
14835 : i;
14836 : int i_tokenname;
14837 : int i_dictname;
14838 :
14839 : /* Do nothing in data-only dump */
14840 170 : if (dopt->dataOnly)
14841 6 : return;
14842 :
14843 164 : q = createPQExpBuffer();
14844 164 : delq = createPQExpBuffer();
14845 164 : query = createPQExpBuffer();
14846 :
14847 164 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
14848 :
14849 : /* Fetch name and namespace of the config's parser */
14850 164 : appendPQExpBuffer(query, "SELECT nspname, prsname "
14851 : "FROM pg_ts_parser p, pg_namespace n "
14852 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
14853 : cfginfo->cfgparser);
14854 164 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14855 164 : nspname = PQgetvalue(res, 0, 0);
14856 164 : prsname = PQgetvalue(res, 0, 1);
14857 :
14858 164 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
14859 164 : fmtQualifiedDumpable(cfginfo));
14860 :
14861 164 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
14862 164 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
14863 :
14864 164 : PQclear(res);
14865 :
14866 164 : resetPQExpBuffer(query);
14867 164 : appendPQExpBuffer(query,
14868 : "SELECT\n"
14869 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
14870 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
14871 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
14872 : "FROM pg_catalog.pg_ts_config_map AS m\n"
14873 : "WHERE m.mapcfg = '%u'\n"
14874 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
14875 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
14876 :
14877 164 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14878 164 : ntups = PQntuples(res);
14879 :
14880 164 : i_tokenname = PQfnumber(res, "tokenname");
14881 164 : i_dictname = PQfnumber(res, "dictname");
14882 :
14883 3550 : for (i = 0; i < ntups; i++)
14884 : {
14885 3386 : char *tokenname = PQgetvalue(res, i, i_tokenname);
14886 3386 : char *dictname = PQgetvalue(res, i, i_dictname);
14887 :
14888 3386 : if (i == 0 ||
14889 3222 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
14890 : {
14891 : /* starting a new token type, so start a new command */
14892 3116 : if (i > 0)
14893 2952 : appendPQExpBufferStr(q, ";\n");
14894 3116 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
14895 3116 : fmtQualifiedDumpable(cfginfo));
14896 : /* tokenname needs quoting, dictname does NOT */
14897 3116 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
14898 : fmtId(tokenname), dictname);
14899 : }
14900 : else
14901 270 : appendPQExpBuffer(q, ", %s", dictname);
14902 : }
14903 :
14904 164 : if (ntups > 0)
14905 164 : appendPQExpBufferStr(q, ";\n");
14906 :
14907 164 : PQclear(res);
14908 :
14909 164 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
14910 164 : fmtQualifiedDumpable(cfginfo));
14911 :
14912 164 : if (dopt->binary_upgrade)
14913 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
14914 : "TEXT SEARCH CONFIGURATION", qcfgname,
14915 10 : cfginfo->dobj.namespace->dobj.name);
14916 :
14917 164 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14918 164 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
14919 164 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
14920 : .namespace = cfginfo->dobj.namespace->dobj.name,
14921 : .owner = cfginfo->rolname,
14922 : .description = "TEXT SEARCH CONFIGURATION",
14923 : .section = SECTION_PRE_DATA,
14924 : .createStmt = q->data,
14925 : .dropStmt = delq->data));
14926 :
14927 : /* Dump Configuration Comments */
14928 164 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14929 124 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
14930 124 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
14931 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
14932 :
14933 164 : destroyPQExpBuffer(q);
14934 164 : destroyPQExpBuffer(delq);
14935 164 : destroyPQExpBuffer(query);
14936 164 : free(qcfgname);
14937 : }
14938 :
14939 : /*
14940 : * dumpForeignDataWrapper
14941 : * write out a single foreign-data wrapper definition
14942 : */
14943 : static void
14944 100 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
14945 : {
14946 100 : DumpOptions *dopt = fout->dopt;
14947 : PQExpBuffer q;
14948 : PQExpBuffer delq;
14949 : char *qfdwname;
14950 :
14951 : /* Do nothing in data-only dump */
14952 100 : if (dopt->dataOnly)
14953 8 : return;
14954 :
14955 92 : q = createPQExpBuffer();
14956 92 : delq = createPQExpBuffer();
14957 :
14958 92 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
14959 :
14960 92 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
14961 : qfdwname);
14962 :
14963 92 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
14964 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
14965 :
14966 92 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
14967 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
14968 :
14969 92 : if (strlen(fdwinfo->fdwoptions) > 0)
14970 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
14971 :
14972 92 : appendPQExpBufferStr(q, ";\n");
14973 :
14974 92 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
14975 : qfdwname);
14976 :
14977 92 : if (dopt->binary_upgrade)
14978 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
14979 : "FOREIGN DATA WRAPPER", qfdwname,
14980 : NULL);
14981 :
14982 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14983 92 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
14984 92 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
14985 : .owner = fdwinfo->rolname,
14986 : .description = "FOREIGN DATA WRAPPER",
14987 : .section = SECTION_PRE_DATA,
14988 : .createStmt = q->data,
14989 : .dropStmt = delq->data));
14990 :
14991 : /* Dump Foreign Data Wrapper Comments */
14992 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14993 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
14994 : NULL, fdwinfo->rolname,
14995 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
14996 :
14997 : /* Handle the ACL */
14998 92 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
14999 64 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
15000 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
15001 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
15002 :
15003 92 : free(qfdwname);
15004 :
15005 92 : destroyPQExpBuffer(q);
15006 92 : destroyPQExpBuffer(delq);
15007 : }
15008 :
15009 : /*
15010 : * dumpForeignServer
15011 : * write out a foreign server definition
15012 : */
15013 : static void
15014 108 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
15015 : {
15016 108 : DumpOptions *dopt = fout->dopt;
15017 : PQExpBuffer q;
15018 : PQExpBuffer delq;
15019 : PQExpBuffer query;
15020 : PGresult *res;
15021 : char *qsrvname;
15022 : char *fdwname;
15023 :
15024 : /* Do nothing in data-only dump */
15025 108 : if (dopt->dataOnly)
15026 12 : return;
15027 :
15028 96 : q = createPQExpBuffer();
15029 96 : delq = createPQExpBuffer();
15030 96 : query = createPQExpBuffer();
15031 :
15032 96 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
15033 :
15034 : /* look up the foreign-data wrapper */
15035 96 : appendPQExpBuffer(query, "SELECT fdwname "
15036 : "FROM pg_foreign_data_wrapper w "
15037 : "WHERE w.oid = '%u'",
15038 : srvinfo->srvfdw);
15039 96 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15040 96 : fdwname = PQgetvalue(res, 0, 0);
15041 :
15042 96 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
15043 96 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
15044 : {
15045 0 : appendPQExpBufferStr(q, " TYPE ");
15046 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
15047 : }
15048 96 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
15049 : {
15050 0 : appendPQExpBufferStr(q, " VERSION ");
15051 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
15052 : }
15053 :
15054 96 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
15055 96 : appendPQExpBufferStr(q, fmtId(fdwname));
15056 :
15057 96 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
15058 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
15059 :
15060 96 : appendPQExpBufferStr(q, ";\n");
15061 :
15062 96 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
15063 : qsrvname);
15064 :
15065 96 : if (dopt->binary_upgrade)
15066 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
15067 : "SERVER", qsrvname, NULL);
15068 :
15069 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15070 96 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
15071 96 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
15072 : .owner = srvinfo->rolname,
15073 : .description = "SERVER",
15074 : .section = SECTION_PRE_DATA,
15075 : .createStmt = q->data,
15076 : .dropStmt = delq->data));
15077 :
15078 : /* Dump Foreign Server Comments */
15079 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15080 0 : dumpComment(fout, "SERVER", qsrvname,
15081 : NULL, srvinfo->rolname,
15082 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
15083 :
15084 : /* Handle the ACL */
15085 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
15086 64 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
15087 : "FOREIGN SERVER", qsrvname, NULL, NULL,
15088 : NULL, srvinfo->rolname, &srvinfo->dacl);
15089 :
15090 : /* Dump user mappings */
15091 96 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
15092 96 : dumpUserMappings(fout,
15093 96 : srvinfo->dobj.name, NULL,
15094 : srvinfo->rolname,
15095 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
15096 :
15097 96 : PQclear(res);
15098 :
15099 96 : free(qsrvname);
15100 :
15101 96 : destroyPQExpBuffer(q);
15102 96 : destroyPQExpBuffer(delq);
15103 96 : destroyPQExpBuffer(query);
15104 : }
15105 :
15106 : /*
15107 : * dumpUserMappings
15108 : *
15109 : * This routine is used to dump any user mappings associated with the
15110 : * server handed to this routine. Should be called after ArchiveEntry()
15111 : * for the server.
15112 : */
15113 : static void
15114 96 : dumpUserMappings(Archive *fout,
15115 : const char *servername, const char *namespace,
15116 : const char *owner,
15117 : CatalogId catalogId, DumpId dumpId)
15118 : {
15119 : PQExpBuffer q;
15120 : PQExpBuffer delq;
15121 : PQExpBuffer query;
15122 : PQExpBuffer tag;
15123 : PGresult *res;
15124 : int ntups;
15125 : int i_usename;
15126 : int i_umoptions;
15127 : int i;
15128 :
15129 96 : q = createPQExpBuffer();
15130 96 : tag = createPQExpBuffer();
15131 96 : delq = createPQExpBuffer();
15132 96 : query = createPQExpBuffer();
15133 :
15134 : /*
15135 : * We read from the publicly accessible view pg_user_mappings, so as not
15136 : * to fail if run by a non-superuser. Note that the view will show
15137 : * umoptions as null if the user hasn't got privileges for the associated
15138 : * server; this means that pg_dump will dump such a mapping, but with no
15139 : * OPTIONS clause. A possible alternative is to skip such mappings
15140 : * altogether, but it's not clear that that's an improvement.
15141 : */
15142 96 : appendPQExpBuffer(query,
15143 : "SELECT usename, "
15144 : "array_to_string(ARRAY("
15145 : "SELECT quote_ident(option_name) || ' ' || "
15146 : "quote_literal(option_value) "
15147 : "FROM pg_options_to_table(umoptions) "
15148 : "ORDER BY option_name"
15149 : "), E',\n ') AS umoptions "
15150 : "FROM pg_user_mappings "
15151 : "WHERE srvid = '%u' "
15152 : "ORDER BY usename",
15153 : catalogId.oid);
15154 :
15155 96 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15156 :
15157 96 : ntups = PQntuples(res);
15158 96 : i_usename = PQfnumber(res, "usename");
15159 96 : i_umoptions = PQfnumber(res, "umoptions");
15160 :
15161 160 : for (i = 0; i < ntups; i++)
15162 : {
15163 : char *usename;
15164 : char *umoptions;
15165 :
15166 64 : usename = PQgetvalue(res, i, i_usename);
15167 64 : umoptions = PQgetvalue(res, i, i_umoptions);
15168 :
15169 64 : resetPQExpBuffer(q);
15170 64 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
15171 64 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
15172 :
15173 64 : if (umoptions && strlen(umoptions) > 0)
15174 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
15175 :
15176 64 : appendPQExpBufferStr(q, ";\n");
15177 :
15178 64 : resetPQExpBuffer(delq);
15179 64 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
15180 64 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
15181 :
15182 64 : resetPQExpBuffer(tag);
15183 64 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
15184 : usename, servername);
15185 :
15186 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15187 64 : ARCHIVE_OPTS(.tag = tag->data,
15188 : .namespace = namespace,
15189 : .owner = owner,
15190 : .description = "USER MAPPING",
15191 : .section = SECTION_PRE_DATA,
15192 : .createStmt = q->data,
15193 : .dropStmt = delq->data));
15194 : }
15195 :
15196 96 : PQclear(res);
15197 :
15198 96 : destroyPQExpBuffer(query);
15199 96 : destroyPQExpBuffer(delq);
15200 96 : destroyPQExpBuffer(tag);
15201 96 : destroyPQExpBuffer(q);
15202 96 : }
15203 :
15204 : /*
15205 : * Write out default privileges information
15206 : */
15207 : static void
15208 284 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
15209 : {
15210 284 : DumpOptions *dopt = fout->dopt;
15211 : PQExpBuffer q;
15212 : PQExpBuffer tag;
15213 : const char *type;
15214 :
15215 : /* Do nothing in data-only dump, or if we're skipping ACLs */
15216 284 : if (dopt->dataOnly || dopt->aclsSkip)
15217 32 : return;
15218 :
15219 252 : q = createPQExpBuffer();
15220 252 : tag = createPQExpBuffer();
15221 :
15222 252 : switch (daclinfo->defaclobjtype)
15223 : {
15224 126 : case DEFACLOBJ_RELATION:
15225 126 : type = "TABLES";
15226 126 : break;
15227 0 : case DEFACLOBJ_SEQUENCE:
15228 0 : type = "SEQUENCES";
15229 0 : break;
15230 126 : case DEFACLOBJ_FUNCTION:
15231 126 : type = "FUNCTIONS";
15232 126 : break;
15233 0 : case DEFACLOBJ_TYPE:
15234 0 : type = "TYPES";
15235 0 : break;
15236 0 : case DEFACLOBJ_NAMESPACE:
15237 0 : type = "SCHEMAS";
15238 0 : break;
15239 0 : default:
15240 : /* shouldn't get here */
15241 0 : pg_fatal("unrecognized object type in default privileges: %d",
15242 : (int) daclinfo->defaclobjtype);
15243 : type = ""; /* keep compiler quiet */
15244 : }
15245 :
15246 252 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
15247 :
15248 : /* build the actual command(s) for this tuple */
15249 252 : if (!buildDefaultACLCommands(type,
15250 252 : daclinfo->dobj.namespace != NULL ?
15251 128 : daclinfo->dobj.namespace->dobj.name : NULL,
15252 252 : daclinfo->dacl.acl,
15253 252 : daclinfo->dacl.acldefault,
15254 : daclinfo->defaclrole,
15255 : fout->remoteVersion,
15256 : q))
15257 0 : pg_fatal("could not parse default ACL list (%s)",
15258 : daclinfo->dacl.acl);
15259 :
15260 252 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
15261 252 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
15262 252 : ARCHIVE_OPTS(.tag = tag->data,
15263 : .namespace = daclinfo->dobj.namespace ?
15264 : daclinfo->dobj.namespace->dobj.name : NULL,
15265 : .owner = daclinfo->defaclrole,
15266 : .description = "DEFAULT ACL",
15267 : .section = SECTION_POST_DATA,
15268 : .createStmt = q->data));
15269 :
15270 252 : destroyPQExpBuffer(tag);
15271 252 : destroyPQExpBuffer(q);
15272 : }
15273 :
15274 : /*----------
15275 : * Write out grant/revoke information
15276 : *
15277 : * 'objDumpId' is the dump ID of the underlying object.
15278 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
15279 : * or InvalidDumpId if there is no need for a second dependency.
15280 : * 'type' must be one of
15281 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
15282 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
15283 : * 'name' is the formatted name of the object. Must be quoted etc. already.
15284 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
15285 : * (Currently we assume that subname is only provided for table columns.)
15286 : * 'nspname' is the namespace the object is in (NULL if none).
15287 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
15288 : * to use the default for the object type.
15289 : * 'owner' is the owner, NULL if there is no owner (for languages).
15290 : * 'dacl' is the DumpableAcl struct for the object.
15291 : *
15292 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
15293 : * no ACL entry was created.
15294 : *----------
15295 : */
15296 : static DumpId
15297 45174 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
15298 : const char *type, const char *name, const char *subname,
15299 : const char *nspname, const char *tag, const char *owner,
15300 : const DumpableAcl *dacl)
15301 : {
15302 45174 : DumpId aclDumpId = InvalidDumpId;
15303 45174 : DumpOptions *dopt = fout->dopt;
15304 45174 : const char *acls = dacl->acl;
15305 45174 : const char *acldefault = dacl->acldefault;
15306 45174 : char privtype = dacl->privtype;
15307 45174 : const char *initprivs = dacl->initprivs;
15308 : const char *baseacls;
15309 : PQExpBuffer sql;
15310 :
15311 : /* Do nothing if ACL dump is not enabled */
15312 45174 : if (dopt->aclsSkip)
15313 636 : return InvalidDumpId;
15314 :
15315 : /* --data-only skips ACLs *except* large object ACLs */
15316 44538 : if (dopt->dataOnly && strcmp(type, "LARGE OBJECT") != 0)
15317 0 : return InvalidDumpId;
15318 :
15319 44538 : sql = createPQExpBuffer();
15320 :
15321 : /*
15322 : * In binary upgrade mode, we don't run an extension's script but instead
15323 : * dump out the objects independently and then recreate them. To preserve
15324 : * any initial privileges which were set on extension objects, we need to
15325 : * compute the set of GRANT and REVOKE commands necessary to get from the
15326 : * default privileges of an object to its initial privileges as recorded
15327 : * in pg_init_privs.
15328 : *
15329 : * At restore time, we apply these commands after having called
15330 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
15331 : * copy the results into pg_init_privs. This is how we preserve the
15332 : * contents of that catalog across binary upgrades.
15333 : */
15334 44538 : if (dopt->binary_upgrade && privtype == 'e' &&
15335 26 : initprivs && *initprivs != '\0')
15336 : {
15337 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
15338 26 : if (!buildACLCommands(name, subname, nspname, type,
15339 : initprivs, acldefault, owner,
15340 : "", fout->remoteVersion, sql))
15341 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
15342 : initprivs, acldefault, name, type);
15343 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
15344 : }
15345 :
15346 : /*
15347 : * Now figure the GRANT and REVOKE commands needed to get to the object's
15348 : * actual current ACL, starting from the initprivs if given, else from the
15349 : * object-type-specific default. Also, while buildACLCommands will assume
15350 : * that a NULL/empty acls string means it needn't do anything, what that
15351 : * actually represents is the object-type-specific default; so we need to
15352 : * substitute the acldefault string to get the right results in that case.
15353 : */
15354 44538 : if (initprivs && *initprivs != '\0')
15355 : {
15356 41070 : baseacls = initprivs;
15357 41070 : if (acls == NULL || *acls == '\0')
15358 34 : acls = acldefault;
15359 : }
15360 : else
15361 3468 : baseacls = acldefault;
15362 :
15363 44538 : if (!buildACLCommands(name, subname, nspname, type,
15364 : acls, baseacls, owner,
15365 : "", fout->remoteVersion, sql))
15366 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
15367 : acls, baseacls, name, type);
15368 :
15369 44538 : if (sql->len > 0)
15370 : {
15371 3636 : PQExpBuffer tagbuf = createPQExpBuffer();
15372 : DumpId aclDeps[2];
15373 3636 : int nDeps = 0;
15374 :
15375 3636 : if (tag)
15376 0 : appendPQExpBufferStr(tagbuf, tag);
15377 3636 : else if (subname)
15378 2146 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
15379 : else
15380 1490 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
15381 :
15382 3636 : aclDeps[nDeps++] = objDumpId;
15383 3636 : if (altDumpId != InvalidDumpId)
15384 1998 : aclDeps[nDeps++] = altDumpId;
15385 :
15386 3636 : aclDumpId = createDumpId();
15387 :
15388 3636 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
15389 3636 : ARCHIVE_OPTS(.tag = tagbuf->data,
15390 : .namespace = nspname,
15391 : .owner = owner,
15392 : .description = "ACL",
15393 : .section = SECTION_NONE,
15394 : .createStmt = sql->data,
15395 : .deps = aclDeps,
15396 : .nDeps = nDeps));
15397 :
15398 3636 : destroyPQExpBuffer(tagbuf);
15399 : }
15400 :
15401 44538 : destroyPQExpBuffer(sql);
15402 :
15403 44538 : return aclDumpId;
15404 : }
15405 :
15406 : /*
15407 : * dumpSecLabel
15408 : *
15409 : * This routine is used to dump any security labels associated with the
15410 : * object handed to this routine. The routine takes the object type
15411 : * and object name (ready to print, except for schema decoration), plus
15412 : * the namespace and owner of the object (for labeling the ArchiveEntry),
15413 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
15414 : * plus the dump ID for the object (for setting a dependency).
15415 : * If a matching pg_seclabel entry is found, it is dumped.
15416 : *
15417 : * Note: although this routine takes a dumpId for dependency purposes,
15418 : * that purpose is just to mark the dependency in the emitted dump file
15419 : * for possible future use by pg_restore. We do NOT use it for determining
15420 : * ordering of the label in the dump file, because this routine is called
15421 : * after dependency sorting occurs. This routine should be called just after
15422 : * calling ArchiveEntry() for the specified object.
15423 : */
15424 : static void
15425 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
15426 : const char *namespace, const char *owner,
15427 : CatalogId catalogId, int subid, DumpId dumpId)
15428 : {
15429 0 : DumpOptions *dopt = fout->dopt;
15430 : SecLabelItem *labels;
15431 : int nlabels;
15432 : int i;
15433 : PQExpBuffer query;
15434 :
15435 : /* do nothing, if --no-security-labels is supplied */
15436 0 : if (dopt->no_security_labels)
15437 0 : return;
15438 :
15439 : /*
15440 : * Security labels are schema not data ... except large object labels are
15441 : * data
15442 : */
15443 0 : if (strcmp(type, "LARGE OBJECT") != 0)
15444 : {
15445 0 : if (dopt->dataOnly)
15446 0 : return;
15447 : }
15448 : else
15449 : {
15450 : /* We do dump large object security labels in binary-upgrade mode */
15451 0 : if (dopt->schemaOnly && !dopt->binary_upgrade)
15452 0 : return;
15453 : }
15454 :
15455 : /* Search for security labels associated with catalogId, using table */
15456 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
15457 :
15458 0 : query = createPQExpBuffer();
15459 :
15460 0 : for (i = 0; i < nlabels; i++)
15461 : {
15462 : /*
15463 : * Ignore label entries for which the subid doesn't match.
15464 : */
15465 0 : if (labels[i].objsubid != subid)
15466 0 : continue;
15467 :
15468 0 : appendPQExpBuffer(query,
15469 : "SECURITY LABEL FOR %s ON %s ",
15470 0 : fmtId(labels[i].provider), type);
15471 0 : if (namespace && *namespace)
15472 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
15473 0 : appendPQExpBuffer(query, "%s IS ", name);
15474 0 : appendStringLiteralAH(query, labels[i].label, fout);
15475 0 : appendPQExpBufferStr(query, ";\n");
15476 : }
15477 :
15478 0 : if (query->len > 0)
15479 : {
15480 0 : PQExpBuffer tag = createPQExpBuffer();
15481 :
15482 0 : appendPQExpBuffer(tag, "%s %s", type, name);
15483 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15484 0 : ARCHIVE_OPTS(.tag = tag->data,
15485 : .namespace = namespace,
15486 : .owner = owner,
15487 : .description = "SECURITY LABEL",
15488 : .section = SECTION_NONE,
15489 : .createStmt = query->data,
15490 : .deps = &dumpId,
15491 : .nDeps = 1));
15492 0 : destroyPQExpBuffer(tag);
15493 : }
15494 :
15495 0 : destroyPQExpBuffer(query);
15496 : }
15497 :
15498 : /*
15499 : * dumpTableSecLabel
15500 : *
15501 : * As above, but dump security label for both the specified table (or view)
15502 : * and its columns.
15503 : */
15504 : static void
15505 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
15506 : {
15507 0 : DumpOptions *dopt = fout->dopt;
15508 : SecLabelItem *labels;
15509 : int nlabels;
15510 : int i;
15511 : PQExpBuffer query;
15512 : PQExpBuffer target;
15513 :
15514 : /* do nothing, if --no-security-labels is supplied */
15515 0 : if (dopt->no_security_labels)
15516 0 : return;
15517 :
15518 : /* SecLabel are SCHEMA not data */
15519 0 : if (dopt->dataOnly)
15520 0 : return;
15521 :
15522 : /* Search for comments associated with relation, using table */
15523 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
15524 : tbinfo->dobj.catId.oid,
15525 : &labels);
15526 :
15527 : /* If security labels exist, build SECURITY LABEL statements */
15528 0 : if (nlabels <= 0)
15529 0 : return;
15530 :
15531 0 : query = createPQExpBuffer();
15532 0 : target = createPQExpBuffer();
15533 :
15534 0 : for (i = 0; i < nlabels; i++)
15535 : {
15536 : const char *colname;
15537 0 : const char *provider = labels[i].provider;
15538 0 : const char *label = labels[i].label;
15539 0 : int objsubid = labels[i].objsubid;
15540 :
15541 0 : resetPQExpBuffer(target);
15542 0 : if (objsubid == 0)
15543 : {
15544 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15545 0 : fmtQualifiedDumpable(tbinfo));
15546 : }
15547 : else
15548 : {
15549 0 : colname = getAttrName(objsubid, tbinfo);
15550 : /* first fmtXXX result must be consumed before calling again */
15551 0 : appendPQExpBuffer(target, "COLUMN %s",
15552 0 : fmtQualifiedDumpable(tbinfo));
15553 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
15554 : }
15555 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
15556 : fmtId(provider), target->data);
15557 0 : appendStringLiteralAH(query, label, fout);
15558 0 : appendPQExpBufferStr(query, ";\n");
15559 : }
15560 0 : if (query->len > 0)
15561 : {
15562 0 : resetPQExpBuffer(target);
15563 0 : appendPQExpBuffer(target, "%s %s", reltypename,
15564 0 : fmtId(tbinfo->dobj.name));
15565 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
15566 0 : ARCHIVE_OPTS(.tag = target->data,
15567 : .namespace = tbinfo->dobj.namespace->dobj.name,
15568 : .owner = tbinfo->rolname,
15569 : .description = "SECURITY LABEL",
15570 : .section = SECTION_NONE,
15571 : .createStmt = query->data,
15572 : .deps = &(tbinfo->dobj.dumpId),
15573 : .nDeps = 1));
15574 : }
15575 0 : destroyPQExpBuffer(query);
15576 0 : destroyPQExpBuffer(target);
15577 : }
15578 :
15579 : /*
15580 : * findSecLabels
15581 : *
15582 : * Find the security label(s), if any, associated with the given object.
15583 : * All the objsubid values associated with the given classoid/objoid are
15584 : * found with one search.
15585 : */
15586 : static int
15587 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
15588 : {
15589 0 : SecLabelItem *middle = NULL;
15590 : SecLabelItem *low;
15591 : SecLabelItem *high;
15592 : int nmatch;
15593 :
15594 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
15595 : {
15596 0 : *items = NULL;
15597 0 : return 0;
15598 : }
15599 :
15600 : /*
15601 : * Do binary search to find some item matching the object.
15602 : */
15603 0 : low = &seclabels[0];
15604 0 : high = &seclabels[nseclabels - 1];
15605 0 : while (low <= high)
15606 : {
15607 0 : middle = low + (high - low) / 2;
15608 :
15609 0 : if (classoid < middle->classoid)
15610 0 : high = middle - 1;
15611 0 : else if (classoid > middle->classoid)
15612 0 : low = middle + 1;
15613 0 : else if (objoid < middle->objoid)
15614 0 : high = middle - 1;
15615 0 : else if (objoid > middle->objoid)
15616 0 : low = middle + 1;
15617 : else
15618 0 : break; /* found a match */
15619 : }
15620 :
15621 0 : if (low > high) /* no matches */
15622 : {
15623 0 : *items = NULL;
15624 0 : return 0;
15625 : }
15626 :
15627 : /*
15628 : * Now determine how many items match the object. The search loop
15629 : * invariant still holds: only items between low and high inclusive could
15630 : * match.
15631 : */
15632 0 : nmatch = 1;
15633 0 : while (middle > low)
15634 : {
15635 0 : if (classoid != middle[-1].classoid ||
15636 0 : objoid != middle[-1].objoid)
15637 : break;
15638 0 : middle--;
15639 0 : nmatch++;
15640 : }
15641 :
15642 0 : *items = middle;
15643 :
15644 0 : middle += nmatch;
15645 0 : while (middle <= high)
15646 : {
15647 0 : if (classoid != middle->classoid ||
15648 0 : objoid != middle->objoid)
15649 : break;
15650 0 : middle++;
15651 0 : nmatch++;
15652 : }
15653 :
15654 0 : return nmatch;
15655 : }
15656 :
15657 : /*
15658 : * collectSecLabels
15659 : *
15660 : * Construct a table of all security labels available for database objects;
15661 : * also set the has-seclabel component flag for each relevant object.
15662 : *
15663 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
15664 : */
15665 : static void
15666 304 : collectSecLabels(Archive *fout)
15667 : {
15668 : PGresult *res;
15669 : PQExpBuffer query;
15670 : int i_label;
15671 : int i_provider;
15672 : int i_classoid;
15673 : int i_objoid;
15674 : int i_objsubid;
15675 : int ntups;
15676 : int i;
15677 : DumpableObject *dobj;
15678 :
15679 304 : query = createPQExpBuffer();
15680 :
15681 304 : appendPQExpBufferStr(query,
15682 : "SELECT label, provider, classoid, objoid, objsubid "
15683 : "FROM pg_catalog.pg_seclabel "
15684 : "ORDER BY classoid, objoid, objsubid");
15685 :
15686 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15687 :
15688 : /* Construct lookup table containing OIDs in numeric form */
15689 304 : i_label = PQfnumber(res, "label");
15690 304 : i_provider = PQfnumber(res, "provider");
15691 304 : i_classoid = PQfnumber(res, "classoid");
15692 304 : i_objoid = PQfnumber(res, "objoid");
15693 304 : i_objsubid = PQfnumber(res, "objsubid");
15694 :
15695 304 : ntups = PQntuples(res);
15696 :
15697 304 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
15698 304 : nseclabels = 0;
15699 304 : dobj = NULL;
15700 :
15701 304 : for (i = 0; i < ntups; i++)
15702 : {
15703 : CatalogId objId;
15704 : int subid;
15705 :
15706 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
15707 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
15708 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
15709 :
15710 : /* We needn't remember labels that don't match any dumpable object */
15711 0 : if (dobj == NULL ||
15712 0 : dobj->catId.tableoid != objId.tableoid ||
15713 0 : dobj->catId.oid != objId.oid)
15714 0 : dobj = findObjectByCatalogId(objId);
15715 0 : if (dobj == NULL)
15716 0 : continue;
15717 :
15718 : /*
15719 : * Labels on columns of composite types are linked to the type's
15720 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
15721 : * in the type's own DumpableObject.
15722 : */
15723 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
15724 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
15725 0 : {
15726 : TypeInfo *cTypeInfo;
15727 :
15728 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
15729 0 : if (cTypeInfo)
15730 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
15731 : }
15732 : else
15733 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
15734 :
15735 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
15736 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
15737 0 : seclabels[nseclabels].classoid = objId.tableoid;
15738 0 : seclabels[nseclabels].objoid = objId.oid;
15739 0 : seclabels[nseclabels].objsubid = subid;
15740 0 : nseclabels++;
15741 : }
15742 :
15743 304 : PQclear(res);
15744 304 : destroyPQExpBuffer(query);
15745 304 : }
15746 :
15747 : /*
15748 : * dumpTable
15749 : * write out to fout the declarations (not data) of a user-defined table
15750 : */
15751 : static void
15752 49336 : dumpTable(Archive *fout, const TableInfo *tbinfo)
15753 : {
15754 49336 : DumpOptions *dopt = fout->dopt;
15755 49336 : DumpId tableAclDumpId = InvalidDumpId;
15756 : char *namecopy;
15757 :
15758 : /* Do nothing in data-only dump */
15759 49336 : if (dopt->dataOnly)
15760 1734 : return;
15761 :
15762 47602 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15763 : {
15764 11228 : if (tbinfo->relkind == RELKIND_SEQUENCE)
15765 686 : dumpSequence(fout, tbinfo);
15766 : else
15767 10542 : dumpTableSchema(fout, tbinfo);
15768 : }
15769 :
15770 : /* Handle the ACL here */
15771 47602 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
15772 47602 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
15773 : {
15774 37238 : const char *objtype =
15775 37238 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
15776 :
15777 : tableAclDumpId =
15778 37238 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
15779 : objtype, namecopy, NULL,
15780 37238 : tbinfo->dobj.namespace->dobj.name,
15781 : NULL, tbinfo->rolname, &tbinfo->dacl);
15782 : }
15783 :
15784 : /*
15785 : * Handle column ACLs, if any. Note: we pull these with a separate query
15786 : * rather than trying to fetch them during getTableAttrs, so that we won't
15787 : * miss ACLs on system columns. Doing it this way also allows us to dump
15788 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
15789 : */
15790 47602 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
15791 : {
15792 498 : PQExpBuffer query = createPQExpBuffer();
15793 : PGresult *res;
15794 : int i;
15795 :
15796 498 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
15797 : {
15798 : /* Set up query for column ACLs */
15799 258 : appendPQExpBufferStr(query,
15800 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
15801 :
15802 258 : if (fout->remoteVersion >= 90600)
15803 : {
15804 : /*
15805 : * In principle we should call acldefault('c', relowner) to
15806 : * get the default ACL for a column. However, we don't
15807 : * currently store the numeric OID of the relowner in
15808 : * TableInfo. We could convert the owner name using regrole,
15809 : * but that creates a risk of failure due to concurrent role
15810 : * renames. Given that the default ACL for columns is empty
15811 : * and is likely to stay that way, it's not worth extra cycles
15812 : * and risk to avoid hard-wiring that knowledge here.
15813 : */
15814 258 : appendPQExpBufferStr(query,
15815 : "SELECT at.attname, "
15816 : "at.attacl, "
15817 : "'{}' AS acldefault, "
15818 : "pip.privtype, pip.initprivs "
15819 : "FROM pg_catalog.pg_attribute at "
15820 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
15821 : "(at.attrelid = pip.objoid "
15822 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
15823 : "AND at.attnum = pip.objsubid) "
15824 : "WHERE at.attrelid = $1 AND "
15825 : "NOT at.attisdropped "
15826 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
15827 : "ORDER BY at.attnum");
15828 : }
15829 : else
15830 : {
15831 0 : appendPQExpBufferStr(query,
15832 : "SELECT attname, attacl, '{}' AS acldefault, "
15833 : "NULL AS privtype, NULL AS initprivs "
15834 : "FROM pg_catalog.pg_attribute "
15835 : "WHERE attrelid = $1 AND NOT attisdropped "
15836 : "AND attacl IS NOT NULL "
15837 : "ORDER BY attnum");
15838 : }
15839 :
15840 258 : ExecuteSqlStatement(fout, query->data);
15841 :
15842 258 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
15843 : }
15844 :
15845 498 : printfPQExpBuffer(query,
15846 : "EXECUTE getColumnACLs('%u')",
15847 : tbinfo->dobj.catId.oid);
15848 :
15849 498 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15850 :
15851 6928 : for (i = 0; i < PQntuples(res); i++)
15852 : {
15853 6430 : char *attname = PQgetvalue(res, i, 0);
15854 6430 : char *attacl = PQgetvalue(res, i, 1);
15855 6430 : char *acldefault = PQgetvalue(res, i, 2);
15856 6430 : char privtype = *(PQgetvalue(res, i, 3));
15857 6430 : char *initprivs = PQgetvalue(res, i, 4);
15858 : DumpableAcl coldacl;
15859 : char *attnamecopy;
15860 :
15861 6430 : coldacl.acl = attacl;
15862 6430 : coldacl.acldefault = acldefault;
15863 6430 : coldacl.privtype = privtype;
15864 6430 : coldacl.initprivs = initprivs;
15865 6430 : attnamecopy = pg_strdup(fmtId(attname));
15866 :
15867 : /*
15868 : * Column's GRANT type is always TABLE. Each column ACL depends
15869 : * on the table-level ACL, since we can restore column ACLs in
15870 : * parallel but the table-level ACL has to be done first.
15871 : */
15872 6430 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
15873 : "TABLE", namecopy, attnamecopy,
15874 6430 : tbinfo->dobj.namespace->dobj.name,
15875 : NULL, tbinfo->rolname, &coldacl);
15876 6430 : free(attnamecopy);
15877 : }
15878 498 : PQclear(res);
15879 498 : destroyPQExpBuffer(query);
15880 : }
15881 :
15882 47602 : free(namecopy);
15883 : }
15884 :
15885 : /*
15886 : * Create the AS clause for a view or materialized view. The semicolon is
15887 : * stripped because a materialized view must add a WITH NO DATA clause.
15888 : *
15889 : * This returns a new buffer which must be freed by the caller.
15890 : */
15891 : static PQExpBuffer
15892 1366 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
15893 : {
15894 1366 : PQExpBuffer query = createPQExpBuffer();
15895 1366 : PQExpBuffer result = createPQExpBuffer();
15896 : PGresult *res;
15897 : int len;
15898 :
15899 : /* Fetch the view definition */
15900 1366 : appendPQExpBuffer(query,
15901 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
15902 : tbinfo->dobj.catId.oid);
15903 :
15904 1366 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15905 :
15906 1366 : if (PQntuples(res) != 1)
15907 : {
15908 0 : if (PQntuples(res) < 1)
15909 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
15910 : tbinfo->dobj.name);
15911 : else
15912 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
15913 : tbinfo->dobj.name);
15914 : }
15915 :
15916 1366 : len = PQgetlength(res, 0, 0);
15917 :
15918 1366 : if (len == 0)
15919 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
15920 : tbinfo->dobj.name);
15921 :
15922 : /* Strip off the trailing semicolon so that other things may follow. */
15923 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
15924 1366 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
15925 :
15926 1366 : PQclear(res);
15927 1366 : destroyPQExpBuffer(query);
15928 :
15929 1366 : return result;
15930 : }
15931 :
15932 : /*
15933 : * Create a dummy AS clause for a view. This is used when the real view
15934 : * definition has to be postponed because of circular dependencies.
15935 : * We must duplicate the view's external properties -- column names and types
15936 : * (including collation) -- so that it works for subsequent references.
15937 : *
15938 : * This returns a new buffer which must be freed by the caller.
15939 : */
15940 : static PQExpBuffer
15941 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
15942 : {
15943 40 : PQExpBuffer result = createPQExpBuffer();
15944 : int j;
15945 :
15946 40 : appendPQExpBufferStr(result, "SELECT");
15947 :
15948 80 : for (j = 0; j < tbinfo->numatts; j++)
15949 : {
15950 40 : if (j > 0)
15951 20 : appendPQExpBufferChar(result, ',');
15952 40 : appendPQExpBufferStr(result, "\n ");
15953 :
15954 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
15955 :
15956 : /*
15957 : * Must add collation if not default for the type, because CREATE OR
15958 : * REPLACE VIEW won't change it
15959 : */
15960 40 : if (OidIsValid(tbinfo->attcollation[j]))
15961 : {
15962 : CollInfo *coll;
15963 :
15964 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
15965 0 : if (coll)
15966 0 : appendPQExpBuffer(result, " COLLATE %s",
15967 0 : fmtQualifiedDumpable(coll));
15968 : }
15969 :
15970 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
15971 : }
15972 :
15973 40 : return result;
15974 : }
15975 :
15976 : /*
15977 : * dumpTableSchema
15978 : * write the declaration (not data) of one user-defined table or view
15979 : */
15980 : static void
15981 10542 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
15982 : {
15983 10542 : DumpOptions *dopt = fout->dopt;
15984 10542 : PQExpBuffer q = createPQExpBuffer();
15985 10542 : PQExpBuffer delq = createPQExpBuffer();
15986 : char *qrelname;
15987 : char *qualrelname;
15988 : int numParents;
15989 : TableInfo **parents;
15990 : int actual_atts; /* number of attrs in this CREATE statement */
15991 : const char *reltypename;
15992 : char *storage;
15993 : int j,
15994 : k;
15995 :
15996 : /* We had better have loaded per-column details about this table */
15997 : Assert(tbinfo->interesting);
15998 :
15999 10542 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
16000 10542 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16001 :
16002 10542 : if (tbinfo->hasoids)
16003 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
16004 : qrelname);
16005 :
16006 10542 : if (dopt->binary_upgrade)
16007 1510 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
16008 :
16009 : /* Is it a table or a view? */
16010 10542 : if (tbinfo->relkind == RELKIND_VIEW)
16011 : {
16012 : PQExpBuffer result;
16013 :
16014 : /*
16015 : * Note: keep this code in sync with the is_view case in dumpRule()
16016 : */
16017 :
16018 692 : reltypename = "VIEW";
16019 :
16020 692 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
16021 :
16022 692 : if (dopt->binary_upgrade)
16023 96 : binary_upgrade_set_pg_class_oids(fout, q,
16024 : tbinfo->dobj.catId.oid, false);
16025 :
16026 692 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
16027 :
16028 692 : if (tbinfo->dummy_view)
16029 20 : result = createDummyViewAsClause(fout, tbinfo);
16030 : else
16031 : {
16032 672 : if (nonemptyReloptions(tbinfo->reloptions))
16033 : {
16034 112 : appendPQExpBufferStr(q, " WITH (");
16035 112 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16036 112 : appendPQExpBufferChar(q, ')');
16037 : }
16038 672 : result = createViewAsClause(fout, tbinfo);
16039 : }
16040 692 : appendPQExpBuffer(q, " AS\n%s", result->data);
16041 692 : destroyPQExpBuffer(result);
16042 :
16043 692 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
16044 66 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
16045 692 : appendPQExpBufferStr(q, ";\n");
16046 : }
16047 : else
16048 : {
16049 9850 : char *partkeydef = NULL;
16050 9850 : char *ftoptions = NULL;
16051 9850 : char *srvname = NULL;
16052 9850 : char *foreign = "";
16053 :
16054 : /*
16055 : * Set reltypename, and collect any relkind-specific data that we
16056 : * didn't fetch during getTables().
16057 : */
16058 9850 : switch (tbinfo->relkind)
16059 : {
16060 1006 : case RELKIND_PARTITIONED_TABLE:
16061 : {
16062 1006 : PQExpBuffer query = createPQExpBuffer();
16063 : PGresult *res;
16064 :
16065 1006 : reltypename = "TABLE";
16066 :
16067 : /* retrieve partition key definition */
16068 1006 : appendPQExpBuffer(query,
16069 : "SELECT pg_get_partkeydef('%u')",
16070 : tbinfo->dobj.catId.oid);
16071 1006 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16072 1006 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
16073 1006 : PQclear(res);
16074 1006 : destroyPQExpBuffer(query);
16075 1006 : break;
16076 : }
16077 70 : case RELKIND_FOREIGN_TABLE:
16078 : {
16079 70 : PQExpBuffer query = createPQExpBuffer();
16080 : PGresult *res;
16081 : int i_srvname;
16082 : int i_ftoptions;
16083 :
16084 70 : reltypename = "FOREIGN TABLE";
16085 :
16086 : /* retrieve name of foreign server and generic options */
16087 70 : appendPQExpBuffer(query,
16088 : "SELECT fs.srvname, "
16089 : "pg_catalog.array_to_string(ARRAY("
16090 : "SELECT pg_catalog.quote_ident(option_name) || "
16091 : "' ' || pg_catalog.quote_literal(option_value) "
16092 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
16093 : "ORDER BY option_name"
16094 : "), E',\n ') AS ftoptions "
16095 : "FROM pg_catalog.pg_foreign_table ft "
16096 : "JOIN pg_catalog.pg_foreign_server fs "
16097 : "ON (fs.oid = ft.ftserver) "
16098 : "WHERE ft.ftrelid = '%u'",
16099 : tbinfo->dobj.catId.oid);
16100 70 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16101 70 : i_srvname = PQfnumber(res, "srvname");
16102 70 : i_ftoptions = PQfnumber(res, "ftoptions");
16103 70 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
16104 70 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
16105 70 : PQclear(res);
16106 70 : destroyPQExpBuffer(query);
16107 :
16108 70 : foreign = "FOREIGN ";
16109 70 : break;
16110 : }
16111 674 : case RELKIND_MATVIEW:
16112 674 : reltypename = "MATERIALIZED VIEW";
16113 674 : break;
16114 8100 : default:
16115 8100 : reltypename = "TABLE";
16116 8100 : break;
16117 : }
16118 :
16119 9850 : numParents = tbinfo->numParents;
16120 9850 : parents = tbinfo->parents;
16121 :
16122 9850 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
16123 :
16124 9850 : if (dopt->binary_upgrade)
16125 1414 : binary_upgrade_set_pg_class_oids(fout, q,
16126 : tbinfo->dobj.catId.oid, false);
16127 :
16128 9850 : appendPQExpBuffer(q, "CREATE %s%s %s",
16129 9850 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
16130 : "UNLOGGED " : "",
16131 : reltypename,
16132 : qualrelname);
16133 :
16134 : /*
16135 : * Attach to type, if reloftype; except in case of a binary upgrade,
16136 : * we dump the table normally and attach it to the type afterward.
16137 : */
16138 9850 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
16139 48 : appendPQExpBuffer(q, " OF %s",
16140 : getFormattedTypeName(fout, tbinfo->reloftype,
16141 : zeroIsError));
16142 :
16143 9850 : if (tbinfo->relkind != RELKIND_MATVIEW)
16144 : {
16145 : /* Dump the attributes */
16146 9176 : actual_atts = 0;
16147 44028 : for (j = 0; j < tbinfo->numatts; j++)
16148 : {
16149 : /*
16150 : * Normally, dump if it's locally defined in this table, and
16151 : * not dropped. But for binary upgrade, we'll dump all the
16152 : * columns, and then fix up the dropped and nonlocal cases
16153 : * below.
16154 : */
16155 34852 : if (shouldPrintColumn(dopt, tbinfo, j))
16156 : {
16157 : bool print_default;
16158 : bool print_notnull;
16159 :
16160 : /*
16161 : * Default value --- suppress if to be printed separately
16162 : * or not at all.
16163 : */
16164 67970 : print_default = (tbinfo->attrdefs[j] != NULL &&
16165 34668 : tbinfo->attrdefs[j]->dobj.dump &&
16166 1446 : !tbinfo->attrdefs[j]->separate);
16167 :
16168 : /*
16169 : * Not Null constraint --- suppress unless it is locally
16170 : * defined, except if partition, or in binary-upgrade case
16171 : * where that won't work.
16172 : */
16173 33222 : print_notnull =
16174 37520 : (tbinfo->notnull_constrs[j] != NULL &&
16175 4298 : (!tbinfo->notnull_inh[j] || tbinfo->ispartition ||
16176 96 : dopt->binary_upgrade));
16177 :
16178 : /*
16179 : * Skip column if fully defined by reloftype, except in
16180 : * binary upgrade
16181 : */
16182 33222 : if (OidIsValid(tbinfo->reloftype) &&
16183 100 : !print_default && !print_notnull &&
16184 60 : !dopt->binary_upgrade)
16185 48 : continue;
16186 :
16187 : /* Format properly if not first attr */
16188 33174 : if (actual_atts == 0)
16189 8682 : appendPQExpBufferStr(q, " (");
16190 : else
16191 24492 : appendPQExpBufferChar(q, ',');
16192 33174 : appendPQExpBufferStr(q, "\n ");
16193 33174 : actual_atts++;
16194 :
16195 : /* Attribute name */
16196 33174 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
16197 :
16198 33174 : if (tbinfo->attisdropped[j])
16199 : {
16200 : /*
16201 : * ALTER TABLE DROP COLUMN clears
16202 : * pg_attribute.atttypid, so we will not have gotten a
16203 : * valid type name; insert INTEGER as a stopgap. We'll
16204 : * clean things up later.
16205 : */
16206 160 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
16207 : /* and skip to the next column */
16208 160 : continue;
16209 : }
16210 :
16211 : /*
16212 : * Attribute type; print it except when creating a typed
16213 : * table ('OF type_name'), but in binary-upgrade mode,
16214 : * print it in that case too.
16215 : */
16216 33014 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
16217 : {
16218 32982 : appendPQExpBuffer(q, " %s",
16219 32982 : tbinfo->atttypnames[j]);
16220 : }
16221 :
16222 33014 : if (print_default)
16223 : {
16224 1232 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
16225 530 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
16226 530 : tbinfo->attrdefs[j]->adef_expr);
16227 : else
16228 702 : appendPQExpBuffer(q, " DEFAULT %s",
16229 702 : tbinfo->attrdefs[j]->adef_expr);
16230 : }
16231 :
16232 :
16233 33014 : if (print_notnull)
16234 : {
16235 4234 : if (tbinfo->notnull_constrs[j][0] == '\0')
16236 1990 : appendPQExpBufferStr(q, " NOT NULL");
16237 : else
16238 2244 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
16239 2244 : fmtId(tbinfo->notnull_constrs[j]));
16240 :
16241 4234 : if (tbinfo->notnull_noinh[j])
16242 1438 : appendPQExpBufferStr(q, " NO INHERIT");
16243 : }
16244 :
16245 : /* Add collation if not default for the type */
16246 33014 : if (OidIsValid(tbinfo->attcollation[j]))
16247 : {
16248 : CollInfo *coll;
16249 :
16250 138 : coll = findCollationByOid(tbinfo->attcollation[j]);
16251 138 : if (coll)
16252 138 : appendPQExpBuffer(q, " COLLATE %s",
16253 138 : fmtQualifiedDumpable(coll));
16254 : }
16255 : }
16256 : }
16257 :
16258 : /*
16259 : * Add non-inherited CHECK constraints, if any.
16260 : *
16261 : * For partitions, we need to include check constraints even if
16262 : * they're not defined locally, because the ALTER TABLE ATTACH
16263 : * PARTITION that we'll emit later expects the constraint to be
16264 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
16265 : */
16266 10232 : for (j = 0; j < tbinfo->ncheck; j++)
16267 : {
16268 1056 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16269 :
16270 1056 : if (constr->separate ||
16271 976 : (!constr->conislocal && !tbinfo->ispartition))
16272 156 : continue;
16273 :
16274 900 : if (actual_atts == 0)
16275 32 : appendPQExpBufferStr(q, " (\n ");
16276 : else
16277 868 : appendPQExpBufferStr(q, ",\n ");
16278 :
16279 900 : appendPQExpBuffer(q, "CONSTRAINT %s ",
16280 900 : fmtId(constr->dobj.name));
16281 900 : appendPQExpBufferStr(q, constr->condef);
16282 :
16283 900 : actual_atts++;
16284 : }
16285 :
16286 9176 : if (actual_atts)
16287 8714 : appendPQExpBufferStr(q, "\n)");
16288 462 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
16289 : {
16290 : /*
16291 : * No attributes? we must have a parenthesized attribute list,
16292 : * even though empty, when not using the OF TYPE syntax.
16293 : */
16294 438 : appendPQExpBufferStr(q, " (\n)");
16295 : }
16296 :
16297 : /*
16298 : * Emit the INHERITS clause (not for partitions), except in
16299 : * binary-upgrade mode.
16300 : */
16301 9176 : if (numParents > 0 && !tbinfo->ispartition &&
16302 702 : !dopt->binary_upgrade)
16303 : {
16304 598 : appendPQExpBufferStr(q, "\nINHERITS (");
16305 1260 : for (k = 0; k < numParents; k++)
16306 : {
16307 662 : TableInfo *parentRel = parents[k];
16308 :
16309 662 : if (k > 0)
16310 64 : appendPQExpBufferStr(q, ", ");
16311 662 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
16312 : }
16313 598 : appendPQExpBufferChar(q, ')');
16314 : }
16315 :
16316 9176 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16317 1006 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
16318 :
16319 9176 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
16320 70 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
16321 : }
16322 :
16323 19414 : if (nonemptyReloptions(tbinfo->reloptions) ||
16324 9564 : nonemptyReloptions(tbinfo->toast_reloptions))
16325 : {
16326 286 : bool addcomma = false;
16327 :
16328 286 : appendPQExpBufferStr(q, "\nWITH (");
16329 286 : if (nonemptyReloptions(tbinfo->reloptions))
16330 : {
16331 286 : addcomma = true;
16332 286 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
16333 : }
16334 286 : if (nonemptyReloptions(tbinfo->toast_reloptions))
16335 : {
16336 10 : if (addcomma)
16337 10 : appendPQExpBufferStr(q, ", ");
16338 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
16339 : fout);
16340 : }
16341 286 : appendPQExpBufferChar(q, ')');
16342 : }
16343 :
16344 : /* Dump generic options if any */
16345 9850 : if (ftoptions && ftoptions[0])
16346 66 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
16347 :
16348 : /*
16349 : * For materialized views, create the AS clause just like a view. At
16350 : * this point, we always mark the view as not populated.
16351 : */
16352 9850 : if (tbinfo->relkind == RELKIND_MATVIEW)
16353 : {
16354 : PQExpBuffer result;
16355 :
16356 674 : result = createViewAsClause(fout, tbinfo);
16357 674 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
16358 : result->data);
16359 674 : destroyPQExpBuffer(result);
16360 : }
16361 : else
16362 9176 : appendPQExpBufferStr(q, ";\n");
16363 :
16364 : /* Materialized views can depend on extensions */
16365 9850 : if (tbinfo->relkind == RELKIND_MATVIEW)
16366 674 : append_depends_on_extension(fout, q, &tbinfo->dobj,
16367 : "pg_catalog.pg_class",
16368 : "MATERIALIZED VIEW",
16369 : qualrelname);
16370 :
16371 : /*
16372 : * in binary upgrade mode, update the catalog with any missing values
16373 : * that might be present.
16374 : */
16375 9850 : if (dopt->binary_upgrade)
16376 : {
16377 7286 : for (j = 0; j < tbinfo->numatts; j++)
16378 : {
16379 5872 : if (tbinfo->attmissingval[j][0] != '\0')
16380 : {
16381 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
16382 4 : appendPQExpBufferStr(q,
16383 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
16384 4 : appendStringLiteralAH(q, qualrelname, fout);
16385 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
16386 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16387 4 : appendPQExpBufferChar(q, ',');
16388 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
16389 4 : appendPQExpBufferStr(q, ");\n\n");
16390 : }
16391 : }
16392 : }
16393 :
16394 : /*
16395 : * To create binary-compatible heap files, we have to ensure the same
16396 : * physical column order, including dropped columns, as in the
16397 : * original. Therefore, we create dropped columns above and drop them
16398 : * here, also updating their attlen/attalign values so that the
16399 : * dropped column can be skipped properly. (We do not bother with
16400 : * restoring the original attbyval setting.) Also, inheritance
16401 : * relationships are set up by doing ALTER TABLE INHERIT rather than
16402 : * using an INHERITS clause --- the latter would possibly mess up the
16403 : * column order. That also means we have to take care about setting
16404 : * attislocal correctly, plus fix up any inherited CHECK constraints.
16405 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
16406 : *
16407 : * We process foreign and partitioned tables here, even though they
16408 : * lack heap storage, because they can participate in inheritance
16409 : * relationships and we want this stuff to be consistent across the
16410 : * inheritance tree. We can exclude indexes, toast tables, sequences
16411 : * and matviews, even though they have storage, because we don't
16412 : * support altering or dropping columns in them, nor can they be part
16413 : * of inheritance trees.
16414 : */
16415 9850 : if (dopt->binary_upgrade &&
16416 1414 : (tbinfo->relkind == RELKIND_RELATION ||
16417 202 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
16418 200 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
16419 : {
16420 7212 : for (j = 0; j < tbinfo->numatts; j++)
16421 : {
16422 5832 : if (tbinfo->attisdropped[j])
16423 : {
16424 160 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n");
16425 160 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n"
16426 : "SET attlen = %d, "
16427 : "attalign = '%c', attbyval = false\n"
16428 : "WHERE attname = ",
16429 160 : tbinfo->attlen[j],
16430 160 : tbinfo->attalign[j]);
16431 160 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16432 160 : appendPQExpBufferStr(q, "\n AND attrelid = ");
16433 160 : appendStringLiteralAH(q, qualrelname, fout);
16434 160 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16435 :
16436 160 : if (tbinfo->relkind == RELKIND_RELATION ||
16437 32 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16438 160 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16439 : qualrelname);
16440 : else
16441 0 : appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ",
16442 : qualrelname);
16443 160 : appendPQExpBuffer(q, "DROP COLUMN %s;\n",
16444 160 : fmtId(tbinfo->attnames[j]));
16445 : }
16446 5672 : else if (!tbinfo->attislocal[j])
16447 : {
16448 1124 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n");
16449 1124 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16450 : "SET attislocal = false\n"
16451 : "WHERE attname = ");
16452 1124 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16453 1124 : appendPQExpBufferStr(q, "\n AND attrelid = ");
16454 1124 : appendStringLiteralAH(q, qualrelname, fout);
16455 1124 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16456 :
16457 : /*
16458 : * If a not-null constraint comes from inheritance, reset
16459 : * conislocal. The inhcount is fixed later.
16460 : */
16461 1124 : if (tbinfo->notnull_constrs[j] != NULL &&
16462 156 : !tbinfo->notnull_throwaway[j] &&
16463 126 : tbinfo->notnull_inh[j] &&
16464 96 : !tbinfo->ispartition)
16465 : {
16466 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16467 : "SET conislocal = false\n"
16468 : "WHERE contype = 'n' AND conrelid = ");
16469 30 : appendStringLiteralAH(q, qualrelname, fout);
16470 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
16471 : "conname = ");
16472 30 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
16473 30 : appendPQExpBufferStr(q, ";\n");
16474 : }
16475 : }
16476 : }
16477 :
16478 : /*
16479 : * Add inherited CHECK constraints, if any.
16480 : *
16481 : * For partitions, they were already dumped, and conislocal
16482 : * doesn't need fixing.
16483 : */
16484 1482 : for (k = 0; k < tbinfo->ncheck; k++)
16485 : {
16486 102 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
16487 :
16488 102 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
16489 98 : continue;
16490 :
16491 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n");
16492 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
16493 : foreign, qualrelname,
16494 4 : fmtId(constr->dobj.name),
16495 : constr->condef);
16496 4 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16497 : "SET conislocal = false\n"
16498 : "WHERE contype = 'c' AND conname = ");
16499 4 : appendStringLiteralAH(q, constr->dobj.name, fout);
16500 4 : appendPQExpBufferStr(q, "\n AND conrelid = ");
16501 4 : appendStringLiteralAH(q, qualrelname, fout);
16502 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16503 : }
16504 :
16505 1380 : if (numParents > 0 && !tbinfo->ispartition)
16506 : {
16507 104 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
16508 224 : for (k = 0; k < numParents; k++)
16509 : {
16510 120 : TableInfo *parentRel = parents[k];
16511 :
16512 120 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
16513 : qualrelname,
16514 120 : fmtQualifiedDumpable(parentRel));
16515 : }
16516 : }
16517 :
16518 1380 : if (OidIsValid(tbinfo->reloftype))
16519 : {
16520 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
16521 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
16522 : qualrelname,
16523 : getFormattedTypeName(fout, tbinfo->reloftype,
16524 : zeroIsError));
16525 : }
16526 : }
16527 :
16528 : /*
16529 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
16530 : * relminmxid of all vacuumable relations. (While vacuum.c processes
16531 : * TOAST tables semi-independently, here we see them only as children
16532 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
16533 : * child toast table is handled below.)
16534 : */
16535 9850 : if (dopt->binary_upgrade &&
16536 1414 : (tbinfo->relkind == RELKIND_RELATION ||
16537 202 : tbinfo->relkind == RELKIND_MATVIEW))
16538 : {
16539 1246 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
16540 1246 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16541 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16542 : "WHERE oid = ",
16543 : tbinfo->frozenxid, tbinfo->minmxid);
16544 1246 : appendStringLiteralAH(q, qualrelname, fout);
16545 1246 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16546 :
16547 1246 : if (tbinfo->toast_oid)
16548 : {
16549 : /*
16550 : * The toast table will have the same OID at restore, so we
16551 : * can safely target it by OID.
16552 : */
16553 550 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
16554 550 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
16555 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
16556 : "WHERE oid = '%u';\n",
16557 : tbinfo->toast_frozenxid,
16558 : tbinfo->toast_minmxid, tbinfo->toast_oid);
16559 : }
16560 : }
16561 :
16562 : /*
16563 : * In binary_upgrade mode, restore matviews' populated status by
16564 : * poking pg_class directly. This is pretty ugly, but we can't use
16565 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
16566 : * matview is not populated even though this matview is; in any case,
16567 : * we want to transfer the matview's heap storage, not run REFRESH.
16568 : */
16569 9850 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
16570 34 : tbinfo->relispopulated)
16571 : {
16572 30 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
16573 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
16574 : "SET relispopulated = 't'\n"
16575 : "WHERE oid = ");
16576 30 : appendStringLiteralAH(q, qualrelname, fout);
16577 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16578 : }
16579 :
16580 : /*
16581 : * Dump additional per-column properties that we can't handle in the
16582 : * main CREATE TABLE command.
16583 : */
16584 45462 : for (j = 0; j < tbinfo->numatts; j++)
16585 : {
16586 : /* None of this applies to dropped columns */
16587 35612 : if (tbinfo->attisdropped[j])
16588 856 : continue;
16589 :
16590 : /*
16591 : * If we didn't dump the column definition explicitly above, and
16592 : * it is not-null and did not inherit that property from a parent,
16593 : * we have to mark it separately.
16594 : */
16595 34756 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
16596 934 : tbinfo->notnull_constrs[j] != NULL &&
16597 200 : (!tbinfo->notnull_inh[j] && !tbinfo->ispartition && !dopt->binary_upgrade))
16598 : {
16599 : /* No constraint name desired? */
16600 24 : if (tbinfo->notnull_constrs[j][0] == '\0')
16601 16 : appendPQExpBuffer(q,
16602 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET NOT NULL;\n",
16603 : foreign, qualrelname,
16604 16 : fmtId(tbinfo->attnames[j]));
16605 : else
16606 16 : appendPQExpBuffer(q,
16607 : "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s NOT NULL %s;\n",
16608 : foreign, qualrelname,
16609 8 : tbinfo->notnull_constrs[j],
16610 8 : fmtId(tbinfo->attnames[j]));
16611 : }
16612 :
16613 : /*
16614 : * Dump per-column statistics information. We only issue an ALTER
16615 : * TABLE statement if the attstattarget entry for this column is
16616 : * not the default value.
16617 : */
16618 34756 : if (tbinfo->attstattarget[j] >= 0)
16619 66 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
16620 : foreign, qualrelname,
16621 66 : fmtId(tbinfo->attnames[j]),
16622 66 : tbinfo->attstattarget[j]);
16623 :
16624 : /*
16625 : * Dump per-column storage information. The statement is only
16626 : * dumped if the storage has been changed from the type's default.
16627 : */
16628 34756 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
16629 : {
16630 162 : switch (tbinfo->attstorage[j])
16631 : {
16632 20 : case TYPSTORAGE_PLAIN:
16633 20 : storage = "PLAIN";
16634 20 : break;
16635 76 : case TYPSTORAGE_EXTERNAL:
16636 76 : storage = "EXTERNAL";
16637 76 : break;
16638 0 : case TYPSTORAGE_EXTENDED:
16639 0 : storage = "EXTENDED";
16640 0 : break;
16641 66 : case TYPSTORAGE_MAIN:
16642 66 : storage = "MAIN";
16643 66 : break;
16644 0 : default:
16645 0 : storage = NULL;
16646 : }
16647 :
16648 : /*
16649 : * Only dump the statement if it's a storage type we recognize
16650 : */
16651 162 : if (storage != NULL)
16652 162 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
16653 : foreign, qualrelname,
16654 162 : fmtId(tbinfo->attnames[j]),
16655 : storage);
16656 : }
16657 :
16658 : /*
16659 : * Dump per-column compression, if it's been set.
16660 : */
16661 34756 : if (!dopt->no_toast_compression)
16662 : {
16663 : const char *cmname;
16664 :
16665 34590 : switch (tbinfo->attcompression[j])
16666 : {
16667 114 : case 'p':
16668 114 : cmname = "pglz";
16669 114 : break;
16670 188 : case 'l':
16671 188 : cmname = "lz4";
16672 188 : break;
16673 34288 : default:
16674 34288 : cmname = NULL;
16675 34288 : break;
16676 : }
16677 :
16678 34590 : if (cmname != NULL)
16679 302 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
16680 : foreign, qualrelname,
16681 302 : fmtId(tbinfo->attnames[j]),
16682 : cmname);
16683 : }
16684 :
16685 : /*
16686 : * Dump per-column attributes.
16687 : */
16688 34756 : if (tbinfo->attoptions[j][0] != '\0')
16689 66 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
16690 : foreign, qualrelname,
16691 66 : fmtId(tbinfo->attnames[j]),
16692 66 : tbinfo->attoptions[j]);
16693 :
16694 : /*
16695 : * Dump per-column fdw options.
16696 : */
16697 34756 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
16698 70 : tbinfo->attfdwoptions[j][0] != '\0')
16699 66 : appendPQExpBuffer(q,
16700 : "ALTER FOREIGN TABLE %s ALTER COLUMN %s OPTIONS (\n"
16701 : " %s\n"
16702 : ");\n",
16703 : qualrelname,
16704 66 : fmtId(tbinfo->attnames[j]),
16705 66 : tbinfo->attfdwoptions[j]);
16706 : } /* end loop over columns */
16707 :
16708 9850 : free(partkeydef);
16709 9850 : free(ftoptions);
16710 9850 : free(srvname);
16711 : }
16712 :
16713 : /*
16714 : * dump properties we only have ALTER TABLE syntax for
16715 : */
16716 10542 : if ((tbinfo->relkind == RELKIND_RELATION ||
16717 2442 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
16718 1436 : tbinfo->relkind == RELKIND_MATVIEW) &&
16719 9780 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
16720 : {
16721 138 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
16722 : {
16723 : /* nothing to do, will be set when the index is dumped */
16724 : }
16725 128 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
16726 : {
16727 128 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
16728 : qualrelname);
16729 : }
16730 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
16731 : {
16732 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
16733 : qualrelname);
16734 : }
16735 : }
16736 :
16737 10542 : if (tbinfo->forcerowsec)
16738 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
16739 : qualrelname);
16740 :
16741 10542 : if (dopt->binary_upgrade)
16742 1510 : binary_upgrade_extension_member(q, &tbinfo->dobj,
16743 : reltypename, qrelname,
16744 1510 : tbinfo->dobj.namespace->dobj.name);
16745 :
16746 10542 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16747 : {
16748 10542 : char *tablespace = NULL;
16749 10542 : char *tableam = NULL;
16750 :
16751 : /*
16752 : * _selectTablespace() relies on tablespace-enabled objects in the
16753 : * default tablespace to have a tablespace of "" (empty string) versus
16754 : * non-tablespace-enabled objects to have a tablespace of NULL.
16755 : * getTables() sets tbinfo->reltablespace to "" for the default
16756 : * tablespace (not NULL).
16757 : */
16758 10542 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
16759 9780 : tablespace = tbinfo->reltablespace;
16760 :
16761 10542 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
16762 1768 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16763 9780 : tableam = tbinfo->amname;
16764 :
16765 10542 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
16766 10542 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
16767 : .namespace = tbinfo->dobj.namespace->dobj.name,
16768 : .tablespace = tablespace,
16769 : .tableam = tableam,
16770 : .relkind = tbinfo->relkind,
16771 : .owner = tbinfo->rolname,
16772 : .description = reltypename,
16773 : .section = tbinfo->postponed_def ?
16774 : SECTION_POST_DATA : SECTION_PRE_DATA,
16775 : .createStmt = q->data,
16776 : .dropStmt = delq->data));
16777 : }
16778 :
16779 : /* Dump Table Comments */
16780 10542 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16781 152 : dumpTableComment(fout, tbinfo, reltypename);
16782 :
16783 : /* Dump Table Security Labels */
16784 10542 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
16785 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
16786 :
16787 : /* Dump comments on inlined table constraints */
16788 11598 : for (j = 0; j < tbinfo->ncheck; j++)
16789 : {
16790 1056 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
16791 :
16792 1056 : if (constr->separate || !constr->conislocal)
16793 428 : continue;
16794 :
16795 628 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
16796 76 : dumpTableConstraintComment(fout, constr);
16797 : }
16798 :
16799 10542 : destroyPQExpBuffer(q);
16800 10542 : destroyPQExpBuffer(delq);
16801 10542 : free(qrelname);
16802 10542 : free(qualrelname);
16803 10542 : }
16804 :
16805 : /*
16806 : * dumpTableAttach
16807 : * write to fout the commands to attach a child partition
16808 : *
16809 : * Child partitions are always made by creating them separately
16810 : * and then using ATTACH PARTITION, rather than using
16811 : * CREATE TABLE ... PARTITION OF. This is important for preserving
16812 : * any possible discrepancy in column layout, to allow assigning the
16813 : * correct tablespace if different, and so that it's possible to restore
16814 : * a partition without restoring its parent. (You'll get an error from
16815 : * the ATTACH PARTITION command, but that can be ignored, or skipped
16816 : * using "pg_restore -L" if you prefer.) The last point motivates
16817 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
16818 : * rather than emitting it within the child partition's ArchiveEntry.
16819 : */
16820 : static void
16821 2476 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
16822 : {
16823 2476 : DumpOptions *dopt = fout->dopt;
16824 : PQExpBuffer q;
16825 : PGresult *res;
16826 : char *partbound;
16827 :
16828 : /* Do nothing in data-only dump */
16829 2476 : if (dopt->dataOnly)
16830 42 : return;
16831 :
16832 2434 : q = createPQExpBuffer();
16833 :
16834 2434 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
16835 : {
16836 : /* Set up query for partbound details */
16837 88 : appendPQExpBufferStr(q,
16838 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
16839 :
16840 88 : appendPQExpBufferStr(q,
16841 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
16842 : "FROM pg_class c "
16843 : "WHERE c.oid = $1");
16844 :
16845 88 : ExecuteSqlStatement(fout, q->data);
16846 :
16847 88 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
16848 : }
16849 :
16850 2434 : printfPQExpBuffer(q,
16851 : "EXECUTE dumpTableAttach('%u')",
16852 2434 : attachinfo->partitionTbl->dobj.catId.oid);
16853 :
16854 2434 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
16855 2434 : partbound = PQgetvalue(res, 0, 0);
16856 :
16857 : /* Perform ALTER TABLE on the parent */
16858 2434 : printfPQExpBuffer(q,
16859 : "ALTER TABLE ONLY %s ",
16860 2434 : fmtQualifiedDumpable(attachinfo->parentTbl));
16861 2434 : appendPQExpBuffer(q,
16862 : "ATTACH PARTITION %s %s;\n",
16863 2434 : fmtQualifiedDumpable(attachinfo->partitionTbl),
16864 : partbound);
16865 :
16866 : /*
16867 : * There is no point in creating a drop query as the drop is done by table
16868 : * drop. (If you think to change this, see also _printTocEntry().)
16869 : * Although this object doesn't really have ownership as such, set the
16870 : * owner field anyway to ensure that the command is run by the correct
16871 : * role at restore time.
16872 : */
16873 2434 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
16874 2434 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
16875 : .namespace = attachinfo->dobj.namespace->dobj.name,
16876 : .owner = attachinfo->partitionTbl->rolname,
16877 : .description = "TABLE ATTACH",
16878 : .section = SECTION_PRE_DATA,
16879 : .createStmt = q->data));
16880 :
16881 2434 : PQclear(res);
16882 2434 : destroyPQExpBuffer(q);
16883 : }
16884 :
16885 : /*
16886 : * dumpAttrDef --- dump an attribute's default-value declaration
16887 : */
16888 : static void
16889 1520 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
16890 : {
16891 1520 : DumpOptions *dopt = fout->dopt;
16892 1520 : TableInfo *tbinfo = adinfo->adtable;
16893 1520 : int adnum = adinfo->adnum;
16894 : PQExpBuffer q;
16895 : PQExpBuffer delq;
16896 : char *qualrelname;
16897 : char *tag;
16898 : char *foreign;
16899 :
16900 : /* Do nothing in data-only dump */
16901 1520 : if (dopt->dataOnly)
16902 0 : return;
16903 :
16904 : /* Skip if not "separate"; it was dumped in the table's definition */
16905 1520 : if (!adinfo->separate)
16906 1232 : return;
16907 :
16908 288 : q = createPQExpBuffer();
16909 288 : delq = createPQExpBuffer();
16910 :
16911 288 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16912 :
16913 288 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
16914 :
16915 288 : appendPQExpBuffer(q,
16916 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
16917 288 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
16918 : adinfo->adef_expr);
16919 :
16920 288 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
16921 : foreign, qualrelname,
16922 288 : fmtId(tbinfo->attnames[adnum - 1]));
16923 :
16924 288 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
16925 :
16926 288 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16927 288 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
16928 288 : ARCHIVE_OPTS(.tag = tag,
16929 : .namespace = tbinfo->dobj.namespace->dobj.name,
16930 : .owner = tbinfo->rolname,
16931 : .description = "DEFAULT",
16932 : .section = SECTION_PRE_DATA,
16933 : .createStmt = q->data,
16934 : .dropStmt = delq->data));
16935 :
16936 288 : free(tag);
16937 288 : destroyPQExpBuffer(q);
16938 288 : destroyPQExpBuffer(delq);
16939 288 : free(qualrelname);
16940 : }
16941 :
16942 : /*
16943 : * getAttrName: extract the correct name for an attribute
16944 : *
16945 : * The array tblInfo->attnames[] only provides names of user attributes;
16946 : * if a system attribute number is supplied, we have to fake it.
16947 : * We also do a little bit of bounds checking for safety's sake.
16948 : */
16949 : static const char *
16950 3212 : getAttrName(int attrnum, const TableInfo *tblInfo)
16951 : {
16952 3212 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
16953 3212 : return tblInfo->attnames[attrnum - 1];
16954 0 : switch (attrnum)
16955 : {
16956 0 : case SelfItemPointerAttributeNumber:
16957 0 : return "ctid";
16958 0 : case MinTransactionIdAttributeNumber:
16959 0 : return "xmin";
16960 0 : case MinCommandIdAttributeNumber:
16961 0 : return "cmin";
16962 0 : case MaxTransactionIdAttributeNumber:
16963 0 : return "xmax";
16964 0 : case MaxCommandIdAttributeNumber:
16965 0 : return "cmax";
16966 0 : case TableOidAttributeNumber:
16967 0 : return "tableoid";
16968 : }
16969 0 : pg_fatal("invalid column number %d for table \"%s\"",
16970 : attrnum, tblInfo->dobj.name);
16971 : return NULL; /* keep compiler quiet */
16972 : }
16973 :
16974 : /*
16975 : * dumpIndex
16976 : * write out to fout a user-defined index
16977 : */
16978 : static void
16979 4244 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
16980 : {
16981 4244 : DumpOptions *dopt = fout->dopt;
16982 4244 : TableInfo *tbinfo = indxinfo->indextable;
16983 4244 : bool is_constraint = (indxinfo->indexconstraint != 0);
16984 : PQExpBuffer q;
16985 : PQExpBuffer delq;
16986 : char *qindxname;
16987 : char *qqindxname;
16988 :
16989 : /* Do nothing in data-only dump */
16990 4244 : if (dopt->dataOnly)
16991 114 : return;
16992 :
16993 4130 : q = createPQExpBuffer();
16994 4130 : delq = createPQExpBuffer();
16995 :
16996 4130 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
16997 4130 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
16998 :
16999 : /*
17000 : * If there's an associated constraint, don't dump the index per se, but
17001 : * do dump any comment for it. (This is safe because dependency ordering
17002 : * will have ensured the constraint is emitted first.) Note that the
17003 : * emitted comment has to be shown as depending on the constraint, not the
17004 : * index, in such cases.
17005 : */
17006 4130 : if (!is_constraint)
17007 : {
17008 1892 : char *indstatcols = indxinfo->indstatcols;
17009 1892 : char *indstatvals = indxinfo->indstatvals;
17010 1892 : char **indstatcolsarray = NULL;
17011 1892 : char **indstatvalsarray = NULL;
17012 1892 : int nstatcols = 0;
17013 1892 : int nstatvals = 0;
17014 :
17015 1892 : if (dopt->binary_upgrade)
17016 296 : binary_upgrade_set_pg_class_oids(fout, q,
17017 : indxinfo->dobj.catId.oid, true);
17018 :
17019 : /* Plain secondary index */
17020 1892 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
17021 :
17022 : /*
17023 : * Append ALTER TABLE commands as needed to set properties that we
17024 : * only have ALTER TABLE syntax for. Keep this in sync with the
17025 : * similar code in dumpConstraint!
17026 : */
17027 :
17028 : /* If the index is clustered, we need to record that. */
17029 1892 : if (indxinfo->indisclustered)
17030 : {
17031 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17032 0 : fmtQualifiedDumpable(tbinfo));
17033 : /* index name is not qualified in this syntax */
17034 0 : appendPQExpBuffer(q, " ON %s;\n",
17035 : qindxname);
17036 : }
17037 :
17038 : /*
17039 : * If the index has any statistics on some of its columns, generate
17040 : * the associated ALTER INDEX queries.
17041 : */
17042 1892 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
17043 : {
17044 : int j;
17045 :
17046 66 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
17047 0 : pg_fatal("could not parse index statistic columns");
17048 66 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
17049 0 : pg_fatal("could not parse index statistic values");
17050 66 : if (nstatcols != nstatvals)
17051 0 : pg_fatal("mismatched number of columns and values for index statistics");
17052 :
17053 198 : for (j = 0; j < nstatcols; j++)
17054 : {
17055 132 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
17056 :
17057 : /*
17058 : * Note that this is a column number, so no quotes should be
17059 : * used.
17060 : */
17061 132 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
17062 132 : indstatcolsarray[j]);
17063 132 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
17064 132 : indstatvalsarray[j]);
17065 : }
17066 : }
17067 :
17068 : /* Indexes can depend on extensions */
17069 1892 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17070 : "pg_catalog.pg_class",
17071 : "INDEX", qqindxname);
17072 :
17073 : /* If the index defines identity, we need to record that. */
17074 1892 : if (indxinfo->indisreplident)
17075 : {
17076 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17077 0 : fmtQualifiedDumpable(tbinfo));
17078 : /* index name is not qualified in this syntax */
17079 0 : appendPQExpBuffer(q, " INDEX %s;\n",
17080 : qindxname);
17081 : }
17082 :
17083 1892 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
17084 :
17085 1892 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17086 1892 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
17087 1892 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
17088 : .namespace = tbinfo->dobj.namespace->dobj.name,
17089 : .tablespace = indxinfo->tablespace,
17090 : .owner = tbinfo->rolname,
17091 : .description = "INDEX",
17092 : .section = SECTION_POST_DATA,
17093 : .createStmt = q->data,
17094 : .dropStmt = delq->data));
17095 :
17096 1892 : free(indstatcolsarray);
17097 1892 : free(indstatvalsarray);
17098 : }
17099 :
17100 : /* Dump Index Comments */
17101 4130 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17102 30 : dumpComment(fout, "INDEX", qindxname,
17103 30 : tbinfo->dobj.namespace->dobj.name,
17104 : tbinfo->rolname,
17105 : indxinfo->dobj.catId, 0,
17106 : is_constraint ? indxinfo->indexconstraint :
17107 : indxinfo->dobj.dumpId);
17108 :
17109 4130 : destroyPQExpBuffer(q);
17110 4130 : destroyPQExpBuffer(delq);
17111 4130 : free(qindxname);
17112 4130 : free(qqindxname);
17113 : }
17114 :
17115 : /*
17116 : * dumpIndexAttach
17117 : * write out to fout a partitioned-index attachment clause
17118 : */
17119 : static void
17120 1086 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
17121 : {
17122 : /* Do nothing in data-only dump */
17123 1086 : if (fout->dopt->dataOnly)
17124 48 : return;
17125 :
17126 1038 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
17127 : {
17128 1038 : PQExpBuffer q = createPQExpBuffer();
17129 :
17130 1038 : appendPQExpBuffer(q, "ALTER INDEX %s ",
17131 1038 : fmtQualifiedDumpable(attachinfo->parentIdx));
17132 1038 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
17133 1038 : fmtQualifiedDumpable(attachinfo->partitionIdx));
17134 :
17135 : /*
17136 : * There is no point in creating a drop query as the drop is done by
17137 : * index drop. (If you think to change this, see also
17138 : * _printTocEntry().) Although this object doesn't really have
17139 : * ownership as such, set the owner field anyway to ensure that the
17140 : * command is run by the correct role at restore time.
17141 : */
17142 1038 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
17143 1038 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
17144 : .namespace = attachinfo->dobj.namespace->dobj.name,
17145 : .owner = attachinfo->parentIdx->indextable->rolname,
17146 : .description = "INDEX ATTACH",
17147 : .section = SECTION_POST_DATA,
17148 : .createStmt = q->data));
17149 :
17150 1038 : destroyPQExpBuffer(q);
17151 : }
17152 : }
17153 :
17154 : /*
17155 : * dumpStatisticsExt
17156 : * write out to fout an extended statistics object
17157 : */
17158 : static void
17159 254 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
17160 : {
17161 254 : DumpOptions *dopt = fout->dopt;
17162 : PQExpBuffer q;
17163 : PQExpBuffer delq;
17164 : PQExpBuffer query;
17165 : char *qstatsextname;
17166 : PGresult *res;
17167 : char *stxdef;
17168 :
17169 : /* Do nothing in data-only dump */
17170 254 : if (dopt->dataOnly)
17171 18 : return;
17172 :
17173 236 : q = createPQExpBuffer();
17174 236 : delq = createPQExpBuffer();
17175 236 : query = createPQExpBuffer();
17176 :
17177 236 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
17178 :
17179 236 : appendPQExpBuffer(query, "SELECT "
17180 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
17181 : statsextinfo->dobj.catId.oid);
17182 :
17183 236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17184 :
17185 236 : stxdef = PQgetvalue(res, 0, 0);
17186 :
17187 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
17188 236 : appendPQExpBuffer(q, "%s;\n", stxdef);
17189 :
17190 : /*
17191 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
17192 : * for this statistics object is not the default value.
17193 : */
17194 236 : if (statsextinfo->stattarget >= 0)
17195 : {
17196 66 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
17197 66 : fmtQualifiedDumpable(statsextinfo));
17198 66 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
17199 : statsextinfo->stattarget);
17200 : }
17201 :
17202 236 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
17203 236 : fmtQualifiedDumpable(statsextinfo));
17204 :
17205 236 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17206 236 : ArchiveEntry(fout, statsextinfo->dobj.catId,
17207 : statsextinfo->dobj.dumpId,
17208 236 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
17209 : .namespace = statsextinfo->dobj.namespace->dobj.name,
17210 : .owner = statsextinfo->rolname,
17211 : .description = "STATISTICS",
17212 : .section = SECTION_POST_DATA,
17213 : .createStmt = q->data,
17214 : .dropStmt = delq->data));
17215 :
17216 : /* Dump Statistics Comments */
17217 236 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17218 0 : dumpComment(fout, "STATISTICS", qstatsextname,
17219 0 : statsextinfo->dobj.namespace->dobj.name,
17220 : statsextinfo->rolname,
17221 : statsextinfo->dobj.catId, 0,
17222 : statsextinfo->dobj.dumpId);
17223 :
17224 236 : PQclear(res);
17225 236 : destroyPQExpBuffer(q);
17226 236 : destroyPQExpBuffer(delq);
17227 236 : destroyPQExpBuffer(query);
17228 236 : free(qstatsextname);
17229 : }
17230 :
17231 : /*
17232 : * dumpConstraint
17233 : * write out to fout a user-defined constraint
17234 : */
17235 : static void
17236 3886 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
17237 : {
17238 3886 : DumpOptions *dopt = fout->dopt;
17239 3886 : TableInfo *tbinfo = coninfo->contable;
17240 : PQExpBuffer q;
17241 : PQExpBuffer delq;
17242 3886 : char *tag = NULL;
17243 : char *foreign;
17244 :
17245 : /* Do nothing in data-only dump */
17246 3886 : if (dopt->dataOnly)
17247 94 : return;
17248 :
17249 3792 : q = createPQExpBuffer();
17250 3792 : delq = createPQExpBuffer();
17251 :
17252 7418 : foreign = tbinfo &&
17253 3792 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
17254 :
17255 3792 : if (coninfo->contype == 'p' ||
17256 1822 : coninfo->contype == 'u' ||
17257 1574 : coninfo->contype == 'x')
17258 2238 : {
17259 : /* Index-related constraint */
17260 : IndxInfo *indxinfo;
17261 : int k;
17262 :
17263 2238 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
17264 :
17265 2238 : if (indxinfo == NULL)
17266 0 : pg_fatal("missing index for constraint \"%s\"",
17267 : coninfo->dobj.name);
17268 :
17269 2238 : if (dopt->binary_upgrade)
17270 254 : binary_upgrade_set_pg_class_oids(fout, q,
17271 : indxinfo->dobj.catId.oid, true);
17272 :
17273 2238 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
17274 2238 : fmtQualifiedDumpable(tbinfo));
17275 2238 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
17276 2238 : fmtId(coninfo->dobj.name));
17277 :
17278 2238 : if (coninfo->condef)
17279 : {
17280 : /* pg_get_constraintdef should have provided everything */
17281 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
17282 : }
17283 : else
17284 : {
17285 2218 : appendPQExpBufferStr(q,
17286 2218 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
17287 :
17288 : /*
17289 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
17290 : * indexes. Being able to create this was fixed, but we need to
17291 : * make the index distinct in order to be able to restore the
17292 : * dump.
17293 : */
17294 2218 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
17295 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
17296 2218 : appendPQExpBufferStr(q, " (");
17297 5350 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
17298 : {
17299 3132 : int indkey = (int) indxinfo->indkeys[k];
17300 : const char *attname;
17301 :
17302 3132 : if (indkey == InvalidAttrNumber)
17303 0 : break;
17304 3132 : attname = getAttrName(indkey, tbinfo);
17305 :
17306 3132 : appendPQExpBuffer(q, "%s%s",
17307 : (k == 0) ? "" : ", ",
17308 : fmtId(attname));
17309 : }
17310 2218 : if (coninfo->conperiod)
17311 222 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
17312 :
17313 2218 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
17314 40 : appendPQExpBufferStr(q, ") INCLUDE (");
17315 :
17316 2298 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
17317 : {
17318 80 : int indkey = (int) indxinfo->indkeys[k];
17319 : const char *attname;
17320 :
17321 80 : if (indkey == InvalidAttrNumber)
17322 0 : break;
17323 80 : attname = getAttrName(indkey, tbinfo);
17324 :
17325 160 : appendPQExpBuffer(q, "%s%s",
17326 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
17327 : fmtId(attname));
17328 : }
17329 :
17330 2218 : appendPQExpBufferChar(q, ')');
17331 :
17332 2218 : if (nonemptyReloptions(indxinfo->indreloptions))
17333 : {
17334 0 : appendPQExpBufferStr(q, " WITH (");
17335 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
17336 0 : appendPQExpBufferChar(q, ')');
17337 : }
17338 :
17339 2218 : if (coninfo->condeferrable)
17340 : {
17341 50 : appendPQExpBufferStr(q, " DEFERRABLE");
17342 50 : if (coninfo->condeferred)
17343 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
17344 : }
17345 :
17346 2218 : appendPQExpBufferStr(q, ";\n");
17347 : }
17348 :
17349 : /*
17350 : * Append ALTER TABLE commands as needed to set properties that we
17351 : * only have ALTER TABLE syntax for. Keep this in sync with the
17352 : * similar code in dumpIndex!
17353 : */
17354 :
17355 : /*
17356 : * Drop any not-null constraints that were added to support the PK,
17357 : * but leave them alone if they have a definition coming from their
17358 : * parent.
17359 : */
17360 2238 : if (coninfo->contype == 'p')
17361 7504 : for (int i = 0; i < tbinfo->numatts; i++)
17362 5534 : if (tbinfo->notnull_throwaway[i] &&
17363 1436 : !tbinfo->notnull_inh[i])
17364 1436 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s DROP CONSTRAINT %s;",
17365 1436 : fmtQualifiedDumpable(tbinfo),
17366 1436 : tbinfo->notnull_constrs[i]);
17367 :
17368 : /* If the index is clustered, we need to record that. */
17369 2238 : if (indxinfo->indisclustered)
17370 : {
17371 66 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
17372 66 : fmtQualifiedDumpable(tbinfo));
17373 : /* index name is not qualified in this syntax */
17374 66 : appendPQExpBuffer(q, " ON %s;\n",
17375 66 : fmtId(indxinfo->dobj.name));
17376 : }
17377 :
17378 : /* If the index defines identity, we need to record that. */
17379 2238 : if (indxinfo->indisreplident)
17380 : {
17381 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
17382 10 : fmtQualifiedDumpable(tbinfo));
17383 : /* index name is not qualified in this syntax */
17384 10 : appendPQExpBuffer(q, " INDEX %s;\n",
17385 10 : fmtId(indxinfo->dobj.name));
17386 : }
17387 :
17388 : /* Indexes can depend on extensions */
17389 2238 : append_depends_on_extension(fout, q, &indxinfo->dobj,
17390 : "pg_catalog.pg_class", "INDEX",
17391 2238 : fmtQualifiedDumpable(indxinfo));
17392 :
17393 2238 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
17394 2238 : fmtQualifiedDumpable(tbinfo));
17395 2238 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17396 2238 : fmtId(coninfo->dobj.name));
17397 :
17398 2238 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17399 :
17400 2238 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17401 2238 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17402 2238 : ARCHIVE_OPTS(.tag = tag,
17403 : .namespace = tbinfo->dobj.namespace->dobj.name,
17404 : .tablespace = indxinfo->tablespace,
17405 : .owner = tbinfo->rolname,
17406 : .description = "CONSTRAINT",
17407 : .section = SECTION_POST_DATA,
17408 : .createStmt = q->data,
17409 : .dropStmt = delq->data));
17410 : }
17411 1554 : else if (coninfo->contype == 'f')
17412 : {
17413 : char *only;
17414 :
17415 : /*
17416 : * Foreign keys on partitioned tables are always declared as
17417 : * inheriting to partitions; for all other cases, emit them as
17418 : * applying ONLY directly to the named table, because that's how they
17419 : * work for regular inherited tables.
17420 : */
17421 332 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
17422 :
17423 : /*
17424 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
17425 : * current table data is not processed
17426 : */
17427 332 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
17428 332 : only, fmtQualifiedDumpable(tbinfo));
17429 332 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17430 332 : fmtId(coninfo->dobj.name),
17431 : coninfo->condef);
17432 :
17433 332 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
17434 332 : only, fmtQualifiedDumpable(tbinfo));
17435 332 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17436 332 : fmtId(coninfo->dobj.name));
17437 :
17438 332 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17439 :
17440 332 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17441 332 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17442 332 : ARCHIVE_OPTS(.tag = tag,
17443 : .namespace = tbinfo->dobj.namespace->dobj.name,
17444 : .owner = tbinfo->rolname,
17445 : .description = "FK CONSTRAINT",
17446 : .section = SECTION_POST_DATA,
17447 : .createStmt = q->data,
17448 : .dropStmt = delq->data));
17449 : }
17450 1222 : else if (coninfo->contype == 'c' && tbinfo)
17451 : {
17452 : /* CHECK constraint on a table */
17453 :
17454 : /* Ignore if not to be dumped separately, or if it was inherited */
17455 1056 : if (coninfo->separate && coninfo->conislocal)
17456 : {
17457 : /* not ONLY since we want it to propagate to children */
17458 50 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
17459 50 : fmtQualifiedDumpable(tbinfo));
17460 50 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17461 50 : fmtId(coninfo->dobj.name),
17462 : coninfo->condef);
17463 :
17464 50 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
17465 50 : fmtQualifiedDumpable(tbinfo));
17466 50 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17467 50 : fmtId(coninfo->dobj.name));
17468 :
17469 50 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
17470 :
17471 50 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17472 50 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17473 50 : ARCHIVE_OPTS(.tag = tag,
17474 : .namespace = tbinfo->dobj.namespace->dobj.name,
17475 : .owner = tbinfo->rolname,
17476 : .description = "CHECK CONSTRAINT",
17477 : .section = SECTION_POST_DATA,
17478 : .createStmt = q->data,
17479 : .dropStmt = delq->data));
17480 : }
17481 : }
17482 166 : else if (coninfo->contype == 'c' && tbinfo == NULL)
17483 166 : {
17484 : /* CHECK constraint on a domain */
17485 166 : TypeInfo *tyinfo = coninfo->condomain;
17486 :
17487 : /* Ignore if not to be dumped separately */
17488 166 : if (coninfo->separate)
17489 : {
17490 0 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
17491 0 : fmtQualifiedDumpable(tyinfo));
17492 0 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
17493 0 : fmtId(coninfo->dobj.name),
17494 : coninfo->condef);
17495 :
17496 0 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
17497 0 : fmtQualifiedDumpable(tyinfo));
17498 0 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
17499 0 : fmtId(coninfo->dobj.name));
17500 :
17501 0 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
17502 :
17503 0 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17504 0 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
17505 0 : ARCHIVE_OPTS(.tag = tag,
17506 : .namespace = tyinfo->dobj.namespace->dobj.name,
17507 : .owner = tyinfo->rolname,
17508 : .description = "CHECK CONSTRAINT",
17509 : .section = SECTION_POST_DATA,
17510 : .createStmt = q->data,
17511 : .dropStmt = delq->data));
17512 : }
17513 : }
17514 : else
17515 : {
17516 0 : pg_fatal("unrecognized constraint type: %c",
17517 : coninfo->contype);
17518 : }
17519 :
17520 : /* Dump Constraint Comments --- only works for table constraints */
17521 3792 : if (tbinfo && coninfo->separate &&
17522 2650 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17523 20 : dumpTableConstraintComment(fout, coninfo);
17524 :
17525 3792 : free(tag);
17526 3792 : destroyPQExpBuffer(q);
17527 3792 : destroyPQExpBuffer(delq);
17528 : }
17529 :
17530 : /*
17531 : * dumpTableConstraintComment --- dump a constraint's comment if any
17532 : *
17533 : * This is split out because we need the function in two different places
17534 : * depending on whether the constraint is dumped as part of CREATE TABLE
17535 : * or as a separate ALTER command.
17536 : */
17537 : static void
17538 96 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
17539 : {
17540 96 : TableInfo *tbinfo = coninfo->contable;
17541 96 : PQExpBuffer conprefix = createPQExpBuffer();
17542 : char *qtabname;
17543 :
17544 96 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17545 :
17546 96 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
17547 96 : fmtId(coninfo->dobj.name));
17548 :
17549 96 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17550 96 : dumpComment(fout, conprefix->data, qtabname,
17551 96 : tbinfo->dobj.namespace->dobj.name,
17552 : tbinfo->rolname,
17553 : coninfo->dobj.catId, 0,
17554 96 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
17555 :
17556 96 : destroyPQExpBuffer(conprefix);
17557 96 : free(qtabname);
17558 96 : }
17559 :
17560 : /*
17561 : * dumpSequence
17562 : * write the declaration (not data) of one user-defined sequence
17563 : */
17564 : static void
17565 686 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
17566 : {
17567 686 : DumpOptions *dopt = fout->dopt;
17568 : PGresult *res;
17569 : char *startv,
17570 : *incby,
17571 : *maxv,
17572 : *minv,
17573 : *cache,
17574 : *seqtype;
17575 : bool cycled;
17576 : bool is_ascending;
17577 : int64 default_minv,
17578 : default_maxv;
17579 : char bufm[32],
17580 : bufx[32];
17581 686 : PQExpBuffer query = createPQExpBuffer();
17582 686 : PQExpBuffer delqry = createPQExpBuffer();
17583 : char *qseqname;
17584 686 : TableInfo *owning_tab = NULL;
17585 :
17586 686 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
17587 :
17588 686 : if (fout->remoteVersion >= 100000)
17589 : {
17590 686 : appendPQExpBuffer(query,
17591 : "SELECT format_type(seqtypid, NULL), "
17592 : "seqstart, seqincrement, "
17593 : "seqmax, seqmin, "
17594 : "seqcache, seqcycle "
17595 : "FROM pg_catalog.pg_sequence "
17596 : "WHERE seqrelid = '%u'::oid",
17597 : tbinfo->dobj.catId.oid);
17598 : }
17599 : else
17600 : {
17601 : /*
17602 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
17603 : *
17604 : * Note: it might seem that 'bigint' potentially needs to be
17605 : * schema-qualified, but actually that's a keyword.
17606 : */
17607 0 : appendPQExpBuffer(query,
17608 : "SELECT 'bigint' AS sequence_type, "
17609 : "start_value, increment_by, max_value, min_value, "
17610 : "cache_value, is_cycled FROM %s",
17611 0 : fmtQualifiedDumpable(tbinfo));
17612 : }
17613 :
17614 686 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17615 :
17616 686 : if (PQntuples(res) != 1)
17617 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17618 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17619 : PQntuples(res)),
17620 : tbinfo->dobj.name, PQntuples(res));
17621 :
17622 686 : seqtype = PQgetvalue(res, 0, 0);
17623 686 : startv = PQgetvalue(res, 0, 1);
17624 686 : incby = PQgetvalue(res, 0, 2);
17625 686 : maxv = PQgetvalue(res, 0, 3);
17626 686 : minv = PQgetvalue(res, 0, 4);
17627 686 : cache = PQgetvalue(res, 0, 5);
17628 686 : cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
17629 :
17630 : /* Calculate default limits for a sequence of this type */
17631 686 : is_ascending = (incby[0] != '-');
17632 686 : if (strcmp(seqtype, "smallint") == 0)
17633 : {
17634 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
17635 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
17636 : }
17637 636 : else if (strcmp(seqtype, "integer") == 0)
17638 : {
17639 514 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
17640 514 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
17641 : }
17642 122 : else if (strcmp(seqtype, "bigint") == 0)
17643 : {
17644 122 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
17645 122 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
17646 : }
17647 : else
17648 : {
17649 0 : pg_fatal("unrecognized sequence type: %s", seqtype);
17650 : default_minv = default_maxv = 0; /* keep compiler quiet */
17651 : }
17652 :
17653 : /*
17654 : * 64-bit strtol() isn't very portable, so convert the limits to strings
17655 : * and compare that way.
17656 : */
17657 686 : snprintf(bufm, sizeof(bufm), INT64_FORMAT, default_minv);
17658 686 : snprintf(bufx, sizeof(bufx), INT64_FORMAT, default_maxv);
17659 :
17660 : /* Don't print minv/maxv if they match the respective default limit */
17661 686 : if (strcmp(minv, bufm) == 0)
17662 656 : minv = NULL;
17663 686 : if (strcmp(maxv, bufx) == 0)
17664 656 : maxv = NULL;
17665 :
17666 : /*
17667 : * Identity sequences are not to be dropped separately.
17668 : */
17669 686 : if (!tbinfo->is_identity_sequence)
17670 : {
17671 410 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
17672 410 : fmtQualifiedDumpable(tbinfo));
17673 : }
17674 :
17675 686 : resetPQExpBuffer(query);
17676 :
17677 686 : if (dopt->binary_upgrade)
17678 : {
17679 110 : binary_upgrade_set_pg_class_oids(fout, query,
17680 : tbinfo->dobj.catId.oid, false);
17681 :
17682 : /*
17683 : * In older PG versions a sequence will have a pg_type entry, but v14
17684 : * and up don't use that, so don't attempt to preserve the type OID.
17685 : */
17686 : }
17687 :
17688 686 : if (tbinfo->is_identity_sequence)
17689 : {
17690 276 : owning_tab = findTableByOid(tbinfo->owning_tab);
17691 :
17692 276 : appendPQExpBuffer(query,
17693 : "ALTER TABLE %s ",
17694 276 : fmtQualifiedDumpable(owning_tab));
17695 276 : appendPQExpBuffer(query,
17696 : "ALTER COLUMN %s ADD GENERATED ",
17697 276 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17698 276 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
17699 186 : appendPQExpBufferStr(query, "ALWAYS");
17700 90 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
17701 90 : appendPQExpBufferStr(query, "BY DEFAULT");
17702 276 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
17703 276 : fmtQualifiedDumpable(tbinfo));
17704 : }
17705 : else
17706 : {
17707 410 : appendPQExpBuffer(query,
17708 : "CREATE %sSEQUENCE %s\n",
17709 410 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17710 : "UNLOGGED " : "",
17711 410 : fmtQualifiedDumpable(tbinfo));
17712 :
17713 410 : if (strcmp(seqtype, "bigint") != 0)
17714 318 : appendPQExpBuffer(query, " AS %s\n", seqtype);
17715 : }
17716 :
17717 686 : appendPQExpBuffer(query, " START WITH %s\n", startv);
17718 :
17719 686 : appendPQExpBuffer(query, " INCREMENT BY %s\n", incby);
17720 :
17721 686 : if (minv)
17722 30 : appendPQExpBuffer(query, " MINVALUE %s\n", minv);
17723 : else
17724 656 : appendPQExpBufferStr(query, " NO MINVALUE\n");
17725 :
17726 686 : if (maxv)
17727 30 : appendPQExpBuffer(query, " MAXVALUE %s\n", maxv);
17728 : else
17729 656 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
17730 :
17731 686 : appendPQExpBuffer(query,
17732 : " CACHE %s%s",
17733 : cache, (cycled ? "\n CYCLE" : ""));
17734 :
17735 686 : if (tbinfo->is_identity_sequence)
17736 : {
17737 276 : appendPQExpBufferStr(query, "\n);\n");
17738 276 : if (tbinfo->relpersistence != owning_tab->relpersistence)
17739 0 : appendPQExpBuffer(query,
17740 : "ALTER SEQUENCE %s SET %s;\n",
17741 0 : fmtQualifiedDumpable(tbinfo),
17742 0 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
17743 : "UNLOGGED" : "LOGGED");
17744 : }
17745 : else
17746 410 : appendPQExpBufferStr(query, ";\n");
17747 :
17748 : /* binary_upgrade: no need to clear TOAST table oid */
17749 :
17750 686 : if (dopt->binary_upgrade)
17751 110 : binary_upgrade_extension_member(query, &tbinfo->dobj,
17752 : "SEQUENCE", qseqname,
17753 110 : tbinfo->dobj.namespace->dobj.name);
17754 :
17755 686 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17756 686 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17757 686 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17758 : .namespace = tbinfo->dobj.namespace->dobj.name,
17759 : .owner = tbinfo->rolname,
17760 : .description = "SEQUENCE",
17761 : .section = SECTION_PRE_DATA,
17762 : .createStmt = query->data,
17763 : .dropStmt = delqry->data));
17764 :
17765 : /*
17766 : * If the sequence is owned by a table column, emit the ALTER for it as a
17767 : * separate TOC entry immediately following the sequence's own entry. It's
17768 : * OK to do this rather than using full sorting logic, because the
17769 : * dependency that tells us it's owned will have forced the table to be
17770 : * created first. We can't just include the ALTER in the TOC entry
17771 : * because it will fail if we haven't reassigned the sequence owner to
17772 : * match the table's owner.
17773 : *
17774 : * We need not schema-qualify the table reference because both sequence
17775 : * and table must be in the same schema.
17776 : */
17777 686 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
17778 : {
17779 228 : owning_tab = findTableByOid(tbinfo->owning_tab);
17780 :
17781 228 : if (owning_tab == NULL)
17782 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
17783 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
17784 :
17785 228 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
17786 : {
17787 224 : resetPQExpBuffer(query);
17788 224 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
17789 224 : fmtQualifiedDumpable(tbinfo));
17790 224 : appendPQExpBuffer(query, " OWNED BY %s",
17791 224 : fmtQualifiedDumpable(owning_tab));
17792 224 : appendPQExpBuffer(query, ".%s;\n",
17793 224 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
17794 :
17795 224 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17796 224 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17797 224 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17798 : .namespace = tbinfo->dobj.namespace->dobj.name,
17799 : .owner = tbinfo->rolname,
17800 : .description = "SEQUENCE OWNED BY",
17801 : .section = SECTION_PRE_DATA,
17802 : .createStmt = query->data,
17803 : .deps = &(tbinfo->dobj.dumpId),
17804 : .nDeps = 1));
17805 : }
17806 : }
17807 :
17808 : /* Dump Sequence Comments and Security Labels */
17809 686 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17810 0 : dumpComment(fout, "SEQUENCE", qseqname,
17811 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17812 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17813 :
17814 686 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17815 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
17816 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17817 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
17818 :
17819 686 : PQclear(res);
17820 :
17821 686 : destroyPQExpBuffer(query);
17822 686 : destroyPQExpBuffer(delqry);
17823 686 : free(qseqname);
17824 686 : }
17825 :
17826 : /*
17827 : * dumpSequenceData
17828 : * write the data of one user-defined sequence
17829 : */
17830 : static void
17831 722 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
17832 : {
17833 722 : TableInfo *tbinfo = tdinfo->tdtable;
17834 : PGresult *res;
17835 : char *last;
17836 : bool called;
17837 722 : PQExpBuffer query = createPQExpBuffer();
17838 :
17839 722 : appendPQExpBuffer(query,
17840 : "SELECT last_value, is_called FROM %s",
17841 722 : fmtQualifiedDumpable(tbinfo));
17842 :
17843 722 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17844 :
17845 722 : if (PQntuples(res) != 1)
17846 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
17847 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
17848 : PQntuples(res)),
17849 : tbinfo->dobj.name, PQntuples(res));
17850 :
17851 722 : last = PQgetvalue(res, 0, 0);
17852 722 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
17853 :
17854 722 : resetPQExpBuffer(query);
17855 722 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
17856 722 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
17857 722 : appendPQExpBuffer(query, ", %s, %s);\n",
17858 : last, (called ? "true" : "false"));
17859 :
17860 722 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
17861 722 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17862 722 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17863 : .namespace = tbinfo->dobj.namespace->dobj.name,
17864 : .owner = tbinfo->rolname,
17865 : .description = "SEQUENCE SET",
17866 : .section = SECTION_DATA,
17867 : .createStmt = query->data,
17868 : .deps = &(tbinfo->dobj.dumpId),
17869 : .nDeps = 1));
17870 :
17871 722 : PQclear(res);
17872 :
17873 722 : destroyPQExpBuffer(query);
17874 722 : }
17875 :
17876 : /*
17877 : * dumpTrigger
17878 : * write the declaration of one user-defined table trigger
17879 : */
17880 : static void
17881 986 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
17882 : {
17883 986 : DumpOptions *dopt = fout->dopt;
17884 986 : TableInfo *tbinfo = tginfo->tgtable;
17885 : PQExpBuffer query;
17886 : PQExpBuffer delqry;
17887 : PQExpBuffer trigprefix;
17888 : PQExpBuffer trigidentity;
17889 : char *qtabname;
17890 : char *tag;
17891 :
17892 : /* Do nothing in data-only dump */
17893 986 : if (dopt->dataOnly)
17894 32 : return;
17895 :
17896 954 : query = createPQExpBuffer();
17897 954 : delqry = createPQExpBuffer();
17898 954 : trigprefix = createPQExpBuffer();
17899 954 : trigidentity = createPQExpBuffer();
17900 :
17901 954 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
17902 :
17903 954 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
17904 954 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
17905 :
17906 954 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
17907 954 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
17908 :
17909 : /* Triggers can depend on extensions */
17910 954 : append_depends_on_extension(fout, query, &tginfo->dobj,
17911 : "pg_catalog.pg_trigger", "TRIGGER",
17912 954 : trigidentity->data);
17913 :
17914 954 : if (tginfo->tgispartition)
17915 : {
17916 : Assert(tbinfo->ispartition);
17917 :
17918 : /*
17919 : * Partition triggers only appear here because their 'tgenabled' flag
17920 : * differs from its parent's. The trigger is created already, so
17921 : * remove the CREATE and replace it with an ALTER. (Clear out the
17922 : * DROP query too, so that pg_dump --create does not cause errors.)
17923 : */
17924 224 : resetPQExpBuffer(query);
17925 224 : resetPQExpBuffer(delqry);
17926 224 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
17927 224 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
17928 224 : fmtQualifiedDumpable(tbinfo));
17929 224 : switch (tginfo->tgenabled)
17930 : {
17931 78 : case 'f':
17932 : case 'D':
17933 78 : appendPQExpBufferStr(query, "DISABLE");
17934 78 : break;
17935 0 : case 't':
17936 : case 'O':
17937 0 : appendPQExpBufferStr(query, "ENABLE");
17938 0 : break;
17939 68 : case 'R':
17940 68 : appendPQExpBufferStr(query, "ENABLE REPLICA");
17941 68 : break;
17942 78 : case 'A':
17943 78 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
17944 78 : break;
17945 : }
17946 224 : appendPQExpBuffer(query, " TRIGGER %s;\n",
17947 224 : fmtId(tginfo->dobj.name));
17948 : }
17949 730 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
17950 : {
17951 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
17952 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
17953 0 : fmtQualifiedDumpable(tbinfo));
17954 0 : switch (tginfo->tgenabled)
17955 : {
17956 0 : case 'D':
17957 : case 'f':
17958 0 : appendPQExpBufferStr(query, "DISABLE");
17959 0 : break;
17960 0 : case 'A':
17961 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
17962 0 : break;
17963 0 : case 'R':
17964 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
17965 0 : break;
17966 0 : default:
17967 0 : appendPQExpBufferStr(query, "ENABLE");
17968 0 : break;
17969 : }
17970 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
17971 0 : fmtId(tginfo->dobj.name));
17972 : }
17973 :
17974 954 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
17975 954 : fmtId(tginfo->dobj.name));
17976 :
17977 954 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
17978 :
17979 954 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17980 954 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
17981 954 : ARCHIVE_OPTS(.tag = tag,
17982 : .namespace = tbinfo->dobj.namespace->dobj.name,
17983 : .owner = tbinfo->rolname,
17984 : .description = "TRIGGER",
17985 : .section = SECTION_POST_DATA,
17986 : .createStmt = query->data,
17987 : .dropStmt = delqry->data));
17988 :
17989 954 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17990 0 : dumpComment(fout, trigprefix->data, qtabname,
17991 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
17992 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
17993 :
17994 954 : free(tag);
17995 954 : destroyPQExpBuffer(query);
17996 954 : destroyPQExpBuffer(delqry);
17997 954 : destroyPQExpBuffer(trigprefix);
17998 954 : destroyPQExpBuffer(trigidentity);
17999 954 : free(qtabname);
18000 : }
18001 :
18002 : /*
18003 : * dumpEventTrigger
18004 : * write the declaration of one user-defined event trigger
18005 : */
18006 : static void
18007 80 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
18008 : {
18009 80 : DumpOptions *dopt = fout->dopt;
18010 : PQExpBuffer query;
18011 : PQExpBuffer delqry;
18012 : char *qevtname;
18013 :
18014 : /* Do nothing in data-only dump */
18015 80 : if (dopt->dataOnly)
18016 6 : return;
18017 :
18018 74 : query = createPQExpBuffer();
18019 74 : delqry = createPQExpBuffer();
18020 :
18021 74 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
18022 :
18023 74 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
18024 74 : appendPQExpBufferStr(query, qevtname);
18025 74 : appendPQExpBufferStr(query, " ON ");
18026 74 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
18027 :
18028 74 : if (strcmp("", evtinfo->evttags) != 0)
18029 : {
18030 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
18031 10 : appendPQExpBufferStr(query, evtinfo->evttags);
18032 10 : appendPQExpBufferChar(query, ')');
18033 : }
18034 :
18035 74 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
18036 74 : appendPQExpBufferStr(query, evtinfo->evtfname);
18037 74 : appendPQExpBufferStr(query, "();\n");
18038 :
18039 74 : if (evtinfo->evtenabled != 'O')
18040 : {
18041 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
18042 : qevtname);
18043 0 : switch (evtinfo->evtenabled)
18044 : {
18045 0 : case 'D':
18046 0 : appendPQExpBufferStr(query, "DISABLE");
18047 0 : break;
18048 0 : case 'A':
18049 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
18050 0 : break;
18051 0 : case 'R':
18052 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
18053 0 : break;
18054 0 : default:
18055 0 : appendPQExpBufferStr(query, "ENABLE");
18056 0 : break;
18057 : }
18058 0 : appendPQExpBufferStr(query, ";\n");
18059 : }
18060 :
18061 74 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
18062 : qevtname);
18063 :
18064 74 : if (dopt->binary_upgrade)
18065 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
18066 : "EVENT TRIGGER", qevtname, NULL);
18067 :
18068 74 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18069 74 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
18070 74 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
18071 : .owner = evtinfo->evtowner,
18072 : .description = "EVENT TRIGGER",
18073 : .section = SECTION_POST_DATA,
18074 : .createStmt = query->data,
18075 : .dropStmt = delqry->data));
18076 :
18077 74 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18078 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
18079 : NULL, evtinfo->evtowner,
18080 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
18081 :
18082 74 : destroyPQExpBuffer(query);
18083 74 : destroyPQExpBuffer(delqry);
18084 74 : free(qevtname);
18085 : }
18086 :
18087 : /*
18088 : * dumpRule
18089 : * Dump a rule
18090 : */
18091 : static void
18092 1820 : dumpRule(Archive *fout, const RuleInfo *rinfo)
18093 : {
18094 1820 : DumpOptions *dopt = fout->dopt;
18095 1820 : TableInfo *tbinfo = rinfo->ruletable;
18096 : bool is_view;
18097 : PQExpBuffer query;
18098 : PQExpBuffer cmd;
18099 : PQExpBuffer delcmd;
18100 : PQExpBuffer ruleprefix;
18101 : char *qtabname;
18102 : PGresult *res;
18103 : char *tag;
18104 :
18105 : /* Do nothing in data-only dump */
18106 1820 : if (dopt->dataOnly)
18107 60 : return;
18108 :
18109 : /*
18110 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
18111 : * we do not want to dump it as a separate object.
18112 : */
18113 1760 : if (!rinfo->separate)
18114 1346 : return;
18115 :
18116 : /*
18117 : * If it's an ON SELECT rule, we want to print it as a view definition,
18118 : * instead of a rule.
18119 : */
18120 414 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
18121 :
18122 414 : query = createPQExpBuffer();
18123 414 : cmd = createPQExpBuffer();
18124 414 : delcmd = createPQExpBuffer();
18125 414 : ruleprefix = createPQExpBuffer();
18126 :
18127 414 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18128 :
18129 414 : if (is_view)
18130 : {
18131 : PQExpBuffer result;
18132 :
18133 : /*
18134 : * We need OR REPLACE here because we'll be replacing a dummy view.
18135 : * Otherwise this should look largely like the regular view dump code.
18136 : */
18137 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
18138 20 : fmtQualifiedDumpable(tbinfo));
18139 20 : if (nonemptyReloptions(tbinfo->reloptions))
18140 : {
18141 0 : appendPQExpBufferStr(cmd, " WITH (");
18142 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
18143 0 : appendPQExpBufferChar(cmd, ')');
18144 : }
18145 20 : result = createViewAsClause(fout, tbinfo);
18146 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
18147 20 : destroyPQExpBuffer(result);
18148 20 : if (tbinfo->checkoption != NULL)
18149 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
18150 : tbinfo->checkoption);
18151 20 : appendPQExpBufferStr(cmd, ";\n");
18152 : }
18153 : else
18154 : {
18155 : /* In the rule case, just print pg_get_ruledef's result verbatim */
18156 394 : appendPQExpBuffer(query,
18157 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
18158 : rinfo->dobj.catId.oid);
18159 :
18160 394 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18161 :
18162 394 : if (PQntuples(res) != 1)
18163 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
18164 : rinfo->dobj.name, tbinfo->dobj.name);
18165 :
18166 394 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
18167 :
18168 394 : PQclear(res);
18169 : }
18170 :
18171 : /*
18172 : * Add the command to alter the rules replication firing semantics if it
18173 : * differs from the default.
18174 : */
18175 414 : if (rinfo->ev_enabled != 'O')
18176 : {
18177 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
18178 30 : switch (rinfo->ev_enabled)
18179 : {
18180 0 : case 'A':
18181 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
18182 0 : fmtId(rinfo->dobj.name));
18183 0 : break;
18184 0 : case 'R':
18185 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
18186 0 : fmtId(rinfo->dobj.name));
18187 0 : break;
18188 30 : case 'D':
18189 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
18190 30 : fmtId(rinfo->dobj.name));
18191 30 : break;
18192 : }
18193 384 : }
18194 :
18195 414 : if (is_view)
18196 : {
18197 : /*
18198 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
18199 : * REPLACE VIEW to replace the rule with something with minimal
18200 : * dependencies.
18201 : */
18202 : PQExpBuffer result;
18203 :
18204 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
18205 20 : fmtQualifiedDumpable(tbinfo));
18206 20 : result = createDummyViewAsClause(fout, tbinfo);
18207 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
18208 20 : destroyPQExpBuffer(result);
18209 : }
18210 : else
18211 : {
18212 394 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
18213 394 : fmtId(rinfo->dobj.name));
18214 394 : appendPQExpBuffer(delcmd, "ON %s;\n",
18215 394 : fmtQualifiedDumpable(tbinfo));
18216 : }
18217 :
18218 414 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
18219 414 : fmtId(rinfo->dobj.name));
18220 :
18221 414 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
18222 :
18223 414 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18224 414 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
18225 414 : ARCHIVE_OPTS(.tag = tag,
18226 : .namespace = tbinfo->dobj.namespace->dobj.name,
18227 : .owner = tbinfo->rolname,
18228 : .description = "RULE",
18229 : .section = SECTION_POST_DATA,
18230 : .createStmt = cmd->data,
18231 : .dropStmt = delcmd->data));
18232 :
18233 : /* Dump rule comments */
18234 414 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18235 0 : dumpComment(fout, ruleprefix->data, qtabname,
18236 0 : tbinfo->dobj.namespace->dobj.name,
18237 : tbinfo->rolname,
18238 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
18239 :
18240 414 : free(tag);
18241 414 : destroyPQExpBuffer(query);
18242 414 : destroyPQExpBuffer(cmd);
18243 414 : destroyPQExpBuffer(delcmd);
18244 414 : destroyPQExpBuffer(ruleprefix);
18245 414 : free(qtabname);
18246 : }
18247 :
18248 : /*
18249 : * getExtensionMembership --- obtain extension membership data
18250 : *
18251 : * We need to identify objects that are extension members as soon as they're
18252 : * loaded, so that we can correctly determine whether they need to be dumped.
18253 : * Generally speaking, extension member objects will get marked as *not* to
18254 : * be dumped, as they will be recreated by the single CREATE EXTENSION
18255 : * command. However, in binary upgrade mode we still need to dump the members
18256 : * individually.
18257 : */
18258 : void
18259 306 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
18260 : int numExtensions)
18261 : {
18262 : PQExpBuffer query;
18263 : PGresult *res;
18264 : int ntups,
18265 : i;
18266 : int i_classid,
18267 : i_objid,
18268 : i_refobjid;
18269 : ExtensionInfo *ext;
18270 :
18271 : /* Nothing to do if no extensions */
18272 306 : if (numExtensions == 0)
18273 0 : return;
18274 :
18275 306 : query = createPQExpBuffer();
18276 :
18277 : /* refclassid constraint is redundant but may speed the search */
18278 306 : appendPQExpBufferStr(query, "SELECT "
18279 : "classid, objid, refobjid "
18280 : "FROM pg_depend "
18281 : "WHERE refclassid = 'pg_extension'::regclass "
18282 : "AND deptype = 'e' "
18283 : "ORDER BY 3");
18284 :
18285 306 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18286 :
18287 306 : ntups = PQntuples(res);
18288 :
18289 306 : i_classid = PQfnumber(res, "classid");
18290 306 : i_objid = PQfnumber(res, "objid");
18291 306 : i_refobjid = PQfnumber(res, "refobjid");
18292 :
18293 : /*
18294 : * Since we ordered the SELECT by referenced ID, we can expect that
18295 : * multiple entries for the same extension will appear together; this
18296 : * saves on searches.
18297 : */
18298 306 : ext = NULL;
18299 :
18300 2730 : for (i = 0; i < ntups; i++)
18301 : {
18302 : CatalogId objId;
18303 : Oid extId;
18304 :
18305 2424 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18306 2424 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18307 2424 : extId = atooid(PQgetvalue(res, i, i_refobjid));
18308 :
18309 2424 : if (ext == NULL ||
18310 2118 : ext->dobj.catId.oid != extId)
18311 356 : ext = findExtensionByOid(extId);
18312 :
18313 2424 : if (ext == NULL)
18314 : {
18315 : /* shouldn't happen */
18316 0 : pg_log_warning("could not find referenced extension %u", extId);
18317 0 : continue;
18318 : }
18319 :
18320 2424 : recordExtensionMembership(objId, ext);
18321 : }
18322 :
18323 306 : PQclear(res);
18324 :
18325 306 : destroyPQExpBuffer(query);
18326 : }
18327 :
18328 : /*
18329 : * processExtensionTables --- deal with extension configuration tables
18330 : *
18331 : * There are two parts to this process:
18332 : *
18333 : * 1. Identify and create dump records for extension configuration tables.
18334 : *
18335 : * Extensions can mark tables as "configuration", which means that the user
18336 : * is able and expected to modify those tables after the extension has been
18337 : * loaded. For these tables, we dump out only the data- the structure is
18338 : * expected to be handled at CREATE EXTENSION time, including any indexes or
18339 : * foreign keys, which brings us to-
18340 : *
18341 : * 2. Record FK dependencies between configuration tables.
18342 : *
18343 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
18344 : * the data is loaded, we have to work out what the best order for reloading
18345 : * the data is, to avoid FK violations when the tables are restored. This is
18346 : * not perfect- we can't handle circular dependencies and if any exist they
18347 : * will cause an invalid dump to be produced (though at least all of the data
18348 : * is included for a user to manually restore). This is currently documented
18349 : * but perhaps we can provide a better solution in the future.
18350 : */
18351 : void
18352 304 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
18353 : int numExtensions)
18354 : {
18355 304 : DumpOptions *dopt = fout->dopt;
18356 : PQExpBuffer query;
18357 : PGresult *res;
18358 : int ntups,
18359 : i;
18360 : int i_conrelid,
18361 : i_confrelid;
18362 :
18363 : /* Nothing to do if no extensions */
18364 304 : if (numExtensions == 0)
18365 0 : return;
18366 :
18367 : /*
18368 : * Identify extension configuration tables and create TableDataInfo
18369 : * objects for them, ensuring their data will be dumped even though the
18370 : * tables themselves won't be.
18371 : *
18372 : * Note that we create TableDataInfo objects even in schemaOnly mode, ie,
18373 : * user data in a configuration table is treated like schema data. This
18374 : * seems appropriate since system data in a config table would get
18375 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
18376 : * list of extensions to be included, none of its data is dumped.
18377 : */
18378 658 : for (i = 0; i < numExtensions; i++)
18379 : {
18380 354 : ExtensionInfo *curext = &(extinfo[i]);
18381 354 : char *extconfig = curext->extconfig;
18382 354 : char *extcondition = curext->extcondition;
18383 354 : char **extconfigarray = NULL;
18384 354 : char **extconditionarray = NULL;
18385 354 : int nconfigitems = 0;
18386 354 : int nconditionitems = 0;
18387 :
18388 : /*
18389 : * Check if this extension is listed as to include in the dump. If
18390 : * not, any table data associated with it is discarded.
18391 : */
18392 354 : if (extension_include_oids.head != NULL &&
18393 16 : !simple_oid_list_member(&extension_include_oids,
18394 : curext->dobj.catId.oid))
18395 12 : continue;
18396 :
18397 : /*
18398 : * Check if this extension is listed as to exclude in the dump. If
18399 : * yes, any table data associated with it is discarded.
18400 : */
18401 354 : if (extension_exclude_oids.head != NULL &&
18402 8 : simple_oid_list_member(&extension_exclude_oids,
18403 : curext->dobj.catId.oid))
18404 4 : continue;
18405 :
18406 342 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
18407 : {
18408 : int j;
18409 :
18410 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
18411 0 : pg_fatal("could not parse %s array", "extconfig");
18412 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
18413 0 : pg_fatal("could not parse %s array", "extcondition");
18414 40 : if (nconfigitems != nconditionitems)
18415 0 : pg_fatal("mismatched number of configurations and conditions for extension");
18416 :
18417 120 : for (j = 0; j < nconfigitems; j++)
18418 : {
18419 : TableInfo *configtbl;
18420 80 : Oid configtbloid = atooid(extconfigarray[j]);
18421 80 : bool dumpobj =
18422 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
18423 :
18424 80 : configtbl = findTableByOid(configtbloid);
18425 80 : if (configtbl == NULL)
18426 0 : continue;
18427 :
18428 : /*
18429 : * Tables of not-to-be-dumped extensions shouldn't be dumped
18430 : * unless the table or its schema is explicitly included
18431 : */
18432 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
18433 : {
18434 : /* check table explicitly requested */
18435 4 : if (table_include_oids.head != NULL &&
18436 0 : simple_oid_list_member(&table_include_oids,
18437 : configtbloid))
18438 0 : dumpobj = true;
18439 :
18440 : /* check table's schema explicitly requested */
18441 4 : if (configtbl->dobj.namespace->dobj.dump &
18442 : DUMP_COMPONENT_DATA)
18443 4 : dumpobj = true;
18444 : }
18445 :
18446 : /* check table excluded by an exclusion switch */
18447 88 : if (table_exclude_oids.head != NULL &&
18448 8 : simple_oid_list_member(&table_exclude_oids,
18449 : configtbloid))
18450 2 : dumpobj = false;
18451 :
18452 : /* check schema excluded by an exclusion switch */
18453 80 : if (simple_oid_list_member(&schema_exclude_oids,
18454 80 : configtbl->dobj.namespace->dobj.catId.oid))
18455 0 : dumpobj = false;
18456 :
18457 80 : if (dumpobj)
18458 : {
18459 78 : makeTableDataInfo(dopt, configtbl);
18460 78 : if (configtbl->dataObj != NULL)
18461 : {
18462 78 : if (strlen(extconditionarray[j]) > 0)
18463 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
18464 : }
18465 : }
18466 : }
18467 : }
18468 342 : if (extconfigarray)
18469 40 : free(extconfigarray);
18470 342 : if (extconditionarray)
18471 40 : free(extconditionarray);
18472 : }
18473 :
18474 : /*
18475 : * Now that all the TableDataInfo objects have been created for all the
18476 : * extensions, check their FK dependencies and register them to try and
18477 : * dump the data out in an order that they can be restored in.
18478 : *
18479 : * Note that this is not a problem for user tables as their FKs are
18480 : * recreated after the data has been loaded.
18481 : */
18482 :
18483 304 : query = createPQExpBuffer();
18484 :
18485 304 : printfPQExpBuffer(query,
18486 : "SELECT conrelid, confrelid "
18487 : "FROM pg_constraint "
18488 : "JOIN pg_depend ON (objid = confrelid) "
18489 : "WHERE contype = 'f' "
18490 : "AND refclassid = 'pg_extension'::regclass "
18491 : "AND classid = 'pg_class'::regclass;");
18492 :
18493 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18494 304 : ntups = PQntuples(res);
18495 :
18496 304 : i_conrelid = PQfnumber(res, "conrelid");
18497 304 : i_confrelid = PQfnumber(res, "confrelid");
18498 :
18499 : /* Now get the dependencies and register them */
18500 304 : for (i = 0; i < ntups; i++)
18501 : {
18502 : Oid conrelid,
18503 : confrelid;
18504 : TableInfo *reftable,
18505 : *contable;
18506 :
18507 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
18508 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
18509 0 : contable = findTableByOid(conrelid);
18510 0 : reftable = findTableByOid(confrelid);
18511 :
18512 0 : if (reftable == NULL ||
18513 0 : reftable->dataObj == NULL ||
18514 0 : contable == NULL ||
18515 0 : contable->dataObj == NULL)
18516 0 : continue;
18517 :
18518 : /*
18519 : * Make referencing TABLE_DATA object depend on the referenced table's
18520 : * TABLE_DATA object.
18521 : */
18522 0 : addObjectDependency(&contable->dataObj->dobj,
18523 0 : reftable->dataObj->dobj.dumpId);
18524 : }
18525 304 : PQclear(res);
18526 304 : destroyPQExpBuffer(query);
18527 : }
18528 :
18529 : /*
18530 : * getDependencies --- obtain available dependency data
18531 : */
18532 : static void
18533 304 : getDependencies(Archive *fout)
18534 : {
18535 : PQExpBuffer query;
18536 : PGresult *res;
18537 : int ntups,
18538 : i;
18539 : int i_classid,
18540 : i_objid,
18541 : i_refclassid,
18542 : i_refobjid,
18543 : i_deptype;
18544 : DumpableObject *dobj,
18545 : *refdobj;
18546 :
18547 304 : pg_log_info("reading dependency data");
18548 :
18549 304 : query = createPQExpBuffer();
18550 :
18551 : /*
18552 : * Messy query to collect the dependency data we need. Note that we
18553 : * ignore the sub-object column, so that dependencies of or on a column
18554 : * look the same as dependencies of or on a whole table.
18555 : *
18556 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
18557 : * already processed by getExtensionMembership.
18558 : */
18559 304 : appendPQExpBufferStr(query, "SELECT "
18560 : "classid, objid, refclassid, refobjid, deptype "
18561 : "FROM pg_depend "
18562 : "WHERE deptype != 'p' AND deptype != 'e'\n");
18563 :
18564 : /*
18565 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
18566 : * have to translate their dependencies into dependencies of their parent
18567 : * opfamily. Ignore internal dependencies though, as those will point to
18568 : * their parent opclass, which we needn't consider here (and if we did,
18569 : * it'd just result in circular dependencies). Also, "loose" opfamily
18570 : * entries will have dependencies on their parent opfamily, which we
18571 : * should drop since they'd likewise become useless self-dependencies.
18572 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
18573 : */
18574 304 : appendPQExpBufferStr(query, "UNION ALL\n"
18575 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
18576 : "FROM pg_depend d, pg_amop o "
18577 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18578 : "classid = 'pg_amop'::regclass AND objid = o.oid "
18579 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
18580 :
18581 : /* Likewise for pg_amproc entries */
18582 304 : appendPQExpBufferStr(query, "UNION ALL\n"
18583 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
18584 : "FROM pg_depend d, pg_amproc p "
18585 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
18586 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
18587 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
18588 :
18589 : /* Sort the output for efficiency below */
18590 304 : appendPQExpBufferStr(query, "ORDER BY 1,2");
18591 :
18592 304 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18593 :
18594 304 : ntups = PQntuples(res);
18595 :
18596 304 : i_classid = PQfnumber(res, "classid");
18597 304 : i_objid = PQfnumber(res, "objid");
18598 304 : i_refclassid = PQfnumber(res, "refclassid");
18599 304 : i_refobjid = PQfnumber(res, "refobjid");
18600 304 : i_deptype = PQfnumber(res, "deptype");
18601 :
18602 : /*
18603 : * Since we ordered the SELECT by referencing ID, we can expect that
18604 : * multiple entries for the same object will appear together; this saves
18605 : * on searches.
18606 : */
18607 304 : dobj = NULL;
18608 :
18609 633686 : for (i = 0; i < ntups; i++)
18610 : {
18611 : CatalogId objId;
18612 : CatalogId refobjId;
18613 : char deptype;
18614 :
18615 633382 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
18616 633382 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
18617 633382 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
18618 633382 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
18619 633382 : deptype = *(PQgetvalue(res, i, i_deptype));
18620 :
18621 633382 : if (dobj == NULL ||
18622 600382 : dobj->catId.tableoid != objId.tableoid ||
18623 596806 : dobj->catId.oid != objId.oid)
18624 269376 : dobj = findObjectByCatalogId(objId);
18625 :
18626 : /*
18627 : * Failure to find objects mentioned in pg_depend is not unexpected,
18628 : * since for example we don't collect info about TOAST tables.
18629 : */
18630 633382 : if (dobj == NULL)
18631 : {
18632 : #ifdef NOT_USED
18633 : pg_log_warning("no referencing object %u %u",
18634 : objId.tableoid, objId.oid);
18635 : #endif
18636 34198 : continue;
18637 : }
18638 :
18639 600670 : refdobj = findObjectByCatalogId(refobjId);
18640 :
18641 600670 : if (refdobj == NULL)
18642 : {
18643 : #ifdef NOT_USED
18644 : pg_log_warning("no referenced object %u %u",
18645 : refobjId.tableoid, refobjId.oid);
18646 : #endif
18647 1486 : continue;
18648 : }
18649 :
18650 : /*
18651 : * For 'x' dependencies, mark the object for later; we still add the
18652 : * normal dependency, for possible ordering purposes. Currently
18653 : * pg_dump_sort.c knows to put extensions ahead of all object types
18654 : * that could possibly depend on them, but this is safer.
18655 : */
18656 599184 : if (deptype == 'x')
18657 88 : dobj->depends_on_ext = true;
18658 :
18659 : /*
18660 : * Ordinarily, table rowtypes have implicit dependencies on their
18661 : * tables. However, for a composite type the implicit dependency goes
18662 : * the other way in pg_depend; which is the right thing for DROP but
18663 : * it doesn't produce the dependency ordering we need. So in that one
18664 : * case, we reverse the direction of the dependency.
18665 : */
18666 599184 : if (deptype == 'i' &&
18667 164788 : dobj->objType == DO_TABLE &&
18668 1758 : refdobj->objType == DO_TYPE)
18669 296 : addObjectDependency(refdobj, dobj->dumpId);
18670 : else
18671 : /* normal case */
18672 598888 : addObjectDependency(dobj, refdobj->dumpId);
18673 : }
18674 :
18675 304 : PQclear(res);
18676 :
18677 304 : destroyPQExpBuffer(query);
18678 304 : }
18679 :
18680 :
18681 : /*
18682 : * createBoundaryObjects - create dummy DumpableObjects to represent
18683 : * dump section boundaries.
18684 : */
18685 : static DumpableObject *
18686 304 : createBoundaryObjects(void)
18687 : {
18688 : DumpableObject *dobjs;
18689 :
18690 304 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
18691 :
18692 304 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
18693 304 : dobjs[0].catId = nilCatalogId;
18694 304 : AssignDumpId(dobjs + 0);
18695 304 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
18696 :
18697 304 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
18698 304 : dobjs[1].catId = nilCatalogId;
18699 304 : AssignDumpId(dobjs + 1);
18700 304 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
18701 :
18702 304 : return dobjs;
18703 : }
18704 :
18705 : /*
18706 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
18707 : * section boundaries.
18708 : */
18709 : static void
18710 304 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
18711 : DumpableObject *boundaryObjs)
18712 : {
18713 304 : DumpableObject *preDataBound = boundaryObjs + 0;
18714 304 : DumpableObject *postDataBound = boundaryObjs + 1;
18715 : int i;
18716 :
18717 1098608 : for (i = 0; i < numObjs; i++)
18718 : {
18719 1098304 : DumpableObject *dobj = dobjs[i];
18720 :
18721 : /*
18722 : * The classification of object types here must match the SECTION_xxx
18723 : * values assigned during subsequent ArchiveEntry calls!
18724 : */
18725 1098304 : switch (dobj->objType)
18726 : {
18727 1030214 : case DO_NAMESPACE:
18728 : case DO_EXTENSION:
18729 : case DO_TYPE:
18730 : case DO_SHELL_TYPE:
18731 : case DO_FUNC:
18732 : case DO_AGG:
18733 : case DO_OPERATOR:
18734 : case DO_ACCESS_METHOD:
18735 : case DO_OPCLASS:
18736 : case DO_OPFAMILY:
18737 : case DO_COLLATION:
18738 : case DO_CONVERSION:
18739 : case DO_TABLE:
18740 : case DO_TABLE_ATTACH:
18741 : case DO_ATTRDEF:
18742 : case DO_PROCLANG:
18743 : case DO_CAST:
18744 : case DO_DUMMY_TYPE:
18745 : case DO_TSPARSER:
18746 : case DO_TSDICT:
18747 : case DO_TSTEMPLATE:
18748 : case DO_TSCONFIG:
18749 : case DO_FDW:
18750 : case DO_FOREIGN_SERVER:
18751 : case DO_TRANSFORM:
18752 : /* Pre-data objects: must come before the pre-data boundary */
18753 1030214 : addObjectDependency(preDataBound, dobj->dumpId);
18754 1030214 : break;
18755 8038 : case DO_TABLE_DATA:
18756 : case DO_SEQUENCE_SET:
18757 : case DO_LARGE_OBJECT:
18758 : case DO_LARGE_OBJECT_DATA:
18759 : /* Data objects: must come between the boundaries */
18760 8038 : addObjectDependency(dobj, preDataBound->dumpId);
18761 8038 : addObjectDependency(postDataBound, dobj->dumpId);
18762 8038 : break;
18763 9632 : case DO_INDEX:
18764 : case DO_INDEX_ATTACH:
18765 : case DO_STATSEXT:
18766 : case DO_REFRESH_MATVIEW:
18767 : case DO_TRIGGER:
18768 : case DO_EVENT_TRIGGER:
18769 : case DO_DEFAULT_ACL:
18770 : case DO_POLICY:
18771 : case DO_PUBLICATION:
18772 : case DO_PUBLICATION_REL:
18773 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
18774 : case DO_SUBSCRIPTION:
18775 : case DO_SUBSCRIPTION_REL:
18776 : /* Post-data objects: must come after the post-data boundary */
18777 9632 : addObjectDependency(dobj, postDataBound->dumpId);
18778 9632 : break;
18779 45880 : case DO_RULE:
18780 : /* Rules are post-data, but only if dumped separately */
18781 45880 : if (((RuleInfo *) dobj)->separate)
18782 998 : addObjectDependency(dobj, postDataBound->dumpId);
18783 45880 : break;
18784 3932 : case DO_CONSTRAINT:
18785 : case DO_FK_CONSTRAINT:
18786 : /* Constraints are post-data, but only if dumped separately */
18787 3932 : if (((ConstraintInfo *) dobj)->separate)
18788 2734 : addObjectDependency(dobj, postDataBound->dumpId);
18789 3932 : break;
18790 304 : case DO_PRE_DATA_BOUNDARY:
18791 : /* nothing to do */
18792 304 : break;
18793 304 : case DO_POST_DATA_BOUNDARY:
18794 : /* must come after the pre-data boundary */
18795 304 : addObjectDependency(dobj, preDataBound->dumpId);
18796 304 : break;
18797 : }
18798 1098304 : }
18799 304 : }
18800 :
18801 :
18802 : /*
18803 : * BuildArchiveDependencies - create dependency data for archive TOC entries
18804 : *
18805 : * The raw dependency data obtained by getDependencies() is not terribly
18806 : * useful in an archive dump, because in many cases there are dependency
18807 : * chains linking through objects that don't appear explicitly in the dump.
18808 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
18809 : * will depend on other objects --- but the rule will not appear as a separate
18810 : * object in the dump. We need to adjust the view's dependencies to include
18811 : * whatever the rule depends on that is included in the dump.
18812 : *
18813 : * Just to make things more complicated, there are also "special" dependencies
18814 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
18815 : * not rearrange because pg_restore knows that TABLE DATA only depends on
18816 : * its table. In these cases we must leave the dependencies strictly as-is
18817 : * even if they refer to not-to-be-dumped objects.
18818 : *
18819 : * To handle this, the convention is that "special" dependencies are created
18820 : * during ArchiveEntry calls, and an archive TOC item that has any such
18821 : * entries will not be touched here. Otherwise, we recursively search the
18822 : * DumpableObject data structures to build the correct dependencies for each
18823 : * archive TOC item.
18824 : */
18825 : static void
18826 62 : BuildArchiveDependencies(Archive *fout)
18827 : {
18828 62 : ArchiveHandle *AH = (ArchiveHandle *) fout;
18829 : TocEntry *te;
18830 :
18831 : /* Scan all TOC entries in the archive */
18832 9658 : for (te = AH->toc->next; te != AH->toc; te = te->next)
18833 : {
18834 : DumpableObject *dobj;
18835 : DumpId *dependencies;
18836 : int nDeps;
18837 : int allocDeps;
18838 :
18839 : /* No need to process entries that will not be dumped */
18840 9596 : if (te->reqs == 0)
18841 2950 : continue;
18842 : /* Ignore entries that already have "special" dependencies */
18843 9590 : if (te->nDeps > 0)
18844 2418 : continue;
18845 : /* Otherwise, look up the item's original DumpableObject, if any */
18846 7172 : dobj = findObjectByDumpId(te->dumpId);
18847 7172 : if (dobj == NULL)
18848 318 : continue;
18849 : /* No work if it has no dependencies */
18850 6854 : if (dobj->nDeps <= 0)
18851 208 : continue;
18852 : /* Set up work array */
18853 6646 : allocDeps = 64;
18854 6646 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
18855 6646 : nDeps = 0;
18856 : /* Recursively find all dumpable dependencies */
18857 6646 : findDumpableDependencies(AH, dobj,
18858 : &dependencies, &nDeps, &allocDeps);
18859 : /* And save 'em ... */
18860 6646 : if (nDeps > 0)
18861 : {
18862 5026 : dependencies = (DumpId *) pg_realloc(dependencies,
18863 : nDeps * sizeof(DumpId));
18864 5026 : te->dependencies = dependencies;
18865 5026 : te->nDeps = nDeps;
18866 : }
18867 : else
18868 1620 : free(dependencies);
18869 : }
18870 62 : }
18871 :
18872 : /* Recursive search subroutine for BuildArchiveDependencies */
18873 : static void
18874 16392 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
18875 : DumpId **dependencies, int *nDeps, int *allocDeps)
18876 : {
18877 : int i;
18878 :
18879 : /*
18880 : * Ignore section boundary objects: if we search through them, we'll
18881 : * report lots of bogus dependencies.
18882 : */
18883 16392 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
18884 16354 : dobj->objType == DO_POST_DATA_BOUNDARY)
18885 2896 : return;
18886 :
18887 34114 : for (i = 0; i < dobj->nDeps; i++)
18888 : {
18889 20618 : DumpId depid = dobj->dependencies[i];
18890 :
18891 20618 : if (TocIDRequired(AH, depid) != 0)
18892 : {
18893 : /* Object will be dumped, so just reference it as a dependency */
18894 10872 : if (*nDeps >= *allocDeps)
18895 : {
18896 0 : *allocDeps *= 2;
18897 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
18898 0 : *allocDeps * sizeof(DumpId));
18899 : }
18900 10872 : (*dependencies)[*nDeps] = depid;
18901 10872 : (*nDeps)++;
18902 : }
18903 : else
18904 : {
18905 : /*
18906 : * Object will not be dumped, so recursively consider its deps. We
18907 : * rely on the assumption that sortDumpableObjects already broke
18908 : * any dependency loops, else we might recurse infinitely.
18909 : */
18910 9746 : DumpableObject *otherdobj = findObjectByDumpId(depid);
18911 :
18912 9746 : if (otherdobj)
18913 9746 : findDumpableDependencies(AH, otherdobj,
18914 : dependencies, nDeps, allocDeps);
18915 : }
18916 : }
18917 : }
18918 :
18919 :
18920 : /*
18921 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
18922 : * given type OID.
18923 : *
18924 : * This does not guarantee to schema-qualify the output, so it should not
18925 : * be used to create the target object name for CREATE or ALTER commands.
18926 : *
18927 : * Note that the result is cached and must not be freed by the caller.
18928 : */
18929 : static const char *
18930 4548 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
18931 : {
18932 : TypeInfo *typeInfo;
18933 : char *result;
18934 : PQExpBuffer query;
18935 : PGresult *res;
18936 :
18937 4548 : if (oid == 0)
18938 : {
18939 0 : if ((opts & zeroAsStar) != 0)
18940 0 : return "*";
18941 0 : else if ((opts & zeroAsNone) != 0)
18942 0 : return "NONE";
18943 : }
18944 :
18945 : /* see if we have the result cached in the type's TypeInfo record */
18946 4548 : typeInfo = findTypeByOid(oid);
18947 4548 : if (typeInfo && typeInfo->ftypname)
18948 3624 : return typeInfo->ftypname;
18949 :
18950 924 : query = createPQExpBuffer();
18951 924 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
18952 : oid);
18953 :
18954 924 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18955 :
18956 : /* result of format_type is already quoted */
18957 924 : result = pg_strdup(PQgetvalue(res, 0, 0));
18958 :
18959 924 : PQclear(res);
18960 924 : destroyPQExpBuffer(query);
18961 :
18962 : /*
18963 : * Cache the result for re-use in later requests, if possible. If we
18964 : * don't have a TypeInfo for the type, the string will be leaked once the
18965 : * caller is done with it ... but that case really should not happen, so
18966 : * leaking if it does seems acceptable.
18967 : */
18968 924 : if (typeInfo)
18969 924 : typeInfo->ftypname = result;
18970 :
18971 924 : return result;
18972 : }
18973 :
18974 : /*
18975 : * Return a column list clause for the given relation.
18976 : *
18977 : * Special case: if there are no undropped columns in the relation, return
18978 : * "", not an invalid "()" column list.
18979 : */
18980 : static const char *
18981 13636 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
18982 : {
18983 13636 : int numatts = ti->numatts;
18984 13636 : char **attnames = ti->attnames;
18985 13636 : bool *attisdropped = ti->attisdropped;
18986 13636 : char *attgenerated = ti->attgenerated;
18987 : bool needComma;
18988 : int i;
18989 :
18990 13636 : appendPQExpBufferChar(buffer, '(');
18991 13636 : needComma = false;
18992 67224 : for (i = 0; i < numatts; i++)
18993 : {
18994 53588 : if (attisdropped[i])
18995 1132 : continue;
18996 52456 : if (attgenerated[i])
18997 1180 : continue;
18998 51276 : if (needComma)
18999 38072 : appendPQExpBufferStr(buffer, ", ");
19000 51276 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
19001 51276 : needComma = true;
19002 : }
19003 :
19004 13636 : if (!needComma)
19005 432 : return ""; /* no undropped columns */
19006 :
19007 13204 : appendPQExpBufferChar(buffer, ')');
19008 13204 : return buffer->data;
19009 : }
19010 :
19011 : /*
19012 : * Check if a reloptions array is nonempty.
19013 : */
19014 : static bool
19015 22896 : nonemptyReloptions(const char *reloptions)
19016 : {
19017 : /* Don't want to print it if it's just "{}" */
19018 22896 : return (reloptions != NULL && strlen(reloptions) > 2);
19019 : }
19020 :
19021 : /*
19022 : * Format a reloptions array and append it to the given buffer.
19023 : *
19024 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
19025 : */
19026 : static void
19027 408 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
19028 : const char *prefix, Archive *fout)
19029 : {
19030 : bool res;
19031 :
19032 408 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
19033 408 : fout->std_strings);
19034 408 : if (!res)
19035 0 : pg_log_warning("could not parse %s array", "reloptions");
19036 408 : }
19037 :
19038 : /*
19039 : * read_dump_filters - retrieve object identifier patterns from file
19040 : *
19041 : * Parse the specified filter file for include and exclude patterns, and add
19042 : * them to the relevant lists. If the filename is "-" then filters will be
19043 : * read from STDIN rather than a file.
19044 : */
19045 : static void
19046 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
19047 : {
19048 : FilterStateData fstate;
19049 : char *objname;
19050 : FilterCommandType comtype;
19051 : FilterObjectType objtype;
19052 :
19053 52 : filter_init(&fstate, filename, exit_nicely);
19054 :
19055 116 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
19056 : {
19057 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
19058 : {
19059 34 : switch (objtype)
19060 : {
19061 0 : case FILTER_OBJECT_TYPE_NONE:
19062 0 : break;
19063 0 : case FILTER_OBJECT_TYPE_DATABASE:
19064 : case FILTER_OBJECT_TYPE_FUNCTION:
19065 : case FILTER_OBJECT_TYPE_INDEX:
19066 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19067 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19068 : case FILTER_OBJECT_TYPE_TRIGGER:
19069 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19070 : "include",
19071 : filter_object_type_name(objtype));
19072 0 : exit_nicely(1);
19073 : break; /* unreachable */
19074 :
19075 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19076 2 : simple_string_list_append(&extension_include_patterns, objname);
19077 2 : break;
19078 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19079 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
19080 2 : break;
19081 2 : case FILTER_OBJECT_TYPE_SCHEMA:
19082 2 : simple_string_list_append(&schema_include_patterns, objname);
19083 2 : dopt->include_everything = false;
19084 2 : break;
19085 26 : case FILTER_OBJECT_TYPE_TABLE:
19086 26 : simple_string_list_append(&table_include_patterns, objname);
19087 26 : dopt->include_everything = false;
19088 26 : break;
19089 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19090 2 : simple_string_list_append(&table_include_patterns_and_children,
19091 : objname);
19092 2 : dopt->include_everything = false;
19093 2 : break;
19094 : }
19095 34 : }
19096 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
19097 : {
19098 18 : switch (objtype)
19099 : {
19100 0 : case FILTER_OBJECT_TYPE_NONE:
19101 0 : break;
19102 2 : case FILTER_OBJECT_TYPE_DATABASE:
19103 : case FILTER_OBJECT_TYPE_FUNCTION:
19104 : case FILTER_OBJECT_TYPE_INDEX:
19105 : case FILTER_OBJECT_TYPE_TRIGGER:
19106 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
19107 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
19108 : "exclude",
19109 : filter_object_type_name(objtype));
19110 2 : exit_nicely(1);
19111 : break;
19112 :
19113 2 : case FILTER_OBJECT_TYPE_EXTENSION:
19114 2 : simple_string_list_append(&extension_exclude_patterns, objname);
19115 2 : break;
19116 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
19117 2 : simple_string_list_append(&tabledata_exclude_patterns,
19118 : objname);
19119 2 : break;
19120 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
19121 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
19122 : objname);
19123 2 : break;
19124 4 : case FILTER_OBJECT_TYPE_SCHEMA:
19125 4 : simple_string_list_append(&schema_exclude_patterns, objname);
19126 4 : break;
19127 4 : case FILTER_OBJECT_TYPE_TABLE:
19128 4 : simple_string_list_append(&table_exclude_patterns, objname);
19129 4 : break;
19130 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
19131 2 : simple_string_list_append(&table_exclude_patterns_and_children,
19132 : objname);
19133 2 : break;
19134 : }
19135 16 : }
19136 : else
19137 : {
19138 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
19139 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
19140 : }
19141 :
19142 64 : if (objname)
19143 50 : free(objname);
19144 : }
19145 :
19146 44 : filter_free(&fstate);
19147 44 : }
|