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-2025, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * pg_dump will read the system catalogs in a database and dump out a
11 : * script that reproduces the schema in terms of SQL that is understood
12 : * by PostgreSQL
13 : *
14 : * Note that pg_dump runs in a transaction-snapshot mode transaction,
15 : * so it sees a consistent snapshot of the database including system
16 : * catalogs. However, it relies in part on various specialized backend
17 : * functions like pg_get_indexdef(), and those things tend to look at
18 : * the currently committed state. So it is possible to get 'cache
19 : * lookup failed' error if someone performs DDL changes while a dump is
20 : * happening. The window for this sort of thing is from the acquisition
21 : * of the transaction snapshot to getSchemaData() (when pg_dump acquires
22 : * AccessShareLock on every table it intends to dump). It isn't very large,
23 : * but it can happen.
24 : *
25 : * http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
26 : *
27 : * IDENTIFICATION
28 : * src/bin/pg_dump/pg_dump.c
29 : *
30 : *-------------------------------------------------------------------------
31 : */
32 : #include "postgres_fe.h"
33 :
34 : #include <unistd.h>
35 : #include <ctype.h>
36 : #include <limits.h>
37 : #ifdef HAVE_TERMIOS_H
38 : #include <termios.h>
39 : #endif
40 :
41 : #include "access/attnum.h"
42 : #include "access/sysattr.h"
43 : #include "access/transam.h"
44 : #include "catalog/pg_aggregate_d.h"
45 : #include "catalog/pg_am_d.h"
46 : #include "catalog/pg_attribute_d.h"
47 : #include "catalog/pg_authid_d.h"
48 : #include "catalog/pg_cast_d.h"
49 : #include "catalog/pg_class_d.h"
50 : #include "catalog/pg_constraint_d.h"
51 : #include "catalog/pg_default_acl_d.h"
52 : #include "catalog/pg_largeobject_d.h"
53 : #include "catalog/pg_largeobject_metadata_d.h"
54 : #include "catalog/pg_proc_d.h"
55 : #include "catalog/pg_publication_d.h"
56 : #include "catalog/pg_shdepend_d.h"
57 : #include "catalog/pg_subscription_d.h"
58 : #include "catalog/pg_type_d.h"
59 : #include "common/connect.h"
60 : #include "common/int.h"
61 : #include "common/relpath.h"
62 : #include "common/shortest_dec.h"
63 : #include "compress_io.h"
64 : #include "dumputils.h"
65 : #include "fe_utils/option_utils.h"
66 : #include "fe_utils/string_utils.h"
67 : #include "filter.h"
68 : #include "getopt_long.h"
69 : #include "libpq/libpq-fs.h"
70 : #include "parallel.h"
71 : #include "pg_backup_db.h"
72 : #include "pg_backup_utils.h"
73 : #include "pg_dump.h"
74 : #include "storage/block.h"
75 :
76 : typedef struct
77 : {
78 : Oid roleoid; /* role's OID */
79 : const char *rolename; /* role's name */
80 : } RoleNameItem;
81 :
82 : typedef struct
83 : {
84 : const char *descr; /* comment for an object */
85 : Oid classoid; /* object class (catalog OID) */
86 : Oid objoid; /* object OID */
87 : int objsubid; /* subobject (table column #) */
88 : } CommentItem;
89 :
90 : typedef struct
91 : {
92 : const char *provider; /* label provider of this security label */
93 : const char *label; /* security label for an object */
94 : Oid classoid; /* object class (catalog OID) */
95 : Oid objoid; /* object OID */
96 : int objsubid; /* subobject (table column #) */
97 : } SecLabelItem;
98 :
99 : typedef struct
100 : {
101 : Oid oid; /* object OID */
102 : char relkind; /* object kind */
103 : RelFileNumber relfilenumber; /* object filenode */
104 : Oid toast_oid; /* toast table OID */
105 : RelFileNumber toast_relfilenumber; /* toast table filenode */
106 : Oid toast_index_oid; /* toast table index OID */
107 : RelFileNumber toast_index_relfilenumber; /* toast table index filenode */
108 : } BinaryUpgradeClassOidItem;
109 :
110 : /* sequence types */
111 : typedef enum SeqType
112 : {
113 : SEQTYPE_SMALLINT,
114 : SEQTYPE_INTEGER,
115 : SEQTYPE_BIGINT,
116 : } SeqType;
117 :
118 : static const char *const SeqTypeNames[] =
119 : {
120 : [SEQTYPE_SMALLINT] = "smallint",
121 : [SEQTYPE_INTEGER] = "integer",
122 : [SEQTYPE_BIGINT] = "bigint",
123 : };
124 :
125 : StaticAssertDecl(lengthof(SeqTypeNames) == (SEQTYPE_BIGINT + 1),
126 : "array length mismatch");
127 :
128 : typedef struct
129 : {
130 : Oid oid; /* sequence OID */
131 : SeqType seqtype; /* data type of sequence */
132 : bool cycled; /* whether sequence cycles */
133 : int64 minv; /* minimum value */
134 : int64 maxv; /* maximum value */
135 : int64 startv; /* start value */
136 : int64 incby; /* increment value */
137 : int64 cache; /* cache size */
138 : int64 last_value; /* last value of sequence */
139 : bool is_called; /* whether nextval advances before returning */
140 : } SequenceItem;
141 :
142 : typedef enum OidOptions
143 : {
144 : zeroIsError = 1,
145 : zeroAsStar = 2,
146 : zeroAsNone = 4,
147 : } OidOptions;
148 :
149 : /* global decls */
150 : static bool dosync = true; /* Issue fsync() to make dump durable on disk. */
151 :
152 : static Oid g_last_builtin_oid; /* value of the last builtin oid */
153 :
154 : /* The specified names/patterns should to match at least one entity */
155 : static int strict_names = 0;
156 :
157 : static pg_compress_algorithm compression_algorithm = PG_COMPRESSION_NONE;
158 :
159 : /*
160 : * Object inclusion/exclusion lists
161 : *
162 : * The string lists record the patterns given by command-line switches,
163 : * which we then convert to lists of OIDs of matching objects.
164 : */
165 : static SimpleStringList schema_include_patterns = {NULL, NULL};
166 : static SimpleOidList schema_include_oids = {NULL, NULL};
167 : static SimpleStringList schema_exclude_patterns = {NULL, NULL};
168 : static SimpleOidList schema_exclude_oids = {NULL, NULL};
169 :
170 : static SimpleStringList table_include_patterns = {NULL, NULL};
171 : static SimpleStringList table_include_patterns_and_children = {NULL, NULL};
172 : static SimpleOidList table_include_oids = {NULL, NULL};
173 : static SimpleStringList table_exclude_patterns = {NULL, NULL};
174 : static SimpleStringList table_exclude_patterns_and_children = {NULL, NULL};
175 : static SimpleOidList table_exclude_oids = {NULL, NULL};
176 : static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
177 : static SimpleStringList tabledata_exclude_patterns_and_children = {NULL, NULL};
178 : static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
179 :
180 : static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
181 : static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
182 :
183 : static SimpleStringList extension_include_patterns = {NULL, NULL};
184 : static SimpleOidList extension_include_oids = {NULL, NULL};
185 :
186 : static SimpleStringList extension_exclude_patterns = {NULL, NULL};
187 : static SimpleOidList extension_exclude_oids = {NULL, NULL};
188 :
189 : static const CatalogId nilCatalogId = {0, 0};
190 :
191 : /* override for standard extra_float_digits setting */
192 : static bool have_extra_float_digits = false;
193 : static int extra_float_digits;
194 :
195 : /* sorted table of role names */
196 : static RoleNameItem *rolenames = NULL;
197 : static int nrolenames = 0;
198 :
199 : /* sorted table of comments */
200 : static CommentItem *comments = NULL;
201 : static int ncomments = 0;
202 :
203 : /* sorted table of security labels */
204 : static SecLabelItem *seclabels = NULL;
205 : static int nseclabels = 0;
206 :
207 : /* sorted table of pg_class information for binary upgrade */
208 : static BinaryUpgradeClassOidItem *binaryUpgradeClassOids = NULL;
209 : static int nbinaryUpgradeClassOids = 0;
210 :
211 : /* sorted table of sequences */
212 : static SequenceItem *sequences = NULL;
213 : static int nsequences = 0;
214 :
215 : /*
216 : * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use
217 : * as a dependency for pg_shdepend and any large object comments/seclabels.
218 : */
219 : static DumpId lo_metadata_dumpId;
220 :
221 : /* Maximum number of relations to fetch in a fetchAttributeStats() call. */
222 : #define MAX_ATTR_STATS_RELS 64
223 :
224 : /*
225 : * The default number of rows per INSERT when
226 : * --inserts is specified without --rows-per-insert
227 : */
228 : #define DUMP_DEFAULT_ROWS_PER_INSERT 1
229 :
230 : /*
231 : * Maximum number of large objects to group into a single ArchiveEntry.
232 : * At some point we might want to make this user-controllable, but for now
233 : * a hard-wired setting will suffice.
234 : */
235 : #define MAX_BLOBS_PER_ARCHIVE_ENTRY 1000
236 :
237 : /*
238 : * Macro for producing quoted, schema-qualified name of a dumpable object.
239 : */
240 : #define fmtQualifiedDumpable(obj) \
241 : fmtQualifiedId((obj)->dobj.namespace->dobj.name, \
242 : (obj)->dobj.name)
243 :
244 : static void help(const char *progname);
245 : static void setup_connection(Archive *AH,
246 : const char *dumpencoding, const char *dumpsnapshot,
247 : char *use_role);
248 : static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
249 : static void expand_schema_name_patterns(Archive *fout,
250 : SimpleStringList *patterns,
251 : SimpleOidList *oids,
252 : bool strict_names);
253 : static void expand_extension_name_patterns(Archive *fout,
254 : SimpleStringList *patterns,
255 : SimpleOidList *oids,
256 : bool strict_names);
257 : static void expand_foreign_server_name_patterns(Archive *fout,
258 : SimpleStringList *patterns,
259 : SimpleOidList *oids);
260 : static void expand_table_name_patterns(Archive *fout,
261 : SimpleStringList *patterns,
262 : SimpleOidList *oids,
263 : bool strict_names,
264 : bool with_child_tables);
265 : static void prohibit_crossdb_refs(PGconn *conn, const char *dbname,
266 : const char *pattern);
267 :
268 : static NamespaceInfo *findNamespace(Oid nsoid);
269 : static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
270 : static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
271 : static const char *getRoleName(const char *roleoid_str);
272 : static void collectRoleNames(Archive *fout);
273 : static void getAdditionalACLs(Archive *fout);
274 : static void dumpCommentExtended(Archive *fout, const char *type,
275 : const char *name, const char *namespace,
276 : const char *owner, CatalogId catalogId,
277 : int subid, DumpId dumpId,
278 : const char *initdb_comment);
279 : static inline void dumpComment(Archive *fout, const char *type,
280 : const char *name, const char *namespace,
281 : const char *owner, CatalogId catalogId,
282 : int subid, DumpId dumpId);
283 : static int findComments(Oid classoid, Oid objoid, CommentItem **items);
284 : static void collectComments(Archive *fout);
285 : static void dumpSecLabel(Archive *fout, const char *type, const char *name,
286 : const char *namespace, const char *owner,
287 : CatalogId catalogId, int subid, DumpId dumpId);
288 : static int findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items);
289 : static void collectSecLabels(Archive *fout);
290 : static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
291 : static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
292 : static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
293 : static void dumpType(Archive *fout, const TypeInfo *tyinfo);
294 : static void dumpBaseType(Archive *fout, const TypeInfo *tyinfo);
295 : static void dumpEnumType(Archive *fout, const TypeInfo *tyinfo);
296 : static void dumpRangeType(Archive *fout, const TypeInfo *tyinfo);
297 : static void dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo);
298 : static void dumpDomain(Archive *fout, const TypeInfo *tyinfo);
299 : static void dumpCompositeType(Archive *fout, const TypeInfo *tyinfo);
300 : static void dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
301 : PGresult *res);
302 : static void dumpShellType(Archive *fout, const ShellTypeInfo *stinfo);
303 : static void dumpProcLang(Archive *fout, const ProcLangInfo *plang);
304 : static void dumpFunc(Archive *fout, const FuncInfo *finfo);
305 : static void dumpCast(Archive *fout, const CastInfo *cast);
306 : static void dumpTransform(Archive *fout, const TransformInfo *transform);
307 : static void dumpOpr(Archive *fout, const OprInfo *oprinfo);
308 : static void dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo);
309 : static void dumpOpclass(Archive *fout, const OpclassInfo *opcinfo);
310 : static void dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo);
311 : static void dumpCollation(Archive *fout, const CollInfo *collinfo);
312 : static void dumpConversion(Archive *fout, const ConvInfo *convinfo);
313 : static void dumpRule(Archive *fout, const RuleInfo *rinfo);
314 : static void dumpAgg(Archive *fout, const AggInfo *agginfo);
315 : static void dumpTrigger(Archive *fout, const TriggerInfo *tginfo);
316 : static void dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo);
317 : static void dumpTable(Archive *fout, const TableInfo *tbinfo);
318 : static void dumpTableSchema(Archive *fout, const TableInfo *tbinfo);
319 : static void dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo);
320 : static void dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo);
321 : static void collectSequences(Archive *fout);
322 : static void dumpSequence(Archive *fout, const TableInfo *tbinfo);
323 : static void dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo);
324 : static void dumpIndex(Archive *fout, const IndxInfo *indxinfo);
325 : static void dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo);
326 : static void dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo);
327 : static void dumpConstraint(Archive *fout, const ConstraintInfo *coninfo);
328 : static void dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo);
329 : static void dumpTSParser(Archive *fout, const TSParserInfo *prsinfo);
330 : static void dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo);
331 : static void dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo);
332 : static void dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo);
333 : static void dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo);
334 : static void dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo);
335 : static void dumpUserMappings(Archive *fout,
336 : const char *servername, const char *namespace,
337 : const char *owner, CatalogId catalogId, DumpId dumpId);
338 : static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
339 :
340 : static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
341 : const char *type, const char *name, const char *subname,
342 : const char *nspname, const char *tag, const char *owner,
343 : const DumpableAcl *dacl);
344 :
345 : static void getDependencies(Archive *fout);
346 : static void BuildArchiveDependencies(Archive *fout);
347 : static void findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
348 : DumpId **dependencies, int *nDeps, int *allocDeps);
349 :
350 : static DumpableObject *createBoundaryObjects(void);
351 : static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
352 : DumpableObject *boundaryObjs);
353 :
354 : static void addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx);
355 : static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo);
356 : static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind);
357 : static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo);
358 : static void buildMatViewRefreshDependencies(Archive *fout);
359 : static void getTableDataFKConstraints(void);
360 : static void determineNotNullFlags(Archive *fout, PGresult *res, int r,
361 : TableInfo *tbinfo, int j,
362 : int i_notnull_name,
363 : int i_notnull_comment,
364 : int i_notnull_invalidoid,
365 : int i_notnull_noinherit,
366 : int i_notnull_islocal,
367 : PQExpBuffer *invalidnotnulloids);
368 : static char *format_function_arguments(const FuncInfo *finfo, const char *funcargs,
369 : bool is_agg);
370 : static char *format_function_signature(Archive *fout,
371 : const FuncInfo *finfo, bool honor_quotes);
372 : static char *convertRegProcReference(const char *proc);
373 : static char *getFormattedOperatorName(const char *oproid);
374 : static char *convertTSFunction(Archive *fout, Oid funcOid);
375 : static const char *getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts);
376 : static void getLOs(Archive *fout);
377 : static void dumpLO(Archive *fout, const LoInfo *loinfo);
378 : static int dumpLOs(Archive *fout, const void *arg);
379 : static void dumpPolicy(Archive *fout, const PolicyInfo *polinfo);
380 : static void dumpPublication(Archive *fout, const PublicationInfo *pubinfo);
381 : static void dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo);
382 : static void dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo);
383 : static void dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo);
384 : static void dumpDatabase(Archive *fout);
385 : static void dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
386 : const char *dbname, Oid dboid);
387 : static void dumpEncoding(Archive *AH);
388 : static void dumpStdStrings(Archive *AH);
389 : static void dumpSearchPath(Archive *AH);
390 : static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
391 : PQExpBuffer upgrade_buffer,
392 : Oid pg_type_oid,
393 : bool force_array_type,
394 : bool include_multirange_type);
395 : static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
396 : PQExpBuffer upgrade_buffer,
397 : const TableInfo *tbinfo);
398 : static void collectBinaryUpgradeClassOids(Archive *fout);
399 : static void binary_upgrade_set_pg_class_oids(Archive *fout,
400 : PQExpBuffer upgrade_buffer,
401 : Oid pg_class_oid);
402 : static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
403 : const DumpableObject *dobj,
404 : const char *objtype,
405 : const char *objname,
406 : const char *objnamespace);
407 : static const char *getAttrName(int attrnum, const TableInfo *tblInfo);
408 : static const char *fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer);
409 : static bool nonemptyReloptions(const char *reloptions);
410 : static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
411 : const char *prefix, Archive *fout);
412 : static char *get_synchronized_snapshot(Archive *fout);
413 : static void set_restrict_relation_kind(Archive *AH, const char *value);
414 : static void setupDumpWorker(Archive *AH);
415 : static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
416 : static bool forcePartitionRootLoad(const TableInfo *tbinfo);
417 : static void read_dump_filters(const char *filename, DumpOptions *dopt);
418 :
419 :
420 : int
421 698 : main(int argc, char **argv)
422 : {
423 : int c;
424 698 : const char *filename = NULL;
425 698 : const char *format = "p";
426 : TableInfo *tblinfo;
427 : int numTables;
428 : DumpableObject **dobjs;
429 : int numObjs;
430 : DumpableObject *boundaryObjs;
431 : int i;
432 : int optindex;
433 : RestoreOptions *ropt;
434 : Archive *fout; /* the script file */
435 698 : bool g_verbose = false;
436 698 : const char *dumpencoding = NULL;
437 698 : const char *dumpsnapshot = NULL;
438 698 : char *use_role = NULL;
439 698 : int numWorkers = 1;
440 698 : int plainText = 0;
441 698 : ArchiveFormat archiveFormat = archUnknown;
442 : ArchiveMode archiveMode;
443 698 : pg_compress_specification compression_spec = {0};
444 698 : char *compression_detail = NULL;
445 698 : char *compression_algorithm_str = "none";
446 698 : char *error_detail = NULL;
447 698 : bool user_compression_defined = false;
448 698 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
449 698 : bool data_only = false;
450 698 : bool schema_only = false;
451 698 : bool statistics_only = false;
452 698 : bool with_data = false;
453 698 : bool with_schema = false;
454 698 : bool with_statistics = false;
455 698 : bool no_data = false;
456 698 : bool no_schema = false;
457 698 : bool no_statistics = false;
458 :
459 : static DumpOptions dopt;
460 :
461 : static struct option long_options[] = {
462 : {"data-only", no_argument, NULL, 'a'},
463 : {"blobs", no_argument, NULL, 'b'},
464 : {"large-objects", no_argument, NULL, 'b'},
465 : {"no-blobs", no_argument, NULL, 'B'},
466 : {"no-large-objects", no_argument, NULL, 'B'},
467 : {"clean", no_argument, NULL, 'c'},
468 : {"create", no_argument, NULL, 'C'},
469 : {"dbname", required_argument, NULL, 'd'},
470 : {"extension", required_argument, NULL, 'e'},
471 : {"file", required_argument, NULL, 'f'},
472 : {"format", required_argument, NULL, 'F'},
473 : {"host", required_argument, NULL, 'h'},
474 : {"jobs", 1, NULL, 'j'},
475 : {"no-reconnect", no_argument, NULL, 'R'},
476 : {"no-owner", no_argument, NULL, 'O'},
477 : {"port", required_argument, NULL, 'p'},
478 : {"schema", required_argument, NULL, 'n'},
479 : {"exclude-schema", required_argument, NULL, 'N'},
480 : {"schema-only", no_argument, NULL, 's'},
481 : {"superuser", required_argument, NULL, 'S'},
482 : {"table", required_argument, NULL, 't'},
483 : {"exclude-table", required_argument, NULL, 'T'},
484 : {"no-password", no_argument, NULL, 'w'},
485 : {"password", no_argument, NULL, 'W'},
486 : {"username", required_argument, NULL, 'U'},
487 : {"verbose", no_argument, NULL, 'v'},
488 : {"no-privileges", no_argument, NULL, 'x'},
489 : {"no-acl", no_argument, NULL, 'x'},
490 : {"compress", required_argument, NULL, 'Z'},
491 : {"encoding", required_argument, NULL, 'E'},
492 : {"help", no_argument, NULL, '?'},
493 : {"version", no_argument, NULL, 'V'},
494 :
495 : /*
496 : * the following options don't have an equivalent short option letter
497 : */
498 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
499 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
500 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
501 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
502 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
503 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
504 : {"exclude-table-data", required_argument, NULL, 4},
505 : {"extra-float-digits", required_argument, NULL, 8},
506 : {"if-exists", no_argument, &dopt.if_exists, 1},
507 : {"inserts", no_argument, NULL, 9},
508 : {"lock-wait-timeout", required_argument, NULL, 2},
509 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
510 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
511 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
512 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
513 : {"role", required_argument, NULL, 3},
514 : {"section", required_argument, NULL, 5},
515 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
516 : {"snapshot", required_argument, NULL, 6},
517 : {"statistics-only", no_argument, NULL, 18},
518 : {"strict-names", no_argument, &strict_names, 1},
519 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
520 : {"no-comments", no_argument, &dopt.no_comments, 1},
521 : {"no-data", no_argument, NULL, 19},
522 : {"no-policies", no_argument, &dopt.no_policies, 1},
523 : {"no-publications", no_argument, &dopt.no_publications, 1},
524 : {"no-schema", no_argument, NULL, 20},
525 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
526 : {"no-statistics", no_argument, NULL, 21},
527 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
528 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
529 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
530 : {"no-sync", no_argument, NULL, 7},
531 : {"with-data", no_argument, NULL, 22},
532 : {"with-schema", no_argument, NULL, 23},
533 : {"with-statistics", no_argument, NULL, 24},
534 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
535 : {"rows-per-insert", required_argument, NULL, 10},
536 : {"include-foreign-data", required_argument, NULL, 11},
537 : {"table-and-children", required_argument, NULL, 12},
538 : {"exclude-table-and-children", required_argument, NULL, 13},
539 : {"exclude-table-data-and-children", required_argument, NULL, 14},
540 : {"sync-method", required_argument, NULL, 15},
541 : {"filter", required_argument, NULL, 16},
542 : {"exclude-extension", required_argument, NULL, 17},
543 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
544 :
545 : {NULL, 0, NULL, 0}
546 : };
547 :
548 698 : pg_logging_init(argv[0]);
549 698 : pg_logging_set_level(PG_LOG_WARNING);
550 698 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
551 :
552 : /*
553 : * Initialize what we need for parallel execution, especially for thread
554 : * support on Windows.
555 : */
556 698 : init_parallel_dump_utils();
557 :
558 698 : progname = get_progname(argv[0]);
559 :
560 698 : if (argc > 1)
561 : {
562 698 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
563 : {
564 2 : help(progname);
565 2 : exit_nicely(0);
566 : }
567 696 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
568 : {
569 140 : puts("pg_dump (PostgreSQL) " PG_VERSION);
570 140 : exit_nicely(0);
571 : }
572 : }
573 :
574 556 : InitDumpOptions(&dopt);
575 :
576 2780 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
577 2780 : long_options, &optindex)) != -1)
578 : {
579 2240 : switch (c)
580 : {
581 18 : case 'a': /* Dump data only */
582 18 : data_only = true;
583 18 : break;
584 :
585 2 : case 'b': /* Dump LOs */
586 2 : dopt.outputLOs = true;
587 2 : break;
588 :
589 4 : case 'B': /* Don't dump LOs */
590 4 : dopt.dontOutputLOs = true;
591 4 : break;
592 :
593 12 : case 'c': /* clean (i.e., drop) schema prior to create */
594 12 : dopt.outputClean = 1;
595 12 : break;
596 :
597 140 : case 'C': /* Create DB */
598 140 : dopt.outputCreateDB = 1;
599 140 : break;
600 :
601 16 : case 'd': /* database name */
602 16 : dopt.cparams.dbname = pg_strdup(optarg);
603 16 : break;
604 :
605 8 : case 'e': /* include extension(s) */
606 8 : simple_string_list_append(&extension_include_patterns, optarg);
607 8 : dopt.include_everything = false;
608 8 : break;
609 :
610 4 : case 'E': /* Dump encoding */
611 4 : dumpencoding = pg_strdup(optarg);
612 4 : break;
613 :
614 466 : case 'f':
615 466 : filename = pg_strdup(optarg);
616 466 : break;
617 :
618 314 : case 'F':
619 314 : format = pg_strdup(optarg);
620 314 : break;
621 :
622 58 : case 'h': /* server host */
623 58 : dopt.cparams.pghost = pg_strdup(optarg);
624 58 : break;
625 :
626 24 : case 'j': /* number of dump jobs */
627 24 : if (!option_parse_int(optarg, "-j/--jobs", 1,
628 : PG_MAX_JOBS,
629 : &numWorkers))
630 2 : exit_nicely(1);
631 22 : break;
632 :
633 34 : case 'n': /* include schema(s) */
634 34 : simple_string_list_append(&schema_include_patterns, optarg);
635 34 : dopt.include_everything = false;
636 34 : break;
637 :
638 2 : case 'N': /* exclude schema(s) */
639 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
640 2 : break;
641 :
642 4 : case 'O': /* Don't reconnect to match owner */
643 4 : dopt.outputNoOwner = 1;
644 4 : break;
645 :
646 134 : case 'p': /* server port */
647 134 : dopt.cparams.pgport = pg_strdup(optarg);
648 134 : break;
649 :
650 4 : case 'R':
651 : /* no-op, still accepted for backwards compatibility */
652 4 : break;
653 :
654 14 : case 's': /* dump schema only */
655 14 : schema_only = true;
656 14 : break;
657 :
658 2 : case 'S': /* Username for superuser in plain text output */
659 2 : dopt.outputSuperuser = pg_strdup(optarg);
660 2 : break;
661 :
662 16 : case 't': /* include table(s) */
663 16 : simple_string_list_append(&table_include_patterns, optarg);
664 16 : dopt.include_everything = false;
665 16 : break;
666 :
667 8 : case 'T': /* exclude table(s) */
668 8 : simple_string_list_append(&table_exclude_patterns, optarg);
669 8 : break;
670 :
671 62 : case 'U':
672 62 : dopt.cparams.username = pg_strdup(optarg);
673 62 : break;
674 :
675 12 : case 'v': /* verbose */
676 12 : g_verbose = true;
677 12 : pg_logging_increase_verbosity();
678 12 : break;
679 :
680 2 : case 'w':
681 2 : dopt.cparams.promptPassword = TRI_NO;
682 2 : break;
683 :
684 0 : case 'W':
685 0 : dopt.cparams.promptPassword = TRI_YES;
686 0 : break;
687 :
688 4 : case 'x': /* skip ACL dump */
689 4 : dopt.aclsSkip = true;
690 4 : break;
691 :
692 24 : case 'Z': /* Compression */
693 24 : parse_compress_options(optarg, &compression_algorithm_str,
694 : &compression_detail);
695 24 : user_compression_defined = true;
696 24 : break;
697 :
698 226 : case 0:
699 : /* This covers the long options. */
700 226 : break;
701 :
702 4 : case 2: /* lock-wait-timeout */
703 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
704 4 : break;
705 :
706 6 : case 3: /* SET ROLE */
707 6 : use_role = pg_strdup(optarg);
708 6 : break;
709 :
710 2 : case 4: /* exclude table(s) data */
711 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
712 2 : break;
713 :
714 12 : case 5: /* section */
715 12 : set_dump_section(optarg, &dopt.dumpSections);
716 12 : break;
717 :
718 0 : case 6: /* snapshot */
719 0 : dumpsnapshot = pg_strdup(optarg);
720 0 : break;
721 :
722 274 : case 7: /* no-sync */
723 274 : dosync = false;
724 274 : break;
725 :
726 2 : case 8:
727 2 : have_extra_float_digits = true;
728 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
729 : &extra_float_digits))
730 2 : exit_nicely(1);
731 0 : break;
732 :
733 4 : case 9: /* inserts */
734 :
735 : /*
736 : * dump_inserts also stores --rows-per-insert, careful not to
737 : * overwrite that.
738 : */
739 4 : if (dopt.dump_inserts == 0)
740 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
741 4 : break;
742 :
743 4 : case 10: /* rows per insert */
744 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
745 : &dopt.dump_inserts))
746 2 : exit_nicely(1);
747 2 : break;
748 :
749 8 : case 11: /* include foreign data */
750 8 : simple_string_list_append(&foreign_servers_include_patterns,
751 : optarg);
752 8 : break;
753 :
754 2 : case 12: /* include table(s) and their children */
755 2 : simple_string_list_append(&table_include_patterns_and_children,
756 : optarg);
757 2 : dopt.include_everything = false;
758 2 : break;
759 :
760 2 : case 13: /* exclude table(s) and their children */
761 2 : simple_string_list_append(&table_exclude_patterns_and_children,
762 : optarg);
763 2 : break;
764 :
765 2 : case 14: /* exclude data of table(s) and children */
766 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
767 : optarg);
768 2 : break;
769 :
770 0 : case 15:
771 0 : if (!parse_sync_method(optarg, &sync_method))
772 0 : exit_nicely(1);
773 0 : break;
774 :
775 52 : case 16: /* read object filters from file */
776 52 : read_dump_filters(optarg, &dopt);
777 44 : break;
778 :
779 2 : case 17: /* exclude extension(s) */
780 2 : simple_string_list_append(&extension_exclude_patterns,
781 : optarg);
782 2 : break;
783 :
784 8 : case 18:
785 8 : statistics_only = true;
786 8 : break;
787 :
788 62 : case 19:
789 62 : no_data = true;
790 62 : break;
791 :
792 4 : case 20:
793 4 : no_schema = true;
794 4 : break;
795 :
796 16 : case 21:
797 16 : no_statistics = true;
798 16 : break;
799 :
800 0 : case 22:
801 0 : with_data = true;
802 0 : break;
803 :
804 0 : case 23:
805 0 : with_schema = true;
806 0 : break;
807 :
808 158 : case 24:
809 158 : with_statistics = true;
810 158 : break;
811 :
812 2 : default:
813 : /* getopt_long already emitted a complaint */
814 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
815 2 : exit_nicely(1);
816 : }
817 : }
818 :
819 : /*
820 : * Non-option argument specifies database name as long as it wasn't
821 : * already specified with -d / --dbname
822 : */
823 540 : if (optind < argc && dopt.cparams.dbname == NULL)
824 464 : dopt.cparams.dbname = argv[optind++];
825 :
826 : /* Complain if any arguments remain */
827 540 : if (optind < argc)
828 : {
829 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
830 : argv[optind]);
831 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
832 2 : exit_nicely(1);
833 : }
834 :
835 : /* --column-inserts implies --inserts */
836 538 : if (dopt.column_inserts && dopt.dump_inserts == 0)
837 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
838 :
839 : /* reject conflicting "-only" options */
840 538 : if (data_only && schema_only)
841 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
842 536 : if (schema_only && statistics_only)
843 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
844 534 : if (data_only && statistics_only)
845 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
846 :
847 : /* reject conflicting "-only" and "no-" options */
848 532 : if (data_only && no_data)
849 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
850 532 : if (schema_only && no_schema)
851 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
852 532 : if (statistics_only && no_statistics)
853 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
854 :
855 : /* reject conflicting "with-" and "no-" options */
856 530 : if (with_data && no_data)
857 0 : pg_fatal("options --with-data and --no-data cannot be used together");
858 530 : if (with_schema && no_schema)
859 0 : pg_fatal("options --with-schema and --no-schema cannot be used together");
860 530 : if (with_statistics && no_statistics)
861 0 : pg_fatal("options --with-statistics and --no-statistics cannot be used together");
862 :
863 530 : if (schema_only && foreign_servers_include_patterns.head != NULL)
864 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
865 :
866 528 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
867 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
868 :
869 526 : if (data_only && dopt.outputClean)
870 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
871 :
872 524 : if (dopt.if_exists && !dopt.outputClean)
873 2 : pg_fatal("option --if-exists requires option -c/--clean");
874 :
875 : /*
876 : * Set derivative flags. An "-only" option may be overridden by an
877 : * explicit "with-" option; e.g. "--schema-only --with-statistics" will
878 : * include schema and statistics. Other ambiguous or nonsensical
879 : * combinations, e.g. "--schema-only --no-schema", will have already
880 : * caused an error in one of the checks above.
881 : */
882 522 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
883 1044 : (data_only || with_data)) && !no_data;
884 522 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
885 1044 : (schema_only || with_schema)) && !no_schema;
886 522 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
887 1044 : (statistics_only || with_statistics)) && !no_statistics;
888 :
889 :
890 : /*
891 : * --inserts are already implied above if --column-inserts or
892 : * --rows-per-insert were specified.
893 : */
894 522 : if (dopt.do_nothing && dopt.dump_inserts == 0)
895 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
896 :
897 : /* Identify archive format to emit */
898 520 : archiveFormat = parseArchiveFormat(format, &archiveMode);
899 :
900 : /* archiveFormat specific setup */
901 518 : if (archiveFormat == archNull)
902 310 : plainText = 1;
903 :
904 : /*
905 : * Custom and directory formats are compressed by default with gzip when
906 : * available, not the others. If gzip is not available, no compression is
907 : * done by default.
908 : */
909 518 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
910 186 : !user_compression_defined)
911 : {
912 : #ifdef HAVE_LIBZ
913 176 : compression_algorithm_str = "gzip";
914 : #else
915 : compression_algorithm_str = "none";
916 : #endif
917 : }
918 :
919 : /*
920 : * Compression options
921 : */
922 518 : if (!parse_compress_algorithm(compression_algorithm_str,
923 : &compression_algorithm))
924 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
925 : compression_algorithm_str);
926 :
927 516 : parse_compress_specification(compression_algorithm, compression_detail,
928 : &compression_spec);
929 516 : error_detail = validate_compress_specification(&compression_spec);
930 516 : if (error_detail != NULL)
931 6 : pg_fatal("invalid compression specification: %s",
932 : error_detail);
933 :
934 510 : error_detail = supports_compression(compression_spec);
935 510 : if (error_detail != NULL)
936 0 : pg_fatal("%s", error_detail);
937 :
938 : /*
939 : * Disable support for zstd workers for now - these are based on
940 : * threading, and it's unclear how it interacts with parallel dumps on
941 : * platforms where that relies on threads too (e.g. Windows).
942 : */
943 510 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
944 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
945 : "workers");
946 :
947 : /*
948 : * If emitting an archive format, we always want to emit a DATABASE item,
949 : * in case --create is specified at pg_restore time.
950 : */
951 510 : if (!plainText)
952 208 : dopt.outputCreateDB = 1;
953 :
954 : /* Parallel backup only in the directory archive format so far */
955 510 : if (archiveFormat != archDirectory && numWorkers > 1)
956 2 : pg_fatal("parallel backup only supported by the directory format");
957 :
958 : /* Open the output file */
959 508 : fout = CreateArchive(filename, archiveFormat, compression_spec,
960 : dosync, archiveMode, setupDumpWorker, sync_method);
961 :
962 : /* Make dump options accessible right away */
963 506 : SetArchiveOptions(fout, &dopt, NULL);
964 :
965 : /* Register the cleanup hook */
966 506 : on_exit_close_archive(fout);
967 :
968 : /* Let the archiver know how noisy to be */
969 506 : fout->verbose = g_verbose;
970 :
971 :
972 : /*
973 : * We allow the server to be back to 9.2, and up to any minor release of
974 : * our own major version. (See also version check in pg_dumpall.c.)
975 : */
976 506 : fout->minRemoteVersion = 90200;
977 506 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
978 :
979 506 : fout->numWorkers = numWorkers;
980 :
981 : /*
982 : * Open the database using the Archiver, so it knows about it. Errors mean
983 : * death.
984 : */
985 506 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
986 502 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
987 :
988 : /*
989 : * On hot standbys, never try to dump unlogged table data, since it will
990 : * just throw an error.
991 : */
992 502 : if (fout->isStandby)
993 8 : dopt.no_unlogged_table_data = true;
994 :
995 : /*
996 : * Find the last built-in OID, if needed (prior to 8.1)
997 : *
998 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
999 : */
1000 502 : g_last_builtin_oid = FirstNormalObjectId - 1;
1001 :
1002 502 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1003 :
1004 : /* Expand schema selection patterns into OID lists */
1005 502 : if (schema_include_patterns.head != NULL)
1006 : {
1007 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
1008 : &schema_include_oids,
1009 : strict_names);
1010 24 : if (schema_include_oids.head == NULL)
1011 2 : pg_fatal("no matching schemas were found");
1012 : }
1013 488 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1014 : &schema_exclude_oids,
1015 : false);
1016 : /* non-matching exclusion patterns aren't an error */
1017 :
1018 : /* Expand table selection patterns into OID lists */
1019 488 : expand_table_name_patterns(fout, &table_include_patterns,
1020 : &table_include_oids,
1021 : strict_names, false);
1022 478 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1023 : &table_include_oids,
1024 : strict_names, true);
1025 478 : if ((table_include_patterns.head != NULL ||
1026 456 : table_include_patterns_and_children.head != NULL) &&
1027 26 : table_include_oids.head == NULL)
1028 4 : pg_fatal("no matching tables were found");
1029 :
1030 474 : expand_table_name_patterns(fout, &table_exclude_patterns,
1031 : &table_exclude_oids,
1032 : false, false);
1033 474 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1034 : &table_exclude_oids,
1035 : false, true);
1036 :
1037 474 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1038 : &tabledata_exclude_oids,
1039 : false, false);
1040 474 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1041 : &tabledata_exclude_oids,
1042 : false, true);
1043 :
1044 474 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1045 : &foreign_servers_include_oids);
1046 :
1047 : /* non-matching exclusion patterns aren't an error */
1048 :
1049 : /* Expand extension selection patterns into OID lists */
1050 472 : if (extension_include_patterns.head != NULL)
1051 : {
1052 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1053 : &extension_include_oids,
1054 : strict_names);
1055 10 : if (extension_include_oids.head == NULL)
1056 2 : pg_fatal("no matching extensions were found");
1057 : }
1058 470 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1059 : &extension_exclude_oids,
1060 : false);
1061 : /* non-matching exclusion patterns aren't an error */
1062 :
1063 : /*
1064 : * Dumping LOs is the default for dumps where an inclusion switch is not
1065 : * used (an "include everything" dump). -B can be used to exclude LOs
1066 : * from those dumps. -b can be used to include LOs even when an inclusion
1067 : * switch is used.
1068 : *
1069 : * -s means "schema only" and LOs are data, not schema, so we never
1070 : * include LOs when -s is used.
1071 : */
1072 470 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1073 348 : dopt.outputLOs = true;
1074 :
1075 : /*
1076 : * Collect role names so we can map object owner OIDs to names.
1077 : */
1078 470 : collectRoleNames(fout);
1079 :
1080 : /*
1081 : * Now scan the database and create DumpableObject structs for all the
1082 : * objects we intend to dump.
1083 : */
1084 470 : tblinfo = getSchemaData(fout, &numTables);
1085 :
1086 468 : if (dopt.dumpData)
1087 : {
1088 396 : getTableData(&dopt, tblinfo, numTables, 0);
1089 396 : buildMatViewRefreshDependencies(fout);
1090 396 : if (!dopt.dumpSchema)
1091 14 : getTableDataFKConstraints();
1092 : }
1093 :
1094 468 : if (!dopt.dumpData && dopt.sequence_data)
1095 56 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1096 :
1097 : /*
1098 : * For binary upgrade mode, dump pg_largeobject_metadata and the
1099 : * associated pg_shdepend rows. This is faster to restore than the
1100 : * equivalent set of large object commands. We can only do this for
1101 : * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1102 : * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1103 : */
1104 468 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1105 : {
1106 62 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1107 62 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1108 :
1109 62 : makeTableDataInfo(&dopt, lo_metadata);
1110 62 : makeTableDataInfo(&dopt, shdepend);
1111 :
1112 : /*
1113 : * Save pg_largeobject_metadata's dump ID for use as a dependency for
1114 : * pg_shdepend and any large object comments/seclabels.
1115 : */
1116 62 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1117 62 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1118 :
1119 : /*
1120 : * Only dump large object shdepend rows for this database.
1121 : */
1122 62 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1123 : "AND dbid = (SELECT oid FROM pg_database "
1124 : " WHERE datname = current_database())";
1125 : }
1126 :
1127 : /*
1128 : * In binary-upgrade mode, we do not have to worry about the actual LO
1129 : * data or the associated metadata that resides in the pg_largeobject and
1130 : * pg_largeobject_metadata tables, respectively.
1131 : *
1132 : * However, we do need to collect LO information as there may be comments
1133 : * or other information on LOs that we do need to dump out.
1134 : */
1135 468 : if (dopt.outputLOs || dopt.binary_upgrade)
1136 410 : getLOs(fout);
1137 :
1138 : /*
1139 : * Collect dependency data to assist in ordering the objects.
1140 : */
1141 468 : getDependencies(fout);
1142 :
1143 : /*
1144 : * Collect ACLs, comments, and security labels, if wanted.
1145 : */
1146 468 : if (!dopt.aclsSkip)
1147 464 : getAdditionalACLs(fout);
1148 468 : if (!dopt.no_comments)
1149 468 : collectComments(fout);
1150 468 : if (!dopt.no_security_labels)
1151 468 : collectSecLabels(fout);
1152 :
1153 : /* For binary upgrade mode, collect required pg_class information. */
1154 468 : if (dopt.binary_upgrade)
1155 62 : collectBinaryUpgradeClassOids(fout);
1156 :
1157 : /* Collect sequence information. */
1158 468 : collectSequences(fout);
1159 :
1160 : /* Lastly, create dummy objects to represent the section boundaries */
1161 468 : boundaryObjs = createBoundaryObjects();
1162 :
1163 : /* Get pointers to all the known DumpableObjects */
1164 468 : getDumpableObjects(&dobjs, &numObjs);
1165 :
1166 : /*
1167 : * Add dummy dependencies to enforce the dump section ordering.
1168 : */
1169 468 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1170 :
1171 : /*
1172 : * Sort the objects into a safe dump order (no forward references).
1173 : *
1174 : * We rely on dependency information to help us determine a safe order, so
1175 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1176 : * ensure that logically identical schemas will dump identically.
1177 : */
1178 468 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1179 :
1180 468 : sortDumpableObjects(dobjs, numObjs,
1181 468 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1182 :
1183 : /*
1184 : * Create archive TOC entries for all the objects to be dumped, in a safe
1185 : * order.
1186 : */
1187 :
1188 : /*
1189 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1190 : */
1191 468 : dumpEncoding(fout);
1192 468 : dumpStdStrings(fout);
1193 468 : dumpSearchPath(fout);
1194 :
1195 : /* The database items are always next, unless we don't want them at all */
1196 468 : if (dopt.outputCreateDB)
1197 264 : dumpDatabase(fout);
1198 :
1199 : /* Now the rearrangeable objects. */
1200 1740674 : for (i = 0; i < numObjs; i++)
1201 1740206 : dumpDumpableObject(fout, dobjs[i]);
1202 :
1203 : /*
1204 : * Set up options info to ensure we dump what we want.
1205 : */
1206 468 : ropt = NewRestoreOptions();
1207 468 : ropt->filename = filename;
1208 :
1209 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1210 468 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1211 468 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1212 468 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1213 468 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1214 468 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1215 468 : ropt->dropSchema = dopt.outputClean;
1216 468 : ropt->dumpData = dopt.dumpData;
1217 468 : ropt->dumpSchema = dopt.dumpSchema;
1218 468 : ropt->dumpStatistics = dopt.dumpStatistics;
1219 468 : ropt->if_exists = dopt.if_exists;
1220 468 : ropt->column_inserts = dopt.column_inserts;
1221 468 : ropt->dumpSections = dopt.dumpSections;
1222 468 : ropt->aclsSkip = dopt.aclsSkip;
1223 468 : ropt->superuser = dopt.outputSuperuser;
1224 468 : ropt->createDB = dopt.outputCreateDB;
1225 468 : ropt->noOwner = dopt.outputNoOwner;
1226 468 : ropt->noTableAm = dopt.outputNoTableAm;
1227 468 : ropt->noTablespace = dopt.outputNoTablespaces;
1228 468 : ropt->disable_triggers = dopt.disable_triggers;
1229 468 : ropt->use_setsessauth = dopt.use_setsessauth;
1230 468 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1231 468 : ropt->dump_inserts = dopt.dump_inserts;
1232 468 : ropt->no_comments = dopt.no_comments;
1233 468 : ropt->no_policies = dopt.no_policies;
1234 468 : ropt->no_publications = dopt.no_publications;
1235 468 : ropt->no_security_labels = dopt.no_security_labels;
1236 468 : ropt->no_subscriptions = dopt.no_subscriptions;
1237 468 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1238 468 : ropt->include_everything = dopt.include_everything;
1239 468 : ropt->enable_row_security = dopt.enable_row_security;
1240 468 : ropt->sequence_data = dopt.sequence_data;
1241 468 : ropt->binary_upgrade = dopt.binary_upgrade;
1242 :
1243 468 : ropt->compression_spec = compression_spec;
1244 :
1245 468 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1246 :
1247 468 : SetArchiveOptions(fout, &dopt, ropt);
1248 :
1249 : /* Mark which entries should be output */
1250 468 : ProcessArchiveRestoreOptions(fout);
1251 :
1252 : /*
1253 : * The archive's TOC entries are now marked as to which ones will actually
1254 : * be output, so we can set up their dependency lists properly. This isn't
1255 : * necessary for plain-text output, though.
1256 : */
1257 468 : if (!plainText)
1258 206 : BuildArchiveDependencies(fout);
1259 :
1260 : /*
1261 : * And finally we can do the actual output.
1262 : *
1263 : * Note: for non-plain-text output formats, the output file is written
1264 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1265 : * right now.
1266 : */
1267 468 : if (plainText)
1268 262 : RestoreArchive(fout, false);
1269 :
1270 466 : CloseArchive(fout);
1271 :
1272 466 : exit_nicely(0);
1273 : }
1274 :
1275 :
1276 : static void
1277 2 : help(const char *progname)
1278 : {
1279 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1280 2 : printf(_("Usage:\n"));
1281 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1282 :
1283 2 : printf(_("\nGeneral options:\n"));
1284 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1285 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1286 : " plain text (default))\n"));
1287 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1288 2 : printf(_(" -v, --verbose verbose mode\n"));
1289 2 : printf(_(" -V, --version output version information, then exit\n"));
1290 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1291 : " compress as specified\n"));
1292 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1293 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1294 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1295 2 : printf(_(" -?, --help show this help, then exit\n"));
1296 :
1297 2 : printf(_("\nOptions controlling the output content:\n"));
1298 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1299 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1300 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1301 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1302 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1303 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1304 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1305 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1306 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1307 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1308 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1309 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1310 : " plain-text format\n"));
1311 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1312 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1313 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1314 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1315 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1316 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1317 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1318 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1319 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1320 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1321 : " access to)\n"));
1322 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1323 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1324 : " do NOT dump the specified table(s), including\n"
1325 : " child and partition tables\n"));
1326 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1327 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1328 : " do NOT dump data for the specified table(s),\n"
1329 : " including child and partition tables\n"));
1330 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1331 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1332 : " based on expressions in FILENAME\n"));
1333 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1334 2 : printf(_(" --include-foreign-data=PATTERN\n"
1335 : " include data of foreign tables on foreign\n"
1336 : " servers matching PATTERN\n"));
1337 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1338 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1339 2 : printf(_(" --no-comments do not dump comment commands\n"));
1340 2 : printf(_(" --no-data do not dump data\n"));
1341 2 : printf(_(" --no-policies do not dump row security policies\n"));
1342 2 : printf(_(" --no-publications do not dump publications\n"));
1343 2 : printf(_(" --no-schema do not dump schema\n"));
1344 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1345 2 : printf(_(" --no-statistics do not dump statistics\n"));
1346 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1347 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1348 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1349 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1350 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1351 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1352 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1353 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1354 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1355 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1356 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1357 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1358 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1359 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1360 : " match at least one entity each\n"));
1361 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1362 : " child and partition tables\n"));
1363 2 : printf(_(" --use-set-session-authorization\n"
1364 : " use SET SESSION AUTHORIZATION commands instead of\n"
1365 : " ALTER OWNER commands to set ownership\n"));
1366 2 : printf(_(" --with-data dump the data\n"));
1367 2 : printf(_(" --with-schema dump the schema\n"));
1368 2 : printf(_(" --with-statistics dump the statistics\n"));
1369 :
1370 2 : printf(_("\nConnection options:\n"));
1371 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1372 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1373 2 : printf(_(" -p, --port=PORT database server port number\n"));
1374 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1375 2 : printf(_(" -w, --no-password never prompt for password\n"));
1376 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1377 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1378 :
1379 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1380 : "variable value is used.\n\n"));
1381 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1382 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1383 2 : }
1384 :
1385 : static void
1386 538 : setup_connection(Archive *AH, const char *dumpencoding,
1387 : const char *dumpsnapshot, char *use_role)
1388 : {
1389 538 : DumpOptions *dopt = AH->dopt;
1390 538 : PGconn *conn = GetConnection(AH);
1391 : const char *std_strings;
1392 :
1393 538 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1394 :
1395 : /*
1396 : * Set the client encoding if requested.
1397 : */
1398 538 : if (dumpencoding)
1399 : {
1400 40 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1401 0 : pg_fatal("invalid client encoding \"%s\" specified",
1402 : dumpencoding);
1403 : }
1404 :
1405 : /*
1406 : * Get the active encoding and the standard_conforming_strings setting, so
1407 : * we know how to escape strings.
1408 : */
1409 538 : AH->encoding = PQclientEncoding(conn);
1410 538 : setFmtEncoding(AH->encoding);
1411 :
1412 538 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1413 538 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1414 :
1415 : /*
1416 : * Set the role if requested. In a parallel dump worker, we'll be passed
1417 : * use_role == NULL, but AH->use_role is already set (if user specified it
1418 : * originally) and we should use that.
1419 : */
1420 538 : if (!use_role && AH->use_role)
1421 4 : use_role = AH->use_role;
1422 :
1423 : /* Set the role if requested */
1424 538 : if (use_role)
1425 : {
1426 10 : PQExpBuffer query = createPQExpBuffer();
1427 :
1428 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1429 10 : ExecuteSqlStatement(AH, query->data);
1430 10 : destroyPQExpBuffer(query);
1431 :
1432 : /* save it for possible later use by parallel workers */
1433 10 : if (!AH->use_role)
1434 6 : AH->use_role = pg_strdup(use_role);
1435 : }
1436 :
1437 : /* Set the datestyle to ISO to ensure the dump's portability */
1438 538 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1439 :
1440 : /* Likewise, avoid using sql_standard intervalstyle */
1441 538 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1442 :
1443 : /*
1444 : * Use an explicitly specified extra_float_digits if it has been provided.
1445 : * Otherwise, set extra_float_digits so that we can dump float data
1446 : * exactly (given correctly implemented float I/O code, anyway).
1447 : */
1448 538 : if (have_extra_float_digits)
1449 : {
1450 0 : PQExpBuffer q = createPQExpBuffer();
1451 :
1452 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1453 : extra_float_digits);
1454 0 : ExecuteSqlStatement(AH, q->data);
1455 0 : destroyPQExpBuffer(q);
1456 : }
1457 : else
1458 538 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1459 :
1460 : /*
1461 : * Disable synchronized scanning, to prevent unpredictable changes in row
1462 : * ordering across a dump and reload.
1463 : */
1464 538 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1465 :
1466 : /*
1467 : * Disable timeouts if supported.
1468 : */
1469 538 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1470 538 : if (AH->remoteVersion >= 90300)
1471 538 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1472 538 : if (AH->remoteVersion >= 90600)
1473 538 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1474 538 : if (AH->remoteVersion >= 170000)
1475 538 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1476 :
1477 : /*
1478 : * Quote all identifiers, if requested.
1479 : */
1480 538 : if (quote_all_identifiers)
1481 58 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1482 :
1483 : /*
1484 : * Adjust row-security mode, if supported.
1485 : */
1486 538 : if (AH->remoteVersion >= 90500)
1487 : {
1488 538 : if (dopt->enable_row_security)
1489 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1490 : else
1491 538 : ExecuteSqlStatement(AH, "SET row_security = off");
1492 : }
1493 :
1494 : /*
1495 : * For security reasons, we restrict the expansion of non-system views and
1496 : * access to foreign tables during the pg_dump process. This restriction
1497 : * is adjusted when dumping foreign table data.
1498 : */
1499 538 : set_restrict_relation_kind(AH, "view, foreign-table");
1500 :
1501 : /*
1502 : * Initialize prepared-query state to "nothing prepared". We do this here
1503 : * so that a parallel dump worker will have its own state.
1504 : */
1505 538 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1506 :
1507 : /*
1508 : * Start transaction-snapshot mode transaction to dump consistent data.
1509 : */
1510 538 : ExecuteSqlStatement(AH, "BEGIN");
1511 :
1512 : /*
1513 : * To support the combination of serializable_deferrable with the jobs
1514 : * option we use REPEATABLE READ for the worker connections that are
1515 : * passed a snapshot. As long as the snapshot is acquired in a
1516 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1517 : * REPEATABLE READ transaction provides the appropriate integrity
1518 : * guarantees. This is a kluge, but safe for back-patching.
1519 : */
1520 538 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1521 0 : ExecuteSqlStatement(AH,
1522 : "SET TRANSACTION ISOLATION LEVEL "
1523 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1524 : else
1525 538 : ExecuteSqlStatement(AH,
1526 : "SET TRANSACTION ISOLATION LEVEL "
1527 : "REPEATABLE READ, READ ONLY");
1528 :
1529 : /*
1530 : * If user specified a snapshot to use, select that. In a parallel dump
1531 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1532 : * is already set (if the server can handle it) and we should use that.
1533 : */
1534 538 : if (dumpsnapshot)
1535 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1536 :
1537 538 : if (AH->sync_snapshot_id)
1538 : {
1539 36 : PQExpBuffer query = createPQExpBuffer();
1540 :
1541 36 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1542 36 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1543 36 : ExecuteSqlStatement(AH, query->data);
1544 36 : destroyPQExpBuffer(query);
1545 : }
1546 502 : else if (AH->numWorkers > 1)
1547 : {
1548 18 : if (AH->isStandby && AH->remoteVersion < 100000)
1549 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1550 18 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1551 : }
1552 538 : }
1553 :
1554 : /* Set up connection for a parallel worker process */
1555 : static void
1556 36 : setupDumpWorker(Archive *AH)
1557 : {
1558 : /*
1559 : * We want to re-select all the same values the leader connection is
1560 : * using. We'll have inherited directly-usable values in
1561 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1562 : * inherited encoding value back to a string to pass to setup_connection.
1563 : */
1564 36 : setup_connection(AH,
1565 : pg_encoding_to_char(AH->encoding),
1566 : NULL,
1567 : NULL);
1568 36 : }
1569 :
1570 : static char *
1571 18 : get_synchronized_snapshot(Archive *fout)
1572 : {
1573 18 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1574 : char *result;
1575 : PGresult *res;
1576 :
1577 18 : res = ExecuteSqlQueryForSingleRow(fout, query);
1578 18 : result = pg_strdup(PQgetvalue(res, 0, 0));
1579 18 : PQclear(res);
1580 :
1581 18 : return result;
1582 : }
1583 :
1584 : static ArchiveFormat
1585 520 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1586 : {
1587 : ArchiveFormat archiveFormat;
1588 :
1589 520 : *mode = archModeWrite;
1590 :
1591 520 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1592 : {
1593 : /* This is used by pg_dumpall, and is not documented */
1594 86 : archiveFormat = archNull;
1595 86 : *mode = archModeAppend;
1596 : }
1597 434 : else if (pg_strcasecmp(format, "c") == 0)
1598 0 : archiveFormat = archCustom;
1599 434 : else if (pg_strcasecmp(format, "custom") == 0)
1600 88 : archiveFormat = archCustom;
1601 346 : else if (pg_strcasecmp(format, "d") == 0)
1602 2 : archiveFormat = archDirectory;
1603 344 : else if (pg_strcasecmp(format, "directory") == 0)
1604 96 : archiveFormat = archDirectory;
1605 248 : else if (pg_strcasecmp(format, "p") == 0)
1606 218 : archiveFormat = archNull;
1607 30 : else if (pg_strcasecmp(format, "plain") == 0)
1608 6 : archiveFormat = archNull;
1609 24 : else if (pg_strcasecmp(format, "t") == 0)
1610 0 : archiveFormat = archTar;
1611 24 : else if (pg_strcasecmp(format, "tar") == 0)
1612 22 : archiveFormat = archTar;
1613 : else
1614 2 : pg_fatal("invalid output format \"%s\" specified", format);
1615 518 : return archiveFormat;
1616 : }
1617 :
1618 : /*
1619 : * Find the OIDs of all schemas matching the given list of patterns,
1620 : * and append them to the given OID list.
1621 : */
1622 : static void
1623 524 : expand_schema_name_patterns(Archive *fout,
1624 : SimpleStringList *patterns,
1625 : SimpleOidList *oids,
1626 : bool strict_names)
1627 : {
1628 : PQExpBuffer query;
1629 : PGresult *res;
1630 : SimpleStringListCell *cell;
1631 : int i;
1632 :
1633 524 : if (patterns->head == NULL)
1634 482 : return; /* nothing to do */
1635 :
1636 42 : query = createPQExpBuffer();
1637 :
1638 : /*
1639 : * The loop below runs multiple SELECTs might sometimes result in
1640 : * duplicate entries in the OID list, but we don't care.
1641 : */
1642 :
1643 72 : for (cell = patterns->head; cell; cell = cell->next)
1644 : {
1645 : PQExpBufferData dbbuf;
1646 : int dotcnt;
1647 :
1648 42 : appendPQExpBufferStr(query,
1649 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1650 42 : initPQExpBuffer(&dbbuf);
1651 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1652 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1653 : &dotcnt);
1654 42 : if (dotcnt > 1)
1655 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1656 : cell->val);
1657 38 : else if (dotcnt == 1)
1658 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1659 32 : termPQExpBuffer(&dbbuf);
1660 :
1661 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1662 32 : if (strict_names && PQntuples(res) == 0)
1663 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1664 :
1665 58 : for (i = 0; i < PQntuples(res); i++)
1666 : {
1667 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1668 : }
1669 :
1670 30 : PQclear(res);
1671 30 : resetPQExpBuffer(query);
1672 : }
1673 :
1674 30 : destroyPQExpBuffer(query);
1675 : }
1676 :
1677 : /*
1678 : * Find the OIDs of all extensions matching the given list of patterns,
1679 : * and append them to the given OID list.
1680 : */
1681 : static void
1682 480 : expand_extension_name_patterns(Archive *fout,
1683 : SimpleStringList *patterns,
1684 : SimpleOidList *oids,
1685 : bool strict_names)
1686 : {
1687 : PQExpBuffer query;
1688 : PGresult *res;
1689 : SimpleStringListCell *cell;
1690 : int i;
1691 :
1692 480 : if (patterns->head == NULL)
1693 466 : return; /* nothing to do */
1694 :
1695 14 : query = createPQExpBuffer();
1696 :
1697 : /*
1698 : * The loop below runs multiple SELECTs might sometimes result in
1699 : * duplicate entries in the OID list, but we don't care.
1700 : */
1701 28 : for (cell = patterns->head; cell; cell = cell->next)
1702 : {
1703 : int dotcnt;
1704 :
1705 14 : appendPQExpBufferStr(query,
1706 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1707 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1708 : false, NULL, "e.extname", NULL, NULL, NULL,
1709 : &dotcnt);
1710 14 : if (dotcnt > 0)
1711 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1712 : cell->val);
1713 :
1714 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1715 14 : if (strict_names && PQntuples(res) == 0)
1716 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1717 :
1718 26 : for (i = 0; i < PQntuples(res); i++)
1719 : {
1720 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1721 : }
1722 :
1723 14 : PQclear(res);
1724 14 : resetPQExpBuffer(query);
1725 : }
1726 :
1727 14 : destroyPQExpBuffer(query);
1728 : }
1729 :
1730 : /*
1731 : * Find the OIDs of all foreign servers matching the given list of patterns,
1732 : * and append them to the given OID list.
1733 : */
1734 : static void
1735 474 : expand_foreign_server_name_patterns(Archive *fout,
1736 : SimpleStringList *patterns,
1737 : SimpleOidList *oids)
1738 : {
1739 : PQExpBuffer query;
1740 : PGresult *res;
1741 : SimpleStringListCell *cell;
1742 : int i;
1743 :
1744 474 : if (patterns->head == NULL)
1745 468 : return; /* nothing to do */
1746 :
1747 6 : query = createPQExpBuffer();
1748 :
1749 : /*
1750 : * The loop below runs multiple SELECTs might sometimes result in
1751 : * duplicate entries in the OID list, but we don't care.
1752 : */
1753 :
1754 10 : for (cell = patterns->head; cell; cell = cell->next)
1755 : {
1756 : int dotcnt;
1757 :
1758 6 : appendPQExpBufferStr(query,
1759 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1760 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1761 : false, NULL, "s.srvname", NULL, NULL, NULL,
1762 : &dotcnt);
1763 6 : if (dotcnt > 0)
1764 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1765 : cell->val);
1766 :
1767 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1768 6 : if (PQntuples(res) == 0)
1769 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1770 :
1771 8 : for (i = 0; i < PQntuples(res); i++)
1772 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1773 :
1774 4 : PQclear(res);
1775 4 : resetPQExpBuffer(query);
1776 : }
1777 :
1778 4 : destroyPQExpBuffer(query);
1779 : }
1780 :
1781 : /*
1782 : * Find the OIDs of all tables matching the given list of patterns,
1783 : * and append them to the given OID list. See also expand_dbname_patterns()
1784 : * in pg_dumpall.c
1785 : */
1786 : static void
1787 2862 : expand_table_name_patterns(Archive *fout,
1788 : SimpleStringList *patterns, SimpleOidList *oids,
1789 : bool strict_names, bool with_child_tables)
1790 : {
1791 : PQExpBuffer query;
1792 : PGresult *res;
1793 : SimpleStringListCell *cell;
1794 : int i;
1795 :
1796 2862 : if (patterns->head == NULL)
1797 2804 : return; /* nothing to do */
1798 :
1799 58 : query = createPQExpBuffer();
1800 :
1801 : /*
1802 : * this might sometimes result in duplicate entries in the OID list, but
1803 : * we don't care.
1804 : */
1805 :
1806 118 : for (cell = patterns->head; cell; cell = cell->next)
1807 : {
1808 : PQExpBufferData dbbuf;
1809 : int dotcnt;
1810 :
1811 : /*
1812 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1813 : * would be unnecessary given a pg_table_is_visible() variant taking a
1814 : * search_path argument.
1815 : *
1816 : * For with_child_tables, we start with the basic query's results and
1817 : * recursively search the inheritance tree to add child tables.
1818 : */
1819 70 : if (with_child_tables)
1820 : {
1821 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1822 : }
1823 :
1824 70 : appendPQExpBuffer(query,
1825 : "SELECT c.oid"
1826 : "\nFROM pg_catalog.pg_class c"
1827 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1828 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1829 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1830 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1831 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1832 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1833 : RELKIND_PARTITIONED_TABLE);
1834 70 : initPQExpBuffer(&dbbuf);
1835 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1836 : false, "n.nspname", "c.relname", NULL,
1837 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1838 : &dotcnt);
1839 70 : if (dotcnt > 2)
1840 2 : pg_fatal("improper relation name (too many dotted names): %s",
1841 : cell->val);
1842 68 : else if (dotcnt == 2)
1843 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1844 64 : termPQExpBuffer(&dbbuf);
1845 :
1846 64 : if (with_child_tables)
1847 : {
1848 12 : appendPQExpBufferStr(query, "UNION"
1849 : "\nSELECT i.inhrelid"
1850 : "\nFROM partition_tree p"
1851 : "\n JOIN pg_catalog.pg_inherits i"
1852 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1853 : "\n)"
1854 : "\nSELECT relid FROM partition_tree");
1855 : }
1856 :
1857 64 : ExecuteSqlStatement(fout, "RESET search_path");
1858 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1859 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1860 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1861 64 : if (strict_names && PQntuples(res) == 0)
1862 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1863 :
1864 148 : for (i = 0; i < PQntuples(res); i++)
1865 : {
1866 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1867 : }
1868 :
1869 60 : PQclear(res);
1870 60 : resetPQExpBuffer(query);
1871 : }
1872 :
1873 48 : destroyPQExpBuffer(query);
1874 : }
1875 :
1876 : /*
1877 : * Verifies that the connected database name matches the given database name,
1878 : * and if not, dies with an error about the given pattern.
1879 : *
1880 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1881 : */
1882 : static void
1883 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1884 : {
1885 : const char *db;
1886 :
1887 10 : db = PQdb(conn);
1888 10 : if (db == NULL)
1889 0 : pg_fatal("You are currently not connected to a database.");
1890 :
1891 10 : if (strcmp(db, dbname) != 0)
1892 10 : pg_fatal("cross-database references are not implemented: %s",
1893 : pattern);
1894 0 : }
1895 :
1896 : /*
1897 : * checkExtensionMembership
1898 : * Determine whether object is an extension member, and if so,
1899 : * record an appropriate dependency and set the object's dump flag.
1900 : *
1901 : * It's important to call this for each object that could be an extension
1902 : * member. Generally, we integrate this with determining the object's
1903 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1904 : *
1905 : * Returns true if object is an extension member, else false.
1906 : */
1907 : static bool
1908 1476554 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1909 : {
1910 1476554 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1911 :
1912 1476554 : if (ext == NULL)
1913 1474900 : return false;
1914 :
1915 1654 : dobj->ext_member = true;
1916 :
1917 : /* Record dependency so that getDependencies needn't deal with that */
1918 1654 : addObjectDependency(dobj, ext->dobj.dumpId);
1919 :
1920 : /*
1921 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1922 : * dumped. (Any initial ACLs will be removed later, using data from
1923 : * pg_init_privs, so that we'll dump only the delta from the extension's
1924 : * initial setup.)
1925 : *
1926 : * Prior to 9.6, we do not include any extension member components.
1927 : *
1928 : * In binary upgrades, we still dump all components of the members
1929 : * individually, since the idea is to exactly reproduce the database
1930 : * contents rather than replace the extension contents with something
1931 : * different.
1932 : *
1933 : * Note: it might be interesting someday to implement storage and delta
1934 : * dumping of extension members' RLS policies and/or security labels.
1935 : * However there is a pitfall for RLS policies: trying to dump them
1936 : * requires getting a lock on their tables, and the calling user might not
1937 : * have privileges for that. We need no lock to examine a table's ACLs,
1938 : * so the current feature doesn't have a problem of that sort.
1939 : */
1940 1654 : if (fout->dopt->binary_upgrade)
1941 288 : dobj->dump = ext->dobj.dump;
1942 : else
1943 : {
1944 1366 : if (fout->remoteVersion < 90600)
1945 0 : dobj->dump = DUMP_COMPONENT_NONE;
1946 : else
1947 1366 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1948 : }
1949 :
1950 1654 : return true;
1951 : }
1952 :
1953 : /*
1954 : * selectDumpableNamespace: policy-setting subroutine
1955 : * Mark a namespace as to be dumped or not
1956 : */
1957 : static void
1958 3892 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1959 : {
1960 : /*
1961 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1962 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1963 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1964 : */
1965 3892 : nsinfo->create = true;
1966 :
1967 : /*
1968 : * If specific tables are being dumped, do not dump any complete
1969 : * namespaces. If specific namespaces are being dumped, dump just those
1970 : * namespaces. Otherwise, dump all non-system namespaces.
1971 : */
1972 3892 : if (table_include_oids.head != NULL)
1973 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1974 3792 : else if (schema_include_oids.head != NULL)
1975 366 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
1976 366 : simple_oid_list_member(&schema_include_oids,
1977 : nsinfo->dobj.catId.oid) ?
1978 366 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
1979 3426 : else if (fout->remoteVersion >= 90600 &&
1980 3426 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
1981 : {
1982 : /*
1983 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
1984 : * they are interesting (and not the original ACLs which were set at
1985 : * initdb time, see pg_init_privs).
1986 : */
1987 426 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
1988 : }
1989 3000 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
1990 1286 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
1991 : {
1992 : /* Other system schemas don't get dumped */
1993 2140 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
1994 : }
1995 860 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
1996 : {
1997 : /*
1998 : * The public schema is a strange beast that sits in a sort of
1999 : * no-mans-land between being a system object and a user object.
2000 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2001 : * a comment and an indication of ownership. If the owner is the
2002 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2003 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2004 : */
2005 418 : nsinfo->create = false;
2006 418 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2007 418 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2008 326 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2009 418 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2010 :
2011 : /*
2012 : * Also, make like it has a comment even if it doesn't; this is so
2013 : * that we'll emit a command to drop the comment, if appropriate.
2014 : * (Without this, we'd not call dumpCommentExtended for it.)
2015 : */
2016 418 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2017 : }
2018 : else
2019 442 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2020 :
2021 : /*
2022 : * In any case, a namespace can be excluded by an exclusion switch
2023 : */
2024 5200 : if (nsinfo->dobj.dump_contains &&
2025 1308 : simple_oid_list_member(&schema_exclude_oids,
2026 : nsinfo->dobj.catId.oid))
2027 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2028 :
2029 : /*
2030 : * If the schema belongs to an extension, allow extension membership to
2031 : * override the dump decision for the schema itself. However, this does
2032 : * not change dump_contains, so this won't change what we do with objects
2033 : * within the schema. (If they belong to the extension, they'll get
2034 : * suppressed by it, otherwise not.)
2035 : */
2036 3892 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2037 3892 : }
2038 :
2039 : /*
2040 : * selectDumpableTable: policy-setting subroutine
2041 : * Mark a table as to be dumped or not
2042 : */
2043 : static void
2044 123860 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2045 : {
2046 123860 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2047 450 : return; /* extension membership overrides all else */
2048 :
2049 : /*
2050 : * If specific tables are being dumped, dump just those tables; else, dump
2051 : * according to the parent namespace's dump flag.
2052 : */
2053 123410 : if (table_include_oids.head != NULL)
2054 10320 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2055 : tbinfo->dobj.catId.oid) ?
2056 5160 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2057 : else
2058 118250 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2059 :
2060 : /*
2061 : * In any case, a table can be excluded by an exclusion switch
2062 : */
2063 204666 : if (tbinfo->dobj.dump &&
2064 81256 : simple_oid_list_member(&table_exclude_oids,
2065 : tbinfo->dobj.catId.oid))
2066 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2067 : }
2068 :
2069 : /*
2070 : * selectDumpableType: policy-setting subroutine
2071 : * Mark a type as to be dumped or not
2072 : *
2073 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2074 : * special type code to facilitate sorting into the desired order. (We don't
2075 : * want to consider those to be ordinary types because that would bring tables
2076 : * up into the datatype part of the dump order.) We still set the object's
2077 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2078 : * need it so that casts involving such types will be dumped correctly -- see
2079 : * dumpCast. This means the flag should be set the same as for the underlying
2080 : * object (the table or base type).
2081 : */
2082 : static void
2083 339894 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2084 : {
2085 : /* skip complex types, except for standalone composite types */
2086 339894 : if (OidIsValid(tyinfo->typrelid) &&
2087 122160 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2088 : {
2089 121692 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2090 :
2091 121692 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2092 121692 : if (tytable != NULL)
2093 121692 : tyinfo->dobj.dump = tytable->dobj.dump;
2094 : else
2095 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2096 121692 : return;
2097 : }
2098 :
2099 : /* skip auto-generated array and multirange types */
2100 218202 : if (tyinfo->isArray || tyinfo->isMultirange)
2101 : {
2102 166226 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2103 :
2104 : /*
2105 : * Fall through to set the dump flag; we assume that the subsequent
2106 : * rules will do the same thing as they would for the array's base
2107 : * type or multirange's range type. (We cannot reliably look up the
2108 : * base type here, since getTypes may not have processed it yet.)
2109 : */
2110 : }
2111 :
2112 218202 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2113 300 : return; /* extension membership overrides all else */
2114 :
2115 : /* Dump based on if the contents of the namespace are being dumped */
2116 217902 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2117 : }
2118 :
2119 : /*
2120 : * selectDumpableDefaultACL: policy-setting subroutine
2121 : * Mark a default ACL as to be dumped or not
2122 : *
2123 : * For per-schema default ACLs, dump if the schema is to be dumped.
2124 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2125 : * and aclsSkip are checked separately.
2126 : */
2127 : static void
2128 392 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2129 : {
2130 : /* Default ACLs can't be extension members */
2131 :
2132 392 : if (dinfo->dobj.namespace)
2133 : /* default ACLs are considered part of the namespace */
2134 196 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2135 : else
2136 196 : dinfo->dobj.dump = dopt->include_everything ?
2137 196 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2138 392 : }
2139 :
2140 : /*
2141 : * selectDumpableCast: policy-setting subroutine
2142 : * Mark a cast as to be dumped or not
2143 : *
2144 : * Casts do not belong to any particular namespace (since they haven't got
2145 : * names), nor do they have identifiable owners. To distinguish user-defined
2146 : * casts from built-in ones, we must resort to checking whether the cast's
2147 : * OID is in the range reserved for initdb.
2148 : */
2149 : static void
2150 110666 : selectDumpableCast(CastInfo *cast, Archive *fout)
2151 : {
2152 110666 : if (checkExtensionMembership(&cast->dobj, fout))
2153 0 : return; /* extension membership overrides all else */
2154 :
2155 : /*
2156 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2157 : * support ACLs currently.
2158 : */
2159 110666 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2160 110448 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2161 : else
2162 218 : cast->dobj.dump = fout->dopt->include_everything ?
2163 218 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2164 : }
2165 :
2166 : /*
2167 : * selectDumpableProcLang: policy-setting subroutine
2168 : * Mark a procedural language as to be dumped or not
2169 : *
2170 : * Procedural languages do not belong to any particular namespace. To
2171 : * identify built-in languages, we must resort to checking whether the
2172 : * language's OID is in the range reserved for initdb.
2173 : */
2174 : static void
2175 566 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2176 : {
2177 566 : if (checkExtensionMembership(&plang->dobj, fout))
2178 468 : return; /* extension membership overrides all else */
2179 :
2180 : /*
2181 : * Only include procedural languages when we are dumping everything.
2182 : *
2183 : * For from-initdb procedural languages, only include ACLs, as we do for
2184 : * the pg_catalog namespace. We need this because procedural languages do
2185 : * not live in any namespace.
2186 : */
2187 98 : if (!fout->dopt->include_everything)
2188 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2189 : else
2190 : {
2191 82 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2192 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2193 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2194 : else
2195 82 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2196 : }
2197 : }
2198 :
2199 : /*
2200 : * selectDumpableAccessMethod: policy-setting subroutine
2201 : * Mark an access method as to be dumped or not
2202 : *
2203 : * Access methods do not belong to any particular namespace. To identify
2204 : * built-in access methods, we must resort to checking whether the
2205 : * method's OID is in the range reserved for initdb.
2206 : */
2207 : static void
2208 3542 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2209 : {
2210 3542 : if (checkExtensionMembership(&method->dobj, fout))
2211 50 : return; /* extension membership overrides all else */
2212 :
2213 : /*
2214 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2215 : * they do not support ACLs currently.
2216 : */
2217 3492 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2218 3276 : method->dobj.dump = DUMP_COMPONENT_NONE;
2219 : else
2220 216 : method->dobj.dump = fout->dopt->include_everything ?
2221 216 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2222 : }
2223 :
2224 : /*
2225 : * selectDumpableExtension: policy-setting subroutine
2226 : * Mark an extension as to be dumped or not
2227 : *
2228 : * Built-in extensions should be skipped except for checking ACLs, since we
2229 : * assume those will already be installed in the target database. We identify
2230 : * such extensions by their having OIDs in the range reserved for initdb.
2231 : * We dump all user-added extensions by default. No extensions are dumped
2232 : * if include_everything is false (i.e., a --schema or --table switch was
2233 : * given), except if --extension specifies a list of extensions to dump.
2234 : */
2235 : static void
2236 520 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2237 : {
2238 : /*
2239 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2240 : * change permissions on their member objects, if they wish to, and have
2241 : * those changes preserved.
2242 : */
2243 520 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2244 470 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2245 : else
2246 : {
2247 : /* check if there is a list of extensions to dump */
2248 50 : if (extension_include_oids.head != NULL)
2249 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2250 8 : simple_oid_list_member(&extension_include_oids,
2251 : extinfo->dobj.catId.oid) ?
2252 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2253 : else
2254 42 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2255 42 : dopt->include_everything ?
2256 42 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2257 :
2258 : /* check that the extension is not explicitly excluded */
2259 92 : if (extinfo->dobj.dump &&
2260 42 : simple_oid_list_member(&extension_exclude_oids,
2261 : extinfo->dobj.catId.oid))
2262 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2263 : }
2264 520 : }
2265 :
2266 : /*
2267 : * selectDumpablePublicationObject: policy-setting subroutine
2268 : * Mark a publication object as to be dumped or not
2269 : *
2270 : * A publication can have schemas and tables which have schemas, but those are
2271 : * ignored in decision making, because publications are only dumped when we are
2272 : * dumping everything.
2273 : */
2274 : static void
2275 882 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2276 : {
2277 882 : if (checkExtensionMembership(dobj, fout))
2278 0 : return; /* extension membership overrides all else */
2279 :
2280 882 : dobj->dump = fout->dopt->include_everything ?
2281 882 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2282 : }
2283 :
2284 : /*
2285 : * selectDumpableStatisticsObject: policy-setting subroutine
2286 : * Mark an extended statistics object as to be dumped or not
2287 : *
2288 : * We dump an extended statistics object if the schema it's in and the table
2289 : * it's for are being dumped. (This'll need more thought if statistics
2290 : * objects ever support cross-table stats.)
2291 : */
2292 : static void
2293 374 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2294 : {
2295 374 : if (checkExtensionMembership(&sobj->dobj, fout))
2296 0 : return; /* extension membership overrides all else */
2297 :
2298 374 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2299 374 : if (sobj->stattable == NULL ||
2300 374 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2301 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2302 : }
2303 :
2304 : /*
2305 : * selectDumpableObject: policy-setting subroutine
2306 : * Mark a generic dumpable object as to be dumped or not
2307 : *
2308 : * Use this only for object types without a special-case routine above.
2309 : */
2310 : static void
2311 1014570 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2312 : {
2313 1014570 : if (checkExtensionMembership(dobj, fout))
2314 336 : return; /* extension membership overrides all else */
2315 :
2316 : /*
2317 : * Default policy is to dump if parent namespace is dumpable, or for
2318 : * non-namespace-associated items, dump if we're dumping "everything".
2319 : */
2320 1014234 : if (dobj->namespace)
2321 1012896 : dobj->dump = dobj->namespace->dobj.dump_contains;
2322 : else
2323 1338 : dobj->dump = fout->dopt->include_everything ?
2324 1338 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2325 : }
2326 :
2327 : /*
2328 : * Dump a table's contents for loading using the COPY command
2329 : * - this routine is called by the Archiver when it wants the table
2330 : * to be dumped.
2331 : */
2332 : static int
2333 12272 : dumpTableData_copy(Archive *fout, const void *dcontext)
2334 : {
2335 12272 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2336 12272 : TableInfo *tbinfo = tdinfo->tdtable;
2337 12272 : const char *classname = tbinfo->dobj.name;
2338 12272 : PQExpBuffer q = createPQExpBuffer();
2339 :
2340 : /*
2341 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2342 : * which uses it already.
2343 : */
2344 12272 : PQExpBuffer clistBuf = createPQExpBuffer();
2345 12272 : PGconn *conn = GetConnection(fout);
2346 : PGresult *res;
2347 : int ret;
2348 : char *copybuf;
2349 : const char *column_list;
2350 :
2351 12272 : pg_log_info("dumping contents of table \"%s.%s\"",
2352 : tbinfo->dobj.namespace->dobj.name, classname);
2353 :
2354 : /*
2355 : * Specify the column list explicitly so that we have no possibility of
2356 : * retrieving data in the wrong column order. (The default column
2357 : * ordering of COPY will not be what we want in certain corner cases
2358 : * involving ADD COLUMN and inheritance.)
2359 : */
2360 12272 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2361 :
2362 : /*
2363 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2364 : * a filter condition was specified. For other cases a simple COPY
2365 : * suffices.
2366 : */
2367 12272 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2368 : {
2369 : /* Temporary allows to access to foreign tables to dump data */
2370 64 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2371 2 : set_restrict_relation_kind(fout, "view");
2372 :
2373 64 : appendPQExpBufferStr(q, "COPY (SELECT ");
2374 : /* klugery to get rid of parens in column list */
2375 64 : if (strlen(column_list) > 2)
2376 : {
2377 64 : appendPQExpBufferStr(q, column_list + 1);
2378 64 : q->data[q->len - 1] = ' ';
2379 : }
2380 : else
2381 0 : appendPQExpBufferStr(q, "* ");
2382 :
2383 128 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2384 64 : fmtQualifiedDumpable(tbinfo),
2385 64 : tdinfo->filtercond ? tdinfo->filtercond : "");
2386 : }
2387 : else
2388 : {
2389 12208 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2390 12208 : fmtQualifiedDumpable(tbinfo),
2391 : column_list);
2392 : }
2393 12272 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2394 12270 : PQclear(res);
2395 12270 : destroyPQExpBuffer(clistBuf);
2396 :
2397 : for (;;)
2398 : {
2399 6136478 : ret = PQgetCopyData(conn, ©buf, 0);
2400 :
2401 6136478 : if (ret < 0)
2402 12270 : break; /* done or error */
2403 :
2404 6124208 : if (copybuf)
2405 : {
2406 6124208 : WriteData(fout, copybuf, ret);
2407 6124208 : PQfreemem(copybuf);
2408 : }
2409 :
2410 : /* ----------
2411 : * THROTTLE:
2412 : *
2413 : * There was considerable discussion in late July, 2000 regarding
2414 : * slowing down pg_dump when backing up large tables. Users with both
2415 : * slow & fast (multi-processor) machines experienced performance
2416 : * degradation when doing a backup.
2417 : *
2418 : * Initial attempts based on sleeping for a number of ms for each ms
2419 : * of work were deemed too complex, then a simple 'sleep in each loop'
2420 : * implementation was suggested. The latter failed because the loop
2421 : * was too tight. Finally, the following was implemented:
2422 : *
2423 : * If throttle is non-zero, then
2424 : * See how long since the last sleep.
2425 : * Work out how long to sleep (based on ratio).
2426 : * If sleep is more than 100ms, then
2427 : * sleep
2428 : * reset timer
2429 : * EndIf
2430 : * EndIf
2431 : *
2432 : * where the throttle value was the number of ms to sleep per ms of
2433 : * work. The calculation was done in each loop.
2434 : *
2435 : * Most of the hard work is done in the backend, and this solution
2436 : * still did not work particularly well: on slow machines, the ratio
2437 : * was 50:1, and on medium paced machines, 1:1, and on fast
2438 : * multi-processor machines, it had little or no effect, for reasons
2439 : * that were unclear.
2440 : *
2441 : * Further discussion ensued, and the proposal was dropped.
2442 : *
2443 : * For those people who want this feature, it can be implemented using
2444 : * gettimeofday in each loop, calculating the time since last sleep,
2445 : * multiplying that by the sleep ratio, then if the result is more
2446 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2447 : * function to sleep for a subsecond period ie.
2448 : *
2449 : * select(0, NULL, NULL, NULL, &tvi);
2450 : *
2451 : * This will return after the interval specified in the structure tvi.
2452 : * Finally, call gettimeofday again to save the 'last sleep time'.
2453 : * ----------
2454 : */
2455 : }
2456 12270 : archprintf(fout, "\\.\n\n\n");
2457 :
2458 12270 : if (ret == -2)
2459 : {
2460 : /* copy data transfer failed */
2461 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2462 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2463 0 : pg_log_error_detail("Command was: %s", q->data);
2464 0 : exit_nicely(1);
2465 : }
2466 :
2467 : /* Check command status and return to normal libpq state */
2468 12270 : res = PQgetResult(conn);
2469 12270 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2470 : {
2471 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2472 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2473 0 : pg_log_error_detail("Command was: %s", q->data);
2474 0 : exit_nicely(1);
2475 : }
2476 12270 : PQclear(res);
2477 :
2478 : /* Do this to ensure we've pumped libpq back to idle state */
2479 12270 : if (PQgetResult(conn) != NULL)
2480 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2481 : classname);
2482 :
2483 12270 : destroyPQExpBuffer(q);
2484 :
2485 : /* Revert back the setting */
2486 12270 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2487 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2488 :
2489 12270 : return 1;
2490 : }
2491 :
2492 : /*
2493 : * Dump table data using INSERT commands.
2494 : *
2495 : * Caution: when we restore from an archive file direct to database, the
2496 : * INSERT commands emitted by this function have to be parsed by
2497 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2498 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2499 : */
2500 : static int
2501 162 : dumpTableData_insert(Archive *fout, const void *dcontext)
2502 : {
2503 162 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2504 162 : TableInfo *tbinfo = tdinfo->tdtable;
2505 162 : DumpOptions *dopt = fout->dopt;
2506 162 : PQExpBuffer q = createPQExpBuffer();
2507 162 : PQExpBuffer insertStmt = NULL;
2508 : char *attgenerated;
2509 : PGresult *res;
2510 : int nfields,
2511 : i;
2512 162 : int rows_per_statement = dopt->dump_inserts;
2513 162 : int rows_this_statement = 0;
2514 :
2515 : /* Temporary allows to access to foreign tables to dump data */
2516 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2517 0 : set_restrict_relation_kind(fout, "view");
2518 :
2519 : /*
2520 : * If we're going to emit INSERTs with column names, the most efficient
2521 : * way to deal with generated columns is to exclude them entirely. For
2522 : * INSERTs without column names, we have to emit DEFAULT rather than the
2523 : * actual column value --- but we can save a few cycles by fetching nulls
2524 : * rather than the uninteresting-to-us value.
2525 : */
2526 162 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2527 162 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2528 162 : nfields = 0;
2529 506 : for (i = 0; i < tbinfo->numatts; i++)
2530 : {
2531 344 : if (tbinfo->attisdropped[i])
2532 4 : continue;
2533 340 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2534 16 : continue;
2535 324 : if (nfields > 0)
2536 176 : appendPQExpBufferStr(q, ", ");
2537 324 : if (tbinfo->attgenerated[i])
2538 16 : appendPQExpBufferStr(q, "NULL");
2539 : else
2540 308 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2541 324 : attgenerated[nfields] = tbinfo->attgenerated[i];
2542 324 : nfields++;
2543 : }
2544 : /* Servers before 9.4 will complain about zero-column SELECT */
2545 162 : if (nfields == 0)
2546 14 : appendPQExpBufferStr(q, "NULL");
2547 162 : appendPQExpBuffer(q, " FROM ONLY %s",
2548 162 : fmtQualifiedDumpable(tbinfo));
2549 162 : if (tdinfo->filtercond)
2550 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2551 :
2552 162 : ExecuteSqlStatement(fout, q->data);
2553 :
2554 : while (1)
2555 : {
2556 266 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2557 : PGRES_TUPLES_OK);
2558 :
2559 : /* cross-check field count, allowing for dummy NULL if any */
2560 266 : if (nfields != PQnfields(res) &&
2561 20 : !(nfields == 0 && PQnfields(res) == 1))
2562 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2563 : tbinfo->dobj.name);
2564 :
2565 : /*
2566 : * First time through, we build as much of the INSERT statement as
2567 : * possible in "insertStmt", which we can then just print for each
2568 : * statement. If the table happens to have zero dumpable columns then
2569 : * this will be a complete statement, otherwise it will end in
2570 : * "VALUES" and be ready to have the row's column values printed.
2571 : */
2572 266 : if (insertStmt == NULL)
2573 : {
2574 : TableInfo *targettab;
2575 :
2576 162 : insertStmt = createPQExpBuffer();
2577 :
2578 : /*
2579 : * When load-via-partition-root is set or forced, get the root
2580 : * table name for the partition table, so that we can reload data
2581 : * through the root table.
2582 : */
2583 162 : if (tbinfo->ispartition &&
2584 80 : (dopt->load_via_partition_root ||
2585 40 : forcePartitionRootLoad(tbinfo)))
2586 6 : targettab = getRootTableInfo(tbinfo);
2587 : else
2588 156 : targettab = tbinfo;
2589 :
2590 162 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2591 162 : fmtQualifiedDumpable(targettab));
2592 :
2593 : /* corner case for zero-column table */
2594 162 : if (nfields == 0)
2595 : {
2596 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2597 : }
2598 : else
2599 : {
2600 : /* append the list of column names if required */
2601 148 : if (dopt->column_inserts)
2602 : {
2603 66 : appendPQExpBufferChar(insertStmt, '(');
2604 202 : for (int field = 0; field < nfields; field++)
2605 : {
2606 136 : if (field > 0)
2607 70 : appendPQExpBufferStr(insertStmt, ", ");
2608 136 : appendPQExpBufferStr(insertStmt,
2609 136 : fmtId(PQfname(res, field)));
2610 : }
2611 66 : appendPQExpBufferStr(insertStmt, ") ");
2612 : }
2613 :
2614 148 : if (tbinfo->needs_override)
2615 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2616 :
2617 148 : appendPQExpBufferStr(insertStmt, "VALUES");
2618 : }
2619 : }
2620 :
2621 6808 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2622 : {
2623 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2624 6542 : if (rows_this_statement == 0)
2625 6530 : archputs(insertStmt->data, fout);
2626 :
2627 : /*
2628 : * If it is zero-column table then we've already written the
2629 : * complete statement, which will mean we've disobeyed
2630 : * --rows-per-insert when it's set greater than 1. We do support
2631 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2632 : * UNION ALL ... but that's non-standard so we should avoid it
2633 : * given that using INSERTs is mostly only ever needed for
2634 : * cross-database exports.
2635 : */
2636 6542 : if (nfields == 0)
2637 12 : continue;
2638 :
2639 : /* Emit a row heading */
2640 6530 : if (rows_per_statement == 1)
2641 6512 : archputs(" (", fout);
2642 18 : else if (rows_this_statement > 0)
2643 12 : archputs(",\n\t(", fout);
2644 : else
2645 6 : archputs("\n\t(", fout);
2646 :
2647 19698 : for (int field = 0; field < nfields; field++)
2648 : {
2649 13168 : if (field > 0)
2650 6638 : archputs(", ", fout);
2651 13168 : if (attgenerated[field])
2652 : {
2653 4 : archputs("DEFAULT", fout);
2654 4 : continue;
2655 : }
2656 13164 : if (PQgetisnull(res, tuple, field))
2657 : {
2658 166 : archputs("NULL", fout);
2659 166 : continue;
2660 : }
2661 :
2662 : /* XXX This code is partially duplicated in ruleutils.c */
2663 12998 : switch (PQftype(res, field))
2664 : {
2665 8938 : case INT2OID:
2666 : case INT4OID:
2667 : case INT8OID:
2668 : case OIDOID:
2669 : case FLOAT4OID:
2670 : case FLOAT8OID:
2671 : case NUMERICOID:
2672 : {
2673 : /*
2674 : * These types are printed without quotes unless
2675 : * they contain values that aren't accepted by the
2676 : * scanner unquoted (e.g., 'NaN'). Note that
2677 : * strtod() and friends might accept NaN, so we
2678 : * can't use that to test.
2679 : *
2680 : * In reality we only need to defend against
2681 : * infinity and NaN, so we need not get too crazy
2682 : * about pattern matching here.
2683 : */
2684 8938 : const char *s = PQgetvalue(res, tuple, field);
2685 :
2686 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2687 8934 : archputs(s, fout);
2688 : else
2689 4 : archprintf(fout, "'%s'", s);
2690 : }
2691 8938 : break;
2692 :
2693 4 : case BITOID:
2694 : case VARBITOID:
2695 4 : archprintf(fout, "B'%s'",
2696 : PQgetvalue(res, tuple, field));
2697 4 : break;
2698 :
2699 8 : case BOOLOID:
2700 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2701 4 : archputs("true", fout);
2702 : else
2703 4 : archputs("false", fout);
2704 8 : break;
2705 :
2706 4048 : default:
2707 : /* All other types are printed as string literals. */
2708 4048 : resetPQExpBuffer(q);
2709 4048 : appendStringLiteralAH(q,
2710 : PQgetvalue(res, tuple, field),
2711 : fout);
2712 4048 : archputs(q->data, fout);
2713 4048 : break;
2714 : }
2715 : }
2716 :
2717 : /* Terminate the row ... */
2718 6530 : archputs(")", fout);
2719 :
2720 : /* ... and the statement, if the target no. of rows is reached */
2721 6530 : if (++rows_this_statement >= rows_per_statement)
2722 : {
2723 6516 : if (dopt->do_nothing)
2724 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2725 : else
2726 6516 : archputs(";\n", fout);
2727 : /* Reset the row counter */
2728 6516 : rows_this_statement = 0;
2729 : }
2730 : }
2731 :
2732 266 : if (PQntuples(res) <= 0)
2733 : {
2734 162 : PQclear(res);
2735 162 : break;
2736 : }
2737 104 : PQclear(res);
2738 : }
2739 :
2740 : /* Terminate any statements that didn't make the row count. */
2741 162 : if (rows_this_statement > 0)
2742 : {
2743 2 : if (dopt->do_nothing)
2744 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2745 : else
2746 2 : archputs(";\n", fout);
2747 : }
2748 :
2749 162 : archputs("\n\n", fout);
2750 :
2751 162 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2752 :
2753 162 : destroyPQExpBuffer(q);
2754 162 : if (insertStmt != NULL)
2755 162 : destroyPQExpBuffer(insertStmt);
2756 162 : free(attgenerated);
2757 :
2758 : /* Revert back the setting */
2759 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2760 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2761 :
2762 162 : return 1;
2763 : }
2764 :
2765 : /*
2766 : * getRootTableInfo:
2767 : * get the root TableInfo for the given partition table.
2768 : */
2769 : static TableInfo *
2770 18 : getRootTableInfo(const TableInfo *tbinfo)
2771 : {
2772 : TableInfo *parentTbinfo;
2773 :
2774 : Assert(tbinfo->ispartition);
2775 : Assert(tbinfo->numParents == 1);
2776 :
2777 18 : parentTbinfo = tbinfo->parents[0];
2778 18 : while (parentTbinfo->ispartition)
2779 : {
2780 : Assert(parentTbinfo->numParents == 1);
2781 0 : parentTbinfo = parentTbinfo->parents[0];
2782 : }
2783 :
2784 18 : return parentTbinfo;
2785 : }
2786 :
2787 : /*
2788 : * forcePartitionRootLoad
2789 : * Check if we must force load_via_partition_root for this partition.
2790 : *
2791 : * This is required if any level of ancestral partitioned table has an
2792 : * unsafe partitioning scheme.
2793 : */
2794 : static bool
2795 3060 : forcePartitionRootLoad(const TableInfo *tbinfo)
2796 : {
2797 : TableInfo *parentTbinfo;
2798 :
2799 : Assert(tbinfo->ispartition);
2800 : Assert(tbinfo->numParents == 1);
2801 :
2802 3060 : parentTbinfo = tbinfo->parents[0];
2803 3060 : if (parentTbinfo->unsafe_partitions)
2804 18 : return true;
2805 3798 : while (parentTbinfo->ispartition)
2806 : {
2807 : Assert(parentTbinfo->numParents == 1);
2808 756 : parentTbinfo = parentTbinfo->parents[0];
2809 756 : if (parentTbinfo->unsafe_partitions)
2810 0 : return true;
2811 : }
2812 :
2813 3042 : return false;
2814 : }
2815 :
2816 : /*
2817 : * dumpTableData -
2818 : * dump the contents of a single table
2819 : *
2820 : * Actually, this just makes an ArchiveEntry for the table contents.
2821 : */
2822 : static void
2823 12594 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2824 : {
2825 12594 : DumpOptions *dopt = fout->dopt;
2826 12594 : TableInfo *tbinfo = tdinfo->tdtable;
2827 12594 : PQExpBuffer copyBuf = createPQExpBuffer();
2828 12594 : PQExpBuffer clistBuf = createPQExpBuffer();
2829 : DataDumperPtr dumpFn;
2830 12594 : char *tdDefn = NULL;
2831 : char *copyStmt;
2832 : const char *copyFrom;
2833 :
2834 : /* We had better have loaded per-column details about this table */
2835 : Assert(tbinfo->interesting);
2836 :
2837 : /*
2838 : * When load-via-partition-root is set or forced, get the root table name
2839 : * for the partition table, so that we can reload data through the root
2840 : * table. Then construct a comment to be inserted into the TOC entry's
2841 : * defn field, so that such cases can be identified reliably.
2842 : */
2843 12594 : if (tbinfo->ispartition &&
2844 6040 : (dopt->load_via_partition_root ||
2845 3020 : forcePartitionRootLoad(tbinfo)))
2846 12 : {
2847 : TableInfo *parentTbinfo;
2848 :
2849 12 : parentTbinfo = getRootTableInfo(tbinfo);
2850 12 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2851 12 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2852 : copyFrom);
2853 12 : tdDefn = pg_strdup(copyBuf->data);
2854 : }
2855 : else
2856 12582 : copyFrom = fmtQualifiedDumpable(tbinfo);
2857 :
2858 12594 : if (dopt->dump_inserts == 0)
2859 : {
2860 : /* Dump/restore using COPY */
2861 12432 : dumpFn = dumpTableData_copy;
2862 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2863 12432 : printfPQExpBuffer(copyBuf, "COPY %s ",
2864 : copyFrom);
2865 12432 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2866 : fmtCopyColumnList(tbinfo, clistBuf));
2867 12432 : copyStmt = copyBuf->data;
2868 : }
2869 : else
2870 : {
2871 : /* Restore using INSERT */
2872 162 : dumpFn = dumpTableData_insert;
2873 162 : copyStmt = NULL;
2874 : }
2875 :
2876 : /*
2877 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2878 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2879 : * See comments for BuildArchiveDependencies.
2880 : */
2881 12594 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2882 : {
2883 : TocEntry *te;
2884 :
2885 12594 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2886 12594 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2887 : .namespace = tbinfo->dobj.namespace->dobj.name,
2888 : .owner = tbinfo->rolname,
2889 : .description = "TABLE DATA",
2890 : .section = SECTION_DATA,
2891 : .createStmt = tdDefn,
2892 : .copyStmt = copyStmt,
2893 : .deps = &(tbinfo->dobj.dumpId),
2894 : .nDeps = 1,
2895 : .dumpFn = dumpFn,
2896 : .dumpArg = tdinfo));
2897 :
2898 : /*
2899 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2900 : * and want to order dump jobs by table size. We choose to measure
2901 : * dataLength in table pages (including TOAST pages) during dump, so
2902 : * no scaling is needed.
2903 : *
2904 : * However, relpages is declared as "integer" in pg_class, and hence
2905 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2906 : * Cast so that we get the right interpretation of table sizes
2907 : * exceeding INT_MAX pages.
2908 : */
2909 12594 : te->dataLength = (BlockNumber) tbinfo->relpages;
2910 12594 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2911 :
2912 : /*
2913 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2914 : * and instead we'd better worry about integer overflow. Clamp to
2915 : * INT_MAX if the correct result exceeds that.
2916 : */
2917 : if (sizeof(te->dataLength) == 4 &&
2918 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2919 : te->dataLength < 0))
2920 : te->dataLength = INT_MAX;
2921 : }
2922 :
2923 12594 : destroyPQExpBuffer(copyBuf);
2924 12594 : destroyPQExpBuffer(clistBuf);
2925 12594 : }
2926 :
2927 : /*
2928 : * refreshMatViewData -
2929 : * load or refresh the contents of a single materialized view
2930 : *
2931 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2932 : * statement.
2933 : */
2934 : static void
2935 852 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2936 : {
2937 852 : TableInfo *tbinfo = tdinfo->tdtable;
2938 : PQExpBuffer q;
2939 :
2940 : /* If the materialized view is not flagged as populated, skip this. */
2941 852 : if (!tbinfo->relispopulated)
2942 148 : return;
2943 :
2944 704 : q = createPQExpBuffer();
2945 :
2946 704 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2947 704 : fmtQualifiedDumpable(tbinfo));
2948 :
2949 704 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2950 704 : ArchiveEntry(fout,
2951 : tdinfo->dobj.catId, /* catalog ID */
2952 704 : tdinfo->dobj.dumpId, /* dump ID */
2953 704 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2954 : .namespace = tbinfo->dobj.namespace->dobj.name,
2955 : .owner = tbinfo->rolname,
2956 : .description = "MATERIALIZED VIEW DATA",
2957 : .section = SECTION_POST_DATA,
2958 : .createStmt = q->data,
2959 : .deps = tdinfo->dobj.dependencies,
2960 : .nDeps = tdinfo->dobj.nDeps));
2961 :
2962 704 : destroyPQExpBuffer(q);
2963 : }
2964 :
2965 : /*
2966 : * getTableData -
2967 : * set up dumpable objects representing the contents of tables
2968 : */
2969 : static void
2970 452 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
2971 : {
2972 : int i;
2973 :
2974 120350 : for (i = 0; i < numTables; i++)
2975 : {
2976 119898 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
2977 1830 : (!relkind || tblinfo[i].relkind == relkind))
2978 17236 : makeTableDataInfo(dopt, &(tblinfo[i]));
2979 : }
2980 452 : }
2981 :
2982 : /*
2983 : * Make a dumpable object for the data of this specific table
2984 : *
2985 : * Note: we make a TableDataInfo if and only if we are going to dump the
2986 : * table data; the "dump" field in such objects isn't very interesting.
2987 : */
2988 : static void
2989 17438 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
2990 : {
2991 : TableDataInfo *tdinfo;
2992 :
2993 : /*
2994 : * Nothing to do if we already decided to dump the table. This will
2995 : * happen for "config" tables.
2996 : */
2997 17438 : if (tbinfo->dataObj != NULL)
2998 2 : return;
2999 :
3000 : /* Skip VIEWs (no data to dump) */
3001 17436 : if (tbinfo->relkind == RELKIND_VIEW)
3002 1274 : return;
3003 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3004 16162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3005 82 : (foreign_servers_include_oids.head == NULL ||
3006 8 : !simple_oid_list_member(&foreign_servers_include_oids,
3007 : tbinfo->foreign_server)))
3008 80 : return;
3009 : /* Skip partitioned tables (data in partitions) */
3010 16082 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3011 1448 : return;
3012 :
3013 : /* Don't dump data in unlogged tables, if so requested */
3014 14634 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3015 136 : dopt->no_unlogged_table_data)
3016 36 : return;
3017 :
3018 : /* Check that the data is not explicitly excluded */
3019 14598 : if (simple_oid_list_member(&tabledata_exclude_oids,
3020 : tbinfo->dobj.catId.oid))
3021 16 : return;
3022 :
3023 : /* OK, let's dump it */
3024 14582 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3025 :
3026 14582 : if (tbinfo->relkind == RELKIND_MATVIEW)
3027 852 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3028 13730 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3029 1136 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3030 : else
3031 12594 : tdinfo->dobj.objType = DO_TABLE_DATA;
3032 :
3033 : /*
3034 : * Note: use tableoid 0 so that this object won't be mistaken for
3035 : * something that pg_depend entries apply to.
3036 : */
3037 14582 : tdinfo->dobj.catId.tableoid = 0;
3038 14582 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3039 14582 : AssignDumpId(&tdinfo->dobj);
3040 14582 : tdinfo->dobj.name = tbinfo->dobj.name;
3041 14582 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3042 14582 : tdinfo->tdtable = tbinfo;
3043 14582 : tdinfo->filtercond = NULL; /* might get set later */
3044 14582 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3045 :
3046 : /* A TableDataInfo contains data, of course */
3047 14582 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3048 :
3049 14582 : tbinfo->dataObj = tdinfo;
3050 :
3051 : /*
3052 : * Materialized view statistics must be restored after the data, because
3053 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3054 : *
3055 : * The dependency is added here because the statistics objects are created
3056 : * first.
3057 : */
3058 14582 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3059 : {
3060 640 : tbinfo->stats->section = SECTION_POST_DATA;
3061 640 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3062 : }
3063 :
3064 : /* Make sure that we'll collect per-column info for this table. */
3065 14582 : tbinfo->interesting = true;
3066 : }
3067 :
3068 : /*
3069 : * The refresh for a materialized view must be dependent on the refresh for
3070 : * any materialized view that this one is dependent on.
3071 : *
3072 : * This must be called after all the objects are created, but before they are
3073 : * sorted.
3074 : */
3075 : static void
3076 396 : buildMatViewRefreshDependencies(Archive *fout)
3077 : {
3078 : PQExpBuffer query;
3079 : PGresult *res;
3080 : int ntups,
3081 : i;
3082 : int i_classid,
3083 : i_objid,
3084 : i_refobjid;
3085 :
3086 : /* No Mat Views before 9.3. */
3087 396 : if (fout->remoteVersion < 90300)
3088 0 : return;
3089 :
3090 396 : query = createPQExpBuffer();
3091 :
3092 396 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3093 : "( "
3094 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3095 : "FROM pg_depend d1 "
3096 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3097 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3098 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3099 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3100 : "AND d2.objid = r1.oid "
3101 : "AND d2.refobjid <> d1.objid "
3102 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3103 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3104 : CppAsString2(RELKIND_VIEW) ") "
3105 : "WHERE d1.classid = 'pg_class'::regclass "
3106 : "UNION "
3107 : "SELECT w.objid, d3.refobjid, c3.relkind "
3108 : "FROM w "
3109 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3110 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3111 : "AND d3.objid = r3.oid "
3112 : "AND d3.refobjid <> w.refobjid "
3113 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3114 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3115 : CppAsString2(RELKIND_VIEW) ") "
3116 : ") "
3117 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3118 : "FROM w "
3119 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3120 :
3121 396 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3122 :
3123 396 : ntups = PQntuples(res);
3124 :
3125 396 : i_classid = PQfnumber(res, "classid");
3126 396 : i_objid = PQfnumber(res, "objid");
3127 396 : i_refobjid = PQfnumber(res, "refobjid");
3128 :
3129 978 : for (i = 0; i < ntups; i++)
3130 : {
3131 : CatalogId objId;
3132 : CatalogId refobjId;
3133 : DumpableObject *dobj;
3134 : DumpableObject *refdobj;
3135 : TableInfo *tbinfo;
3136 : TableInfo *reftbinfo;
3137 :
3138 582 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3139 582 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3140 582 : refobjId.tableoid = objId.tableoid;
3141 582 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3142 :
3143 582 : dobj = findObjectByCatalogId(objId);
3144 582 : if (dobj == NULL)
3145 96 : continue;
3146 :
3147 : Assert(dobj->objType == DO_TABLE);
3148 582 : tbinfo = (TableInfo *) dobj;
3149 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3150 582 : dobj = (DumpableObject *) tbinfo->dataObj;
3151 582 : if (dobj == NULL)
3152 96 : continue;
3153 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3154 :
3155 486 : refdobj = findObjectByCatalogId(refobjId);
3156 486 : if (refdobj == NULL)
3157 0 : continue;
3158 :
3159 : Assert(refdobj->objType == DO_TABLE);
3160 486 : reftbinfo = (TableInfo *) refdobj;
3161 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3162 486 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3163 486 : if (refdobj == NULL)
3164 0 : continue;
3165 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3166 :
3167 486 : addObjectDependency(dobj, refdobj->dumpId);
3168 :
3169 486 : if (!reftbinfo->relispopulated)
3170 74 : tbinfo->relispopulated = false;
3171 : }
3172 :
3173 396 : PQclear(res);
3174 :
3175 396 : destroyPQExpBuffer(query);
3176 : }
3177 :
3178 : /*
3179 : * getTableDataFKConstraints -
3180 : * add dump-order dependencies reflecting foreign key constraints
3181 : *
3182 : * This code is executed only in a data-only dump --- in schema+data dumps
3183 : * we handle foreign key issues by not creating the FK constraints until
3184 : * after the data is loaded. In a data-only dump, however, we want to
3185 : * order the table data objects in such a way that a table's referenced
3186 : * tables are restored first. (In the presence of circular references or
3187 : * self-references this may be impossible; we'll detect and complain about
3188 : * that during the dependency sorting step.)
3189 : */
3190 : static void
3191 14 : getTableDataFKConstraints(void)
3192 : {
3193 : DumpableObject **dobjs;
3194 : int numObjs;
3195 : int i;
3196 :
3197 : /* Search through all the dumpable objects for FK constraints */
3198 14 : getDumpableObjects(&dobjs, &numObjs);
3199 51434 : for (i = 0; i < numObjs; i++)
3200 : {
3201 51420 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3202 : {
3203 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3204 : TableInfo *ftable;
3205 :
3206 : /* Not interesting unless both tables are to be dumped */
3207 16 : if (cinfo->contable == NULL ||
3208 16 : cinfo->contable->dataObj == NULL)
3209 8 : continue;
3210 8 : ftable = findTableByOid(cinfo->confrelid);
3211 8 : if (ftable == NULL ||
3212 8 : ftable->dataObj == NULL)
3213 0 : continue;
3214 :
3215 : /*
3216 : * Okay, make referencing table's TABLE_DATA object depend on the
3217 : * referenced table's TABLE_DATA object.
3218 : */
3219 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3220 8 : ftable->dataObj->dobj.dumpId);
3221 : }
3222 : }
3223 14 : free(dobjs);
3224 14 : }
3225 :
3226 :
3227 : /*
3228 : * dumpDatabase:
3229 : * dump the database definition
3230 : */
3231 : static void
3232 264 : dumpDatabase(Archive *fout)
3233 : {
3234 264 : DumpOptions *dopt = fout->dopt;
3235 264 : PQExpBuffer dbQry = createPQExpBuffer();
3236 264 : PQExpBuffer delQry = createPQExpBuffer();
3237 264 : PQExpBuffer creaQry = createPQExpBuffer();
3238 264 : PQExpBuffer labelq = createPQExpBuffer();
3239 264 : PGconn *conn = GetConnection(fout);
3240 : PGresult *res;
3241 : int i_tableoid,
3242 : i_oid,
3243 : i_datname,
3244 : i_datdba,
3245 : i_encoding,
3246 : i_datlocprovider,
3247 : i_collate,
3248 : i_ctype,
3249 : i_datlocale,
3250 : i_daticurules,
3251 : i_frozenxid,
3252 : i_minmxid,
3253 : i_datacl,
3254 : i_acldefault,
3255 : i_datistemplate,
3256 : i_datconnlimit,
3257 : i_datcollversion,
3258 : i_tablespace;
3259 : CatalogId dbCatId;
3260 : DumpId dbDumpId;
3261 : DumpableAcl dbdacl;
3262 : const char *datname,
3263 : *dba,
3264 : *encoding,
3265 : *datlocprovider,
3266 : *collate,
3267 : *ctype,
3268 : *locale,
3269 : *icurules,
3270 : *datistemplate,
3271 : *datconnlimit,
3272 : *tablespace;
3273 : uint32 frozenxid,
3274 : minmxid;
3275 : char *qdatname;
3276 :
3277 264 : pg_log_info("saving database definition");
3278 :
3279 : /*
3280 : * Fetch the database-level properties for this database.
3281 : */
3282 264 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3283 : "datdba, "
3284 : "pg_encoding_to_char(encoding) AS encoding, "
3285 : "datcollate, datctype, datfrozenxid, "
3286 : "datacl, acldefault('d', datdba) AS acldefault, "
3287 : "datistemplate, datconnlimit, ");
3288 264 : if (fout->remoteVersion >= 90300)
3289 264 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3290 : else
3291 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3292 264 : if (fout->remoteVersion >= 170000)
3293 264 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3294 0 : else if (fout->remoteVersion >= 150000)
3295 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3296 : else
3297 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3298 264 : if (fout->remoteVersion >= 160000)
3299 264 : appendPQExpBufferStr(dbQry, "daticurules, ");
3300 : else
3301 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3302 264 : appendPQExpBufferStr(dbQry,
3303 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3304 : "shobj_description(oid, 'pg_database') AS description "
3305 : "FROM pg_database "
3306 : "WHERE datname = current_database()");
3307 :
3308 264 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3309 :
3310 264 : i_tableoid = PQfnumber(res, "tableoid");
3311 264 : i_oid = PQfnumber(res, "oid");
3312 264 : i_datname = PQfnumber(res, "datname");
3313 264 : i_datdba = PQfnumber(res, "datdba");
3314 264 : i_encoding = PQfnumber(res, "encoding");
3315 264 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3316 264 : i_collate = PQfnumber(res, "datcollate");
3317 264 : i_ctype = PQfnumber(res, "datctype");
3318 264 : i_datlocale = PQfnumber(res, "datlocale");
3319 264 : i_daticurules = PQfnumber(res, "daticurules");
3320 264 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3321 264 : i_minmxid = PQfnumber(res, "datminmxid");
3322 264 : i_datacl = PQfnumber(res, "datacl");
3323 264 : i_acldefault = PQfnumber(res, "acldefault");
3324 264 : i_datistemplate = PQfnumber(res, "datistemplate");
3325 264 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3326 264 : i_datcollversion = PQfnumber(res, "datcollversion");
3327 264 : i_tablespace = PQfnumber(res, "tablespace");
3328 :
3329 264 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3330 264 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3331 264 : datname = PQgetvalue(res, 0, i_datname);
3332 264 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3333 264 : encoding = PQgetvalue(res, 0, i_encoding);
3334 264 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3335 264 : collate = PQgetvalue(res, 0, i_collate);
3336 264 : ctype = PQgetvalue(res, 0, i_ctype);
3337 264 : if (!PQgetisnull(res, 0, i_datlocale))
3338 30 : locale = PQgetvalue(res, 0, i_datlocale);
3339 : else
3340 234 : locale = NULL;
3341 264 : if (!PQgetisnull(res, 0, i_daticurules))
3342 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3343 : else
3344 264 : icurules = NULL;
3345 264 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3346 264 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3347 264 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3348 264 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3349 264 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3350 264 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3351 264 : tablespace = PQgetvalue(res, 0, i_tablespace);
3352 :
3353 264 : qdatname = pg_strdup(fmtId(datname));
3354 :
3355 : /*
3356 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3357 : * to preserve that), as well as the encoding, locale, and tablespace
3358 : * since those can't be altered later. Other DB properties are left to
3359 : * the DATABASE PROPERTIES entry, so that they can be applied after
3360 : * reconnecting to the target DB.
3361 : *
3362 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3363 : * shown it to be faster. When the server is in binary upgrade mode, it
3364 : * will also skip the checkpoints this strategy ordinarily performs.
3365 : */
3366 264 : if (dopt->binary_upgrade)
3367 : {
3368 60 : appendPQExpBuffer(creaQry,
3369 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3370 : "OID = %u STRATEGY = FILE_COPY",
3371 : qdatname, dbCatId.oid);
3372 : }
3373 : else
3374 : {
3375 204 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3376 : qdatname);
3377 : }
3378 264 : if (strlen(encoding) > 0)
3379 : {
3380 264 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3381 264 : appendStringLiteralAH(creaQry, encoding, fout);
3382 : }
3383 :
3384 264 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3385 264 : if (datlocprovider[0] == 'b')
3386 30 : appendPQExpBufferStr(creaQry, "builtin");
3387 234 : else if (datlocprovider[0] == 'c')
3388 234 : appendPQExpBufferStr(creaQry, "libc");
3389 0 : else if (datlocprovider[0] == 'i')
3390 0 : appendPQExpBufferStr(creaQry, "icu");
3391 : else
3392 0 : pg_fatal("unrecognized locale provider: %s",
3393 : datlocprovider);
3394 :
3395 264 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3396 : {
3397 264 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3398 264 : appendStringLiteralAH(creaQry, collate, fout);
3399 : }
3400 : else
3401 : {
3402 0 : if (strlen(collate) > 0)
3403 : {
3404 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3405 0 : appendStringLiteralAH(creaQry, collate, fout);
3406 : }
3407 0 : if (strlen(ctype) > 0)
3408 : {
3409 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3410 0 : appendStringLiteralAH(creaQry, ctype, fout);
3411 : }
3412 : }
3413 264 : if (locale)
3414 : {
3415 30 : if (datlocprovider[0] == 'b')
3416 30 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3417 : else
3418 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3419 :
3420 30 : appendStringLiteralAH(creaQry, locale, fout);
3421 : }
3422 :
3423 264 : if (icurules)
3424 : {
3425 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3426 0 : appendStringLiteralAH(creaQry, icurules, fout);
3427 : }
3428 :
3429 : /*
3430 : * For binary upgrade, carry over the collation version. For normal
3431 : * dump/restore, omit the version, so that it is computed upon restore.
3432 : */
3433 264 : if (dopt->binary_upgrade)
3434 : {
3435 60 : if (!PQgetisnull(res, 0, i_datcollversion))
3436 : {
3437 60 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3438 60 : appendStringLiteralAH(creaQry,
3439 : PQgetvalue(res, 0, i_datcollversion),
3440 : fout);
3441 : }
3442 : }
3443 :
3444 : /*
3445 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3446 : * thing; the decision whether to specify a tablespace should be left till
3447 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3448 : * label the DATABASE entry with the tablespace and let the normal
3449 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3450 : * attention to default_tablespace, so that won't work.
3451 : */
3452 264 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3453 0 : !dopt->outputNoTablespaces)
3454 0 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3455 : fmtId(tablespace));
3456 264 : appendPQExpBufferStr(creaQry, ";\n");
3457 :
3458 264 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3459 : qdatname);
3460 :
3461 264 : dbDumpId = createDumpId();
3462 :
3463 264 : ArchiveEntry(fout,
3464 : dbCatId, /* catalog ID */
3465 : dbDumpId, /* dump ID */
3466 264 : ARCHIVE_OPTS(.tag = datname,
3467 : .owner = dba,
3468 : .description = "DATABASE",
3469 : .section = SECTION_PRE_DATA,
3470 : .createStmt = creaQry->data,
3471 : .dropStmt = delQry->data));
3472 :
3473 : /* Compute correct tag for archive entry */
3474 264 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3475 :
3476 : /* Dump DB comment if any */
3477 : {
3478 : /*
3479 : * 8.2 and up keep comments on shared objects in a shared table, so we
3480 : * cannot use the dumpComment() code used for other database objects.
3481 : * Be careful that the ArchiveEntry parameters match that function.
3482 : */
3483 264 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3484 :
3485 264 : if (comment && *comment && !dopt->no_comments)
3486 : {
3487 102 : resetPQExpBuffer(dbQry);
3488 :
3489 : /*
3490 : * Generates warning when loaded into a differently-named
3491 : * database.
3492 : */
3493 102 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3494 102 : appendStringLiteralAH(dbQry, comment, fout);
3495 102 : appendPQExpBufferStr(dbQry, ";\n");
3496 :
3497 102 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3498 102 : ARCHIVE_OPTS(.tag = labelq->data,
3499 : .owner = dba,
3500 : .description = "COMMENT",
3501 : .section = SECTION_NONE,
3502 : .createStmt = dbQry->data,
3503 : .deps = &dbDumpId,
3504 : .nDeps = 1));
3505 : }
3506 : }
3507 :
3508 : /* Dump DB security label, if enabled */
3509 264 : if (!dopt->no_security_labels)
3510 : {
3511 : PGresult *shres;
3512 : PQExpBuffer seclabelQry;
3513 :
3514 264 : seclabelQry = createPQExpBuffer();
3515 :
3516 264 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3517 264 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3518 264 : resetPQExpBuffer(seclabelQry);
3519 264 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3520 264 : if (seclabelQry->len > 0)
3521 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3522 0 : ARCHIVE_OPTS(.tag = labelq->data,
3523 : .owner = dba,
3524 : .description = "SECURITY LABEL",
3525 : .section = SECTION_NONE,
3526 : .createStmt = seclabelQry->data,
3527 : .deps = &dbDumpId,
3528 : .nDeps = 1));
3529 264 : destroyPQExpBuffer(seclabelQry);
3530 264 : PQclear(shres);
3531 : }
3532 :
3533 : /*
3534 : * Dump ACL if any. Note that we do not support initial privileges
3535 : * (pg_init_privs) on databases.
3536 : */
3537 264 : dbdacl.privtype = 0;
3538 264 : dbdacl.initprivs = NULL;
3539 :
3540 264 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3541 : qdatname, NULL, NULL,
3542 : NULL, dba, &dbdacl);
3543 :
3544 : /*
3545 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3546 : * non-default database-level properties. (The reason this must be
3547 : * separate is that we cannot put any additional commands into the TOC
3548 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3549 : * in an implicit transaction block, and the backend won't allow CREATE
3550 : * DATABASE in that context.)
3551 : */
3552 264 : resetPQExpBuffer(creaQry);
3553 264 : resetPQExpBuffer(delQry);
3554 :
3555 264 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3556 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3557 : qdatname, datconnlimit);
3558 :
3559 264 : if (strcmp(datistemplate, "t") == 0)
3560 : {
3561 34 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3562 : qdatname);
3563 :
3564 : /*
3565 : * The backend won't accept DROP DATABASE on a template database. We
3566 : * can deal with that by removing the template marking before the DROP
3567 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3568 : * since no such command is currently supported, fake it with a direct
3569 : * UPDATE on pg_database.
3570 : */
3571 34 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3572 : "SET datistemplate = false WHERE datname = ");
3573 34 : appendStringLiteralAH(delQry, datname, fout);
3574 34 : appendPQExpBufferStr(delQry, ";\n");
3575 : }
3576 :
3577 : /*
3578 : * We do not restore pg_database.dathasloginevt because it is set
3579 : * automatically on login event trigger creation.
3580 : */
3581 :
3582 : /* Add database-specific SET options */
3583 264 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3584 :
3585 : /*
3586 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3587 : * entry, too, for lack of a better place.
3588 : */
3589 264 : if (dopt->binary_upgrade)
3590 : {
3591 60 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3592 60 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3593 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3594 : "WHERE datname = ",
3595 : frozenxid, minmxid);
3596 60 : appendStringLiteralAH(creaQry, datname, fout);
3597 60 : appendPQExpBufferStr(creaQry, ";\n");
3598 : }
3599 :
3600 264 : if (creaQry->len > 0)
3601 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3602 84 : ARCHIVE_OPTS(.tag = datname,
3603 : .owner = dba,
3604 : .description = "DATABASE PROPERTIES",
3605 : .section = SECTION_PRE_DATA,
3606 : .createStmt = creaQry->data,
3607 : .dropStmt = delQry->data,
3608 : .deps = &dbDumpId));
3609 :
3610 : /*
3611 : * pg_largeobject comes from the old system intact, so set its
3612 : * relfrozenxids, relminmxids and relfilenode.
3613 : */
3614 264 : if (dopt->binary_upgrade)
3615 : {
3616 : PGresult *lo_res;
3617 60 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3618 60 : PQExpBuffer loOutQry = createPQExpBuffer();
3619 60 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3620 : int ii_relfrozenxid,
3621 : ii_relfilenode,
3622 : ii_oid,
3623 : ii_relminmxid;
3624 :
3625 : /*
3626 : * pg_largeobject
3627 : */
3628 60 : if (fout->remoteVersion >= 90300)
3629 60 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3630 : "FROM pg_catalog.pg_class\n"
3631 : "WHERE oid IN (%u, %u);\n",
3632 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3633 : else
3634 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3635 : "FROM pg_catalog.pg_class\n"
3636 : "WHERE oid IN (%u, %u);\n",
3637 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3638 :
3639 60 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3640 :
3641 60 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3642 60 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3643 60 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3644 60 : ii_oid = PQfnumber(lo_res, "oid");
3645 :
3646 60 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3647 60 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3648 180 : for (int i = 0; i < PQntuples(lo_res); ++i)
3649 : {
3650 : Oid oid;
3651 : RelFileNumber relfilenumber;
3652 :
3653 120 : appendPQExpBuffer(loHorizonQry, "UPDATE pg_catalog.pg_class\n"
3654 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3655 : "WHERE oid = %u;\n",
3656 120 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3657 120 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3658 120 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3659 :
3660 120 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3661 120 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3662 :
3663 120 : if (oid == LargeObjectRelationId)
3664 60 : appendPQExpBuffer(loOutQry,
3665 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3666 : relfilenumber);
3667 60 : else if (oid == LargeObjectLOidPNIndexId)
3668 60 : appendPQExpBuffer(loOutQry,
3669 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3670 : relfilenumber);
3671 : }
3672 :
3673 60 : appendPQExpBufferStr(loOutQry,
3674 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3675 60 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3676 :
3677 60 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3678 60 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3679 : .description = "pg_largeobject",
3680 : .section = SECTION_PRE_DATA,
3681 : .createStmt = loOutQry->data));
3682 :
3683 60 : PQclear(lo_res);
3684 :
3685 60 : destroyPQExpBuffer(loFrozenQry);
3686 60 : destroyPQExpBuffer(loHorizonQry);
3687 60 : destroyPQExpBuffer(loOutQry);
3688 : }
3689 :
3690 264 : PQclear(res);
3691 :
3692 264 : free(qdatname);
3693 264 : destroyPQExpBuffer(dbQry);
3694 264 : destroyPQExpBuffer(delQry);
3695 264 : destroyPQExpBuffer(creaQry);
3696 264 : destroyPQExpBuffer(labelq);
3697 264 : }
3698 :
3699 : /*
3700 : * Collect any database-specific or role-and-database-specific SET options
3701 : * for this database, and append them to outbuf.
3702 : */
3703 : static void
3704 264 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3705 : const char *dbname, Oid dboid)
3706 : {
3707 264 : PGconn *conn = GetConnection(AH);
3708 264 : PQExpBuffer buf = createPQExpBuffer();
3709 : PGresult *res;
3710 :
3711 : /* First collect database-specific options */
3712 264 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3713 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3714 : dboid);
3715 :
3716 264 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3717 :
3718 336 : for (int i = 0; i < PQntuples(res); i++)
3719 72 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3720 : "DATABASE", dbname, NULL, NULL,
3721 : outbuf);
3722 :
3723 264 : PQclear(res);
3724 :
3725 : /* Now look for role-and-database-specific options */
3726 264 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3727 : "FROM pg_db_role_setting s, pg_roles r "
3728 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3729 : dboid);
3730 :
3731 264 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3732 :
3733 264 : for (int i = 0; i < PQntuples(res); i++)
3734 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3735 0 : "ROLE", PQgetvalue(res, i, 0),
3736 : "DATABASE", dbname,
3737 : outbuf);
3738 :
3739 264 : PQclear(res);
3740 :
3741 264 : destroyPQExpBuffer(buf);
3742 264 : }
3743 :
3744 : /*
3745 : * dumpEncoding: put the correct encoding into the archive
3746 : */
3747 : static void
3748 468 : dumpEncoding(Archive *AH)
3749 : {
3750 468 : const char *encname = pg_encoding_to_char(AH->encoding);
3751 468 : PQExpBuffer qry = createPQExpBuffer();
3752 :
3753 468 : pg_log_info("saving encoding = %s", encname);
3754 :
3755 468 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3756 468 : appendStringLiteralAH(qry, encname, AH);
3757 468 : appendPQExpBufferStr(qry, ";\n");
3758 :
3759 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3760 468 : ARCHIVE_OPTS(.tag = "ENCODING",
3761 : .description = "ENCODING",
3762 : .section = SECTION_PRE_DATA,
3763 : .createStmt = qry->data));
3764 :
3765 468 : destroyPQExpBuffer(qry);
3766 468 : }
3767 :
3768 :
3769 : /*
3770 : * dumpStdStrings: put the correct escape string behavior into the archive
3771 : */
3772 : static void
3773 468 : dumpStdStrings(Archive *AH)
3774 : {
3775 468 : const char *stdstrings = AH->std_strings ? "on" : "off";
3776 468 : PQExpBuffer qry = createPQExpBuffer();
3777 :
3778 468 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3779 : stdstrings);
3780 :
3781 468 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3782 : stdstrings);
3783 :
3784 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3785 468 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3786 : .description = "STDSTRINGS",
3787 : .section = SECTION_PRE_DATA,
3788 : .createStmt = qry->data));
3789 :
3790 468 : destroyPQExpBuffer(qry);
3791 468 : }
3792 :
3793 : /*
3794 : * dumpSearchPath: record the active search_path in the archive
3795 : */
3796 : static void
3797 468 : dumpSearchPath(Archive *AH)
3798 : {
3799 468 : PQExpBuffer qry = createPQExpBuffer();
3800 468 : PQExpBuffer path = createPQExpBuffer();
3801 : PGresult *res;
3802 468 : char **schemanames = NULL;
3803 468 : int nschemanames = 0;
3804 : int i;
3805 :
3806 : /*
3807 : * We use the result of current_schemas(), not the search_path GUC,
3808 : * because that might contain wildcards such as "$user", which won't
3809 : * necessarily have the same value during restore. Also, this way avoids
3810 : * listing schemas that may appear in search_path but not actually exist,
3811 : * which seems like a prudent exclusion.
3812 : */
3813 468 : res = ExecuteSqlQueryForSingleRow(AH,
3814 : "SELECT pg_catalog.current_schemas(false)");
3815 :
3816 468 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3817 0 : pg_fatal("could not parse result of current_schemas()");
3818 :
3819 : /*
3820 : * We use set_config(), not a simple "SET search_path" command, because
3821 : * the latter has less-clean behavior if the search path is empty. While
3822 : * that's likely to get fixed at some point, it seems like a good idea to
3823 : * be as backwards-compatible as possible in what we put into archives.
3824 : */
3825 468 : for (i = 0; i < nschemanames; i++)
3826 : {
3827 0 : if (i > 0)
3828 0 : appendPQExpBufferStr(path, ", ");
3829 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3830 : }
3831 :
3832 468 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3833 468 : appendStringLiteralAH(qry, path->data, AH);
3834 468 : appendPQExpBufferStr(qry, ", false);\n");
3835 :
3836 468 : pg_log_info("saving \"search_path = %s\"", path->data);
3837 :
3838 468 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3839 468 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3840 : .description = "SEARCHPATH",
3841 : .section = SECTION_PRE_DATA,
3842 : .createStmt = qry->data));
3843 :
3844 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3845 468 : AH->searchpath = pg_strdup(qry->data);
3846 :
3847 468 : free(schemanames);
3848 468 : PQclear(res);
3849 468 : destroyPQExpBuffer(qry);
3850 468 : destroyPQExpBuffer(path);
3851 468 : }
3852 :
3853 :
3854 : /*
3855 : * getLOs:
3856 : * Collect schema-level data about large objects
3857 : */
3858 : static void
3859 410 : getLOs(Archive *fout)
3860 : {
3861 410 : DumpOptions *dopt = fout->dopt;
3862 410 : PQExpBuffer loQry = createPQExpBuffer();
3863 : PGresult *res;
3864 : int ntups;
3865 : int i;
3866 : int n;
3867 : int i_oid;
3868 : int i_lomowner;
3869 : int i_lomacl;
3870 : int i_acldefault;
3871 :
3872 410 : pg_log_info("reading large objects");
3873 :
3874 : /*
3875 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3876 : * with the same owner/ACL appear together.
3877 : */
3878 410 : appendPQExpBufferStr(loQry,
3879 : "SELECT oid, lomowner, lomacl, "
3880 : "acldefault('L', lomowner) AS acldefault "
3881 : "FROM pg_largeobject_metadata "
3882 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3883 :
3884 410 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3885 :
3886 410 : i_oid = PQfnumber(res, "oid");
3887 410 : i_lomowner = PQfnumber(res, "lomowner");
3888 410 : i_lomacl = PQfnumber(res, "lomacl");
3889 410 : i_acldefault = PQfnumber(res, "acldefault");
3890 :
3891 410 : ntups = PQntuples(res);
3892 :
3893 : /*
3894 : * Group the blobs into suitably-sized groups that have the same owner and
3895 : * ACL setting, and build a metadata and a data DumpableObject for each
3896 : * group. (If we supported initprivs for blobs, we'd have to insist that
3897 : * groups also share initprivs settings, since the DumpableObject only has
3898 : * room for one.) i is the index of the first tuple in the current group,
3899 : * and n is the number of tuples we include in the group.
3900 : */
3901 574 : for (i = 0; i < ntups; i += n)
3902 : {
3903 164 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3904 164 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3905 164 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3906 : LoInfo *loinfo;
3907 : DumpableObject *lodata;
3908 : char namebuf[64];
3909 :
3910 : /* Scan to find first tuple not to be included in group */
3911 164 : n = 1;
3912 196 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3913 : {
3914 106 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3915 106 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3916 : break;
3917 32 : n++;
3918 : }
3919 :
3920 : /* Build the metadata DumpableObject */
3921 164 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3922 :
3923 164 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3924 164 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3925 164 : loinfo->dobj.catId.oid = thisoid;
3926 164 : AssignDumpId(&loinfo->dobj);
3927 :
3928 164 : if (n > 1)
3929 16 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
3930 16 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
3931 : else
3932 148 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
3933 164 : loinfo->dobj.name = pg_strdup(namebuf);
3934 164 : loinfo->dacl.acl = pg_strdup(thisacl);
3935 164 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
3936 164 : loinfo->dacl.privtype = 0;
3937 164 : loinfo->dacl.initprivs = NULL;
3938 164 : loinfo->rolname = getRoleName(thisowner);
3939 164 : loinfo->numlos = n;
3940 164 : loinfo->looids[0] = thisoid;
3941 : /* Collect OIDs of the remaining blobs in this group */
3942 196 : for (int k = 1; k < n; k++)
3943 : {
3944 : CatalogId extraID;
3945 :
3946 32 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
3947 :
3948 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
3949 32 : extraID.tableoid = LargeObjectRelationId;
3950 32 : extraID.oid = loinfo->looids[k];
3951 32 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
3952 : }
3953 :
3954 : /* LOs have data */
3955 164 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
3956 :
3957 : /* Mark whether LO group has a non-empty ACL */
3958 164 : if (!PQgetisnull(res, i, i_lomacl))
3959 74 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
3960 :
3961 : /*
3962 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
3963 : * as it will be copied by pg_upgrade, which simply copies the
3964 : * pg_largeobject table. We *do* however dump out anything but the
3965 : * data, as pg_upgrade copies just pg_largeobject, but not
3966 : * pg_largeobject_metadata, after the dump is restored. In versions
3967 : * before v12, this is done via proper large object commands. In
3968 : * newer versions, we dump the content of pg_largeobject_metadata and
3969 : * any associated pg_shdepend rows, which is faster to restore. (On
3970 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
3971 : * column is hidden and won't be dumped.)
3972 : */
3973 164 : if (dopt->binary_upgrade)
3974 : {
3975 6 : if (fout->remoteVersion >= 120000)
3976 : {
3977 : /*
3978 : * We should've saved pg_largeobject_metadata's dump ID before
3979 : * this point.
3980 : */
3981 : Assert(lo_metadata_dumpId);
3982 :
3983 6 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
3984 :
3985 : /*
3986 : * Mark the large object as dependent on
3987 : * pg_largeobject_metadata so that any large object
3988 : * comments/seclables are dumped after it.
3989 : */
3990 6 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
3991 6 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
3992 6 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
3993 : }
3994 : else
3995 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
3996 : }
3997 :
3998 : /*
3999 : * Create a "BLOBS" data item for the group, too. This is just a
4000 : * placeholder for sorting; it carries no data now.
4001 : */
4002 164 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4003 164 : lodata->objType = DO_LARGE_OBJECT_DATA;
4004 164 : lodata->catId = nilCatalogId;
4005 164 : AssignDumpId(lodata);
4006 164 : lodata->name = pg_strdup(namebuf);
4007 164 : lodata->components |= DUMP_COMPONENT_DATA;
4008 : /* Set up explicit dependency from data to metadata */
4009 164 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4010 164 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4011 164 : lodata->nDeps = lodata->allocDeps = 1;
4012 : }
4013 :
4014 410 : PQclear(res);
4015 410 : destroyPQExpBuffer(loQry);
4016 410 : }
4017 :
4018 : /*
4019 : * dumpLO
4020 : *
4021 : * dump the definition (metadata) of the given large object group
4022 : */
4023 : static void
4024 162 : dumpLO(Archive *fout, const LoInfo *loinfo)
4025 : {
4026 162 : PQExpBuffer cquery = createPQExpBuffer();
4027 :
4028 : /*
4029 : * The "definition" is just a newline-separated list of OIDs. We need to
4030 : * put something into the dropStmt too, but it can just be a comment.
4031 : */
4032 356 : for (int i = 0; i < loinfo->numlos; i++)
4033 194 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4034 :
4035 162 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4036 158 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4037 158 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4038 : .owner = loinfo->rolname,
4039 : .description = "BLOB METADATA",
4040 : .section = SECTION_DATA,
4041 : .createStmt = cquery->data,
4042 : .dropStmt = "-- dummy"));
4043 :
4044 : /*
4045 : * Dump per-blob comments and seclabels if any. We assume these are rare
4046 : * enough that it's okay to generate retail TOC entries for them.
4047 : */
4048 162 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4049 : DUMP_COMPONENT_SECLABEL))
4050 : {
4051 212 : for (int i = 0; i < loinfo->numlos; i++)
4052 : {
4053 : CatalogId catId;
4054 : char namebuf[32];
4055 :
4056 : /* Build identifying info for this blob */
4057 122 : catId.tableoid = loinfo->dobj.catId.tableoid;
4058 122 : catId.oid = loinfo->looids[i];
4059 122 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4060 :
4061 122 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4062 122 : dumpComment(fout, "LARGE OBJECT", namebuf,
4063 122 : NULL, loinfo->rolname,
4064 122 : catId, 0, loinfo->dobj.dumpId);
4065 :
4066 122 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4067 0 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4068 0 : NULL, loinfo->rolname,
4069 0 : catId, 0, loinfo->dobj.dumpId);
4070 : }
4071 : }
4072 :
4073 : /*
4074 : * Dump the ACLs if any (remember that all blobs in the group will have
4075 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4076 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4077 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4078 : * string to emit a mutated version for each blob.
4079 : */
4080 162 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4081 : {
4082 : char namebuf[32];
4083 :
4084 : /* Build identifying info for the first blob */
4085 72 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4086 :
4087 72 : if (loinfo->numlos > 1)
4088 : {
4089 : char tagbuf[64];
4090 :
4091 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4092 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4093 :
4094 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4095 : "LARGE OBJECT", namebuf, NULL, NULL,
4096 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4097 : }
4098 : else
4099 : {
4100 72 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4101 : "LARGE OBJECT", namebuf, NULL, NULL,
4102 72 : NULL, loinfo->rolname, &loinfo->dacl);
4103 : }
4104 : }
4105 :
4106 162 : destroyPQExpBuffer(cquery);
4107 162 : }
4108 :
4109 : /*
4110 : * dumpLOs:
4111 : * dump the data contents of the large objects in the given group
4112 : */
4113 : static int
4114 150 : dumpLOs(Archive *fout, const void *arg)
4115 : {
4116 150 : const LoInfo *loinfo = (const LoInfo *) arg;
4117 150 : PGconn *conn = GetConnection(fout);
4118 : char buf[LOBBUFSIZE];
4119 :
4120 150 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4121 :
4122 328 : for (int i = 0; i < loinfo->numlos; i++)
4123 : {
4124 178 : Oid loOid = loinfo->looids[i];
4125 : int loFd;
4126 : int cnt;
4127 :
4128 : /* Open the LO */
4129 178 : loFd = lo_open(conn, loOid, INV_READ);
4130 178 : if (loFd == -1)
4131 0 : pg_fatal("could not open large object %u: %s",
4132 : loOid, PQerrorMessage(conn));
4133 :
4134 178 : StartLO(fout, loOid);
4135 :
4136 : /* Now read it in chunks, sending data to archive */
4137 : do
4138 : {
4139 274 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4140 274 : if (cnt < 0)
4141 0 : pg_fatal("error reading large object %u: %s",
4142 : loOid, PQerrorMessage(conn));
4143 :
4144 274 : WriteData(fout, buf, cnt);
4145 274 : } while (cnt > 0);
4146 :
4147 178 : lo_close(conn, loFd);
4148 :
4149 178 : EndLO(fout, loOid);
4150 : }
4151 :
4152 150 : return 1;
4153 : }
4154 :
4155 : /*
4156 : * getPolicies
4157 : * get information about all RLS policies on dumpable tables.
4158 : */
4159 : void
4160 468 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4161 : {
4162 468 : DumpOptions *dopt = fout->dopt;
4163 : PQExpBuffer query;
4164 : PQExpBuffer tbloids;
4165 : PGresult *res;
4166 : PolicyInfo *polinfo;
4167 : int i_oid;
4168 : int i_tableoid;
4169 : int i_polrelid;
4170 : int i_polname;
4171 : int i_polcmd;
4172 : int i_polpermissive;
4173 : int i_polroles;
4174 : int i_polqual;
4175 : int i_polwithcheck;
4176 : int i,
4177 : j,
4178 : ntups;
4179 :
4180 : /* No policies before 9.5 */
4181 468 : if (fout->remoteVersion < 90500)
4182 0 : return;
4183 :
4184 : /* Skip if --no-policies was specified */
4185 468 : if (dopt->no_policies)
4186 2 : return;
4187 :
4188 466 : query = createPQExpBuffer();
4189 466 : tbloids = createPQExpBuffer();
4190 :
4191 : /*
4192 : * Identify tables of interest, and check which ones have RLS enabled.
4193 : */
4194 466 : appendPQExpBufferChar(tbloids, '{');
4195 123720 : for (i = 0; i < numTables; i++)
4196 : {
4197 123254 : TableInfo *tbinfo = &tblinfo[i];
4198 :
4199 : /* Ignore row security on tables not to be dumped */
4200 123254 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4201 103984 : continue;
4202 :
4203 : /* It can't have RLS or policies if it's not a table */
4204 19270 : if (tbinfo->relkind != RELKIND_RELATION &&
4205 5206 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4206 3560 : continue;
4207 :
4208 : /* Add it to the list of table OIDs to be probed below */
4209 15710 : if (tbloids->len > 1) /* do we have more than the '{'? */
4210 15398 : appendPQExpBufferChar(tbloids, ',');
4211 15710 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4212 :
4213 : /* Is RLS enabled? (That's separate from whether it has policies) */
4214 15710 : if (tbinfo->rowsec)
4215 : {
4216 132 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4217 :
4218 : /*
4219 : * We represent RLS being enabled on a table by creating a
4220 : * PolicyInfo object with null polname.
4221 : *
4222 : * Note: use tableoid 0 so that this object won't be mistaken for
4223 : * something that pg_depend entries apply to.
4224 : */
4225 132 : polinfo = pg_malloc(sizeof(PolicyInfo));
4226 132 : polinfo->dobj.objType = DO_POLICY;
4227 132 : polinfo->dobj.catId.tableoid = 0;
4228 132 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4229 132 : AssignDumpId(&polinfo->dobj);
4230 132 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4231 132 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4232 132 : polinfo->poltable = tbinfo;
4233 132 : polinfo->polname = NULL;
4234 132 : polinfo->polcmd = '\0';
4235 132 : polinfo->polpermissive = 0;
4236 132 : polinfo->polroles = NULL;
4237 132 : polinfo->polqual = NULL;
4238 132 : polinfo->polwithcheck = NULL;
4239 : }
4240 : }
4241 466 : appendPQExpBufferChar(tbloids, '}');
4242 :
4243 : /*
4244 : * Now, read all RLS policies belonging to the tables of interest, and
4245 : * create PolicyInfo objects for them. (Note that we must filter the
4246 : * results server-side not locally, because we dare not apply pg_get_expr
4247 : * to tables we don't have lock on.)
4248 : */
4249 466 : pg_log_info("reading row-level security policies");
4250 :
4251 466 : printfPQExpBuffer(query,
4252 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4253 466 : if (fout->remoteVersion >= 100000)
4254 466 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4255 : else
4256 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4257 466 : appendPQExpBuffer(query,
4258 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4259 : " 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, "
4260 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4261 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4262 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4263 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4264 : tbloids->data);
4265 :
4266 466 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4267 :
4268 466 : ntups = PQntuples(res);
4269 466 : if (ntups > 0)
4270 : {
4271 100 : i_oid = PQfnumber(res, "oid");
4272 100 : i_tableoid = PQfnumber(res, "tableoid");
4273 100 : i_polrelid = PQfnumber(res, "polrelid");
4274 100 : i_polname = PQfnumber(res, "polname");
4275 100 : i_polcmd = PQfnumber(res, "polcmd");
4276 100 : i_polpermissive = PQfnumber(res, "polpermissive");
4277 100 : i_polroles = PQfnumber(res, "polroles");
4278 100 : i_polqual = PQfnumber(res, "polqual");
4279 100 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4280 :
4281 100 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4282 :
4283 748 : for (j = 0; j < ntups; j++)
4284 : {
4285 648 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4286 648 : TableInfo *tbinfo = findTableByOid(polrelid);
4287 :
4288 648 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4289 :
4290 648 : polinfo[j].dobj.objType = DO_POLICY;
4291 648 : polinfo[j].dobj.catId.tableoid =
4292 648 : atooid(PQgetvalue(res, j, i_tableoid));
4293 648 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4294 648 : AssignDumpId(&polinfo[j].dobj);
4295 648 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4296 648 : polinfo[j].poltable = tbinfo;
4297 648 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4298 648 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4299 :
4300 648 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4301 648 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4302 :
4303 648 : if (PQgetisnull(res, j, i_polroles))
4304 312 : polinfo[j].polroles = NULL;
4305 : else
4306 336 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4307 :
4308 648 : if (PQgetisnull(res, j, i_polqual))
4309 84 : polinfo[j].polqual = NULL;
4310 : else
4311 564 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4312 :
4313 648 : if (PQgetisnull(res, j, i_polwithcheck))
4314 348 : polinfo[j].polwithcheck = NULL;
4315 : else
4316 300 : polinfo[j].polwithcheck
4317 300 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4318 : }
4319 : }
4320 :
4321 466 : PQclear(res);
4322 :
4323 466 : destroyPQExpBuffer(query);
4324 466 : destroyPQExpBuffer(tbloids);
4325 : }
4326 :
4327 : /*
4328 : * dumpPolicy
4329 : * dump the definition of the given policy
4330 : */
4331 : static void
4332 780 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4333 : {
4334 780 : DumpOptions *dopt = fout->dopt;
4335 780 : TableInfo *tbinfo = polinfo->poltable;
4336 : PQExpBuffer query;
4337 : PQExpBuffer delqry;
4338 : PQExpBuffer polprefix;
4339 : char *qtabname;
4340 : const char *cmd;
4341 : char *tag;
4342 :
4343 : /* Do nothing if not dumping schema */
4344 780 : if (!dopt->dumpSchema)
4345 98 : return;
4346 :
4347 : /*
4348 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4349 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4350 : * ROW LEVEL SECURITY.
4351 : */
4352 682 : if (polinfo->polname == NULL)
4353 : {
4354 118 : query = createPQExpBuffer();
4355 :
4356 118 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4357 118 : fmtQualifiedDumpable(tbinfo));
4358 :
4359 : /*
4360 : * We must emit the ROW SECURITY object's dependency on its table
4361 : * explicitly, because it will not match anything in pg_depend (unlike
4362 : * the case for other PolicyInfo objects).
4363 : */
4364 118 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4365 118 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4366 118 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4367 : .namespace = polinfo->dobj.namespace->dobj.name,
4368 : .owner = tbinfo->rolname,
4369 : .description = "ROW SECURITY",
4370 : .section = SECTION_POST_DATA,
4371 : .createStmt = query->data,
4372 : .deps = &(tbinfo->dobj.dumpId),
4373 : .nDeps = 1));
4374 :
4375 118 : destroyPQExpBuffer(query);
4376 118 : return;
4377 : }
4378 :
4379 564 : if (polinfo->polcmd == '*')
4380 188 : cmd = "";
4381 376 : else if (polinfo->polcmd == 'r')
4382 102 : cmd = " FOR SELECT";
4383 274 : else if (polinfo->polcmd == 'a')
4384 70 : cmd = " FOR INSERT";
4385 204 : else if (polinfo->polcmd == 'w')
4386 102 : cmd = " FOR UPDATE";
4387 102 : else if (polinfo->polcmd == 'd')
4388 102 : cmd = " FOR DELETE";
4389 : else
4390 0 : pg_fatal("unexpected policy command type: %c",
4391 : polinfo->polcmd);
4392 :
4393 564 : query = createPQExpBuffer();
4394 564 : delqry = createPQExpBuffer();
4395 564 : polprefix = createPQExpBuffer();
4396 :
4397 564 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4398 :
4399 564 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4400 :
4401 564 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4402 564 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4403 :
4404 564 : if (polinfo->polroles != NULL)
4405 280 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4406 :
4407 564 : if (polinfo->polqual != NULL)
4408 494 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4409 :
4410 564 : if (polinfo->polwithcheck != NULL)
4411 258 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4412 :
4413 564 : appendPQExpBufferStr(query, ";\n");
4414 :
4415 564 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4416 564 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4417 :
4418 564 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4419 564 : fmtId(polinfo->polname));
4420 :
4421 564 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4422 :
4423 564 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4424 564 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4425 564 : ARCHIVE_OPTS(.tag = tag,
4426 : .namespace = polinfo->dobj.namespace->dobj.name,
4427 : .owner = tbinfo->rolname,
4428 : .description = "POLICY",
4429 : .section = SECTION_POST_DATA,
4430 : .createStmt = query->data,
4431 : .dropStmt = delqry->data));
4432 :
4433 564 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4434 0 : dumpComment(fout, polprefix->data, qtabname,
4435 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4436 0 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4437 :
4438 564 : free(tag);
4439 564 : destroyPQExpBuffer(query);
4440 564 : destroyPQExpBuffer(delqry);
4441 564 : destroyPQExpBuffer(polprefix);
4442 564 : free(qtabname);
4443 : }
4444 :
4445 : /*
4446 : * getPublications
4447 : * get information about publications
4448 : */
4449 : void
4450 468 : getPublications(Archive *fout)
4451 : {
4452 468 : DumpOptions *dopt = fout->dopt;
4453 : PQExpBuffer query;
4454 : PGresult *res;
4455 : PublicationInfo *pubinfo;
4456 : int i_tableoid;
4457 : int i_oid;
4458 : int i_pubname;
4459 : int i_pubowner;
4460 : int i_puballtables;
4461 : int i_pubinsert;
4462 : int i_pubupdate;
4463 : int i_pubdelete;
4464 : int i_pubtruncate;
4465 : int i_pubviaroot;
4466 : int i_pubgencols;
4467 : int i,
4468 : ntups;
4469 :
4470 468 : if (dopt->no_publications || fout->remoteVersion < 100000)
4471 0 : return;
4472 :
4473 468 : query = createPQExpBuffer();
4474 :
4475 : /* Get the publications. */
4476 468 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4477 : "p.pubowner, p.puballtables, p.pubinsert, "
4478 : "p.pubupdate, p.pubdelete, ");
4479 :
4480 468 : if (fout->remoteVersion >= 110000)
4481 468 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4482 : else
4483 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4484 :
4485 468 : if (fout->remoteVersion >= 130000)
4486 468 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4487 : else
4488 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4489 :
4490 468 : if (fout->remoteVersion >= 180000)
4491 468 : appendPQExpBufferStr(query, "p.pubgencols ");
4492 : else
4493 0 : appendPQExpBuffer(query, "'%c' AS pubgencols ", PUBLISH_GENCOLS_NONE);
4494 :
4495 468 : appendPQExpBufferStr(query, "FROM pg_publication p");
4496 :
4497 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4498 :
4499 468 : ntups = PQntuples(res);
4500 :
4501 468 : if (ntups == 0)
4502 368 : goto cleanup;
4503 :
4504 100 : i_tableoid = PQfnumber(res, "tableoid");
4505 100 : i_oid = PQfnumber(res, "oid");
4506 100 : i_pubname = PQfnumber(res, "pubname");
4507 100 : i_pubowner = PQfnumber(res, "pubowner");
4508 100 : i_puballtables = PQfnumber(res, "puballtables");
4509 100 : i_pubinsert = PQfnumber(res, "pubinsert");
4510 100 : i_pubupdate = PQfnumber(res, "pubupdate");
4511 100 : i_pubdelete = PQfnumber(res, "pubdelete");
4512 100 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4513 100 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4514 100 : i_pubgencols = PQfnumber(res, "pubgencols");
4515 :
4516 100 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4517 :
4518 592 : for (i = 0; i < ntups; i++)
4519 : {
4520 492 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4521 492 : pubinfo[i].dobj.catId.tableoid =
4522 492 : atooid(PQgetvalue(res, i, i_tableoid));
4523 492 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4524 492 : AssignDumpId(&pubinfo[i].dobj);
4525 492 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4526 492 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4527 492 : pubinfo[i].puballtables =
4528 492 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4529 492 : pubinfo[i].pubinsert =
4530 492 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4531 492 : pubinfo[i].pubupdate =
4532 492 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4533 492 : pubinfo[i].pubdelete =
4534 492 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4535 492 : pubinfo[i].pubtruncate =
4536 492 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4537 492 : pubinfo[i].pubviaroot =
4538 492 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4539 492 : pubinfo[i].pubgencols_type =
4540 492 : *(PQgetvalue(res, i, i_pubgencols));
4541 :
4542 : /* Decide whether we want to dump it */
4543 492 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4544 : }
4545 :
4546 100 : cleanup:
4547 468 : PQclear(res);
4548 :
4549 468 : destroyPQExpBuffer(query);
4550 : }
4551 :
4552 : /*
4553 : * dumpPublication
4554 : * dump the definition of the given publication
4555 : */
4556 : static void
4557 412 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4558 : {
4559 412 : DumpOptions *dopt = fout->dopt;
4560 : PQExpBuffer delq;
4561 : PQExpBuffer query;
4562 : char *qpubname;
4563 412 : bool first = true;
4564 :
4565 : /* Do nothing if not dumping schema */
4566 412 : if (!dopt->dumpSchema)
4567 60 : return;
4568 :
4569 352 : delq = createPQExpBuffer();
4570 352 : query = createPQExpBuffer();
4571 :
4572 352 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4573 :
4574 352 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4575 : qpubname);
4576 :
4577 352 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4578 : qpubname);
4579 :
4580 352 : if (pubinfo->puballtables)
4581 72 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4582 :
4583 352 : appendPQExpBufferStr(query, " WITH (publish = '");
4584 352 : if (pubinfo->pubinsert)
4585 : {
4586 282 : appendPQExpBufferStr(query, "insert");
4587 282 : first = false;
4588 : }
4589 :
4590 352 : if (pubinfo->pubupdate)
4591 : {
4592 282 : if (!first)
4593 282 : appendPQExpBufferStr(query, ", ");
4594 :
4595 282 : appendPQExpBufferStr(query, "update");
4596 282 : first = false;
4597 : }
4598 :
4599 352 : if (pubinfo->pubdelete)
4600 : {
4601 282 : if (!first)
4602 282 : appendPQExpBufferStr(query, ", ");
4603 :
4604 282 : appendPQExpBufferStr(query, "delete");
4605 282 : first = false;
4606 : }
4607 :
4608 352 : if (pubinfo->pubtruncate)
4609 : {
4610 282 : if (!first)
4611 282 : appendPQExpBufferStr(query, ", ");
4612 :
4613 282 : appendPQExpBufferStr(query, "truncate");
4614 282 : first = false;
4615 : }
4616 :
4617 352 : appendPQExpBufferChar(query, '\'');
4618 :
4619 352 : if (pubinfo->pubviaroot)
4620 0 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4621 :
4622 352 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4623 70 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4624 :
4625 352 : appendPQExpBufferStr(query, ");\n");
4626 :
4627 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4628 352 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4629 352 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4630 : .owner = pubinfo->rolname,
4631 : .description = "PUBLICATION",
4632 : .section = SECTION_POST_DATA,
4633 : .createStmt = query->data,
4634 : .dropStmt = delq->data));
4635 :
4636 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4637 70 : dumpComment(fout, "PUBLICATION", qpubname,
4638 70 : NULL, pubinfo->rolname,
4639 70 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4640 :
4641 352 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4642 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4643 0 : NULL, pubinfo->rolname,
4644 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4645 :
4646 352 : destroyPQExpBuffer(delq);
4647 352 : destroyPQExpBuffer(query);
4648 352 : free(qpubname);
4649 : }
4650 :
4651 : /*
4652 : * getPublicationNamespaces
4653 : * get information about publication membership for dumpable schemas.
4654 : */
4655 : void
4656 468 : getPublicationNamespaces(Archive *fout)
4657 : {
4658 : PQExpBuffer query;
4659 : PGresult *res;
4660 : PublicationSchemaInfo *pubsinfo;
4661 468 : DumpOptions *dopt = fout->dopt;
4662 : int i_tableoid;
4663 : int i_oid;
4664 : int i_pnpubid;
4665 : int i_pnnspid;
4666 : int i,
4667 : j,
4668 : ntups;
4669 :
4670 468 : if (dopt->no_publications || fout->remoteVersion < 150000)
4671 0 : return;
4672 :
4673 468 : query = createPQExpBuffer();
4674 :
4675 : /* Collect all publication membership info. */
4676 468 : appendPQExpBufferStr(query,
4677 : "SELECT tableoid, oid, pnpubid, pnnspid "
4678 : "FROM pg_catalog.pg_publication_namespace");
4679 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4680 :
4681 468 : ntups = PQntuples(res);
4682 :
4683 468 : i_tableoid = PQfnumber(res, "tableoid");
4684 468 : i_oid = PQfnumber(res, "oid");
4685 468 : i_pnpubid = PQfnumber(res, "pnpubid");
4686 468 : i_pnnspid = PQfnumber(res, "pnnspid");
4687 :
4688 : /* this allocation may be more than we need */
4689 468 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4690 468 : j = 0;
4691 :
4692 664 : for (i = 0; i < ntups; i++)
4693 : {
4694 196 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4695 196 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4696 : PublicationInfo *pubinfo;
4697 : NamespaceInfo *nspinfo;
4698 :
4699 : /*
4700 : * Ignore any entries for which we aren't interested in either the
4701 : * publication or the rel.
4702 : */
4703 196 : pubinfo = findPublicationByOid(pnpubid);
4704 196 : if (pubinfo == NULL)
4705 0 : continue;
4706 196 : nspinfo = findNamespaceByOid(pnnspid);
4707 196 : if (nspinfo == NULL)
4708 0 : continue;
4709 :
4710 : /* OK, make a DumpableObject for this relationship */
4711 196 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4712 196 : pubsinfo[j].dobj.catId.tableoid =
4713 196 : atooid(PQgetvalue(res, i, i_tableoid));
4714 196 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4715 196 : AssignDumpId(&pubsinfo[j].dobj);
4716 196 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4717 196 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4718 196 : pubsinfo[j].publication = pubinfo;
4719 196 : pubsinfo[j].pubschema = nspinfo;
4720 :
4721 : /* Decide whether we want to dump it */
4722 196 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4723 :
4724 196 : j++;
4725 : }
4726 :
4727 468 : PQclear(res);
4728 468 : destroyPQExpBuffer(query);
4729 : }
4730 :
4731 : /*
4732 : * getPublicationTables
4733 : * get information about publication membership for dumpable tables.
4734 : */
4735 : void
4736 468 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4737 : {
4738 : PQExpBuffer query;
4739 : PGresult *res;
4740 : PublicationRelInfo *pubrinfo;
4741 468 : DumpOptions *dopt = fout->dopt;
4742 : int i_tableoid;
4743 : int i_oid;
4744 : int i_prpubid;
4745 : int i_prrelid;
4746 : int i_prrelqual;
4747 : int i_prattrs;
4748 : int i,
4749 : j,
4750 : ntups;
4751 :
4752 468 : if (dopt->no_publications || fout->remoteVersion < 100000)
4753 0 : return;
4754 :
4755 468 : query = createPQExpBuffer();
4756 :
4757 : /* Collect all publication membership info. */
4758 468 : if (fout->remoteVersion >= 150000)
4759 468 : appendPQExpBufferStr(query,
4760 : "SELECT tableoid, oid, prpubid, prrelid, "
4761 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4762 : "(CASE\n"
4763 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4764 : " (SELECT array_agg(attname)\n"
4765 : " FROM\n"
4766 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4767 : " pg_catalog.pg_attribute\n"
4768 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4769 : " ELSE NULL END) prattrs "
4770 : "FROM pg_catalog.pg_publication_rel pr");
4771 : else
4772 0 : appendPQExpBufferStr(query,
4773 : "SELECT tableoid, oid, prpubid, prrelid, "
4774 : "NULL AS prrelqual, NULL AS prattrs "
4775 : "FROM pg_catalog.pg_publication_rel");
4776 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4777 :
4778 468 : ntups = PQntuples(res);
4779 :
4780 468 : i_tableoid = PQfnumber(res, "tableoid");
4781 468 : i_oid = PQfnumber(res, "oid");
4782 468 : i_prpubid = PQfnumber(res, "prpubid");
4783 468 : i_prrelid = PQfnumber(res, "prrelid");
4784 468 : i_prrelqual = PQfnumber(res, "prrelqual");
4785 468 : i_prattrs = PQfnumber(res, "prattrs");
4786 :
4787 : /* this allocation may be more than we need */
4788 468 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4789 468 : j = 0;
4790 :
4791 1154 : for (i = 0; i < ntups; i++)
4792 : {
4793 686 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4794 686 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4795 : PublicationInfo *pubinfo;
4796 : TableInfo *tbinfo;
4797 :
4798 : /*
4799 : * Ignore any entries for which we aren't interested in either the
4800 : * publication or the rel.
4801 : */
4802 686 : pubinfo = findPublicationByOid(prpubid);
4803 686 : if (pubinfo == NULL)
4804 0 : continue;
4805 686 : tbinfo = findTableByOid(prrelid);
4806 686 : if (tbinfo == NULL)
4807 0 : continue;
4808 :
4809 : /* OK, make a DumpableObject for this relationship */
4810 686 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4811 686 : pubrinfo[j].dobj.catId.tableoid =
4812 686 : atooid(PQgetvalue(res, i, i_tableoid));
4813 686 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4814 686 : AssignDumpId(&pubrinfo[j].dobj);
4815 686 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4816 686 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4817 686 : pubrinfo[j].publication = pubinfo;
4818 686 : pubrinfo[j].pubtable = tbinfo;
4819 686 : if (PQgetisnull(res, i, i_prrelqual))
4820 392 : pubrinfo[j].pubrelqual = NULL;
4821 : else
4822 294 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4823 :
4824 686 : if (!PQgetisnull(res, i, i_prattrs))
4825 : {
4826 : char **attnames;
4827 : int nattnames;
4828 : PQExpBuffer attribs;
4829 :
4830 196 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4831 : &attnames, &nattnames))
4832 0 : pg_fatal("could not parse %s array", "prattrs");
4833 196 : attribs = createPQExpBuffer();
4834 588 : for (int k = 0; k < nattnames; k++)
4835 : {
4836 392 : if (k > 0)
4837 196 : appendPQExpBufferStr(attribs, ", ");
4838 :
4839 392 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4840 : }
4841 196 : pubrinfo[j].pubrattrs = attribs->data;
4842 196 : free(attribs); /* but not attribs->data */
4843 196 : free(attnames);
4844 : }
4845 : else
4846 490 : pubrinfo[j].pubrattrs = NULL;
4847 :
4848 : /* Decide whether we want to dump it */
4849 686 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4850 :
4851 686 : j++;
4852 : }
4853 :
4854 468 : PQclear(res);
4855 468 : destroyPQExpBuffer(query);
4856 : }
4857 :
4858 : /*
4859 : * dumpPublicationNamespace
4860 : * dump the definition of the given publication schema mapping.
4861 : */
4862 : static void
4863 164 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4864 : {
4865 164 : DumpOptions *dopt = fout->dopt;
4866 164 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4867 164 : PublicationInfo *pubinfo = pubsinfo->publication;
4868 : PQExpBuffer query;
4869 : char *tag;
4870 :
4871 : /* Do nothing if not dumping schema */
4872 164 : if (!dopt->dumpSchema)
4873 24 : return;
4874 :
4875 140 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4876 :
4877 140 : query = createPQExpBuffer();
4878 :
4879 140 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4880 140 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4881 :
4882 : /*
4883 : * There is no point in creating drop query as the drop is done by schema
4884 : * drop.
4885 : */
4886 140 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4887 140 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4888 140 : ARCHIVE_OPTS(.tag = tag,
4889 : .namespace = schemainfo->dobj.name,
4890 : .owner = pubinfo->rolname,
4891 : .description = "PUBLICATION TABLES IN SCHEMA",
4892 : .section = SECTION_POST_DATA,
4893 : .createStmt = query->data));
4894 :
4895 : /* These objects can't currently have comments or seclabels */
4896 :
4897 140 : free(tag);
4898 140 : destroyPQExpBuffer(query);
4899 : }
4900 :
4901 : /*
4902 : * dumpPublicationTable
4903 : * dump the definition of the given publication table mapping
4904 : */
4905 : static void
4906 574 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4907 : {
4908 574 : DumpOptions *dopt = fout->dopt;
4909 574 : PublicationInfo *pubinfo = pubrinfo->publication;
4910 574 : TableInfo *tbinfo = pubrinfo->pubtable;
4911 : PQExpBuffer query;
4912 : char *tag;
4913 :
4914 : /* Do nothing if not dumping schema */
4915 574 : if (!dopt->dumpSchema)
4916 84 : return;
4917 :
4918 490 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
4919 :
4920 490 : query = createPQExpBuffer();
4921 :
4922 490 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
4923 490 : fmtId(pubinfo->dobj.name));
4924 490 : appendPQExpBuffer(query, " %s",
4925 490 : fmtQualifiedDumpable(tbinfo));
4926 :
4927 490 : if (pubrinfo->pubrattrs)
4928 140 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
4929 :
4930 490 : if (pubrinfo->pubrelqual)
4931 : {
4932 : /*
4933 : * It's necessary to add parentheses around the expression because
4934 : * pg_get_expr won't supply the parentheses for things like WHERE
4935 : * TRUE.
4936 : */
4937 210 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
4938 : }
4939 490 : appendPQExpBufferStr(query, ";\n");
4940 :
4941 : /*
4942 : * There is no point in creating a drop query as the drop is done by table
4943 : * drop. (If you think to change this, see also _printTocEntry().)
4944 : * Although this object doesn't really have ownership as such, set the
4945 : * owner field anyway to ensure that the command is run by the correct
4946 : * role at restore time.
4947 : */
4948 490 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4949 490 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
4950 490 : ARCHIVE_OPTS(.tag = tag,
4951 : .namespace = tbinfo->dobj.namespace->dobj.name,
4952 : .owner = pubinfo->rolname,
4953 : .description = "PUBLICATION TABLE",
4954 : .section = SECTION_POST_DATA,
4955 : .createStmt = query->data));
4956 :
4957 : /* These objects can't currently have comments or seclabels */
4958 :
4959 490 : free(tag);
4960 490 : destroyPQExpBuffer(query);
4961 : }
4962 :
4963 : /*
4964 : * Is the currently connected user a superuser?
4965 : */
4966 : static bool
4967 468 : is_superuser(Archive *fout)
4968 : {
4969 468 : ArchiveHandle *AH = (ArchiveHandle *) fout;
4970 : const char *val;
4971 :
4972 468 : val = PQparameterStatus(AH->connection, "is_superuser");
4973 :
4974 468 : if (val && strcmp(val, "on") == 0)
4975 462 : return true;
4976 :
4977 6 : return false;
4978 : }
4979 :
4980 : /*
4981 : * Set the given value to restrict_nonsystem_relation_kind value. Since
4982 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
4983 : * the setting query is effective only where available.
4984 : */
4985 : static void
4986 540 : set_restrict_relation_kind(Archive *AH, const char *value)
4987 : {
4988 540 : PQExpBuffer query = createPQExpBuffer();
4989 : PGresult *res;
4990 :
4991 540 : appendPQExpBuffer(query,
4992 : "SELECT set_config(name, '%s', false) "
4993 : "FROM pg_settings "
4994 : "WHERE name = 'restrict_nonsystem_relation_kind'",
4995 : value);
4996 540 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
4997 :
4998 540 : PQclear(res);
4999 540 : destroyPQExpBuffer(query);
5000 540 : }
5001 :
5002 : /*
5003 : * getSubscriptions
5004 : * get information about subscriptions
5005 : */
5006 : void
5007 468 : getSubscriptions(Archive *fout)
5008 : {
5009 468 : DumpOptions *dopt = fout->dopt;
5010 : PQExpBuffer query;
5011 : PGresult *res;
5012 : SubscriptionInfo *subinfo;
5013 : int i_tableoid;
5014 : int i_oid;
5015 : int i_subname;
5016 : int i_subowner;
5017 : int i_subbinary;
5018 : int i_substream;
5019 : int i_subtwophasestate;
5020 : int i_subdisableonerr;
5021 : int i_subpasswordrequired;
5022 : int i_subrunasowner;
5023 : int i_subconninfo;
5024 : int i_subslotname;
5025 : int i_subsynccommit;
5026 : int i_subpublications;
5027 : int i_suborigin;
5028 : int i_suboriginremotelsn;
5029 : int i_subenabled;
5030 : int i_subfailover;
5031 : int i_subretaindeadtuples;
5032 : int i,
5033 : ntups;
5034 :
5035 468 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5036 0 : return;
5037 :
5038 468 : if (!is_superuser(fout))
5039 : {
5040 : int n;
5041 :
5042 6 : res = ExecuteSqlQuery(fout,
5043 : "SELECT count(*) FROM pg_subscription "
5044 : "WHERE subdbid = (SELECT oid FROM pg_database"
5045 : " WHERE datname = current_database())",
5046 : PGRES_TUPLES_OK);
5047 6 : n = atoi(PQgetvalue(res, 0, 0));
5048 6 : if (n > 0)
5049 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5050 6 : PQclear(res);
5051 6 : return;
5052 : }
5053 :
5054 462 : query = createPQExpBuffer();
5055 :
5056 : /* Get the subscriptions in current database. */
5057 462 : appendPQExpBufferStr(query,
5058 : "SELECT s.tableoid, s.oid, s.subname,\n"
5059 : " s.subowner,\n"
5060 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5061 : " s.subpublications,\n");
5062 :
5063 462 : if (fout->remoteVersion >= 140000)
5064 462 : appendPQExpBufferStr(query, " s.subbinary,\n");
5065 : else
5066 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5067 :
5068 462 : if (fout->remoteVersion >= 140000)
5069 462 : appendPQExpBufferStr(query, " s.substream,\n");
5070 : else
5071 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5072 :
5073 462 : if (fout->remoteVersion >= 150000)
5074 462 : appendPQExpBufferStr(query,
5075 : " s.subtwophasestate,\n"
5076 : " s.subdisableonerr,\n");
5077 : else
5078 0 : appendPQExpBuffer(query,
5079 : " '%c' AS subtwophasestate,\n"
5080 : " false AS subdisableonerr,\n",
5081 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5082 :
5083 462 : if (fout->remoteVersion >= 160000)
5084 462 : appendPQExpBufferStr(query,
5085 : " s.subpasswordrequired,\n"
5086 : " s.subrunasowner,\n"
5087 : " s.suborigin,\n");
5088 : else
5089 0 : appendPQExpBuffer(query,
5090 : " 't' AS subpasswordrequired,\n"
5091 : " 't' AS subrunasowner,\n"
5092 : " '%s' AS suborigin,\n",
5093 : LOGICALREP_ORIGIN_ANY);
5094 :
5095 462 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5096 62 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5097 : " s.subenabled,\n");
5098 : else
5099 400 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5100 : " false AS subenabled,\n");
5101 :
5102 462 : if (fout->remoteVersion >= 170000)
5103 462 : appendPQExpBufferStr(query,
5104 : " s.subfailover,\n");
5105 : else
5106 0 : appendPQExpBufferStr(query,
5107 : " false AS subfailover,\n");
5108 :
5109 462 : if (fout->remoteVersion >= 190000)
5110 462 : appendPQExpBufferStr(query,
5111 : " s.subretaindeadtuples\n");
5112 : else
5113 0 : appendPQExpBufferStr(query,
5114 : " false AS subretaindeadtuples\n");
5115 :
5116 462 : appendPQExpBufferStr(query,
5117 : "FROM pg_subscription s\n");
5118 :
5119 462 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5120 62 : appendPQExpBufferStr(query,
5121 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5122 : " ON o.external_id = 'pg_' || s.oid::text \n");
5123 :
5124 462 : appendPQExpBufferStr(query,
5125 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5126 : " WHERE datname = current_database())");
5127 :
5128 462 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5129 :
5130 462 : ntups = PQntuples(res);
5131 :
5132 : /*
5133 : * Get subscription fields. We don't include subskiplsn in the dump as
5134 : * after restoring the dump this value may no longer be relevant.
5135 : */
5136 462 : i_tableoid = PQfnumber(res, "tableoid");
5137 462 : i_oid = PQfnumber(res, "oid");
5138 462 : i_subname = PQfnumber(res, "subname");
5139 462 : i_subowner = PQfnumber(res, "subowner");
5140 462 : i_subenabled = PQfnumber(res, "subenabled");
5141 462 : i_subbinary = PQfnumber(res, "subbinary");
5142 462 : i_substream = PQfnumber(res, "substream");
5143 462 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5144 462 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5145 462 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5146 462 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5147 462 : i_subfailover = PQfnumber(res, "subfailover");
5148 462 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5149 462 : i_subconninfo = PQfnumber(res, "subconninfo");
5150 462 : i_subslotname = PQfnumber(res, "subslotname");
5151 462 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5152 462 : i_subpublications = PQfnumber(res, "subpublications");
5153 462 : i_suborigin = PQfnumber(res, "suborigin");
5154 462 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5155 :
5156 462 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5157 :
5158 748 : for (i = 0; i < ntups; i++)
5159 : {
5160 286 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5161 286 : subinfo[i].dobj.catId.tableoid =
5162 286 : atooid(PQgetvalue(res, i, i_tableoid));
5163 286 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5164 286 : AssignDumpId(&subinfo[i].dobj);
5165 286 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5166 286 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5167 :
5168 286 : subinfo[i].subenabled =
5169 286 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5170 286 : subinfo[i].subbinary =
5171 286 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5172 286 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5173 286 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5174 286 : subinfo[i].subdisableonerr =
5175 286 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5176 286 : subinfo[i].subpasswordrequired =
5177 286 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5178 286 : subinfo[i].subrunasowner =
5179 286 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5180 286 : subinfo[i].subfailover =
5181 286 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5182 286 : subinfo[i].subretaindeadtuples =
5183 286 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5184 572 : subinfo[i].subconninfo =
5185 286 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5186 286 : if (PQgetisnull(res, i, i_subslotname))
5187 0 : subinfo[i].subslotname = NULL;
5188 : else
5189 286 : subinfo[i].subslotname =
5190 286 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5191 572 : subinfo[i].subsynccommit =
5192 286 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5193 572 : subinfo[i].subpublications =
5194 286 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5195 286 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5196 286 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5197 284 : subinfo[i].suboriginremotelsn = NULL;
5198 : else
5199 2 : subinfo[i].suboriginremotelsn =
5200 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5201 :
5202 : /* Decide whether we want to dump it */
5203 286 : selectDumpableObject(&(subinfo[i].dobj), fout);
5204 : }
5205 462 : PQclear(res);
5206 :
5207 462 : destroyPQExpBuffer(query);
5208 : }
5209 :
5210 : /*
5211 : * getSubscriptionTables
5212 : * Get information about subscription membership for dumpable tables. This
5213 : * will be used only in binary-upgrade mode for PG17 or later versions.
5214 : */
5215 : void
5216 468 : getSubscriptionTables(Archive *fout)
5217 : {
5218 468 : DumpOptions *dopt = fout->dopt;
5219 468 : SubscriptionInfo *subinfo = NULL;
5220 : SubRelInfo *subrinfo;
5221 : PGresult *res;
5222 : int i_srsubid;
5223 : int i_srrelid;
5224 : int i_srsubstate;
5225 : int i_srsublsn;
5226 : int ntups;
5227 468 : Oid last_srsubid = InvalidOid;
5228 :
5229 468 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5230 62 : fout->remoteVersion < 170000)
5231 406 : return;
5232 :
5233 62 : res = ExecuteSqlQuery(fout,
5234 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5235 : "FROM pg_catalog.pg_subscription_rel "
5236 : "ORDER BY srsubid",
5237 : PGRES_TUPLES_OK);
5238 62 : ntups = PQntuples(res);
5239 62 : if (ntups == 0)
5240 60 : goto cleanup;
5241 :
5242 : /* Get pg_subscription_rel attributes */
5243 2 : i_srsubid = PQfnumber(res, "srsubid");
5244 2 : i_srrelid = PQfnumber(res, "srrelid");
5245 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5246 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5247 :
5248 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5249 6 : for (int i = 0; i < ntups; i++)
5250 : {
5251 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5252 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5253 : TableInfo *tblinfo;
5254 :
5255 : /*
5256 : * If we switched to a new subscription, check if the subscription
5257 : * exists.
5258 : */
5259 4 : if (cur_srsubid != last_srsubid)
5260 : {
5261 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5262 4 : if (subinfo == NULL)
5263 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5264 :
5265 4 : last_srsubid = cur_srsubid;
5266 : }
5267 :
5268 4 : tblinfo = findTableByOid(relid);
5269 4 : if (tblinfo == NULL)
5270 0 : pg_fatal("failed sanity check, table with OID %u not found",
5271 : relid);
5272 :
5273 : /* OK, make a DumpableObject for this relationship */
5274 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5275 4 : subrinfo[i].dobj.catId.tableoid = relid;
5276 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5277 4 : AssignDumpId(&subrinfo[i].dobj);
5278 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5279 4 : subrinfo[i].tblinfo = tblinfo;
5280 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5281 4 : if (PQgetisnull(res, i, i_srsublsn))
5282 2 : subrinfo[i].srsublsn = NULL;
5283 : else
5284 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5285 :
5286 4 : subrinfo[i].subinfo = subinfo;
5287 :
5288 : /* Decide whether we want to dump it */
5289 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5290 : }
5291 :
5292 2 : cleanup:
5293 62 : PQclear(res);
5294 : }
5295 :
5296 : /*
5297 : * dumpSubscriptionTable
5298 : * Dump the definition of the given subscription table mapping. This will be
5299 : * used only in binary-upgrade mode for PG17 or later versions.
5300 : */
5301 : static void
5302 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5303 : {
5304 4 : DumpOptions *dopt = fout->dopt;
5305 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5306 : PQExpBuffer query;
5307 : char *tag;
5308 :
5309 : /* Do nothing if not dumping schema */
5310 4 : if (!dopt->dumpSchema)
5311 0 : return;
5312 :
5313 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5314 :
5315 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5316 :
5317 4 : query = createPQExpBuffer();
5318 :
5319 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5320 : {
5321 : /*
5322 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5323 : * to pg_subscription_rel table. This will be used only in
5324 : * binary-upgrade mode.
5325 : */
5326 4 : appendPQExpBufferStr(query,
5327 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5328 4 : appendPQExpBufferStr(query,
5329 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5330 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5331 4 : appendPQExpBuffer(query,
5332 : ", %u, '%c'",
5333 4 : subrinfo->tblinfo->dobj.catId.oid,
5334 4 : subrinfo->srsubstate);
5335 :
5336 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5337 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5338 : else
5339 2 : appendPQExpBufferStr(query, ", NULL");
5340 :
5341 4 : appendPQExpBufferStr(query, ");\n");
5342 : }
5343 :
5344 : /*
5345 : * There is no point in creating a drop query as the drop is done by table
5346 : * drop. (If you think to change this, see also _printTocEntry().)
5347 : * Although this object doesn't really have ownership as such, set the
5348 : * owner field anyway to ensure that the command is run by the correct
5349 : * role at restore time.
5350 : */
5351 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5352 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5353 4 : ARCHIVE_OPTS(.tag = tag,
5354 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5355 : .owner = subinfo->rolname,
5356 : .description = "SUBSCRIPTION TABLE",
5357 : .section = SECTION_POST_DATA,
5358 : .createStmt = query->data));
5359 :
5360 : /* These objects can't currently have comments or seclabels */
5361 :
5362 4 : free(tag);
5363 4 : destroyPQExpBuffer(query);
5364 : }
5365 :
5366 : /*
5367 : * dumpSubscription
5368 : * dump the definition of the given subscription
5369 : */
5370 : static void
5371 250 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5372 : {
5373 250 : DumpOptions *dopt = fout->dopt;
5374 : PQExpBuffer delq;
5375 : PQExpBuffer query;
5376 : PQExpBuffer publications;
5377 : char *qsubname;
5378 250 : char **pubnames = NULL;
5379 250 : int npubnames = 0;
5380 : int i;
5381 :
5382 : /* Do nothing if not dumping schema */
5383 250 : if (!dopt->dumpSchema)
5384 36 : return;
5385 :
5386 214 : delq = createPQExpBuffer();
5387 214 : query = createPQExpBuffer();
5388 :
5389 214 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5390 :
5391 214 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5392 : qsubname);
5393 :
5394 214 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5395 : qsubname);
5396 214 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5397 :
5398 : /* Build list of quoted publications and append them to query. */
5399 214 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5400 0 : pg_fatal("could not parse %s array", "subpublications");
5401 :
5402 214 : publications = createPQExpBuffer();
5403 428 : for (i = 0; i < npubnames; i++)
5404 : {
5405 214 : if (i > 0)
5406 0 : appendPQExpBufferStr(publications, ", ");
5407 :
5408 214 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5409 : }
5410 :
5411 214 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5412 214 : if (subinfo->subslotname)
5413 214 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5414 : else
5415 0 : appendPQExpBufferStr(query, "NONE");
5416 :
5417 214 : if (subinfo->subbinary)
5418 0 : appendPQExpBufferStr(query, ", binary = true");
5419 :
5420 214 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5421 70 : appendPQExpBufferStr(query, ", streaming = on");
5422 144 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5423 74 : appendPQExpBufferStr(query, ", streaming = parallel");
5424 : else
5425 70 : appendPQExpBufferStr(query, ", streaming = off");
5426 :
5427 214 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5428 0 : appendPQExpBufferStr(query, ", two_phase = on");
5429 :
5430 214 : if (subinfo->subdisableonerr)
5431 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5432 :
5433 214 : if (!subinfo->subpasswordrequired)
5434 0 : appendPQExpBufferStr(query, ", password_required = false");
5435 :
5436 214 : if (subinfo->subrunasowner)
5437 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5438 :
5439 214 : if (subinfo->subfailover)
5440 2 : appendPQExpBufferStr(query, ", failover = true");
5441 :
5442 214 : if (subinfo->subretaindeadtuples)
5443 2 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5444 :
5445 214 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5446 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5447 :
5448 214 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5449 70 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5450 :
5451 214 : appendPQExpBufferStr(query, ");\n");
5452 :
5453 : /*
5454 : * In binary-upgrade mode, we allow the replication to continue after the
5455 : * upgrade.
5456 : */
5457 214 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5458 : {
5459 10 : if (subinfo->suboriginremotelsn)
5460 : {
5461 : /*
5462 : * Preserve the remote_lsn for the subscriber's replication
5463 : * origin. This value is required to start the replication from
5464 : * the position before the upgrade. This value will be stale if
5465 : * the publisher gets upgraded before the subscriber node.
5466 : * However, this shouldn't be a problem as the upgrade of the
5467 : * publisher ensures that all the transactions were replicated
5468 : * before upgrading it.
5469 : */
5470 2 : appendPQExpBufferStr(query,
5471 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5472 2 : appendPQExpBufferStr(query,
5473 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5474 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5475 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5476 : }
5477 :
5478 10 : if (subinfo->subenabled)
5479 : {
5480 : /*
5481 : * Enable the subscription to allow the replication to continue
5482 : * after the upgrade.
5483 : */
5484 2 : appendPQExpBufferStr(query,
5485 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5486 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5487 : }
5488 : }
5489 :
5490 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5491 214 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5492 214 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5493 : .owner = subinfo->rolname,
5494 : .description = "SUBSCRIPTION",
5495 : .section = SECTION_POST_DATA,
5496 : .createStmt = query->data,
5497 : .dropStmt = delq->data));
5498 :
5499 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5500 70 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5501 70 : NULL, subinfo->rolname,
5502 70 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5503 :
5504 214 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5505 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5506 0 : NULL, subinfo->rolname,
5507 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5508 :
5509 214 : destroyPQExpBuffer(publications);
5510 214 : free(pubnames);
5511 :
5512 214 : destroyPQExpBuffer(delq);
5513 214 : destroyPQExpBuffer(query);
5514 214 : free(qsubname);
5515 : }
5516 :
5517 : /*
5518 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5519 : * the object needs.
5520 : */
5521 : static void
5522 14266 : append_depends_on_extension(Archive *fout,
5523 : PQExpBuffer create,
5524 : const DumpableObject *dobj,
5525 : const char *catalog,
5526 : const char *keyword,
5527 : const char *objname)
5528 : {
5529 14266 : if (dobj->depends_on_ext)
5530 : {
5531 : char *nm;
5532 : PGresult *res;
5533 : PQExpBuffer query;
5534 : int ntups;
5535 : int i_extname;
5536 : int i;
5537 :
5538 : /* dodge fmtId() non-reentrancy */
5539 84 : nm = pg_strdup(objname);
5540 :
5541 84 : query = createPQExpBuffer();
5542 84 : appendPQExpBuffer(query,
5543 : "SELECT e.extname "
5544 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5545 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5546 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5547 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5548 : catalog,
5549 84 : dobj->catId.oid);
5550 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5551 84 : ntups = PQntuples(res);
5552 84 : i_extname = PQfnumber(res, "extname");
5553 168 : for (i = 0; i < ntups; i++)
5554 : {
5555 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5556 : keyword, nm,
5557 84 : fmtId(PQgetvalue(res, i, i_extname)));
5558 : }
5559 :
5560 84 : PQclear(res);
5561 84 : destroyPQExpBuffer(query);
5562 84 : pg_free(nm);
5563 : }
5564 14266 : }
5565 :
5566 : static Oid
5567 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5568 : {
5569 : /*
5570 : * If the old version didn't assign an array type, but the new version
5571 : * does, we must select an unused type OID to assign. This currently only
5572 : * happens for domains, when upgrading pre-v11 to v11 and up.
5573 : *
5574 : * Note: local state here is kind of ugly, but we must have some, since we
5575 : * mustn't choose the same unused OID more than once.
5576 : */
5577 : static Oid next_possible_free_oid = FirstNormalObjectId;
5578 : PGresult *res;
5579 : bool is_dup;
5580 :
5581 : do
5582 : {
5583 0 : ++next_possible_free_oid;
5584 0 : printfPQExpBuffer(upgrade_query,
5585 : "SELECT EXISTS(SELECT 1 "
5586 : "FROM pg_catalog.pg_type "
5587 : "WHERE oid = '%u'::pg_catalog.oid);",
5588 : next_possible_free_oid);
5589 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5590 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5591 0 : PQclear(res);
5592 0 : } while (is_dup);
5593 :
5594 0 : return next_possible_free_oid;
5595 : }
5596 :
5597 : static void
5598 1852 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5599 : PQExpBuffer upgrade_buffer,
5600 : Oid pg_type_oid,
5601 : bool force_array_type,
5602 : bool include_multirange_type)
5603 : {
5604 1852 : PQExpBuffer upgrade_query = createPQExpBuffer();
5605 : PGresult *res;
5606 : Oid pg_type_array_oid;
5607 : Oid pg_type_multirange_oid;
5608 : Oid pg_type_multirange_array_oid;
5609 : TypeInfo *tinfo;
5610 :
5611 1852 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5612 1852 : appendPQExpBuffer(upgrade_buffer,
5613 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5614 : pg_type_oid);
5615 :
5616 1852 : tinfo = findTypeByOid(pg_type_oid);
5617 1852 : if (tinfo)
5618 1852 : pg_type_array_oid = tinfo->typarray;
5619 : else
5620 0 : pg_type_array_oid = InvalidOid;
5621 :
5622 1852 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5623 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5624 :
5625 1852 : if (OidIsValid(pg_type_array_oid))
5626 : {
5627 1848 : appendPQExpBufferStr(upgrade_buffer,
5628 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5629 1848 : appendPQExpBuffer(upgrade_buffer,
5630 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5631 : pg_type_array_oid);
5632 : }
5633 :
5634 : /*
5635 : * Pre-set the multirange type oid and its own array type oid.
5636 : */
5637 1852 : if (include_multirange_type)
5638 : {
5639 16 : if (fout->remoteVersion >= 140000)
5640 : {
5641 16 : printfPQExpBuffer(upgrade_query,
5642 : "SELECT t.oid, t.typarray "
5643 : "FROM pg_catalog.pg_type t "
5644 : "JOIN pg_catalog.pg_range r "
5645 : "ON t.oid = r.rngmultitypid "
5646 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5647 : pg_type_oid);
5648 :
5649 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5650 :
5651 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5652 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5653 :
5654 16 : PQclear(res);
5655 : }
5656 : else
5657 : {
5658 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5659 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5660 : }
5661 :
5662 16 : appendPQExpBufferStr(upgrade_buffer,
5663 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5664 16 : appendPQExpBuffer(upgrade_buffer,
5665 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5666 : pg_type_multirange_oid);
5667 16 : appendPQExpBufferStr(upgrade_buffer,
5668 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5669 16 : appendPQExpBuffer(upgrade_buffer,
5670 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5671 : pg_type_multirange_array_oid);
5672 : }
5673 :
5674 1852 : destroyPQExpBuffer(upgrade_query);
5675 1852 : }
5676 :
5677 : static void
5678 1704 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5679 : PQExpBuffer upgrade_buffer,
5680 : const TableInfo *tbinfo)
5681 : {
5682 1704 : Oid pg_type_oid = tbinfo->reltype;
5683 :
5684 1704 : if (OidIsValid(pg_type_oid))
5685 1704 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5686 : pg_type_oid, false, false);
5687 1704 : }
5688 :
5689 : /*
5690 : * bsearch() comparator for BinaryUpgradeClassOidItem
5691 : */
5692 : static int
5693 24448 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5694 : {
5695 24448 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5696 24448 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5697 :
5698 24448 : return pg_cmp_u32(v1.oid, v2.oid);
5699 : }
5700 :
5701 : /*
5702 : * collectBinaryUpgradeClassOids
5703 : *
5704 : * Construct a table of pg_class information required for
5705 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5706 : * lookup.
5707 : */
5708 : static void
5709 62 : collectBinaryUpgradeClassOids(Archive *fout)
5710 : {
5711 : PGresult *res;
5712 : const char *query;
5713 :
5714 62 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5715 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5716 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5717 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5718 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5719 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5720 : "ORDER BY c.oid;";
5721 :
5722 62 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5723 :
5724 62 : nbinaryUpgradeClassOids = PQntuples(res);
5725 62 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5726 62 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5727 :
5728 29450 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5729 : {
5730 29388 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5731 29388 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5732 29388 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5733 29388 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5734 29388 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5735 29388 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5736 29388 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5737 : }
5738 :
5739 62 : PQclear(res);
5740 62 : }
5741 :
5742 : static void
5743 2476 : binary_upgrade_set_pg_class_oids(Archive *fout,
5744 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5745 : {
5746 2476 : BinaryUpgradeClassOidItem key = {0};
5747 : BinaryUpgradeClassOidItem *entry;
5748 :
5749 : Assert(binaryUpgradeClassOids);
5750 :
5751 : /*
5752 : * Preserve the OID and relfilenumber of the table, table's index, table's
5753 : * toast table and toast table's index if any.
5754 : *
5755 : * One complexity is that the current table definition might not require
5756 : * the creation of a TOAST table, but the old database might have a TOAST
5757 : * table that was created earlier, before some wide columns were dropped.
5758 : * By setting the TOAST oid we force creation of the TOAST heap and index
5759 : * by the new backend, so we can copy the files during binary upgrade
5760 : * without worrying about this case.
5761 : */
5762 2476 : key.oid = pg_class_oid;
5763 2476 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5764 : sizeof(BinaryUpgradeClassOidItem),
5765 : BinaryUpgradeClassOidItemCmp);
5766 :
5767 2476 : appendPQExpBufferStr(upgrade_buffer,
5768 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5769 :
5770 2476 : if (entry->relkind != RELKIND_INDEX &&
5771 1922 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5772 : {
5773 1872 : appendPQExpBuffer(upgrade_buffer,
5774 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5775 : pg_class_oid);
5776 :
5777 : /*
5778 : * Not every relation has storage. Also, in a pre-v12 database,
5779 : * partitioned tables have a relfilenumber, which should not be
5780 : * preserved when upgrading.
5781 : */
5782 1872 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5783 1550 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5784 1550 : appendPQExpBuffer(upgrade_buffer,
5785 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5786 : entry->relfilenumber);
5787 :
5788 : /*
5789 : * In a pre-v12 database, partitioned tables might be marked as having
5790 : * toast tables, but we should ignore them if so.
5791 : */
5792 1872 : if (OidIsValid(entry->toast_oid) &&
5793 560 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5794 : {
5795 560 : appendPQExpBuffer(upgrade_buffer,
5796 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5797 : entry->toast_oid);
5798 560 : appendPQExpBuffer(upgrade_buffer,
5799 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5800 : entry->toast_relfilenumber);
5801 :
5802 : /* every toast table has an index */
5803 560 : appendPQExpBuffer(upgrade_buffer,
5804 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5805 : entry->toast_index_oid);
5806 560 : appendPQExpBuffer(upgrade_buffer,
5807 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5808 : entry->toast_index_relfilenumber);
5809 : }
5810 : }
5811 : else
5812 : {
5813 : /* Preserve the OID and relfilenumber of the index */
5814 604 : appendPQExpBuffer(upgrade_buffer,
5815 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5816 : pg_class_oid);
5817 604 : appendPQExpBuffer(upgrade_buffer,
5818 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5819 : entry->relfilenumber);
5820 : }
5821 :
5822 2476 : appendPQExpBufferChar(upgrade_buffer, '\n');
5823 2476 : }
5824 :
5825 : /*
5826 : * If the DumpableObject is a member of an extension, add a suitable
5827 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5828 : *
5829 : * For somewhat historical reasons, objname should already be quoted,
5830 : * but not objnamespace (if any).
5831 : */
5832 : static void
5833 2940 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5834 : const DumpableObject *dobj,
5835 : const char *objtype,
5836 : const char *objname,
5837 : const char *objnamespace)
5838 : {
5839 2940 : DumpableObject *extobj = NULL;
5840 : int i;
5841 :
5842 2940 : if (!dobj->ext_member)
5843 2908 : return;
5844 :
5845 : /*
5846 : * Find the parent extension. We could avoid this search if we wanted to
5847 : * add a link field to DumpableObject, but the space costs of that would
5848 : * be considerable. We assume that member objects could only have a
5849 : * direct dependency on their own extension, not any others.
5850 : */
5851 32 : for (i = 0; i < dobj->nDeps; i++)
5852 : {
5853 32 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5854 32 : if (extobj && extobj->objType == DO_EXTENSION)
5855 32 : break;
5856 0 : extobj = NULL;
5857 : }
5858 32 : if (extobj == NULL)
5859 0 : pg_fatal("could not find parent extension for %s %s",
5860 : objtype, objname);
5861 :
5862 32 : appendPQExpBufferStr(upgrade_buffer,
5863 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5864 32 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5865 32 : fmtId(extobj->name),
5866 : objtype);
5867 32 : if (objnamespace && *objnamespace)
5868 26 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5869 32 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5870 : }
5871 :
5872 : /*
5873 : * getNamespaces:
5874 : * get information about all namespaces in the system catalogs
5875 : */
5876 : void
5877 470 : getNamespaces(Archive *fout)
5878 : {
5879 : PGresult *res;
5880 : int ntups;
5881 : int i;
5882 : PQExpBuffer query;
5883 : NamespaceInfo *nsinfo;
5884 : int i_tableoid;
5885 : int i_oid;
5886 : int i_nspname;
5887 : int i_nspowner;
5888 : int i_nspacl;
5889 : int i_acldefault;
5890 :
5891 470 : query = createPQExpBuffer();
5892 :
5893 : /*
5894 : * we fetch all namespaces including system ones, so that every object we
5895 : * read in can be linked to a containing namespace.
5896 : */
5897 470 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5898 : "n.nspowner, "
5899 : "n.nspacl, "
5900 : "acldefault('n', n.nspowner) AS acldefault "
5901 : "FROM pg_namespace n");
5902 :
5903 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5904 :
5905 470 : ntups = PQntuples(res);
5906 :
5907 470 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
5908 :
5909 470 : i_tableoid = PQfnumber(res, "tableoid");
5910 470 : i_oid = PQfnumber(res, "oid");
5911 470 : i_nspname = PQfnumber(res, "nspname");
5912 470 : i_nspowner = PQfnumber(res, "nspowner");
5913 470 : i_nspacl = PQfnumber(res, "nspacl");
5914 470 : i_acldefault = PQfnumber(res, "acldefault");
5915 :
5916 4362 : for (i = 0; i < ntups; i++)
5917 : {
5918 : const char *nspowner;
5919 :
5920 3892 : nsinfo[i].dobj.objType = DO_NAMESPACE;
5921 3892 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
5922 3892 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5923 3892 : AssignDumpId(&nsinfo[i].dobj);
5924 3892 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
5925 3892 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
5926 3892 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
5927 3892 : nsinfo[i].dacl.privtype = 0;
5928 3892 : nsinfo[i].dacl.initprivs = NULL;
5929 3892 : nspowner = PQgetvalue(res, i, i_nspowner);
5930 3892 : nsinfo[i].nspowner = atooid(nspowner);
5931 3892 : nsinfo[i].rolname = getRoleName(nspowner);
5932 :
5933 : /* Decide whether to dump this namespace */
5934 3892 : selectDumpableNamespace(&nsinfo[i], fout);
5935 :
5936 : /* Mark whether namespace has an ACL */
5937 3892 : if (!PQgetisnull(res, i, i_nspacl))
5938 1572 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5939 :
5940 : /*
5941 : * We ignore any pg_init_privs.initprivs entry for the public schema
5942 : * and assume a predetermined default, for several reasons. First,
5943 : * dropping and recreating the schema removes its pg_init_privs entry,
5944 : * but an empty destination database starts with this ACL nonetheless.
5945 : * Second, we support dump/reload of public schema ownership changes.
5946 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
5947 : * initprivs continues to reflect the initial owner. Hence,
5948 : * synthesize the value that nspacl will have after the restore's
5949 : * ALTER SCHEMA OWNER. Third, this makes the destination database
5950 : * match the source's ACL, even if the latter was an initdb-default
5951 : * ACL, which changed in v15. An upgrade pulls in changes to most
5952 : * system object ACLs that the DBA had not customized. We've made the
5953 : * public schema depart from that, because changing its ACL so easily
5954 : * breaks applications.
5955 : */
5956 3892 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
5957 : {
5958 462 : PQExpBuffer aclarray = createPQExpBuffer();
5959 462 : PQExpBuffer aclitem = createPQExpBuffer();
5960 :
5961 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
5962 462 : appendPQExpBufferChar(aclarray, '{');
5963 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5964 462 : appendPQExpBufferStr(aclitem, "=UC/");
5965 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5966 462 : appendPGArray(aclarray, aclitem->data);
5967 462 : resetPQExpBuffer(aclitem);
5968 462 : appendPQExpBufferStr(aclitem, "=U/");
5969 462 : quoteAclUserName(aclitem, nsinfo[i].rolname);
5970 462 : appendPGArray(aclarray, aclitem->data);
5971 462 : appendPQExpBufferChar(aclarray, '}');
5972 :
5973 462 : nsinfo[i].dacl.privtype = 'i';
5974 462 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
5975 462 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
5976 :
5977 462 : destroyPQExpBuffer(aclarray);
5978 462 : destroyPQExpBuffer(aclitem);
5979 : }
5980 : }
5981 :
5982 470 : PQclear(res);
5983 470 : destroyPQExpBuffer(query);
5984 470 : }
5985 :
5986 : /*
5987 : * findNamespace:
5988 : * given a namespace OID, look up the info read by getNamespaces
5989 : */
5990 : static NamespaceInfo *
5991 1478026 : findNamespace(Oid nsoid)
5992 : {
5993 : NamespaceInfo *nsinfo;
5994 :
5995 1478026 : nsinfo = findNamespaceByOid(nsoid);
5996 1478026 : if (nsinfo == NULL)
5997 0 : pg_fatal("schema with OID %u does not exist", nsoid);
5998 1478026 : return nsinfo;
5999 : }
6000 :
6001 : /*
6002 : * getExtensions:
6003 : * read all extensions in the system catalogs and return them in the
6004 : * ExtensionInfo* structure
6005 : *
6006 : * numExtensions is set to the number of extensions read in
6007 : */
6008 : ExtensionInfo *
6009 470 : getExtensions(Archive *fout, int *numExtensions)
6010 : {
6011 470 : DumpOptions *dopt = fout->dopt;
6012 : PGresult *res;
6013 : int ntups;
6014 : int i;
6015 : PQExpBuffer query;
6016 470 : ExtensionInfo *extinfo = NULL;
6017 : int i_tableoid;
6018 : int i_oid;
6019 : int i_extname;
6020 : int i_nspname;
6021 : int i_extrelocatable;
6022 : int i_extversion;
6023 : int i_extconfig;
6024 : int i_extcondition;
6025 :
6026 470 : query = createPQExpBuffer();
6027 :
6028 470 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6029 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6030 : "FROM pg_extension x "
6031 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6032 :
6033 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6034 :
6035 470 : ntups = PQntuples(res);
6036 470 : if (ntups == 0)
6037 0 : goto cleanup;
6038 :
6039 470 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6040 :
6041 470 : i_tableoid = PQfnumber(res, "tableoid");
6042 470 : i_oid = PQfnumber(res, "oid");
6043 470 : i_extname = PQfnumber(res, "extname");
6044 470 : i_nspname = PQfnumber(res, "nspname");
6045 470 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6046 470 : i_extversion = PQfnumber(res, "extversion");
6047 470 : i_extconfig = PQfnumber(res, "extconfig");
6048 470 : i_extcondition = PQfnumber(res, "extcondition");
6049 :
6050 990 : for (i = 0; i < ntups; i++)
6051 : {
6052 520 : extinfo[i].dobj.objType = DO_EXTENSION;
6053 520 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6054 520 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6055 520 : AssignDumpId(&extinfo[i].dobj);
6056 520 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6057 520 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6058 520 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6059 520 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6060 520 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6061 520 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6062 :
6063 : /* Decide whether we want to dump it */
6064 520 : selectDumpableExtension(&(extinfo[i]), dopt);
6065 : }
6066 :
6067 470 : cleanup:
6068 470 : PQclear(res);
6069 470 : destroyPQExpBuffer(query);
6070 :
6071 470 : *numExtensions = ntups;
6072 :
6073 470 : return extinfo;
6074 : }
6075 :
6076 : /*
6077 : * getTypes:
6078 : * get information about all types in the system catalogs
6079 : *
6080 : * NB: this must run after getFuncs() because we assume we can do
6081 : * findFuncByOid().
6082 : */
6083 : void
6084 468 : getTypes(Archive *fout)
6085 : {
6086 : PGresult *res;
6087 : int ntups;
6088 : int i;
6089 468 : PQExpBuffer query = createPQExpBuffer();
6090 : TypeInfo *tyinfo;
6091 : ShellTypeInfo *stinfo;
6092 : int i_tableoid;
6093 : int i_oid;
6094 : int i_typname;
6095 : int i_typnamespace;
6096 : int i_typacl;
6097 : int i_acldefault;
6098 : int i_typowner;
6099 : int i_typelem;
6100 : int i_typrelid;
6101 : int i_typrelkind;
6102 : int i_typtype;
6103 : int i_typisdefined;
6104 : int i_isarray;
6105 : int i_typarray;
6106 :
6107 : /*
6108 : * we include even the built-in types because those may be used as array
6109 : * elements by user-defined types
6110 : *
6111 : * we filter out the built-in types when we dump out the types
6112 : *
6113 : * same approach for undefined (shell) types and array types
6114 : *
6115 : * Note: as of 8.3 we can reliably detect whether a type is an
6116 : * auto-generated array type by checking the element type's typarray.
6117 : * (Before that the test is capable of generating false positives.) We
6118 : * still check for name beginning with '_', though, so as to avoid the
6119 : * cost of the subselect probe for all standard types. This would have to
6120 : * be revisited if the backend ever allows renaming of array types.
6121 : */
6122 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6123 : "typnamespace, typacl, "
6124 : "acldefault('T', typowner) AS acldefault, "
6125 : "typowner, "
6126 : "typelem, typrelid, typarray, "
6127 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6128 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6129 : "typtype, typisdefined, "
6130 : "typname[0] = '_' AND typelem != 0 AND "
6131 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6132 : "FROM pg_type");
6133 :
6134 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6135 :
6136 468 : ntups = PQntuples(res);
6137 :
6138 468 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6139 :
6140 468 : i_tableoid = PQfnumber(res, "tableoid");
6141 468 : i_oid = PQfnumber(res, "oid");
6142 468 : i_typname = PQfnumber(res, "typname");
6143 468 : i_typnamespace = PQfnumber(res, "typnamespace");
6144 468 : i_typacl = PQfnumber(res, "typacl");
6145 468 : i_acldefault = PQfnumber(res, "acldefault");
6146 468 : i_typowner = PQfnumber(res, "typowner");
6147 468 : i_typelem = PQfnumber(res, "typelem");
6148 468 : i_typrelid = PQfnumber(res, "typrelid");
6149 468 : i_typrelkind = PQfnumber(res, "typrelkind");
6150 468 : i_typtype = PQfnumber(res, "typtype");
6151 468 : i_typisdefined = PQfnumber(res, "typisdefined");
6152 468 : i_isarray = PQfnumber(res, "isarray");
6153 468 : i_typarray = PQfnumber(res, "typarray");
6154 :
6155 340362 : for (i = 0; i < ntups; i++)
6156 : {
6157 339894 : tyinfo[i].dobj.objType = DO_TYPE;
6158 339894 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6159 339894 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6160 339894 : AssignDumpId(&tyinfo[i].dobj);
6161 339894 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6162 679788 : tyinfo[i].dobj.namespace =
6163 339894 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6164 339894 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6165 339894 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6166 339894 : tyinfo[i].dacl.privtype = 0;
6167 339894 : tyinfo[i].dacl.initprivs = NULL;
6168 339894 : tyinfo[i].ftypname = NULL; /* may get filled later */
6169 339894 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6170 339894 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6171 339894 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6172 339894 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6173 339894 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6174 339894 : tyinfo[i].shellType = NULL;
6175 :
6176 339894 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6177 339776 : tyinfo[i].isDefined = true;
6178 : else
6179 118 : tyinfo[i].isDefined = false;
6180 :
6181 339894 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6182 163102 : tyinfo[i].isArray = true;
6183 : else
6184 176792 : tyinfo[i].isArray = false;
6185 :
6186 339894 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6187 :
6188 339894 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6189 3124 : tyinfo[i].isMultirange = true;
6190 : else
6191 336770 : tyinfo[i].isMultirange = false;
6192 :
6193 : /* Decide whether we want to dump it */
6194 339894 : selectDumpableType(&tyinfo[i], fout);
6195 :
6196 : /* Mark whether type has an ACL */
6197 339894 : if (!PQgetisnull(res, i, i_typacl))
6198 442 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6199 :
6200 : /*
6201 : * If it's a domain, fetch info about its constraints, if any
6202 : */
6203 339894 : tyinfo[i].nDomChecks = 0;
6204 339894 : tyinfo[i].domChecks = NULL;
6205 339894 : tyinfo[i].notnull = NULL;
6206 339894 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6207 41044 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6208 468 : getDomainConstraints(fout, &(tyinfo[i]));
6209 :
6210 : /*
6211 : * If it's a base type, make a DumpableObject representing a shell
6212 : * definition of the type. We will need to dump that ahead of the I/O
6213 : * functions for the type. Similarly, range types need a shell
6214 : * definition in case they have a canonicalize function.
6215 : *
6216 : * Note: the shell type doesn't have a catId. You might think it
6217 : * should copy the base type's catId, but then it might capture the
6218 : * pg_depend entries for the type, which we don't want.
6219 : */
6220 339894 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6221 41044 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6222 20052 : tyinfo[i].typtype == TYPTYPE_RANGE))
6223 : {
6224 21292 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6225 21292 : stinfo->dobj.objType = DO_SHELL_TYPE;
6226 21292 : stinfo->dobj.catId = nilCatalogId;
6227 21292 : AssignDumpId(&stinfo->dobj);
6228 21292 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6229 21292 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6230 21292 : stinfo->baseType = &(tyinfo[i]);
6231 21292 : tyinfo[i].shellType = stinfo;
6232 :
6233 : /*
6234 : * Initially mark the shell type as not to be dumped. We'll only
6235 : * dump it if the I/O or canonicalize functions need to be dumped;
6236 : * this is taken care of while sorting dependencies.
6237 : */
6238 21292 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6239 : }
6240 : }
6241 :
6242 468 : PQclear(res);
6243 :
6244 468 : destroyPQExpBuffer(query);
6245 468 : }
6246 :
6247 : /*
6248 : * getOperators:
6249 : * get information about all operators in the system catalogs
6250 : */
6251 : void
6252 468 : getOperators(Archive *fout)
6253 : {
6254 : PGresult *res;
6255 : int ntups;
6256 : int i;
6257 468 : PQExpBuffer query = createPQExpBuffer();
6258 : OprInfo *oprinfo;
6259 : int i_tableoid;
6260 : int i_oid;
6261 : int i_oprname;
6262 : int i_oprnamespace;
6263 : int i_oprowner;
6264 : int i_oprkind;
6265 : int i_oprcode;
6266 :
6267 : /*
6268 : * find all operators, including builtin operators; we filter out
6269 : * system-defined operators at dump-out time.
6270 : */
6271 :
6272 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6273 : "oprnamespace, "
6274 : "oprowner, "
6275 : "oprkind, "
6276 : "oprcode::oid AS oprcode "
6277 : "FROM pg_operator");
6278 :
6279 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6280 :
6281 468 : ntups = PQntuples(res);
6282 :
6283 468 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6284 :
6285 468 : i_tableoid = PQfnumber(res, "tableoid");
6286 468 : i_oid = PQfnumber(res, "oid");
6287 468 : i_oprname = PQfnumber(res, "oprname");
6288 468 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6289 468 : i_oprowner = PQfnumber(res, "oprowner");
6290 468 : i_oprkind = PQfnumber(res, "oprkind");
6291 468 : i_oprcode = PQfnumber(res, "oprcode");
6292 :
6293 374774 : for (i = 0; i < ntups; i++)
6294 : {
6295 374306 : oprinfo[i].dobj.objType = DO_OPERATOR;
6296 374306 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6297 374306 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6298 374306 : AssignDumpId(&oprinfo[i].dobj);
6299 374306 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6300 748612 : oprinfo[i].dobj.namespace =
6301 374306 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6302 374306 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6303 374306 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6304 374306 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6305 :
6306 : /* Decide whether we want to dump it */
6307 374306 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6308 : }
6309 :
6310 468 : PQclear(res);
6311 :
6312 468 : destroyPQExpBuffer(query);
6313 468 : }
6314 :
6315 : /*
6316 : * getCollations:
6317 : * get information about all collations in the system catalogs
6318 : */
6319 : void
6320 468 : getCollations(Archive *fout)
6321 : {
6322 : PGresult *res;
6323 : int ntups;
6324 : int i;
6325 : PQExpBuffer query;
6326 : CollInfo *collinfo;
6327 : int i_tableoid;
6328 : int i_oid;
6329 : int i_collname;
6330 : int i_collnamespace;
6331 : int i_collowner;
6332 :
6333 468 : query = createPQExpBuffer();
6334 :
6335 : /*
6336 : * find all collations, including builtin collations; we filter out
6337 : * system-defined collations at dump-out time.
6338 : */
6339 :
6340 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6341 : "collnamespace, "
6342 : "collowner "
6343 : "FROM pg_collation");
6344 :
6345 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6346 :
6347 468 : ntups = PQntuples(res);
6348 :
6349 468 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6350 :
6351 468 : i_tableoid = PQfnumber(res, "tableoid");
6352 468 : i_oid = PQfnumber(res, "oid");
6353 468 : i_collname = PQfnumber(res, "collname");
6354 468 : i_collnamespace = PQfnumber(res, "collnamespace");
6355 468 : i_collowner = PQfnumber(res, "collowner");
6356 :
6357 382612 : for (i = 0; i < ntups; i++)
6358 : {
6359 382144 : collinfo[i].dobj.objType = DO_COLLATION;
6360 382144 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6361 382144 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6362 382144 : AssignDumpId(&collinfo[i].dobj);
6363 382144 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6364 764288 : collinfo[i].dobj.namespace =
6365 382144 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6366 382144 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6367 :
6368 : /* Decide whether we want to dump it */
6369 382144 : selectDumpableObject(&(collinfo[i].dobj), fout);
6370 : }
6371 :
6372 468 : PQclear(res);
6373 :
6374 468 : destroyPQExpBuffer(query);
6375 468 : }
6376 :
6377 : /*
6378 : * getConversions:
6379 : * get information about all conversions in the system catalogs
6380 : */
6381 : void
6382 468 : getConversions(Archive *fout)
6383 : {
6384 : PGresult *res;
6385 : int ntups;
6386 : int i;
6387 : PQExpBuffer query;
6388 : ConvInfo *convinfo;
6389 : int i_tableoid;
6390 : int i_oid;
6391 : int i_conname;
6392 : int i_connamespace;
6393 : int i_conowner;
6394 :
6395 468 : query = createPQExpBuffer();
6396 :
6397 : /*
6398 : * find all conversions, including builtin conversions; we filter out
6399 : * system-defined conversions at dump-out time.
6400 : */
6401 :
6402 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6403 : "connamespace, "
6404 : "conowner "
6405 : "FROM pg_conversion");
6406 :
6407 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6408 :
6409 468 : ntups = PQntuples(res);
6410 :
6411 468 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6412 :
6413 468 : i_tableoid = PQfnumber(res, "tableoid");
6414 468 : i_oid = PQfnumber(res, "oid");
6415 468 : i_conname = PQfnumber(res, "conname");
6416 468 : i_connamespace = PQfnumber(res, "connamespace");
6417 468 : i_conowner = PQfnumber(res, "conowner");
6418 :
6419 60470 : for (i = 0; i < ntups; i++)
6420 : {
6421 60002 : convinfo[i].dobj.objType = DO_CONVERSION;
6422 60002 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6423 60002 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6424 60002 : AssignDumpId(&convinfo[i].dobj);
6425 60002 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6426 120004 : convinfo[i].dobj.namespace =
6427 60002 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6428 60002 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6429 :
6430 : /* Decide whether we want to dump it */
6431 60002 : selectDumpableObject(&(convinfo[i].dobj), fout);
6432 : }
6433 :
6434 468 : PQclear(res);
6435 :
6436 468 : destroyPQExpBuffer(query);
6437 468 : }
6438 :
6439 : /*
6440 : * getAccessMethods:
6441 : * get information about all user-defined access methods
6442 : */
6443 : void
6444 468 : getAccessMethods(Archive *fout)
6445 : {
6446 : PGresult *res;
6447 : int ntups;
6448 : int i;
6449 : PQExpBuffer query;
6450 : AccessMethodInfo *aminfo;
6451 : int i_tableoid;
6452 : int i_oid;
6453 : int i_amname;
6454 : int i_amhandler;
6455 : int i_amtype;
6456 :
6457 : /* Before 9.6, there are no user-defined access methods */
6458 468 : if (fout->remoteVersion < 90600)
6459 0 : return;
6460 :
6461 468 : query = createPQExpBuffer();
6462 :
6463 : /* Select all access methods from pg_am table */
6464 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, amtype, "
6465 : "amhandler::pg_catalog.regproc AS amhandler "
6466 : "FROM pg_am");
6467 :
6468 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6469 :
6470 468 : ntups = PQntuples(res);
6471 :
6472 468 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6473 :
6474 468 : i_tableoid = PQfnumber(res, "tableoid");
6475 468 : i_oid = PQfnumber(res, "oid");
6476 468 : i_amname = PQfnumber(res, "amname");
6477 468 : i_amhandler = PQfnumber(res, "amhandler");
6478 468 : i_amtype = PQfnumber(res, "amtype");
6479 :
6480 4010 : for (i = 0; i < ntups; i++)
6481 : {
6482 3542 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6483 3542 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6484 3542 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6485 3542 : AssignDumpId(&aminfo[i].dobj);
6486 3542 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6487 3542 : aminfo[i].dobj.namespace = NULL;
6488 3542 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6489 3542 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6490 :
6491 : /* Decide whether we want to dump it */
6492 3542 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6493 : }
6494 :
6495 468 : PQclear(res);
6496 :
6497 468 : destroyPQExpBuffer(query);
6498 : }
6499 :
6500 :
6501 : /*
6502 : * getOpclasses:
6503 : * get information about all opclasses in the system catalogs
6504 : */
6505 : void
6506 468 : getOpclasses(Archive *fout)
6507 : {
6508 : PGresult *res;
6509 : int ntups;
6510 : int i;
6511 468 : PQExpBuffer query = createPQExpBuffer();
6512 : OpclassInfo *opcinfo;
6513 : int i_tableoid;
6514 : int i_oid;
6515 : int i_opcname;
6516 : int i_opcnamespace;
6517 : int i_opcowner;
6518 :
6519 : /*
6520 : * find all opclasses, including builtin opclasses; we filter out
6521 : * system-defined opclasses at dump-out time.
6522 : */
6523 :
6524 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcname, "
6525 : "opcnamespace, "
6526 : "opcowner "
6527 : "FROM pg_opclass");
6528 :
6529 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6530 :
6531 468 : ntups = PQntuples(res);
6532 :
6533 468 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6534 :
6535 468 : i_tableoid = PQfnumber(res, "tableoid");
6536 468 : i_oid = PQfnumber(res, "oid");
6537 468 : i_opcname = PQfnumber(res, "opcname");
6538 468 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6539 468 : i_opcowner = PQfnumber(res, "opcowner");
6540 :
6541 83658 : for (i = 0; i < ntups; i++)
6542 : {
6543 83190 : opcinfo[i].dobj.objType = DO_OPCLASS;
6544 83190 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6545 83190 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6546 83190 : AssignDumpId(&opcinfo[i].dobj);
6547 83190 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6548 166380 : opcinfo[i].dobj.namespace =
6549 83190 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6550 83190 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6551 :
6552 : /* Decide whether we want to dump it */
6553 83190 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6554 : }
6555 :
6556 468 : PQclear(res);
6557 :
6558 468 : destroyPQExpBuffer(query);
6559 468 : }
6560 :
6561 : /*
6562 : * getOpfamilies:
6563 : * get information about all opfamilies in the system catalogs
6564 : */
6565 : void
6566 468 : getOpfamilies(Archive *fout)
6567 : {
6568 : PGresult *res;
6569 : int ntups;
6570 : int i;
6571 : PQExpBuffer query;
6572 : OpfamilyInfo *opfinfo;
6573 : int i_tableoid;
6574 : int i_oid;
6575 : int i_opfname;
6576 : int i_opfnamespace;
6577 : int i_opfowner;
6578 :
6579 468 : query = createPQExpBuffer();
6580 :
6581 : /*
6582 : * find all opfamilies, including builtin opfamilies; we filter out
6583 : * system-defined opfamilies at dump-out time.
6584 : */
6585 :
6586 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfname, "
6587 : "opfnamespace, "
6588 : "opfowner "
6589 : "FROM pg_opfamily");
6590 :
6591 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6592 :
6593 468 : ntups = PQntuples(res);
6594 :
6595 468 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6596 :
6597 468 : i_tableoid = PQfnumber(res, "tableoid");
6598 468 : i_oid = PQfnumber(res, "oid");
6599 468 : i_opfname = PQfnumber(res, "opfname");
6600 468 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6601 468 : i_opfowner = PQfnumber(res, "opfowner");
6602 :
6603 69132 : for (i = 0; i < ntups; i++)
6604 : {
6605 68664 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6606 68664 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6607 68664 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6608 68664 : AssignDumpId(&opfinfo[i].dobj);
6609 68664 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6610 137328 : opfinfo[i].dobj.namespace =
6611 68664 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6612 68664 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6613 :
6614 : /* Decide whether we want to dump it */
6615 68664 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6616 : }
6617 :
6618 468 : PQclear(res);
6619 :
6620 468 : destroyPQExpBuffer(query);
6621 468 : }
6622 :
6623 : /*
6624 : * getAggregates:
6625 : * get information about all user-defined aggregates in the system catalogs
6626 : */
6627 : void
6628 468 : getAggregates(Archive *fout)
6629 : {
6630 468 : DumpOptions *dopt = fout->dopt;
6631 : PGresult *res;
6632 : int ntups;
6633 : int i;
6634 468 : PQExpBuffer query = createPQExpBuffer();
6635 : AggInfo *agginfo;
6636 : int i_tableoid;
6637 : int i_oid;
6638 : int i_aggname;
6639 : int i_aggnamespace;
6640 : int i_pronargs;
6641 : int i_proargtypes;
6642 : int i_proowner;
6643 : int i_aggacl;
6644 : int i_acldefault;
6645 :
6646 : /*
6647 : * Find all interesting aggregates. See comment in getFuncs() for the
6648 : * rationale behind the filtering logic.
6649 : */
6650 468 : if (fout->remoteVersion >= 90600)
6651 : {
6652 : const char *agg_check;
6653 :
6654 936 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6655 468 : : "p.proisagg");
6656 :
6657 468 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6658 : "p.proname AS aggname, "
6659 : "p.pronamespace AS aggnamespace, "
6660 : "p.pronargs, p.proargtypes, "
6661 : "p.proowner, "
6662 : "p.proacl AS aggacl, "
6663 : "acldefault('f', p.proowner) AS acldefault "
6664 : "FROM pg_proc p "
6665 : "LEFT JOIN pg_init_privs pip ON "
6666 : "(p.oid = pip.objoid "
6667 : "AND pip.classoid = 'pg_proc'::regclass "
6668 : "AND pip.objsubid = 0) "
6669 : "WHERE %s AND ("
6670 : "p.pronamespace != "
6671 : "(SELECT oid FROM pg_namespace "
6672 : "WHERE nspname = 'pg_catalog') OR "
6673 : "p.proacl IS DISTINCT FROM pip.initprivs",
6674 : agg_check);
6675 468 : if (dopt->binary_upgrade)
6676 62 : appendPQExpBufferStr(query,
6677 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6678 : "classid = 'pg_proc'::regclass AND "
6679 : "objid = p.oid AND "
6680 : "refclassid = 'pg_extension'::regclass AND "
6681 : "deptype = 'e')");
6682 468 : appendPQExpBufferChar(query, ')');
6683 : }
6684 : else
6685 : {
6686 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6687 : "pronamespace AS aggnamespace, "
6688 : "pronargs, proargtypes, "
6689 : "proowner, "
6690 : "proacl AS aggacl, "
6691 : "acldefault('f', proowner) AS acldefault "
6692 : "FROM pg_proc p "
6693 : "WHERE proisagg AND ("
6694 : "pronamespace != "
6695 : "(SELECT oid FROM pg_namespace "
6696 : "WHERE nspname = 'pg_catalog')");
6697 0 : if (dopt->binary_upgrade)
6698 0 : appendPQExpBufferStr(query,
6699 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6700 : "classid = 'pg_proc'::regclass AND "
6701 : "objid = p.oid AND "
6702 : "refclassid = 'pg_extension'::regclass AND "
6703 : "deptype = 'e')");
6704 0 : appendPQExpBufferChar(query, ')');
6705 : }
6706 :
6707 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6708 :
6709 468 : ntups = PQntuples(res);
6710 :
6711 468 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6712 :
6713 468 : i_tableoid = PQfnumber(res, "tableoid");
6714 468 : i_oid = PQfnumber(res, "oid");
6715 468 : i_aggname = PQfnumber(res, "aggname");
6716 468 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6717 468 : i_pronargs = PQfnumber(res, "pronargs");
6718 468 : i_proargtypes = PQfnumber(res, "proargtypes");
6719 468 : i_proowner = PQfnumber(res, "proowner");
6720 468 : i_aggacl = PQfnumber(res, "aggacl");
6721 468 : i_acldefault = PQfnumber(res, "acldefault");
6722 :
6723 1556 : for (i = 0; i < ntups; i++)
6724 : {
6725 1088 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6726 1088 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6727 1088 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6728 1088 : AssignDumpId(&agginfo[i].aggfn.dobj);
6729 1088 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6730 2176 : agginfo[i].aggfn.dobj.namespace =
6731 1088 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6732 1088 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6733 1088 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6734 1088 : agginfo[i].aggfn.dacl.privtype = 0;
6735 1088 : agginfo[i].aggfn.dacl.initprivs = NULL;
6736 1088 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6737 1088 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6738 1088 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6739 1088 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6740 1088 : if (agginfo[i].aggfn.nargs == 0)
6741 160 : agginfo[i].aggfn.argtypes = NULL;
6742 : else
6743 : {
6744 928 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6745 928 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6746 928 : agginfo[i].aggfn.argtypes,
6747 928 : agginfo[i].aggfn.nargs);
6748 : }
6749 1088 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6750 :
6751 : /* Decide whether we want to dump it */
6752 1088 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6753 :
6754 : /* Mark whether aggregate has an ACL */
6755 1088 : if (!PQgetisnull(res, i, i_aggacl))
6756 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6757 : }
6758 :
6759 468 : PQclear(res);
6760 :
6761 468 : destroyPQExpBuffer(query);
6762 468 : }
6763 :
6764 : /*
6765 : * getFuncs:
6766 : * get information about all user-defined functions in the system catalogs
6767 : */
6768 : void
6769 468 : getFuncs(Archive *fout)
6770 : {
6771 468 : DumpOptions *dopt = fout->dopt;
6772 : PGresult *res;
6773 : int ntups;
6774 : int i;
6775 468 : PQExpBuffer query = createPQExpBuffer();
6776 : FuncInfo *finfo;
6777 : int i_tableoid;
6778 : int i_oid;
6779 : int i_proname;
6780 : int i_pronamespace;
6781 : int i_proowner;
6782 : int i_prolang;
6783 : int i_pronargs;
6784 : int i_proargtypes;
6785 : int i_prorettype;
6786 : int i_proacl;
6787 : int i_acldefault;
6788 :
6789 : /*
6790 : * Find all interesting functions. This is a bit complicated:
6791 : *
6792 : * 1. Always exclude aggregates; those are handled elsewhere.
6793 : *
6794 : * 2. Always exclude functions that are internally dependent on something
6795 : * else, since presumably those will be created as a result of creating
6796 : * the something else. This currently acts only to suppress constructor
6797 : * functions for range types. Note this is OK only because the
6798 : * constructors don't have any dependencies the range type doesn't have;
6799 : * otherwise we might not get creation ordering correct.
6800 : *
6801 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6802 : * they're members of extensions and we are in binary-upgrade mode then
6803 : * include them, since we want to dump extension members individually in
6804 : * that mode. Also, if they are used by casts or transforms then we need
6805 : * to gather the information about them, though they won't be dumped if
6806 : * they are built-in. Also, in 9.6 and up, include functions in
6807 : * pg_catalog if they have an ACL different from what's shown in
6808 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6809 : */
6810 468 : if (fout->remoteVersion >= 90600)
6811 : {
6812 : const char *not_agg_check;
6813 :
6814 936 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6815 468 : : "NOT p.proisagg");
6816 :
6817 468 : appendPQExpBuffer(query,
6818 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6819 : "p.pronargs, p.proargtypes, p.prorettype, "
6820 : "p.proacl, "
6821 : "acldefault('f', p.proowner) AS acldefault, "
6822 : "p.pronamespace, "
6823 : "p.proowner "
6824 : "FROM pg_proc p "
6825 : "LEFT JOIN pg_init_privs pip ON "
6826 : "(p.oid = pip.objoid "
6827 : "AND pip.classoid = 'pg_proc'::regclass "
6828 : "AND pip.objsubid = 0) "
6829 : "WHERE %s"
6830 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6831 : "WHERE classid = 'pg_proc'::regclass AND "
6832 : "objid = p.oid AND deptype = 'i')"
6833 : "\n AND ("
6834 : "\n pronamespace != "
6835 : "(SELECT oid FROM pg_namespace "
6836 : "WHERE nspname = 'pg_catalog')"
6837 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6838 : "\n WHERE pg_cast.oid > %u "
6839 : "\n AND p.oid = pg_cast.castfunc)"
6840 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6841 : "\n WHERE pg_transform.oid > %u AND "
6842 : "\n (p.oid = pg_transform.trffromsql"
6843 : "\n OR p.oid = pg_transform.trftosql))",
6844 : not_agg_check,
6845 : g_last_builtin_oid,
6846 : g_last_builtin_oid);
6847 468 : if (dopt->binary_upgrade)
6848 62 : appendPQExpBufferStr(query,
6849 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6850 : "classid = 'pg_proc'::regclass AND "
6851 : "objid = p.oid AND "
6852 : "refclassid = 'pg_extension'::regclass AND "
6853 : "deptype = 'e')");
6854 468 : appendPQExpBufferStr(query,
6855 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6856 468 : appendPQExpBufferChar(query, ')');
6857 : }
6858 : else
6859 : {
6860 0 : appendPQExpBuffer(query,
6861 : "SELECT tableoid, oid, proname, prolang, "
6862 : "pronargs, proargtypes, prorettype, proacl, "
6863 : "acldefault('f', proowner) AS acldefault, "
6864 : "pronamespace, "
6865 : "proowner "
6866 : "FROM pg_proc p "
6867 : "WHERE NOT proisagg"
6868 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6869 : "WHERE classid = 'pg_proc'::regclass AND "
6870 : "objid = p.oid AND deptype = 'i')"
6871 : "\n AND ("
6872 : "\n pronamespace != "
6873 : "(SELECT oid FROM pg_namespace "
6874 : "WHERE nspname = 'pg_catalog')"
6875 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6876 : "\n WHERE pg_cast.oid > '%u'::oid"
6877 : "\n AND p.oid = pg_cast.castfunc)",
6878 : g_last_builtin_oid);
6879 :
6880 0 : if (fout->remoteVersion >= 90500)
6881 0 : appendPQExpBuffer(query,
6882 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6883 : "\n WHERE pg_transform.oid > '%u'::oid"
6884 : "\n AND (p.oid = pg_transform.trffromsql"
6885 : "\n OR p.oid = pg_transform.trftosql))",
6886 : g_last_builtin_oid);
6887 :
6888 0 : if (dopt->binary_upgrade)
6889 0 : appendPQExpBufferStr(query,
6890 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6891 : "classid = 'pg_proc'::regclass AND "
6892 : "objid = p.oid AND "
6893 : "refclassid = 'pg_extension'::regclass AND "
6894 : "deptype = 'e')");
6895 0 : appendPQExpBufferChar(query, ')');
6896 : }
6897 :
6898 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6899 :
6900 468 : ntups = PQntuples(res);
6901 :
6902 468 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
6903 :
6904 468 : i_tableoid = PQfnumber(res, "tableoid");
6905 468 : i_oid = PQfnumber(res, "oid");
6906 468 : i_proname = PQfnumber(res, "proname");
6907 468 : i_pronamespace = PQfnumber(res, "pronamespace");
6908 468 : i_proowner = PQfnumber(res, "proowner");
6909 468 : i_prolang = PQfnumber(res, "prolang");
6910 468 : i_pronargs = PQfnumber(res, "pronargs");
6911 468 : i_proargtypes = PQfnumber(res, "proargtypes");
6912 468 : i_prorettype = PQfnumber(res, "prorettype");
6913 468 : i_proacl = PQfnumber(res, "proacl");
6914 468 : i_acldefault = PQfnumber(res, "acldefault");
6915 :
6916 12766 : for (i = 0; i < ntups; i++)
6917 : {
6918 12298 : finfo[i].dobj.objType = DO_FUNC;
6919 12298 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6920 12298 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6921 12298 : AssignDumpId(&finfo[i].dobj);
6922 12298 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
6923 24596 : finfo[i].dobj.namespace =
6924 12298 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
6925 12298 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
6926 12298 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6927 12298 : finfo[i].dacl.privtype = 0;
6928 12298 : finfo[i].dacl.initprivs = NULL;
6929 12298 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6930 12298 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
6931 12298 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
6932 12298 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
6933 12298 : if (finfo[i].nargs == 0)
6934 2768 : finfo[i].argtypes = NULL;
6935 : else
6936 : {
6937 9530 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
6938 9530 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6939 9530 : finfo[i].argtypes, finfo[i].nargs);
6940 : }
6941 12298 : finfo[i].postponed_def = false; /* might get set during sort */
6942 :
6943 : /* Decide whether we want to dump it */
6944 12298 : selectDumpableObject(&(finfo[i].dobj), fout);
6945 :
6946 : /* Mark whether function has an ACL */
6947 12298 : if (!PQgetisnull(res, i, i_proacl))
6948 310 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6949 : }
6950 :
6951 468 : PQclear(res);
6952 :
6953 468 : destroyPQExpBuffer(query);
6954 468 : }
6955 :
6956 : /*
6957 : * getRelationStatistics
6958 : * register the statistics object as a dependent of the relation.
6959 : *
6960 : * reltuples is passed as a string to avoid complexities in converting from/to
6961 : * floating point.
6962 : */
6963 : static RelStatsInfo *
6964 26484 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
6965 : char *reltuples, int32 relallvisible,
6966 : int32 relallfrozen, char relkind,
6967 : char **indAttNames, int nindAttNames)
6968 : {
6969 26484 : if (!fout->dopt->dumpStatistics)
6970 18918 : return NULL;
6971 :
6972 7566 : if ((relkind == RELKIND_RELATION) ||
6973 3278 : (relkind == RELKIND_PARTITIONED_TABLE) ||
6974 2002 : (relkind == RELKIND_INDEX) ||
6975 1314 : (relkind == RELKIND_PARTITIONED_INDEX) ||
6976 578 : (relkind == RELKIND_MATVIEW ||
6977 : relkind == RELKIND_FOREIGN_TABLE))
6978 : {
6979 7060 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
6980 7060 : DumpableObject *dobj = &info->dobj;
6981 :
6982 7060 : dobj->objType = DO_REL_STATS;
6983 7060 : dobj->catId.tableoid = 0;
6984 7060 : dobj->catId.oid = 0;
6985 7060 : AssignDumpId(dobj);
6986 7060 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
6987 7060 : dobj->dependencies[0] = rel->dumpId;
6988 7060 : dobj->nDeps = 1;
6989 7060 : dobj->allocDeps = 1;
6990 7060 : dobj->components |= DUMP_COMPONENT_STATISTICS;
6991 7060 : dobj->name = pg_strdup(rel->name);
6992 7060 : dobj->namespace = rel->namespace;
6993 7060 : info->relpages = relpages;
6994 7060 : info->reltuples = pstrdup(reltuples);
6995 7060 : info->relallvisible = relallvisible;
6996 7060 : info->relallfrozen = relallfrozen;
6997 7060 : info->relkind = relkind;
6998 7060 : info->indAttNames = indAttNames;
6999 7060 : info->nindAttNames = nindAttNames;
7000 :
7001 : /*
7002 : * Ordinarily, stats go in SECTION_DATA for tables and
7003 : * SECTION_POST_DATA for indexes.
7004 : *
7005 : * However, the section may be updated later for materialized view
7006 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7007 : * the stats, so the stats must be restored after the data. Also, the
7008 : * materialized view definition may be postponed to SECTION_POST_DATA
7009 : * (see repairMatViewBoundaryMultiLoop()).
7010 : */
7011 7060 : switch (info->relkind)
7012 : {
7013 5096 : case RELKIND_RELATION:
7014 : case RELKIND_PARTITIONED_TABLE:
7015 : case RELKIND_MATVIEW:
7016 : case RELKIND_FOREIGN_TABLE:
7017 5096 : info->section = SECTION_DATA;
7018 5096 : break;
7019 1964 : case RELKIND_INDEX:
7020 : case RELKIND_PARTITIONED_INDEX:
7021 1964 : info->section = SECTION_POST_DATA;
7022 1964 : break;
7023 0 : default:
7024 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7025 : info->relkind);
7026 : }
7027 :
7028 7060 : return info;
7029 : }
7030 506 : return NULL;
7031 : }
7032 :
7033 : /*
7034 : * getTables
7035 : * read all the tables (no indexes) in the system catalogs,
7036 : * and return them as an array of TableInfo structures
7037 : *
7038 : * *numTables is set to the number of tables read in
7039 : */
7040 : TableInfo *
7041 470 : getTables(Archive *fout, int *numTables)
7042 : {
7043 470 : DumpOptions *dopt = fout->dopt;
7044 : PGresult *res;
7045 : int ntups;
7046 : int i;
7047 470 : PQExpBuffer query = createPQExpBuffer();
7048 : TableInfo *tblinfo;
7049 : int i_reltableoid;
7050 : int i_reloid;
7051 : int i_relname;
7052 : int i_relnamespace;
7053 : int i_relkind;
7054 : int i_reltype;
7055 : int i_relowner;
7056 : int i_relchecks;
7057 : int i_relhasindex;
7058 : int i_relhasrules;
7059 : int i_relpages;
7060 : int i_reltuples;
7061 : int i_relallvisible;
7062 : int i_relallfrozen;
7063 : int i_toastpages;
7064 : int i_owning_tab;
7065 : int i_owning_col;
7066 : int i_reltablespace;
7067 : int i_relhasoids;
7068 : int i_relhastriggers;
7069 : int i_relpersistence;
7070 : int i_relispopulated;
7071 : int i_relreplident;
7072 : int i_relrowsec;
7073 : int i_relforcerowsec;
7074 : int i_relfrozenxid;
7075 : int i_toastfrozenxid;
7076 : int i_toastoid;
7077 : int i_relminmxid;
7078 : int i_toastminmxid;
7079 : int i_reloptions;
7080 : int i_checkoption;
7081 : int i_toastreloptions;
7082 : int i_reloftype;
7083 : int i_foreignserver;
7084 : int i_amname;
7085 : int i_is_identity_sequence;
7086 : int i_relacl;
7087 : int i_acldefault;
7088 : int i_ispartition;
7089 :
7090 : /*
7091 : * Find all the tables and table-like objects.
7092 : *
7093 : * We must fetch all tables in this phase because otherwise we cannot
7094 : * correctly identify inherited columns, owned sequences, etc.
7095 : *
7096 : * We include system catalogs, so that we can work if a user table is
7097 : * defined to inherit from a system catalog (pretty weird, but...)
7098 : *
7099 : * Note: in this phase we should collect only a minimal amount of
7100 : * information about each table, basically just enough to decide if it is
7101 : * interesting. In particular, since we do not yet have lock on any user
7102 : * table, we MUST NOT invoke any server-side data collection functions
7103 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7104 : * wrong answers if any concurrent DDL is happening.
7105 : */
7106 :
7107 470 : appendPQExpBufferStr(query,
7108 : "SELECT c.tableoid, c.oid, c.relname, "
7109 : "c.relnamespace, c.relkind, c.reltype, "
7110 : "c.relowner, "
7111 : "c.relchecks, "
7112 : "c.relhasindex, c.relhasrules, c.relpages, "
7113 : "c.reltuples, c.relallvisible, ");
7114 :
7115 470 : if (fout->remoteVersion >= 180000)
7116 470 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7117 : else
7118 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7119 :
7120 470 : appendPQExpBufferStr(query,
7121 : "c.relhastriggers, c.relpersistence, "
7122 : "c.reloftype, "
7123 : "c.relacl, "
7124 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7125 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7126 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7127 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7128 : "ELSE 0 END AS foreignserver, "
7129 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7130 : "tc.oid AS toid, "
7131 : "tc.relpages AS toastpages, "
7132 : "tc.reloptions AS toast_reloptions, "
7133 : "d.refobjid AS owning_tab, "
7134 : "d.refobjsubid AS owning_col, "
7135 : "tsp.spcname AS reltablespace, ");
7136 :
7137 470 : if (fout->remoteVersion >= 120000)
7138 470 : appendPQExpBufferStr(query,
7139 : "false AS relhasoids, ");
7140 : else
7141 0 : appendPQExpBufferStr(query,
7142 : "c.relhasoids, ");
7143 :
7144 470 : if (fout->remoteVersion >= 90300)
7145 470 : appendPQExpBufferStr(query,
7146 : "c.relispopulated, ");
7147 : else
7148 0 : appendPQExpBufferStr(query,
7149 : "'t' as relispopulated, ");
7150 :
7151 470 : if (fout->remoteVersion >= 90400)
7152 470 : appendPQExpBufferStr(query,
7153 : "c.relreplident, ");
7154 : else
7155 0 : appendPQExpBufferStr(query,
7156 : "'d' AS relreplident, ");
7157 :
7158 470 : if (fout->remoteVersion >= 90500)
7159 470 : appendPQExpBufferStr(query,
7160 : "c.relrowsecurity, c.relforcerowsecurity, ");
7161 : else
7162 0 : appendPQExpBufferStr(query,
7163 : "false AS relrowsecurity, "
7164 : "false AS relforcerowsecurity, ");
7165 :
7166 470 : if (fout->remoteVersion >= 90300)
7167 470 : appendPQExpBufferStr(query,
7168 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7169 : else
7170 0 : appendPQExpBufferStr(query,
7171 : "0 AS relminmxid, 0 AS tminmxid, ");
7172 :
7173 470 : if (fout->remoteVersion >= 90300)
7174 470 : appendPQExpBufferStr(query,
7175 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7176 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7177 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7178 : else
7179 0 : appendPQExpBufferStr(query,
7180 : "c.reloptions, NULL AS checkoption, ");
7181 :
7182 470 : if (fout->remoteVersion >= 90600)
7183 470 : appendPQExpBufferStr(query,
7184 : "am.amname, ");
7185 : else
7186 0 : appendPQExpBufferStr(query,
7187 : "NULL AS amname, ");
7188 :
7189 470 : if (fout->remoteVersion >= 90600)
7190 470 : appendPQExpBufferStr(query,
7191 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7192 : else
7193 0 : appendPQExpBufferStr(query,
7194 : "false AS is_identity_sequence, ");
7195 :
7196 470 : if (fout->remoteVersion >= 100000)
7197 470 : appendPQExpBufferStr(query,
7198 : "c.relispartition AS ispartition ");
7199 : else
7200 0 : appendPQExpBufferStr(query,
7201 : "false AS ispartition ");
7202 :
7203 : /*
7204 : * Left join to pg_depend to pick up dependency info linking sequences to
7205 : * their owning column, if any (note this dependency is AUTO except for
7206 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7207 : * collect the spcname.
7208 : */
7209 470 : appendPQExpBufferStr(query,
7210 : "\nFROM pg_class c\n"
7211 : "LEFT JOIN pg_depend d ON "
7212 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7213 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7214 : "d.objsubid = 0 AND "
7215 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7216 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7217 :
7218 : /*
7219 : * In 9.6 and up, left join to pg_am to pick up the amname.
7220 : */
7221 470 : if (fout->remoteVersion >= 90600)
7222 470 : appendPQExpBufferStr(query,
7223 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7224 :
7225 : /*
7226 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7227 : * that versions 10 and 11 have them, but later versions do not, so
7228 : * emitting them causes the upgrade to fail.
7229 : */
7230 470 : appendPQExpBufferStr(query,
7231 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7232 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7233 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7234 :
7235 : /*
7236 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7237 : * relkinds are possible in older servers, but it's not worth the trouble
7238 : * to emit a version-dependent list.
7239 : *
7240 : * Composite-type table entries won't be dumped as such, but we have to
7241 : * make a DumpableObject for them so that we can track dependencies of the
7242 : * composite type (pg_depend entries for columns of the composite type
7243 : * link to the pg_class entry not the pg_type entry).
7244 : */
7245 470 : appendPQExpBufferStr(query,
7246 : "WHERE c.relkind IN ("
7247 : CppAsString2(RELKIND_RELATION) ", "
7248 : CppAsString2(RELKIND_SEQUENCE) ", "
7249 : CppAsString2(RELKIND_VIEW) ", "
7250 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7251 : CppAsString2(RELKIND_MATVIEW) ", "
7252 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7253 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7254 : "ORDER BY c.oid");
7255 :
7256 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7257 :
7258 470 : ntups = PQntuples(res);
7259 :
7260 470 : *numTables = ntups;
7261 :
7262 : /*
7263 : * Extract data from result and lock dumpable tables. We do the locking
7264 : * before anything else, to minimize the window wherein a table could
7265 : * disappear under us.
7266 : *
7267 : * Note that we have to save info about all tables here, even when dumping
7268 : * only one, because we don't yet know which tables might be inheritance
7269 : * ancestors of the target table.
7270 : */
7271 470 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7272 :
7273 470 : i_reltableoid = PQfnumber(res, "tableoid");
7274 470 : i_reloid = PQfnumber(res, "oid");
7275 470 : i_relname = PQfnumber(res, "relname");
7276 470 : i_relnamespace = PQfnumber(res, "relnamespace");
7277 470 : i_relkind = PQfnumber(res, "relkind");
7278 470 : i_reltype = PQfnumber(res, "reltype");
7279 470 : i_relowner = PQfnumber(res, "relowner");
7280 470 : i_relchecks = PQfnumber(res, "relchecks");
7281 470 : i_relhasindex = PQfnumber(res, "relhasindex");
7282 470 : i_relhasrules = PQfnumber(res, "relhasrules");
7283 470 : i_relpages = PQfnumber(res, "relpages");
7284 470 : i_reltuples = PQfnumber(res, "reltuples");
7285 470 : i_relallvisible = PQfnumber(res, "relallvisible");
7286 470 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7287 470 : i_toastpages = PQfnumber(res, "toastpages");
7288 470 : i_owning_tab = PQfnumber(res, "owning_tab");
7289 470 : i_owning_col = PQfnumber(res, "owning_col");
7290 470 : i_reltablespace = PQfnumber(res, "reltablespace");
7291 470 : i_relhasoids = PQfnumber(res, "relhasoids");
7292 470 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7293 470 : i_relpersistence = PQfnumber(res, "relpersistence");
7294 470 : i_relispopulated = PQfnumber(res, "relispopulated");
7295 470 : i_relreplident = PQfnumber(res, "relreplident");
7296 470 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7297 470 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7298 470 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7299 470 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7300 470 : i_toastoid = PQfnumber(res, "toid");
7301 470 : i_relminmxid = PQfnumber(res, "relminmxid");
7302 470 : i_toastminmxid = PQfnumber(res, "tminmxid");
7303 470 : i_reloptions = PQfnumber(res, "reloptions");
7304 470 : i_checkoption = PQfnumber(res, "checkoption");
7305 470 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7306 470 : i_reloftype = PQfnumber(res, "reloftype");
7307 470 : i_foreignserver = PQfnumber(res, "foreignserver");
7308 470 : i_amname = PQfnumber(res, "amname");
7309 470 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7310 470 : i_relacl = PQfnumber(res, "relacl");
7311 470 : i_acldefault = PQfnumber(res, "acldefault");
7312 470 : i_ispartition = PQfnumber(res, "ispartition");
7313 :
7314 470 : if (dopt->lockWaitTimeout)
7315 : {
7316 : /*
7317 : * Arrange to fail instead of waiting forever for a table lock.
7318 : *
7319 : * NB: this coding assumes that the only queries issued within the
7320 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7321 : * applied to other things too.
7322 : */
7323 4 : resetPQExpBuffer(query);
7324 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7325 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7326 4 : ExecuteSqlStatement(fout, query->data);
7327 : }
7328 :
7329 470 : resetPQExpBuffer(query);
7330 :
7331 124800 : for (i = 0; i < ntups; i++)
7332 : {
7333 124330 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7334 124330 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7335 :
7336 124330 : tblinfo[i].dobj.objType = DO_TABLE;
7337 124330 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7338 124330 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7339 124330 : AssignDumpId(&tblinfo[i].dobj);
7340 124330 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7341 248660 : tblinfo[i].dobj.namespace =
7342 124330 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7343 124330 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7344 124330 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7345 124330 : tblinfo[i].dacl.privtype = 0;
7346 124330 : tblinfo[i].dacl.initprivs = NULL;
7347 124330 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7348 124330 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7349 124330 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7350 124330 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7351 124330 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7352 124330 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7353 124330 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7354 124330 : if (PQgetisnull(res, i, i_toastpages))
7355 99272 : tblinfo[i].toastpages = 0;
7356 : else
7357 25058 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7358 124330 : if (PQgetisnull(res, i, i_owning_tab))
7359 : {
7360 123260 : tblinfo[i].owning_tab = InvalidOid;
7361 123260 : tblinfo[i].owning_col = 0;
7362 : }
7363 : else
7364 : {
7365 1070 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7366 1070 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7367 : }
7368 124330 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7369 124330 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7370 124330 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7371 124330 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7372 124330 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7373 124330 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7374 124330 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7375 124330 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7376 124330 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7377 124330 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7378 124330 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7379 124330 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7380 124330 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7381 124330 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7382 124330 : if (PQgetisnull(res, i, i_checkoption))
7383 124230 : tblinfo[i].checkoption = NULL;
7384 : else
7385 100 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7386 124330 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7387 124330 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7388 124330 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7389 124330 : if (PQgetisnull(res, i, i_amname))
7390 73904 : tblinfo[i].amname = NULL;
7391 : else
7392 50426 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7393 124330 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7394 124330 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7395 :
7396 : /* other fields were zeroed above */
7397 :
7398 : /*
7399 : * Decide whether we want to dump this table.
7400 : */
7401 124330 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7402 470 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7403 : else
7404 123860 : selectDumpableTable(&tblinfo[i], fout);
7405 :
7406 : /*
7407 : * Now, consider the table "interesting" if we need to dump its
7408 : * definition, data or its statistics. Later on, we'll skip a lot of
7409 : * data collection for uninteresting tables.
7410 : *
7411 : * Note: the "interesting" flag will also be set by flagInhTables for
7412 : * parents of interesting tables, so that we collect necessary
7413 : * inheritance info even when the parents are not themselves being
7414 : * dumped. This is the main reason why we need an "interesting" flag
7415 : * that's separate from the components-to-dump bitmask.
7416 : */
7417 124330 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7418 : (DUMP_COMPONENT_DEFINITION |
7419 : DUMP_COMPONENT_DATA |
7420 124330 : DUMP_COMPONENT_STATISTICS)) != 0;
7421 :
7422 124330 : tblinfo[i].dummy_view = false; /* might get set during sort */
7423 124330 : tblinfo[i].postponed_def = false; /* might get set during sort */
7424 :
7425 : /* Tables have data */
7426 124330 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7427 :
7428 : /* Mark whether table has an ACL */
7429 124330 : if (!PQgetisnull(res, i, i_relacl))
7430 98556 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7431 124330 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7432 :
7433 : /* Add statistics */
7434 124330 : if (tblinfo[i].interesting)
7435 : {
7436 : RelStatsInfo *stats;
7437 :
7438 38960 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7439 19480 : tblinfo[i].relpages,
7440 : PQgetvalue(res, i, i_reltuples),
7441 : relallvisible, relallfrozen,
7442 19480 : tblinfo[i].relkind, NULL, 0);
7443 19480 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7444 988 : tblinfo[i].stats = stats;
7445 : }
7446 :
7447 : /*
7448 : * Read-lock target tables to make sure they aren't DROPPED or altered
7449 : * in schema before we get around to dumping them.
7450 : *
7451 : * Note that we don't explicitly lock parents of the target tables; we
7452 : * assume our lock on the child is enough to prevent schema
7453 : * alterations to parent tables.
7454 : *
7455 : * NOTE: it'd be kinda nice to lock other relations too, not only
7456 : * plain or partitioned tables, but the backend doesn't presently
7457 : * allow that.
7458 : *
7459 : * We only need to lock the table for certain components; see
7460 : * pg_dump.h
7461 : */
7462 124330 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7463 19480 : (tblinfo[i].relkind == RELKIND_RELATION ||
7464 5272 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7465 : {
7466 : /*
7467 : * Tables are locked in batches. When dumping from a remote
7468 : * server this can save a significant amount of time by reducing
7469 : * the number of round trips.
7470 : */
7471 15866 : if (query->len == 0)
7472 316 : appendPQExpBuffer(query, "LOCK TABLE %s",
7473 316 : fmtQualifiedDumpable(&tblinfo[i]));
7474 : else
7475 : {
7476 15550 : appendPQExpBuffer(query, ", %s",
7477 15550 : fmtQualifiedDumpable(&tblinfo[i]));
7478 :
7479 : /* Arbitrarily end a batch when query length reaches 100K. */
7480 15550 : if (query->len >= 100000)
7481 : {
7482 : /* Lock another batch of tables. */
7483 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7484 0 : ExecuteSqlStatement(fout, query->data);
7485 0 : resetPQExpBuffer(query);
7486 : }
7487 : }
7488 : }
7489 : }
7490 :
7491 470 : if (query->len != 0)
7492 : {
7493 : /* Lock the tables in the last batch. */
7494 316 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7495 316 : ExecuteSqlStatement(fout, query->data);
7496 : }
7497 :
7498 468 : if (dopt->lockWaitTimeout)
7499 : {
7500 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7501 : }
7502 :
7503 468 : PQclear(res);
7504 :
7505 468 : destroyPQExpBuffer(query);
7506 :
7507 468 : return tblinfo;
7508 : }
7509 :
7510 : /*
7511 : * getOwnedSeqs
7512 : * identify owned sequences and mark them as dumpable if owning table is
7513 : *
7514 : * We used to do this in getTables(), but it's better to do it after the
7515 : * index used by findTableByOid() has been set up.
7516 : */
7517 : void
7518 468 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7519 : {
7520 : int i;
7521 :
7522 : /*
7523 : * Force sequences that are "owned" by table columns to be dumped whenever
7524 : * their owning table is being dumped.
7525 : */
7526 124260 : for (i = 0; i < numTables; i++)
7527 : {
7528 123792 : TableInfo *seqinfo = &tblinfo[i];
7529 : TableInfo *owning_tab;
7530 :
7531 123792 : if (!OidIsValid(seqinfo->owning_tab))
7532 122728 : continue; /* not an owned sequence */
7533 :
7534 1064 : owning_tab = findTableByOid(seqinfo->owning_tab);
7535 1064 : if (owning_tab == NULL)
7536 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7537 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7538 :
7539 : /*
7540 : * For an identity sequence, dump exactly the same components for the
7541 : * sequence as for the owning table. This is important because we
7542 : * treat the identity sequence as an integral part of the table. For
7543 : * example, there is not any DDL command that allows creation of such
7544 : * a sequence independently of the table.
7545 : *
7546 : * For other owned sequences such as serial sequences, we need to dump
7547 : * the components that are being dumped for the table and any
7548 : * components that the sequence is explicitly marked with.
7549 : *
7550 : * We can't simply use the set of components which are being dumped
7551 : * for the table as the table might be in an extension (and only the
7552 : * non-extension components, eg: ACLs if changed, security labels, and
7553 : * policies, are being dumped) while the sequence is not (and
7554 : * therefore the definition and other components should also be
7555 : * dumped).
7556 : *
7557 : * If the sequence is part of the extension then it should be properly
7558 : * marked by checkExtensionMembership() and this will be a no-op as
7559 : * the table will be equivalently marked.
7560 : */
7561 1064 : if (seqinfo->is_identity_sequence)
7562 538 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7563 : else
7564 526 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7565 :
7566 : /* Make sure that necessary data is available if we're dumping it */
7567 1064 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7568 : {
7569 872 : seqinfo->interesting = true;
7570 872 : owning_tab->interesting = true;
7571 : }
7572 : }
7573 468 : }
7574 :
7575 : /*
7576 : * getInherits
7577 : * read all the inheritance information
7578 : * from the system catalogs return them in the InhInfo* structure
7579 : *
7580 : * numInherits is set to the number of pairs read in
7581 : */
7582 : InhInfo *
7583 468 : getInherits(Archive *fout, int *numInherits)
7584 : {
7585 : PGresult *res;
7586 : int ntups;
7587 : int i;
7588 468 : PQExpBuffer query = createPQExpBuffer();
7589 : InhInfo *inhinfo;
7590 :
7591 : int i_inhrelid;
7592 : int i_inhparent;
7593 :
7594 : /* find all the inheritance information */
7595 468 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7596 :
7597 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7598 :
7599 468 : ntups = PQntuples(res);
7600 :
7601 468 : *numInherits = ntups;
7602 :
7603 468 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7604 :
7605 468 : i_inhrelid = PQfnumber(res, "inhrelid");
7606 468 : i_inhparent = PQfnumber(res, "inhparent");
7607 :
7608 9106 : for (i = 0; i < ntups; i++)
7609 : {
7610 8638 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7611 8638 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7612 : }
7613 :
7614 468 : PQclear(res);
7615 :
7616 468 : destroyPQExpBuffer(query);
7617 :
7618 468 : return inhinfo;
7619 : }
7620 :
7621 : /*
7622 : * getPartitioningInfo
7623 : * get information about partitioning
7624 : *
7625 : * For the most part, we only collect partitioning info about tables we
7626 : * intend to dump. However, this function has to consider all partitioned
7627 : * tables in the database, because we need to know about parents of partitions
7628 : * we are going to dump even if the parents themselves won't be dumped.
7629 : *
7630 : * Specifically, what we need to know is whether each partitioned table
7631 : * has an "unsafe" partitioning scheme that requires us to force
7632 : * load-via-partition-root mode for its children. Currently the only case
7633 : * for which we force that is hash partitioning on enum columns, since the
7634 : * hash codes depend on enum value OIDs which won't be replicated across
7635 : * dump-and-reload. There are other cases in which load-via-partition-root
7636 : * might be necessary, but we expect users to cope with them.
7637 : */
7638 : void
7639 468 : getPartitioningInfo(Archive *fout)
7640 : {
7641 : PQExpBuffer query;
7642 : PGresult *res;
7643 : int ntups;
7644 :
7645 : /* hash partitioning didn't exist before v11 */
7646 468 : if (fout->remoteVersion < 110000)
7647 0 : return;
7648 : /* needn't bother if not dumping data */
7649 468 : if (!fout->dopt->dumpData)
7650 72 : return;
7651 :
7652 396 : query = createPQExpBuffer();
7653 :
7654 : /*
7655 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7656 : * appears among the partition opclasses. We needn't check partstrat.
7657 : *
7658 : * Note that this query may well retrieve info about tables we aren't
7659 : * going to dump and hence have no lock on. That's okay since we need not
7660 : * invoke any unsafe server-side functions.
7661 : */
7662 396 : appendPQExpBufferStr(query,
7663 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7664 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7665 : "ON c.opcmethod = a.oid\n"
7666 : "WHERE opcname = 'enum_ops' "
7667 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7668 : "AND amname = 'hash') = ANY(partclass)");
7669 :
7670 396 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7671 :
7672 396 : ntups = PQntuples(res);
7673 :
7674 400 : for (int i = 0; i < ntups; i++)
7675 : {
7676 4 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7677 : TableInfo *tbinfo;
7678 :
7679 4 : tbinfo = findTableByOid(tabrelid);
7680 4 : if (tbinfo == NULL)
7681 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7682 : tabrelid);
7683 4 : tbinfo->unsafe_partitions = true;
7684 : }
7685 :
7686 396 : PQclear(res);
7687 :
7688 396 : destroyPQExpBuffer(query);
7689 : }
7690 :
7691 : /*
7692 : * getIndexes
7693 : * get information about every index on a dumpable table
7694 : *
7695 : * Note: index data is not returned directly to the caller, but it
7696 : * does get entered into the DumpableObject tables.
7697 : */
7698 : void
7699 468 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7700 : {
7701 468 : PQExpBuffer query = createPQExpBuffer();
7702 468 : PQExpBuffer tbloids = createPQExpBuffer();
7703 : PGresult *res;
7704 : int ntups;
7705 : int curtblindx;
7706 : IndxInfo *indxinfo;
7707 : int i_tableoid,
7708 : i_oid,
7709 : i_indrelid,
7710 : i_indexname,
7711 : i_relpages,
7712 : i_reltuples,
7713 : i_relallvisible,
7714 : i_relallfrozen,
7715 : i_parentidx,
7716 : i_indexdef,
7717 : i_indnkeyatts,
7718 : i_indnatts,
7719 : i_indkey,
7720 : i_indisclustered,
7721 : i_indisreplident,
7722 : i_indnullsnotdistinct,
7723 : i_contype,
7724 : i_conname,
7725 : i_condeferrable,
7726 : i_condeferred,
7727 : i_conperiod,
7728 : i_contableoid,
7729 : i_conoid,
7730 : i_condef,
7731 : i_indattnames,
7732 : i_tablespace,
7733 : i_indreloptions,
7734 : i_indstatcols,
7735 : i_indstatvals;
7736 :
7737 : /*
7738 : * We want to perform just one query against pg_index. However, we
7739 : * mustn't try to select every row of the catalog and then sort it out on
7740 : * the client side, because some of the server-side functions we need
7741 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7742 : * build an array of the OIDs of tables we care about (and now have lock
7743 : * on!), and use a WHERE clause to constrain which rows are selected.
7744 : */
7745 468 : appendPQExpBufferChar(tbloids, '{');
7746 124260 : for (int i = 0; i < numTables; i++)
7747 : {
7748 123792 : TableInfo *tbinfo = &tblinfo[i];
7749 :
7750 123792 : if (!tbinfo->hasindex)
7751 87526 : continue;
7752 :
7753 : /*
7754 : * We can ignore indexes of uninteresting tables.
7755 : */
7756 36266 : if (!tbinfo->interesting)
7757 30722 : continue;
7758 :
7759 : /* OK, we need info for this table */
7760 5544 : if (tbloids->len > 1) /* do we have more than the '{'? */
7761 5374 : appendPQExpBufferChar(tbloids, ',');
7762 5544 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7763 : }
7764 468 : appendPQExpBufferChar(tbloids, '}');
7765 :
7766 468 : appendPQExpBufferStr(query,
7767 : "SELECT t.tableoid, t.oid, i.indrelid, "
7768 : "t.relname AS indexname, "
7769 : "t.relpages, t.reltuples, t.relallvisible, ");
7770 :
7771 468 : if (fout->remoteVersion >= 180000)
7772 468 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7773 : else
7774 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7775 :
7776 468 : appendPQExpBufferStr(query,
7777 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7778 : "i.indkey, i.indisclustered, "
7779 : "c.contype, c.conname, "
7780 : "c.condeferrable, c.condeferred, "
7781 : "c.tableoid AS contableoid, "
7782 : "c.oid AS conoid, "
7783 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7784 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7785 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7786 : " FROM pg_catalog.pg_attribute "
7787 : " WHERE attrelid = i.indexrelid) "
7788 : "ELSE NULL END AS indattnames, "
7789 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7790 : "t.reloptions AS indreloptions, ");
7791 :
7792 :
7793 468 : if (fout->remoteVersion >= 90400)
7794 468 : appendPQExpBufferStr(query,
7795 : "i.indisreplident, ");
7796 : else
7797 0 : appendPQExpBufferStr(query,
7798 : "false AS indisreplident, ");
7799 :
7800 468 : if (fout->remoteVersion >= 110000)
7801 468 : appendPQExpBufferStr(query,
7802 : "inh.inhparent AS parentidx, "
7803 : "i.indnkeyatts AS indnkeyatts, "
7804 : "i.indnatts AS indnatts, "
7805 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7806 : " FROM pg_catalog.pg_attribute "
7807 : " WHERE attrelid = i.indexrelid AND "
7808 : " attstattarget >= 0) AS indstatcols, "
7809 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7810 : " FROM pg_catalog.pg_attribute "
7811 : " WHERE attrelid = i.indexrelid AND "
7812 : " attstattarget >= 0) AS indstatvals, ");
7813 : else
7814 0 : appendPQExpBufferStr(query,
7815 : "0 AS parentidx, "
7816 : "i.indnatts AS indnkeyatts, "
7817 : "i.indnatts AS indnatts, "
7818 : "'' AS indstatcols, "
7819 : "'' AS indstatvals, ");
7820 :
7821 468 : if (fout->remoteVersion >= 150000)
7822 468 : appendPQExpBufferStr(query,
7823 : "i.indnullsnotdistinct, ");
7824 : else
7825 0 : appendPQExpBufferStr(query,
7826 : "false AS indnullsnotdistinct, ");
7827 :
7828 468 : if (fout->remoteVersion >= 180000)
7829 468 : appendPQExpBufferStr(query,
7830 : "c.conperiod ");
7831 : else
7832 0 : appendPQExpBufferStr(query,
7833 : "NULL AS conperiod ");
7834 :
7835 : /*
7836 : * The point of the messy-looking outer join is to find a constraint that
7837 : * is related by an internal dependency link to the index. If we find one,
7838 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7839 : * index won't have more than one internal dependency.
7840 : *
7841 : * Note: the check on conrelid is redundant, but useful because that
7842 : * column is indexed while conindid is not.
7843 : */
7844 468 : if (fout->remoteVersion >= 110000)
7845 : {
7846 468 : appendPQExpBuffer(query,
7847 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7848 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7849 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7850 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7851 : "LEFT JOIN pg_catalog.pg_constraint c "
7852 : "ON (i.indrelid = c.conrelid AND "
7853 : "i.indexrelid = c.conindid AND "
7854 : "c.contype IN ('p','u','x')) "
7855 : "LEFT JOIN pg_catalog.pg_inherits inh "
7856 : "ON (inh.inhrelid = indexrelid) "
7857 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7858 : "AND i.indisready "
7859 : "ORDER BY i.indrelid, indexname",
7860 : tbloids->data);
7861 : }
7862 : else
7863 : {
7864 : /*
7865 : * the test on indisready is necessary in 9.2, and harmless in
7866 : * earlier/later versions
7867 : */
7868 0 : appendPQExpBuffer(query,
7869 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7870 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7871 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7872 : "LEFT JOIN pg_catalog.pg_constraint c "
7873 : "ON (i.indrelid = c.conrelid AND "
7874 : "i.indexrelid = c.conindid AND "
7875 : "c.contype IN ('p','u','x')) "
7876 : "WHERE i.indisvalid AND i.indisready "
7877 : "ORDER BY i.indrelid, indexname",
7878 : tbloids->data);
7879 : }
7880 :
7881 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7882 :
7883 468 : ntups = PQntuples(res);
7884 :
7885 468 : i_tableoid = PQfnumber(res, "tableoid");
7886 468 : i_oid = PQfnumber(res, "oid");
7887 468 : i_indrelid = PQfnumber(res, "indrelid");
7888 468 : i_indexname = PQfnumber(res, "indexname");
7889 468 : i_relpages = PQfnumber(res, "relpages");
7890 468 : i_reltuples = PQfnumber(res, "reltuples");
7891 468 : i_relallvisible = PQfnumber(res, "relallvisible");
7892 468 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7893 468 : i_parentidx = PQfnumber(res, "parentidx");
7894 468 : i_indexdef = PQfnumber(res, "indexdef");
7895 468 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
7896 468 : i_indnatts = PQfnumber(res, "indnatts");
7897 468 : i_indkey = PQfnumber(res, "indkey");
7898 468 : i_indisclustered = PQfnumber(res, "indisclustered");
7899 468 : i_indisreplident = PQfnumber(res, "indisreplident");
7900 468 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
7901 468 : i_contype = PQfnumber(res, "contype");
7902 468 : i_conname = PQfnumber(res, "conname");
7903 468 : i_condeferrable = PQfnumber(res, "condeferrable");
7904 468 : i_condeferred = PQfnumber(res, "condeferred");
7905 468 : i_conperiod = PQfnumber(res, "conperiod");
7906 468 : i_contableoid = PQfnumber(res, "contableoid");
7907 468 : i_conoid = PQfnumber(res, "conoid");
7908 468 : i_condef = PQfnumber(res, "condef");
7909 468 : i_indattnames = PQfnumber(res, "indattnames");
7910 468 : i_tablespace = PQfnumber(res, "tablespace");
7911 468 : i_indreloptions = PQfnumber(res, "indreloptions");
7912 468 : i_indstatcols = PQfnumber(res, "indstatcols");
7913 468 : i_indstatvals = PQfnumber(res, "indstatvals");
7914 :
7915 468 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
7916 :
7917 : /*
7918 : * Outer loop iterates once per table, not once per row. Incrementing of
7919 : * j is handled by the inner loop.
7920 : */
7921 468 : curtblindx = -1;
7922 5956 : for (int j = 0; j < ntups;)
7923 : {
7924 5488 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
7925 5488 : TableInfo *tbinfo = NULL;
7926 5488 : char **indAttNames = NULL;
7927 5488 : int nindAttNames = 0;
7928 : int numinds;
7929 :
7930 : /* Count rows for this table */
7931 7004 : for (numinds = 1; numinds < ntups - j; numinds++)
7932 6834 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
7933 5318 : break;
7934 :
7935 : /*
7936 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
7937 : * order.
7938 : */
7939 54514 : while (++curtblindx < numTables)
7940 : {
7941 54514 : tbinfo = &tblinfo[curtblindx];
7942 54514 : if (tbinfo->dobj.catId.oid == indrelid)
7943 5488 : break;
7944 : }
7945 5488 : if (curtblindx >= numTables)
7946 0 : pg_fatal("unrecognized table OID %u", indrelid);
7947 : /* cross-check that we only got requested tables */
7948 5488 : if (!tbinfo->hasindex ||
7949 5488 : !tbinfo->interesting)
7950 0 : pg_fatal("unexpected index data for table \"%s\"",
7951 : tbinfo->dobj.name);
7952 :
7953 : /* Save data for this table */
7954 5488 : tbinfo->indexes = indxinfo + j;
7955 5488 : tbinfo->numIndexes = numinds;
7956 :
7957 12492 : for (int c = 0; c < numinds; c++, j++)
7958 : {
7959 : char contype;
7960 : char indexkind;
7961 : RelStatsInfo *relstats;
7962 7004 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
7963 7004 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
7964 7004 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
7965 :
7966 7004 : indxinfo[j].dobj.objType = DO_INDEX;
7967 7004 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
7968 7004 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
7969 7004 : AssignDumpId(&indxinfo[j].dobj);
7970 7004 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
7971 7004 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
7972 7004 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
7973 7004 : indxinfo[j].indextable = tbinfo;
7974 7004 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
7975 7004 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
7976 7004 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
7977 7004 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
7978 7004 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
7979 7004 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
7980 7004 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
7981 7004 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
7982 7004 : parseOidArray(PQgetvalue(res, j, i_indkey),
7983 7004 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
7984 7004 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
7985 7004 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
7986 7004 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
7987 7004 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
7988 7004 : indxinfo[j].partattaches = (SimplePtrList)
7989 : {
7990 : NULL, NULL
7991 : };
7992 :
7993 7004 : if (indxinfo[j].parentidx == 0)
7994 5524 : indexkind = RELKIND_INDEX;
7995 : else
7996 1480 : indexkind = RELKIND_PARTITIONED_INDEX;
7997 :
7998 7004 : if (!PQgetisnull(res, j, i_indattnames))
7999 : {
8000 392 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8001 : &indAttNames, &nindAttNames))
8002 0 : pg_fatal("could not parse %s array", "indattnames");
8003 : }
8004 :
8005 7004 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8006 : PQgetvalue(res, j, i_reltuples),
8007 : relallvisible, relallfrozen, indexkind,
8008 : indAttNames, nindAttNames);
8009 :
8010 7004 : contype = *(PQgetvalue(res, j, i_contype));
8011 7004 : if (contype == 'p' || contype == 'u' || contype == 'x')
8012 3896 : {
8013 : /*
8014 : * If we found a constraint matching the index, create an
8015 : * entry for it.
8016 : */
8017 : ConstraintInfo *constrinfo;
8018 :
8019 3896 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8020 3896 : constrinfo->dobj.objType = DO_CONSTRAINT;
8021 3896 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8022 3896 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8023 3896 : AssignDumpId(&constrinfo->dobj);
8024 3896 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8025 3896 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8026 3896 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8027 3896 : constrinfo->contable = tbinfo;
8028 3896 : constrinfo->condomain = NULL;
8029 3896 : constrinfo->contype = contype;
8030 3896 : if (contype == 'x')
8031 32 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8032 : else
8033 3864 : constrinfo->condef = NULL;
8034 3896 : constrinfo->confrelid = InvalidOid;
8035 3896 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8036 3896 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8037 3896 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8038 3896 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8039 3896 : constrinfo->conislocal = true;
8040 3896 : constrinfo->separate = true;
8041 :
8042 3896 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8043 3896 : if (relstats != NULL)
8044 1148 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8045 : }
8046 : else
8047 : {
8048 : /* Plain secondary index */
8049 3108 : indxinfo[j].indexconstraint = 0;
8050 : }
8051 : }
8052 : }
8053 :
8054 468 : PQclear(res);
8055 :
8056 468 : destroyPQExpBuffer(query);
8057 468 : destroyPQExpBuffer(tbloids);
8058 468 : }
8059 :
8060 : /*
8061 : * getExtendedStatistics
8062 : * get information about extended-statistics objects.
8063 : *
8064 : * Note: extended statistics data is not returned directly to the caller, but
8065 : * it does get entered into the DumpableObject tables.
8066 : */
8067 : void
8068 468 : getExtendedStatistics(Archive *fout)
8069 : {
8070 : PQExpBuffer query;
8071 : PGresult *res;
8072 : StatsExtInfo *statsextinfo;
8073 : int ntups;
8074 : int i_tableoid;
8075 : int i_oid;
8076 : int i_stxname;
8077 : int i_stxnamespace;
8078 : int i_stxowner;
8079 : int i_stxrelid;
8080 : int i_stattarget;
8081 : int i;
8082 :
8083 : /* Extended statistics were new in v10 */
8084 468 : if (fout->remoteVersion < 100000)
8085 0 : return;
8086 :
8087 468 : query = createPQExpBuffer();
8088 :
8089 468 : if (fout->remoteVersion < 130000)
8090 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8091 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8092 : "FROM pg_catalog.pg_statistic_ext");
8093 : else
8094 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8095 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8096 : "FROM pg_catalog.pg_statistic_ext");
8097 :
8098 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8099 :
8100 468 : ntups = PQntuples(res);
8101 :
8102 468 : i_tableoid = PQfnumber(res, "tableoid");
8103 468 : i_oid = PQfnumber(res, "oid");
8104 468 : i_stxname = PQfnumber(res, "stxname");
8105 468 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8106 468 : i_stxowner = PQfnumber(res, "stxowner");
8107 468 : i_stxrelid = PQfnumber(res, "stxrelid");
8108 468 : i_stattarget = PQfnumber(res, "stxstattarget");
8109 :
8110 468 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8111 :
8112 842 : for (i = 0; i < ntups; i++)
8113 : {
8114 374 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8115 374 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8116 374 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8117 374 : AssignDumpId(&statsextinfo[i].dobj);
8118 374 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8119 748 : statsextinfo[i].dobj.namespace =
8120 374 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8121 374 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8122 748 : statsextinfo[i].stattable =
8123 374 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8124 374 : if (PQgetisnull(res, i, i_stattarget))
8125 276 : statsextinfo[i].stattarget = -1;
8126 : else
8127 98 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8128 :
8129 : /* Decide whether we want to dump it */
8130 374 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8131 : }
8132 :
8133 468 : PQclear(res);
8134 468 : destroyPQExpBuffer(query);
8135 : }
8136 :
8137 : /*
8138 : * getConstraints
8139 : *
8140 : * Get info about constraints on dumpable tables.
8141 : *
8142 : * Currently handles foreign keys only.
8143 : * Unique and primary key constraints are handled with indexes,
8144 : * while check constraints are processed in getTableAttrs().
8145 : */
8146 : void
8147 468 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8148 : {
8149 468 : PQExpBuffer query = createPQExpBuffer();
8150 468 : PQExpBuffer tbloids = createPQExpBuffer();
8151 : PGresult *res;
8152 : int ntups;
8153 : int curtblindx;
8154 468 : TableInfo *tbinfo = NULL;
8155 : ConstraintInfo *constrinfo;
8156 : int i_contableoid,
8157 : i_conoid,
8158 : i_conrelid,
8159 : i_conname,
8160 : i_confrelid,
8161 : i_conindid,
8162 : i_condef;
8163 :
8164 : /*
8165 : * We want to perform just one query against pg_constraint. However, we
8166 : * mustn't try to select every row of the catalog and then sort it out on
8167 : * the client side, because some of the server-side functions we need
8168 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8169 : * build an array of the OIDs of tables we care about (and now have lock
8170 : * on!), and use a WHERE clause to constrain which rows are selected.
8171 : */
8172 468 : appendPQExpBufferChar(tbloids, '{');
8173 124260 : for (int i = 0; i < numTables; i++)
8174 : {
8175 123792 : TableInfo *tinfo = &tblinfo[i];
8176 :
8177 123792 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8178 104414 : continue;
8179 :
8180 : /* OK, we need info for this table */
8181 19378 : if (tbloids->len > 1) /* do we have more than the '{'? */
8182 19060 : appendPQExpBufferChar(tbloids, ',');
8183 19378 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8184 : }
8185 468 : appendPQExpBufferChar(tbloids, '}');
8186 :
8187 468 : appendPQExpBufferStr(query,
8188 : "SELECT c.tableoid, c.oid, "
8189 : "conrelid, conname, confrelid, ");
8190 468 : if (fout->remoteVersion >= 110000)
8191 468 : appendPQExpBufferStr(query, "conindid, ");
8192 : else
8193 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8194 468 : appendPQExpBuffer(query,
8195 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8196 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8197 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8198 : "WHERE contype = 'f' ",
8199 : tbloids->data);
8200 468 : if (fout->remoteVersion >= 110000)
8201 468 : appendPQExpBufferStr(query,
8202 : "AND conparentid = 0 ");
8203 468 : appendPQExpBufferStr(query,
8204 : "ORDER BY conrelid, conname");
8205 :
8206 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8207 :
8208 468 : ntups = PQntuples(res);
8209 :
8210 468 : i_contableoid = PQfnumber(res, "tableoid");
8211 468 : i_conoid = PQfnumber(res, "oid");
8212 468 : i_conrelid = PQfnumber(res, "conrelid");
8213 468 : i_conname = PQfnumber(res, "conname");
8214 468 : i_confrelid = PQfnumber(res, "confrelid");
8215 468 : i_conindid = PQfnumber(res, "conindid");
8216 468 : i_condef = PQfnumber(res, "condef");
8217 :
8218 468 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8219 :
8220 468 : curtblindx = -1;
8221 940 : for (int j = 0; j < ntups; j++)
8222 : {
8223 472 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8224 : TableInfo *reftable;
8225 :
8226 : /*
8227 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8228 : * order.
8229 : */
8230 472 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8231 : {
8232 34350 : while (++curtblindx < numTables)
8233 : {
8234 34350 : tbinfo = &tblinfo[curtblindx];
8235 34350 : if (tbinfo->dobj.catId.oid == conrelid)
8236 440 : break;
8237 : }
8238 440 : if (curtblindx >= numTables)
8239 0 : pg_fatal("unrecognized table OID %u", conrelid);
8240 : }
8241 :
8242 472 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8243 472 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8244 472 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8245 472 : AssignDumpId(&constrinfo[j].dobj);
8246 472 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8247 472 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8248 472 : constrinfo[j].contable = tbinfo;
8249 472 : constrinfo[j].condomain = NULL;
8250 472 : constrinfo[j].contype = 'f';
8251 472 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8252 472 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8253 472 : constrinfo[j].conindex = 0;
8254 472 : constrinfo[j].condeferrable = false;
8255 472 : constrinfo[j].condeferred = false;
8256 472 : constrinfo[j].conislocal = true;
8257 472 : constrinfo[j].separate = true;
8258 :
8259 : /*
8260 : * Restoring an FK that points to a partitioned table requires that
8261 : * all partition indexes have been attached beforehand. Ensure that
8262 : * happens by making the constraint depend on each index partition
8263 : * attach object.
8264 : */
8265 472 : reftable = findTableByOid(constrinfo[j].confrelid);
8266 472 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8267 : {
8268 64 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8269 :
8270 64 : if (indexOid != InvalidOid)
8271 : {
8272 64 : for (int k = 0; k < reftable->numIndexes; k++)
8273 : {
8274 : IndxInfo *refidx;
8275 :
8276 : /* not our index? */
8277 64 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8278 0 : continue;
8279 :
8280 64 : refidx = &reftable->indexes[k];
8281 64 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8282 64 : break;
8283 : }
8284 : }
8285 : }
8286 : }
8287 :
8288 468 : PQclear(res);
8289 :
8290 468 : destroyPQExpBuffer(query);
8291 468 : destroyPQExpBuffer(tbloids);
8292 468 : }
8293 :
8294 : /*
8295 : * addConstrChildIdxDeps
8296 : *
8297 : * Recursive subroutine for getConstraints
8298 : *
8299 : * Given an object representing a foreign key constraint and an index on the
8300 : * partitioned table it references, mark the constraint object as dependent
8301 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8302 : * drilling down to their partitions if any. This ensures that the FK is not
8303 : * restored until the index is fully marked valid.
8304 : */
8305 : static void
8306 144 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8307 : {
8308 : SimplePtrListCell *cell;
8309 :
8310 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8311 :
8312 496 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8313 : {
8314 352 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8315 :
8316 352 : addObjectDependency(dobj, attach->dobj.dumpId);
8317 :
8318 352 : if (attach->partitionIdx->partattaches.head != NULL)
8319 80 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8320 : }
8321 144 : }
8322 :
8323 : /*
8324 : * getDomainConstraints
8325 : *
8326 : * Get info about constraints on a domain.
8327 : */
8328 : static void
8329 468 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8330 : {
8331 : ConstraintInfo *constrinfo;
8332 468 : PQExpBuffer query = createPQExpBuffer();
8333 : PGresult *res;
8334 : int i_tableoid,
8335 : i_oid,
8336 : i_conname,
8337 : i_consrc,
8338 : i_convalidated,
8339 : i_contype;
8340 : int ntups;
8341 :
8342 468 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8343 : {
8344 : /*
8345 : * Set up query for constraint-specific details. For servers 17 and
8346 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8347 : * just the latter.
8348 : */
8349 100 : appendPQExpBuffer(query,
8350 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8351 : "SELECT tableoid, oid, conname, "
8352 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8353 : "convalidated, contype "
8354 : "FROM pg_catalog.pg_constraint "
8355 : "WHERE contypid = $1 AND contype IN (%s) "
8356 : "ORDER BY conname",
8357 100 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8358 :
8359 100 : ExecuteSqlStatement(fout, query->data);
8360 :
8361 100 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8362 : }
8363 :
8364 468 : printfPQExpBuffer(query,
8365 : "EXECUTE getDomainConstraints('%u')",
8366 : tyinfo->dobj.catId.oid);
8367 :
8368 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8369 :
8370 468 : ntups = PQntuples(res);
8371 :
8372 468 : i_tableoid = PQfnumber(res, "tableoid");
8373 468 : i_oid = PQfnumber(res, "oid");
8374 468 : i_conname = PQfnumber(res, "conname");
8375 468 : i_consrc = PQfnumber(res, "consrc");
8376 468 : i_convalidated = PQfnumber(res, "convalidated");
8377 468 : i_contype = PQfnumber(res, "contype");
8378 :
8379 468 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8380 468 : tyinfo->domChecks = constrinfo;
8381 :
8382 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8383 924 : for (int i = 0, j = 0; i < ntups; i++)
8384 : {
8385 456 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8386 456 : char contype = (PQgetvalue(res, i, i_contype))[0];
8387 : ConstraintInfo *constraint;
8388 :
8389 456 : if (contype == CONSTRAINT_CHECK)
8390 : {
8391 324 : constraint = &constrinfo[j++];
8392 324 : tyinfo->nDomChecks++;
8393 : }
8394 : else
8395 : {
8396 : Assert(contype == CONSTRAINT_NOTNULL);
8397 : Assert(tyinfo->notnull == NULL);
8398 : /* use last item in array for the not-null constraint */
8399 132 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8400 132 : constraint = tyinfo->notnull;
8401 : }
8402 :
8403 456 : constraint->dobj.objType = DO_CONSTRAINT;
8404 456 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8405 456 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8406 456 : AssignDumpId(&(constraint->dobj));
8407 456 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8408 456 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8409 456 : constraint->contable = NULL;
8410 456 : constraint->condomain = tyinfo;
8411 456 : constraint->contype = contype;
8412 456 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8413 456 : constraint->confrelid = InvalidOid;
8414 456 : constraint->conindex = 0;
8415 456 : constraint->condeferrable = false;
8416 456 : constraint->condeferred = false;
8417 456 : constraint->conislocal = true;
8418 :
8419 456 : constraint->separate = !validated;
8420 :
8421 : /*
8422 : * Make the domain depend on the constraint, ensuring it won't be
8423 : * output till any constraint dependencies are OK. If the constraint
8424 : * has not been validated, it's going to be dumped after the domain
8425 : * anyway, so this doesn't matter.
8426 : */
8427 456 : if (validated)
8428 440 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8429 : }
8430 :
8431 468 : PQclear(res);
8432 :
8433 468 : destroyPQExpBuffer(query);
8434 468 : }
8435 :
8436 : /*
8437 : * getRules
8438 : * get basic information about every rule in the system
8439 : */
8440 : void
8441 468 : getRules(Archive *fout)
8442 : {
8443 : PGresult *res;
8444 : int ntups;
8445 : int i;
8446 468 : PQExpBuffer query = createPQExpBuffer();
8447 : RuleInfo *ruleinfo;
8448 : int i_tableoid;
8449 : int i_oid;
8450 : int i_rulename;
8451 : int i_ruletable;
8452 : int i_ev_type;
8453 : int i_is_instead;
8454 : int i_ev_enabled;
8455 :
8456 468 : appendPQExpBufferStr(query, "SELECT "
8457 : "tableoid, oid, rulename, "
8458 : "ev_class AS ruletable, ev_type, is_instead, "
8459 : "ev_enabled "
8460 : "FROM pg_rewrite "
8461 : "ORDER BY oid");
8462 :
8463 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8464 :
8465 468 : ntups = PQntuples(res);
8466 :
8467 468 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8468 :
8469 468 : i_tableoid = PQfnumber(res, "tableoid");
8470 468 : i_oid = PQfnumber(res, "oid");
8471 468 : i_rulename = PQfnumber(res, "rulename");
8472 468 : i_ruletable = PQfnumber(res, "ruletable");
8473 468 : i_ev_type = PQfnumber(res, "ev_type");
8474 468 : i_is_instead = PQfnumber(res, "is_instead");
8475 468 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8476 :
8477 72770 : for (i = 0; i < ntups; i++)
8478 : {
8479 : Oid ruletableoid;
8480 :
8481 72302 : ruleinfo[i].dobj.objType = DO_RULE;
8482 72302 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8483 72302 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8484 72302 : AssignDumpId(&ruleinfo[i].dobj);
8485 72302 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8486 72302 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8487 72302 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8488 72302 : if (ruleinfo[i].ruletable == NULL)
8489 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8490 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8491 72302 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8492 72302 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8493 72302 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8494 72302 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8495 72302 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8496 72302 : if (ruleinfo[i].ruletable)
8497 : {
8498 : /*
8499 : * If the table is a view or materialized view, force its ON
8500 : * SELECT rule to be sorted before the view itself --- this
8501 : * ensures that any dependencies for the rule affect the table's
8502 : * positioning. Other rules are forced to appear after their
8503 : * table.
8504 : */
8505 72302 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8506 1800 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8507 71642 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8508 : {
8509 70586 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8510 70586 : ruleinfo[i].dobj.dumpId);
8511 : /* We'll merge the rule into CREATE VIEW, if possible */
8512 70586 : ruleinfo[i].separate = false;
8513 : }
8514 : else
8515 : {
8516 1716 : addObjectDependency(&ruleinfo[i].dobj,
8517 1716 : ruleinfo[i].ruletable->dobj.dumpId);
8518 1716 : ruleinfo[i].separate = true;
8519 : }
8520 : }
8521 : else
8522 0 : ruleinfo[i].separate = true;
8523 : }
8524 :
8525 468 : PQclear(res);
8526 :
8527 468 : destroyPQExpBuffer(query);
8528 468 : }
8529 :
8530 : /*
8531 : * getTriggers
8532 : * get information about every trigger on a dumpable table
8533 : *
8534 : * Note: trigger data is not returned directly to the caller, but it
8535 : * does get entered into the DumpableObject tables.
8536 : */
8537 : void
8538 468 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8539 : {
8540 468 : PQExpBuffer query = createPQExpBuffer();
8541 468 : PQExpBuffer tbloids = createPQExpBuffer();
8542 : PGresult *res;
8543 : int ntups;
8544 : int curtblindx;
8545 : TriggerInfo *tginfo;
8546 : int i_tableoid,
8547 : i_oid,
8548 : i_tgrelid,
8549 : i_tgname,
8550 : i_tgenabled,
8551 : i_tgispartition,
8552 : i_tgdef;
8553 :
8554 : /*
8555 : * We want to perform just one query against pg_trigger. However, we
8556 : * mustn't try to select every row of the catalog and then sort it out on
8557 : * the client side, because some of the server-side functions we need
8558 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8559 : * build an array of the OIDs of tables we care about (and now have lock
8560 : * on!), and use a WHERE clause to constrain which rows are selected.
8561 : */
8562 468 : appendPQExpBufferChar(tbloids, '{');
8563 124260 : for (int i = 0; i < numTables; i++)
8564 : {
8565 123792 : TableInfo *tbinfo = &tblinfo[i];
8566 :
8567 123792 : if (!tbinfo->hastriggers ||
8568 2898 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8569 121418 : continue;
8570 :
8571 : /* OK, we need info for this table */
8572 2374 : if (tbloids->len > 1) /* do we have more than the '{'? */
8573 2258 : appendPQExpBufferChar(tbloids, ',');
8574 2374 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8575 : }
8576 468 : appendPQExpBufferChar(tbloids, '}');
8577 :
8578 468 : if (fout->remoteVersion >= 150000)
8579 : {
8580 : /*
8581 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8582 : * result in non-forward-compatible dumps of WHEN clauses due to
8583 : * under-parenthesization.
8584 : *
8585 : * NB: We need to see partition triggers in case the tgenabled flag
8586 : * has been changed from the parent.
8587 : */
8588 468 : appendPQExpBuffer(query,
8589 : "SELECT t.tgrelid, t.tgname, "
8590 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8591 : "t.tgenabled, t.tableoid, t.oid, "
8592 : "t.tgparentid <> 0 AS tgispartition\n"
8593 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8594 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8595 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8596 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8597 : "OR t.tgenabled != u.tgenabled) "
8598 : "ORDER BY t.tgrelid, t.tgname",
8599 : tbloids->data);
8600 : }
8601 0 : else if (fout->remoteVersion >= 130000)
8602 : {
8603 : /*
8604 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8605 : * result in non-forward-compatible dumps of WHEN clauses due to
8606 : * under-parenthesization.
8607 : *
8608 : * NB: We need to see tgisinternal triggers in partitions, in case the
8609 : * tgenabled flag has been changed from the parent.
8610 : */
8611 0 : appendPQExpBuffer(query,
8612 : "SELECT t.tgrelid, t.tgname, "
8613 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8614 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8615 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8616 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8617 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8618 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8619 : "ORDER BY t.tgrelid, t.tgname",
8620 : tbloids->data);
8621 : }
8622 0 : else if (fout->remoteVersion >= 110000)
8623 : {
8624 : /*
8625 : * NB: We need to see tgisinternal triggers in partitions, in case the
8626 : * tgenabled flag has been changed from the parent. No tgparentid in
8627 : * version 11-12, so we have to match them via pg_depend.
8628 : *
8629 : * See above about pretty=true in pg_get_triggerdef.
8630 : */
8631 0 : appendPQExpBuffer(query,
8632 : "SELECT t.tgrelid, t.tgname, "
8633 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8634 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8635 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8636 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8637 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8638 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8639 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8640 : " d.objid = t.oid "
8641 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8642 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8643 : "ORDER BY t.tgrelid, t.tgname",
8644 : tbloids->data);
8645 : }
8646 : else
8647 : {
8648 : /* See above about pretty=true in pg_get_triggerdef */
8649 0 : appendPQExpBuffer(query,
8650 : "SELECT t.tgrelid, t.tgname, "
8651 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8652 : "t.tgenabled, false as tgispartition, "
8653 : "t.tableoid, t.oid "
8654 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8655 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8656 : "WHERE NOT tgisinternal "
8657 : "ORDER BY t.tgrelid, t.tgname",
8658 : tbloids->data);
8659 : }
8660 :
8661 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8662 :
8663 468 : ntups = PQntuples(res);
8664 :
8665 468 : i_tableoid = PQfnumber(res, "tableoid");
8666 468 : i_oid = PQfnumber(res, "oid");
8667 468 : i_tgrelid = PQfnumber(res, "tgrelid");
8668 468 : i_tgname = PQfnumber(res, "tgname");
8669 468 : i_tgenabled = PQfnumber(res, "tgenabled");
8670 468 : i_tgispartition = PQfnumber(res, "tgispartition");
8671 468 : i_tgdef = PQfnumber(res, "tgdef");
8672 :
8673 468 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8674 :
8675 : /*
8676 : * Outer loop iterates once per table, not once per row. Incrementing of
8677 : * j is handled by the inner loop.
8678 : */
8679 468 : curtblindx = -1;
8680 1252 : for (int j = 0; j < ntups;)
8681 : {
8682 784 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8683 784 : TableInfo *tbinfo = NULL;
8684 : int numtrigs;
8685 :
8686 : /* Count rows for this table */
8687 1476 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8688 1360 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8689 668 : break;
8690 :
8691 : /*
8692 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8693 : * order.
8694 : */
8695 39738 : while (++curtblindx < numTables)
8696 : {
8697 39738 : tbinfo = &tblinfo[curtblindx];
8698 39738 : if (tbinfo->dobj.catId.oid == tgrelid)
8699 784 : break;
8700 : }
8701 784 : if (curtblindx >= numTables)
8702 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8703 :
8704 : /* Save data for this table */
8705 784 : tbinfo->triggers = tginfo + j;
8706 784 : tbinfo->numTriggers = numtrigs;
8707 :
8708 2260 : for (int c = 0; c < numtrigs; c++, j++)
8709 : {
8710 1476 : tginfo[j].dobj.objType = DO_TRIGGER;
8711 1476 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8712 1476 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8713 1476 : AssignDumpId(&tginfo[j].dobj);
8714 1476 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8715 1476 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8716 1476 : tginfo[j].tgtable = tbinfo;
8717 1476 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8718 1476 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8719 1476 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8720 : }
8721 : }
8722 :
8723 468 : PQclear(res);
8724 :
8725 468 : destroyPQExpBuffer(query);
8726 468 : destroyPQExpBuffer(tbloids);
8727 468 : }
8728 :
8729 : /*
8730 : * getEventTriggers
8731 : * get information about event triggers
8732 : */
8733 : void
8734 468 : getEventTriggers(Archive *fout)
8735 : {
8736 : int i;
8737 : PQExpBuffer query;
8738 : PGresult *res;
8739 : EventTriggerInfo *evtinfo;
8740 : int i_tableoid,
8741 : i_oid,
8742 : i_evtname,
8743 : i_evtevent,
8744 : i_evtowner,
8745 : i_evttags,
8746 : i_evtfname,
8747 : i_evtenabled;
8748 : int ntups;
8749 :
8750 : /* Before 9.3, there are no event triggers */
8751 468 : if (fout->remoteVersion < 90300)
8752 0 : return;
8753 :
8754 468 : query = createPQExpBuffer();
8755 :
8756 468 : appendPQExpBufferStr(query,
8757 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8758 : "evtevent, evtowner, "
8759 : "array_to_string(array("
8760 : "select quote_literal(x) "
8761 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8762 : "e.evtfoid::regproc as evtfname "
8763 : "FROM pg_event_trigger e "
8764 : "ORDER BY e.oid");
8765 :
8766 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8767 :
8768 468 : ntups = PQntuples(res);
8769 :
8770 468 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8771 :
8772 468 : i_tableoid = PQfnumber(res, "tableoid");
8773 468 : i_oid = PQfnumber(res, "oid");
8774 468 : i_evtname = PQfnumber(res, "evtname");
8775 468 : i_evtevent = PQfnumber(res, "evtevent");
8776 468 : i_evtowner = PQfnumber(res, "evtowner");
8777 468 : i_evttags = PQfnumber(res, "evttags");
8778 468 : i_evtfname = PQfnumber(res, "evtfname");
8779 468 : i_evtenabled = PQfnumber(res, "evtenabled");
8780 :
8781 586 : for (i = 0; i < ntups; i++)
8782 : {
8783 118 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8784 118 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8785 118 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8786 118 : AssignDumpId(&evtinfo[i].dobj);
8787 118 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8788 118 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8789 118 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8790 118 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8791 118 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8792 118 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8793 118 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8794 :
8795 : /* Decide whether we want to dump it */
8796 118 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8797 : }
8798 :
8799 468 : PQclear(res);
8800 :
8801 468 : destroyPQExpBuffer(query);
8802 : }
8803 :
8804 : /*
8805 : * getProcLangs
8806 : * get basic information about every procedural language in the system
8807 : *
8808 : * NB: this must run after getFuncs() because we assume we can do
8809 : * findFuncByOid().
8810 : */
8811 : void
8812 468 : getProcLangs(Archive *fout)
8813 : {
8814 : PGresult *res;
8815 : int ntups;
8816 : int i;
8817 468 : PQExpBuffer query = createPQExpBuffer();
8818 : ProcLangInfo *planginfo;
8819 : int i_tableoid;
8820 : int i_oid;
8821 : int i_lanname;
8822 : int i_lanpltrusted;
8823 : int i_lanplcallfoid;
8824 : int i_laninline;
8825 : int i_lanvalidator;
8826 : int i_lanacl;
8827 : int i_acldefault;
8828 : int i_lanowner;
8829 :
8830 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8831 : "lanname, lanpltrusted, lanplcallfoid, "
8832 : "laninline, lanvalidator, "
8833 : "lanacl, "
8834 : "acldefault('l', lanowner) AS acldefault, "
8835 : "lanowner "
8836 : "FROM pg_language "
8837 : "WHERE lanispl "
8838 : "ORDER BY oid");
8839 :
8840 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8841 :
8842 468 : ntups = PQntuples(res);
8843 :
8844 468 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8845 :
8846 468 : i_tableoid = PQfnumber(res, "tableoid");
8847 468 : i_oid = PQfnumber(res, "oid");
8848 468 : i_lanname = PQfnumber(res, "lanname");
8849 468 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8850 468 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8851 468 : i_laninline = PQfnumber(res, "laninline");
8852 468 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8853 468 : i_lanacl = PQfnumber(res, "lanacl");
8854 468 : i_acldefault = PQfnumber(res, "acldefault");
8855 468 : i_lanowner = PQfnumber(res, "lanowner");
8856 :
8857 1034 : for (i = 0; i < ntups; i++)
8858 : {
8859 566 : planginfo[i].dobj.objType = DO_PROCLANG;
8860 566 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8861 566 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8862 566 : AssignDumpId(&planginfo[i].dobj);
8863 :
8864 566 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8865 566 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8866 566 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8867 566 : planginfo[i].dacl.privtype = 0;
8868 566 : planginfo[i].dacl.initprivs = NULL;
8869 566 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
8870 566 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
8871 566 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
8872 566 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
8873 566 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
8874 :
8875 : /* Decide whether we want to dump it */
8876 566 : selectDumpableProcLang(&(planginfo[i]), fout);
8877 :
8878 : /* Mark whether language has an ACL */
8879 566 : if (!PQgetisnull(res, i, i_lanacl))
8880 98 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
8881 : }
8882 :
8883 468 : PQclear(res);
8884 :
8885 468 : destroyPQExpBuffer(query);
8886 468 : }
8887 :
8888 : /*
8889 : * getCasts
8890 : * get basic information about most casts in the system
8891 : *
8892 : * Skip casts from a range to its multirange, since we'll create those
8893 : * automatically.
8894 : */
8895 : void
8896 468 : getCasts(Archive *fout)
8897 : {
8898 : PGresult *res;
8899 : int ntups;
8900 : int i;
8901 468 : PQExpBuffer query = createPQExpBuffer();
8902 : CastInfo *castinfo;
8903 : int i_tableoid;
8904 : int i_oid;
8905 : int i_castsource;
8906 : int i_casttarget;
8907 : int i_castfunc;
8908 : int i_castcontext;
8909 : int i_castmethod;
8910 :
8911 468 : if (fout->remoteVersion >= 140000)
8912 : {
8913 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8914 : "castsource, casttarget, castfunc, castcontext, "
8915 : "castmethod "
8916 : "FROM pg_cast c "
8917 : "WHERE NOT EXISTS ( "
8918 : "SELECT 1 FROM pg_range r "
8919 : "WHERE c.castsource = r.rngtypid "
8920 : "AND c.casttarget = r.rngmultitypid "
8921 : ") "
8922 : "ORDER BY 3,4");
8923 : }
8924 : else
8925 : {
8926 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8927 : "castsource, casttarget, castfunc, castcontext, "
8928 : "castmethod "
8929 : "FROM pg_cast ORDER BY 3,4");
8930 : }
8931 :
8932 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8933 :
8934 468 : ntups = PQntuples(res);
8935 :
8936 468 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
8937 :
8938 468 : i_tableoid = PQfnumber(res, "tableoid");
8939 468 : i_oid = PQfnumber(res, "oid");
8940 468 : i_castsource = PQfnumber(res, "castsource");
8941 468 : i_casttarget = PQfnumber(res, "casttarget");
8942 468 : i_castfunc = PQfnumber(res, "castfunc");
8943 468 : i_castcontext = PQfnumber(res, "castcontext");
8944 468 : i_castmethod = PQfnumber(res, "castmethod");
8945 :
8946 111134 : for (i = 0; i < ntups; i++)
8947 : {
8948 : PQExpBufferData namebuf;
8949 : TypeInfo *sTypeInfo;
8950 : TypeInfo *tTypeInfo;
8951 :
8952 110666 : castinfo[i].dobj.objType = DO_CAST;
8953 110666 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8954 110666 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8955 110666 : AssignDumpId(&castinfo[i].dobj);
8956 110666 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
8957 110666 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
8958 110666 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
8959 110666 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
8960 110666 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
8961 :
8962 : /*
8963 : * Try to name cast as concatenation of typnames. This is only used
8964 : * for purposes of sorting. If we fail to find either type, the name
8965 : * will be an empty string.
8966 : */
8967 110666 : initPQExpBuffer(&namebuf);
8968 110666 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
8969 110666 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
8970 110666 : if (sTypeInfo && tTypeInfo)
8971 110666 : appendPQExpBuffer(&namebuf, "%s %s",
8972 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
8973 110666 : castinfo[i].dobj.name = namebuf.data;
8974 :
8975 : /* Decide whether we want to dump it */
8976 110666 : selectDumpableCast(&(castinfo[i]), fout);
8977 : }
8978 :
8979 468 : PQclear(res);
8980 :
8981 468 : destroyPQExpBuffer(query);
8982 468 : }
8983 :
8984 : static char *
8985 204 : get_language_name(Archive *fout, Oid langid)
8986 : {
8987 : PQExpBuffer query;
8988 : PGresult *res;
8989 : char *lanname;
8990 :
8991 204 : query = createPQExpBuffer();
8992 204 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
8993 204 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
8994 204 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
8995 204 : destroyPQExpBuffer(query);
8996 204 : PQclear(res);
8997 :
8998 204 : return lanname;
8999 : }
9000 :
9001 : /*
9002 : * getTransforms
9003 : * get basic information about every transform in the system
9004 : */
9005 : void
9006 468 : getTransforms(Archive *fout)
9007 : {
9008 : PGresult *res;
9009 : int ntups;
9010 : int i;
9011 : PQExpBuffer query;
9012 : TransformInfo *transforminfo;
9013 : int i_tableoid;
9014 : int i_oid;
9015 : int i_trftype;
9016 : int i_trflang;
9017 : int i_trffromsql;
9018 : int i_trftosql;
9019 :
9020 : /* Transforms didn't exist pre-9.5 */
9021 468 : if (fout->remoteVersion < 90500)
9022 0 : return;
9023 :
9024 468 : query = createPQExpBuffer();
9025 :
9026 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9027 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9028 : "FROM pg_transform "
9029 : "ORDER BY 3,4");
9030 :
9031 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9032 :
9033 468 : ntups = PQntuples(res);
9034 :
9035 468 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9036 :
9037 468 : i_tableoid = PQfnumber(res, "tableoid");
9038 468 : i_oid = PQfnumber(res, "oid");
9039 468 : i_trftype = PQfnumber(res, "trftype");
9040 468 : i_trflang = PQfnumber(res, "trflang");
9041 468 : i_trffromsql = PQfnumber(res, "trffromsql");
9042 468 : i_trftosql = PQfnumber(res, "trftosql");
9043 :
9044 586 : for (i = 0; i < ntups; i++)
9045 : {
9046 : PQExpBufferData namebuf;
9047 : TypeInfo *typeInfo;
9048 : char *lanname;
9049 :
9050 118 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9051 118 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9052 118 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9053 118 : AssignDumpId(&transforminfo[i].dobj);
9054 118 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9055 118 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9056 118 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9057 118 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9058 :
9059 : /*
9060 : * Try to name transform as concatenation of type and language name.
9061 : * This is only used for purposes of sorting. If we fail to find
9062 : * either, the name will be an empty string.
9063 : */
9064 118 : initPQExpBuffer(&namebuf);
9065 118 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9066 118 : lanname = get_language_name(fout, transforminfo[i].trflang);
9067 118 : if (typeInfo && lanname)
9068 118 : appendPQExpBuffer(&namebuf, "%s %s",
9069 : typeInfo->dobj.name, lanname);
9070 118 : transforminfo[i].dobj.name = namebuf.data;
9071 118 : free(lanname);
9072 :
9073 : /* Decide whether we want to dump it */
9074 118 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9075 : }
9076 :
9077 468 : PQclear(res);
9078 :
9079 468 : destroyPQExpBuffer(query);
9080 : }
9081 :
9082 : /*
9083 : * getTableAttrs -
9084 : * for each interesting table, read info about its attributes
9085 : * (names, types, default values, CHECK constraints, etc)
9086 : *
9087 : * modifies tblinfo
9088 : */
9089 : void
9090 468 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9091 : {
9092 468 : DumpOptions *dopt = fout->dopt;
9093 468 : PQExpBuffer q = createPQExpBuffer();
9094 468 : PQExpBuffer tbloids = createPQExpBuffer();
9095 468 : PQExpBuffer checkoids = createPQExpBuffer();
9096 468 : PQExpBuffer invalidnotnulloids = NULL;
9097 : PGresult *res;
9098 : int ntups;
9099 : int curtblindx;
9100 : int i_attrelid;
9101 : int i_attnum;
9102 : int i_attname;
9103 : int i_atttypname;
9104 : int i_attstattarget;
9105 : int i_attstorage;
9106 : int i_typstorage;
9107 : int i_attidentity;
9108 : int i_attgenerated;
9109 : int i_attisdropped;
9110 : int i_attlen;
9111 : int i_attalign;
9112 : int i_attislocal;
9113 : int i_notnull_name;
9114 : int i_notnull_comment;
9115 : int i_notnull_noinherit;
9116 : int i_notnull_islocal;
9117 : int i_notnull_invalidoid;
9118 : int i_attoptions;
9119 : int i_attcollation;
9120 : int i_attcompression;
9121 : int i_attfdwoptions;
9122 : int i_attmissingval;
9123 : int i_atthasdef;
9124 :
9125 : /*
9126 : * We want to perform just one query against pg_attribute, and then just
9127 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9128 : * (for CHECK constraints and for NOT NULL constraints). However, we
9129 : * mustn't try to select every row of those catalogs and then sort it out
9130 : * on the client side, because some of the server-side functions we need
9131 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9132 : * build an array of the OIDs of tables we care about (and now have lock
9133 : * on!), and use a WHERE clause to constrain which rows are selected.
9134 : */
9135 468 : appendPQExpBufferChar(tbloids, '{');
9136 468 : appendPQExpBufferChar(checkoids, '{');
9137 124260 : for (int i = 0; i < numTables; i++)
9138 : {
9139 123792 : TableInfo *tbinfo = &tblinfo[i];
9140 :
9141 : /* Don't bother to collect info for sequences */
9142 123792 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9143 1632 : continue;
9144 :
9145 : /*
9146 : * Don't bother with uninteresting tables, either. For binary
9147 : * upgrades, this is bypassed for pg_largeobject_metadata and
9148 : * pg_shdepend so that the columns names are collected for the
9149 : * corresponding COPY commands. Restoring the data for those catalogs
9150 : * is faster than restoring the equivalent set of large object
9151 : * commands. We can only do this for upgrades from v12 and newer; in
9152 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9153 : * the OID column is hidden and won't be dumped.
9154 : */
9155 122160 : if (!tbinfo->interesting &&
9156 103844 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9157 13304 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9158 13242 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9159 103720 : continue;
9160 :
9161 : /* OK, we need info for this table */
9162 18440 : if (tbloids->len > 1) /* do we have more than the '{'? */
9163 18086 : appendPQExpBufferChar(tbloids, ',');
9164 18440 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9165 :
9166 18440 : if (tbinfo->ncheck > 0)
9167 : {
9168 : /* Also make a list of the ones with check constraints */
9169 1362 : if (checkoids->len > 1) /* do we have more than the '{'? */
9170 1210 : appendPQExpBufferChar(checkoids, ',');
9171 1362 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9172 : }
9173 : }
9174 468 : appendPQExpBufferChar(tbloids, '}');
9175 468 : appendPQExpBufferChar(checkoids, '}');
9176 :
9177 : /*
9178 : * Find all the user attributes and their types.
9179 : *
9180 : * Since we only want to dump COLLATE clauses for attributes whose
9181 : * collation is different from their type's default, we use a CASE here to
9182 : * suppress uninteresting attcollations cheaply.
9183 : */
9184 468 : appendPQExpBufferStr(q,
9185 : "SELECT\n"
9186 : "a.attrelid,\n"
9187 : "a.attnum,\n"
9188 : "a.attname,\n"
9189 : "a.attstattarget,\n"
9190 : "a.attstorage,\n"
9191 : "t.typstorage,\n"
9192 : "a.atthasdef,\n"
9193 : "a.attisdropped,\n"
9194 : "a.attlen,\n"
9195 : "a.attalign,\n"
9196 : "a.attislocal,\n"
9197 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9198 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9199 : "CASE WHEN a.attcollation <> t.typcollation "
9200 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9201 : "pg_catalog.array_to_string(ARRAY("
9202 : "SELECT pg_catalog.quote_ident(option_name) || "
9203 : "' ' || pg_catalog.quote_literal(option_value) "
9204 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9205 : "ORDER BY option_name"
9206 : "), E',\n ') AS attfdwoptions,\n");
9207 :
9208 : /*
9209 : * Find out any NOT NULL markings for each column. In 18 and up we read
9210 : * pg_constraint to obtain the constraint name, and for valid constraints
9211 : * also pg_description to obtain its comment. notnull_noinherit is set
9212 : * according to the NO INHERIT property. For versions prior to 18, we
9213 : * store an empty string as the name when a constraint is marked as
9214 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9215 : * without a name); also, such cases are never NO INHERIT.
9216 : *
9217 : * For invalid constraints, we need to store their OIDs for processing
9218 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9219 : * is invalid, and NULL otherwise. Their comments are handled not here
9220 : * but by collectComments, because they're their own dumpable object.
9221 : *
9222 : * We track in notnull_islocal whether the constraint was defined directly
9223 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9224 : * might modify this later; that routine is also in charge of determining
9225 : * the correct inhcount.
9226 : */
9227 468 : if (fout->remoteVersion >= 180000)
9228 468 : appendPQExpBufferStr(q,
9229 : "co.conname AS notnull_name,\n"
9230 : "CASE WHEN co.convalidated THEN pt.description"
9231 : " ELSE NULL END AS notnull_comment,\n"
9232 : "CASE WHEN NOT co.convalidated THEN co.oid "
9233 : "ELSE NULL END AS notnull_invalidoid,\n"
9234 : "co.connoinherit AS notnull_noinherit,\n"
9235 : "co.conislocal AS notnull_islocal,\n");
9236 : else
9237 0 : appendPQExpBufferStr(q,
9238 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9239 : "NULL AS notnull_comment,\n"
9240 : "NULL AS notnull_invalidoid,\n"
9241 : "false AS notnull_noinherit,\n"
9242 : "a.attislocal AS notnull_islocal,\n");
9243 :
9244 468 : if (fout->remoteVersion >= 140000)
9245 468 : appendPQExpBufferStr(q,
9246 : "a.attcompression AS attcompression,\n");
9247 : else
9248 0 : appendPQExpBufferStr(q,
9249 : "'' AS attcompression,\n");
9250 :
9251 468 : if (fout->remoteVersion >= 100000)
9252 468 : appendPQExpBufferStr(q,
9253 : "a.attidentity,\n");
9254 : else
9255 0 : appendPQExpBufferStr(q,
9256 : "'' AS attidentity,\n");
9257 :
9258 468 : if (fout->remoteVersion >= 110000)
9259 468 : appendPQExpBufferStr(q,
9260 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9261 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9262 : else
9263 0 : appendPQExpBufferStr(q,
9264 : "NULL AS attmissingval,\n");
9265 :
9266 468 : if (fout->remoteVersion >= 120000)
9267 468 : appendPQExpBufferStr(q,
9268 : "a.attgenerated\n");
9269 : else
9270 0 : appendPQExpBufferStr(q,
9271 : "'' AS attgenerated\n");
9272 :
9273 : /* need left join to pg_type to not fail on dropped columns ... */
9274 468 : appendPQExpBuffer(q,
9275 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9276 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9277 : "LEFT JOIN pg_catalog.pg_type t "
9278 : "ON (a.atttypid = t.oid)\n",
9279 : tbloids->data);
9280 :
9281 : /*
9282 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9283 : * entries and pg_description to get their comments.
9284 : */
9285 468 : if (fout->remoteVersion >= 180000)
9286 468 : appendPQExpBufferStr(q,
9287 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9288 : "(a.attrelid = co.conrelid\n"
9289 : " AND co.contype = 'n' AND "
9290 : "co.conkey = array[a.attnum])\n"
9291 : " LEFT JOIN pg_catalog.pg_description pt ON "
9292 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9293 :
9294 468 : appendPQExpBufferStr(q,
9295 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9296 : "ORDER BY a.attrelid, a.attnum");
9297 :
9298 468 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9299 :
9300 468 : ntups = PQntuples(res);
9301 :
9302 468 : i_attrelid = PQfnumber(res, "attrelid");
9303 468 : i_attnum = PQfnumber(res, "attnum");
9304 468 : i_attname = PQfnumber(res, "attname");
9305 468 : i_atttypname = PQfnumber(res, "atttypname");
9306 468 : i_attstattarget = PQfnumber(res, "attstattarget");
9307 468 : i_attstorage = PQfnumber(res, "attstorage");
9308 468 : i_typstorage = PQfnumber(res, "typstorage");
9309 468 : i_attidentity = PQfnumber(res, "attidentity");
9310 468 : i_attgenerated = PQfnumber(res, "attgenerated");
9311 468 : i_attisdropped = PQfnumber(res, "attisdropped");
9312 468 : i_attlen = PQfnumber(res, "attlen");
9313 468 : i_attalign = PQfnumber(res, "attalign");
9314 468 : i_attislocal = PQfnumber(res, "attislocal");
9315 468 : i_notnull_name = PQfnumber(res, "notnull_name");
9316 468 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9317 468 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9318 468 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9319 468 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9320 468 : i_attoptions = PQfnumber(res, "attoptions");
9321 468 : i_attcollation = PQfnumber(res, "attcollation");
9322 468 : i_attcompression = PQfnumber(res, "attcompression");
9323 468 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9324 468 : i_attmissingval = PQfnumber(res, "attmissingval");
9325 468 : i_atthasdef = PQfnumber(res, "atthasdef");
9326 :
9327 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9328 468 : resetPQExpBuffer(tbloids);
9329 468 : appendPQExpBufferChar(tbloids, '{');
9330 :
9331 : /*
9332 : * Outer loop iterates once per table, not once per row. Incrementing of
9333 : * r is handled by the inner loop.
9334 : */
9335 468 : curtblindx = -1;
9336 18582 : for (int r = 0; r < ntups;)
9337 : {
9338 18114 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9339 18114 : TableInfo *tbinfo = NULL;
9340 : int numatts;
9341 : bool hasdefaults;
9342 :
9343 : /* Count rows for this table */
9344 68800 : for (numatts = 1; numatts < ntups - r; numatts++)
9345 68452 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9346 17766 : break;
9347 :
9348 : /*
9349 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9350 : * order.
9351 : */
9352 88102 : while (++curtblindx < numTables)
9353 : {
9354 88102 : tbinfo = &tblinfo[curtblindx];
9355 88102 : if (tbinfo->dobj.catId.oid == attrelid)
9356 18114 : break;
9357 : }
9358 18114 : if (curtblindx >= numTables)
9359 0 : pg_fatal("unrecognized table OID %u", attrelid);
9360 : /* cross-check that we only got requested tables */
9361 18114 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9362 18114 : (!tbinfo->interesting &&
9363 124 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9364 124 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9365 62 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9366 0 : pg_fatal("unexpected column data for table \"%s\"",
9367 : tbinfo->dobj.name);
9368 :
9369 : /* Save data for this table */
9370 18114 : tbinfo->numatts = numatts;
9371 18114 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9372 18114 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9373 18114 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9374 18114 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9375 18114 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9376 18114 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9377 18114 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9378 18114 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9379 18114 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9380 18114 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9381 18114 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9382 18114 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9383 18114 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9384 18114 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9385 18114 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9386 18114 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9387 18114 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9388 18114 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9389 18114 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9390 18114 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9391 18114 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9392 18114 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9393 18114 : hasdefaults = false;
9394 :
9395 86914 : for (int j = 0; j < numatts; j++, r++)
9396 : {
9397 68800 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9398 0 : pg_fatal("invalid column numbering in table \"%s\"",
9399 : tbinfo->dobj.name);
9400 68800 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9401 68800 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9402 68800 : if (PQgetisnull(res, r, i_attstattarget))
9403 68712 : tbinfo->attstattarget[j] = -1;
9404 : else
9405 88 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9406 68800 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9407 68800 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9408 68800 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9409 68800 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9410 68800 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9411 68800 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9412 68800 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9413 68800 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9414 68800 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9415 :
9416 : /* Handle not-null constraint name and flags */
9417 68800 : determineNotNullFlags(fout, res, r,
9418 : tbinfo, j,
9419 : i_notnull_name,
9420 : i_notnull_comment,
9421 : i_notnull_invalidoid,
9422 : i_notnull_noinherit,
9423 : i_notnull_islocal,
9424 : &invalidnotnulloids);
9425 :
9426 68800 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9427 68800 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9428 68800 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9429 68800 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9430 68800 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9431 68800 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9432 68800 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9433 68800 : tbinfo->attrdefs[j] = NULL; /* fix below */
9434 68800 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9435 3420 : hasdefaults = true;
9436 : }
9437 :
9438 18114 : if (hasdefaults)
9439 : {
9440 : /* Collect OIDs of interesting tables that have defaults */
9441 2598 : if (tbloids->len > 1) /* do we have more than the '{'? */
9442 2448 : appendPQExpBufferChar(tbloids, ',');
9443 2598 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9444 : }
9445 : }
9446 :
9447 : /* If invalidnotnulloids has any data, finalize it */
9448 468 : if (invalidnotnulloids != NULL)
9449 100 : appendPQExpBufferChar(invalidnotnulloids, '}');
9450 :
9451 468 : PQclear(res);
9452 :
9453 : /*
9454 : * Now get info about column defaults. This is skipped for a data-only
9455 : * dump, as it is only needed for table schemas.
9456 : */
9457 468 : if (dopt->dumpSchema && tbloids->len > 1)
9458 : {
9459 : AttrDefInfo *attrdefs;
9460 : int numDefaults;
9461 134 : TableInfo *tbinfo = NULL;
9462 :
9463 134 : pg_log_info("finding table default expressions");
9464 :
9465 134 : appendPQExpBufferChar(tbloids, '}');
9466 :
9467 134 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9468 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9469 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9470 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9471 : "ORDER BY a.adrelid, a.adnum",
9472 : tbloids->data);
9473 :
9474 134 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9475 :
9476 134 : numDefaults = PQntuples(res);
9477 134 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9478 :
9479 134 : curtblindx = -1;
9480 3358 : for (int j = 0; j < numDefaults; j++)
9481 : {
9482 3224 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9483 3224 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9484 3224 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9485 3224 : int adnum = atoi(PQgetvalue(res, j, 3));
9486 3224 : char *adsrc = PQgetvalue(res, j, 4);
9487 :
9488 : /*
9489 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9490 : * OID order.
9491 : */
9492 3224 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9493 : {
9494 45744 : while (++curtblindx < numTables)
9495 : {
9496 45744 : tbinfo = &tblinfo[curtblindx];
9497 45744 : if (tbinfo->dobj.catId.oid == adrelid)
9498 2462 : break;
9499 : }
9500 2462 : if (curtblindx >= numTables)
9501 0 : pg_fatal("unrecognized table OID %u", adrelid);
9502 : }
9503 :
9504 3224 : if (adnum <= 0 || adnum > tbinfo->numatts)
9505 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9506 : adnum, tbinfo->dobj.name);
9507 :
9508 : /*
9509 : * dropped columns shouldn't have defaults, but just in case,
9510 : * ignore 'em
9511 : */
9512 3224 : if (tbinfo->attisdropped[adnum - 1])
9513 0 : continue;
9514 :
9515 3224 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9516 3224 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9517 3224 : attrdefs[j].dobj.catId.oid = adoid;
9518 3224 : AssignDumpId(&attrdefs[j].dobj);
9519 3224 : attrdefs[j].adtable = tbinfo;
9520 3224 : attrdefs[j].adnum = adnum;
9521 3224 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9522 :
9523 3224 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9524 3224 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9525 :
9526 3224 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9527 :
9528 : /*
9529 : * Figure out whether the default/generation expression should be
9530 : * dumped as part of the main CREATE TABLE (or similar) command or
9531 : * as a separate ALTER TABLE (or similar) command. The preference
9532 : * is to put it into the CREATE command, but in some cases that's
9533 : * not possible.
9534 : */
9535 3224 : if (tbinfo->attgenerated[adnum - 1])
9536 : {
9537 : /*
9538 : * Column generation expressions cannot be dumped separately,
9539 : * because there is no syntax for it. By setting separate to
9540 : * false here we prevent the "default" from being processed as
9541 : * its own dumpable object. Later, flagInhAttrs() will mark
9542 : * it as not to be dumped at all, if possible (that is, if it
9543 : * can be inherited from a parent).
9544 : */
9545 1856 : attrdefs[j].separate = false;
9546 : }
9547 1368 : else if (tbinfo->relkind == RELKIND_VIEW)
9548 : {
9549 : /*
9550 : * Defaults on a VIEW must always be dumped as separate ALTER
9551 : * TABLE commands.
9552 : */
9553 72 : attrdefs[j].separate = true;
9554 : }
9555 1296 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9556 : {
9557 : /* column will be suppressed, print default separately */
9558 14 : attrdefs[j].separate = true;
9559 : }
9560 : else
9561 : {
9562 1282 : attrdefs[j].separate = false;
9563 : }
9564 :
9565 3224 : if (!attrdefs[j].separate)
9566 : {
9567 : /*
9568 : * Mark the default as needing to appear before the table, so
9569 : * that any dependencies it has must be emitted before the
9570 : * CREATE TABLE. If this is not possible, we'll change to
9571 : * "separate" mode while sorting dependencies.
9572 : */
9573 3138 : addObjectDependency(&tbinfo->dobj,
9574 3138 : attrdefs[j].dobj.dumpId);
9575 : }
9576 :
9577 3224 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9578 : }
9579 :
9580 134 : PQclear(res);
9581 : }
9582 :
9583 : /*
9584 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9585 : * data-only dump, as it is only needed for table schemas.
9586 : */
9587 468 : if (dopt->dumpSchema && invalidnotnulloids)
9588 : {
9589 : ConstraintInfo *constrs;
9590 : int numConstrs;
9591 : int i_tableoid;
9592 : int i_oid;
9593 : int i_conrelid;
9594 : int i_conname;
9595 : int i_consrc;
9596 : int i_conislocal;
9597 :
9598 88 : pg_log_info("finding invalid not-null constraints");
9599 :
9600 88 : resetPQExpBuffer(q);
9601 88 : appendPQExpBuffer(q,
9602 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9603 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9604 : "conislocal, convalidated "
9605 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9606 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9607 : "ORDER BY c.conrelid, c.conname",
9608 88 : invalidnotnulloids->data);
9609 :
9610 88 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9611 :
9612 88 : numConstrs = PQntuples(res);
9613 88 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9614 :
9615 88 : i_tableoid = PQfnumber(res, "tableoid");
9616 88 : i_oid = PQfnumber(res, "oid");
9617 88 : i_conrelid = PQfnumber(res, "conrelid");
9618 88 : i_conname = PQfnumber(res, "conname");
9619 88 : i_consrc = PQfnumber(res, "consrc");
9620 88 : i_conislocal = PQfnumber(res, "conislocal");
9621 :
9622 : /* As above, this loop iterates once per table, not once per row */
9623 88 : curtblindx = -1;
9624 272 : for (int j = 0; j < numConstrs;)
9625 : {
9626 184 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9627 184 : TableInfo *tbinfo = NULL;
9628 : int numcons;
9629 :
9630 : /* Count rows for this table */
9631 184 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9632 96 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9633 96 : break;
9634 :
9635 : /*
9636 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9637 : * OID order.
9638 : */
9639 31630 : while (++curtblindx < numTables)
9640 : {
9641 31630 : tbinfo = &tblinfo[curtblindx];
9642 31630 : if (tbinfo->dobj.catId.oid == conrelid)
9643 184 : break;
9644 : }
9645 184 : if (curtblindx >= numTables)
9646 0 : pg_fatal("unrecognized table OID %u", conrelid);
9647 :
9648 368 : for (int c = 0; c < numcons; c++, j++)
9649 : {
9650 184 : constrs[j].dobj.objType = DO_CONSTRAINT;
9651 184 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9652 184 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9653 184 : AssignDumpId(&constrs[j].dobj);
9654 184 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9655 184 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9656 184 : constrs[j].contable = tbinfo;
9657 184 : constrs[j].condomain = NULL;
9658 184 : constrs[j].contype = 'n';
9659 184 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9660 184 : constrs[j].confrelid = InvalidOid;
9661 184 : constrs[j].conindex = 0;
9662 184 : constrs[j].condeferrable = false;
9663 184 : constrs[j].condeferred = false;
9664 184 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9665 :
9666 : /*
9667 : * All invalid not-null constraints must be dumped separately,
9668 : * because CREATE TABLE would not create them as invalid, and
9669 : * also because they must be created after potentially
9670 : * violating data has been loaded.
9671 : */
9672 184 : constrs[j].separate = true;
9673 :
9674 184 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9675 : }
9676 : }
9677 88 : PQclear(res);
9678 : }
9679 :
9680 : /*
9681 : * Get info about table CHECK constraints. This is skipped for a
9682 : * data-only dump, as it is only needed for table schemas.
9683 : */
9684 468 : if (dopt->dumpSchema && checkoids->len > 2)
9685 : {
9686 : ConstraintInfo *constrs;
9687 : int numConstrs;
9688 : int i_tableoid;
9689 : int i_oid;
9690 : int i_conrelid;
9691 : int i_conname;
9692 : int i_consrc;
9693 : int i_conislocal;
9694 : int i_convalidated;
9695 :
9696 136 : pg_log_info("finding table check constraints");
9697 :
9698 136 : resetPQExpBuffer(q);
9699 136 : appendPQExpBuffer(q,
9700 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9701 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9702 : "conislocal, convalidated "
9703 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9704 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9705 : "WHERE contype = 'c' "
9706 : "ORDER BY c.conrelid, c.conname",
9707 : checkoids->data);
9708 :
9709 136 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9710 :
9711 136 : numConstrs = PQntuples(res);
9712 136 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9713 :
9714 136 : i_tableoid = PQfnumber(res, "tableoid");
9715 136 : i_oid = PQfnumber(res, "oid");
9716 136 : i_conrelid = PQfnumber(res, "conrelid");
9717 136 : i_conname = PQfnumber(res, "conname");
9718 136 : i_consrc = PQfnumber(res, "consrc");
9719 136 : i_conislocal = PQfnumber(res, "conislocal");
9720 136 : i_convalidated = PQfnumber(res, "convalidated");
9721 :
9722 : /* As above, this loop iterates once per table, not once per row */
9723 136 : curtblindx = -1;
9724 1396 : for (int j = 0; j < numConstrs;)
9725 : {
9726 1260 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9727 1260 : TableInfo *tbinfo = NULL;
9728 : int numcons;
9729 :
9730 : /* Count rows for this table */
9731 1630 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9732 1494 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9733 1124 : break;
9734 :
9735 : /*
9736 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9737 : * OID order.
9738 : */
9739 43582 : while (++curtblindx < numTables)
9740 : {
9741 43582 : tbinfo = &tblinfo[curtblindx];
9742 43582 : if (tbinfo->dobj.catId.oid == conrelid)
9743 1260 : break;
9744 : }
9745 1260 : if (curtblindx >= numTables)
9746 0 : pg_fatal("unrecognized table OID %u", conrelid);
9747 :
9748 1260 : if (numcons != tbinfo->ncheck)
9749 : {
9750 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9751 : "expected %d check constraints on table \"%s\" but found %d",
9752 : tbinfo->ncheck),
9753 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9754 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9755 0 : exit_nicely(1);
9756 : }
9757 :
9758 1260 : tbinfo->checkexprs = constrs + j;
9759 :
9760 2890 : for (int c = 0; c < numcons; c++, j++)
9761 : {
9762 1630 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9763 :
9764 1630 : constrs[j].dobj.objType = DO_CONSTRAINT;
9765 1630 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9766 1630 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9767 1630 : AssignDumpId(&constrs[j].dobj);
9768 1630 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9769 1630 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9770 1630 : constrs[j].contable = tbinfo;
9771 1630 : constrs[j].condomain = NULL;
9772 1630 : constrs[j].contype = 'c';
9773 1630 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9774 1630 : constrs[j].confrelid = InvalidOid;
9775 1630 : constrs[j].conindex = 0;
9776 1630 : constrs[j].condeferrable = false;
9777 1630 : constrs[j].condeferred = false;
9778 1630 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9779 :
9780 : /*
9781 : * An unvalidated constraint needs to be dumped separately, so
9782 : * that potentially-violating existing data is loaded before
9783 : * the constraint.
9784 : */
9785 1630 : constrs[j].separate = !validated;
9786 :
9787 1630 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9788 :
9789 : /*
9790 : * Mark the constraint as needing to appear before the table
9791 : * --- this is so that any other dependencies of the
9792 : * constraint will be emitted before we try to create the
9793 : * table. If the constraint is to be dumped separately, it
9794 : * will be dumped after data is loaded anyway, so don't do it.
9795 : * (There's an automatic dependency in the opposite direction
9796 : * anyway, so don't need to add one manually here.)
9797 : */
9798 1630 : if (!constrs[j].separate)
9799 1422 : addObjectDependency(&tbinfo->dobj,
9800 1422 : constrs[j].dobj.dumpId);
9801 :
9802 : /*
9803 : * We will detect later whether the constraint must be split
9804 : * out from the table definition.
9805 : */
9806 : }
9807 : }
9808 :
9809 136 : PQclear(res);
9810 : }
9811 :
9812 468 : destroyPQExpBuffer(q);
9813 468 : destroyPQExpBuffer(tbloids);
9814 468 : destroyPQExpBuffer(checkoids);
9815 468 : }
9816 :
9817 : /*
9818 : * Based on the getTableAttrs query's row corresponding to one column, set
9819 : * the name and flags to handle a not-null constraint for that column in
9820 : * the tbinfo struct.
9821 : *
9822 : * Result row 'r' is for tbinfo's attribute 'j'.
9823 : *
9824 : * There are four possibilities:
9825 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9826 : * (the constraint name) remains NULL.
9827 : * 2) The column has a constraint with no name (this is the case when
9828 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9829 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9830 : * 3) The column has an invalid not-null constraint. This must be treated
9831 : * as a separate object (because it must be created after the table data
9832 : * is loaded). So we add its OID to invalidnotnulloids for processing
9833 : * elsewhere and do nothing further with it here. We distinguish this
9834 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9835 : * value, which is the constraint OID. Valid constraints have a null OID.
9836 : * 4) The column has a constraint with a known name; in that case
9837 : * notnull_constrs carries that name and dumpTableSchema will print
9838 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9839 : * (table_column_not_null) and there's no comment on the constraint,
9840 : * there's no need to print that name in the dump, so notnull_constrs
9841 : * is set to the empty string and it behaves as case 2.
9842 : *
9843 : * In a child table that inherits from a parent already containing NOT NULL
9844 : * constraints and the columns in the child don't have their own NOT NULL
9845 : * declarations, we suppress printing constraints in the child: the
9846 : * constraints are acquired at the point where the child is attached to the
9847 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9848 : * set not here but in flagInhAttrs. That flag is also used when the
9849 : * constraint was validated in a child but all its parent have it as NOT
9850 : * VALID.
9851 : *
9852 : * Any of these constraints might have the NO INHERIT bit. If so we set
9853 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9854 : *
9855 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9856 : * to do the right thing in all but the trivial case. However, the downside
9857 : * of getting it wrong is simply that the name is printed rather than
9858 : * suppressed, so it's not a big deal.
9859 : *
9860 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9861 : * constraints are found, it is initialized and filled with the array of
9862 : * OIDs of such constraints, for later processing.
9863 : */
9864 : static void
9865 68800 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9866 : TableInfo *tbinfo, int j,
9867 : int i_notnull_name,
9868 : int i_notnull_comment,
9869 : int i_notnull_invalidoid,
9870 : int i_notnull_noinherit,
9871 : int i_notnull_islocal,
9872 : PQExpBuffer *invalidnotnulloids)
9873 : {
9874 68800 : DumpOptions *dopt = fout->dopt;
9875 :
9876 : /*
9877 : * If this not-null constraint is not valid, list its OID in
9878 : * invalidnotnulloids and do nothing further. It'll be processed
9879 : * elsewhere later.
9880 : *
9881 : * Because invalid not-null constraints are rare, we don't want to malloc
9882 : * invalidnotnulloids until we're sure we're going it need it, which
9883 : * happens here.
9884 : */
9885 68800 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
9886 : {
9887 196 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
9888 :
9889 196 : if (*invalidnotnulloids == NULL)
9890 : {
9891 100 : *invalidnotnulloids = createPQExpBuffer();
9892 100 : appendPQExpBufferChar(*invalidnotnulloids, '{');
9893 100 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
9894 : }
9895 : else
9896 96 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
9897 :
9898 : /*
9899 : * Track when a parent constraint is invalid for the cases where a
9900 : * child constraint has been validated independenly.
9901 : */
9902 196 : tbinfo->notnull_invalid[j] = true;
9903 :
9904 : /* nothing else to do */
9905 196 : tbinfo->notnull_constrs[j] = NULL;
9906 196 : return;
9907 : }
9908 :
9909 : /*
9910 : * notnull_noinh is straight from the query result. notnull_islocal also,
9911 : * though flagInhAttrs may change that one later.
9912 : */
9913 68604 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
9914 68604 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
9915 68604 : tbinfo->notnull_invalid[j] = false;
9916 :
9917 : /*
9918 : * Determine a constraint name to use. If the column is not marked not-
9919 : * null, we set NULL which cues ... to do nothing. An empty string says
9920 : * to print an unnamed NOT NULL, and anything else is a constraint name to
9921 : * use.
9922 : */
9923 68604 : if (fout->remoteVersion < 180000)
9924 : {
9925 : /*
9926 : * < 18 doesn't have not-null names, so an unnamed constraint is
9927 : * sufficient.
9928 : */
9929 0 : if (PQgetisnull(res, r, i_notnull_name))
9930 0 : tbinfo->notnull_constrs[j] = NULL;
9931 : else
9932 0 : tbinfo->notnull_constrs[j] = "";
9933 : }
9934 : else
9935 : {
9936 68604 : if (PQgetisnull(res, r, i_notnull_name))
9937 61638 : tbinfo->notnull_constrs[j] = NULL;
9938 : else
9939 : {
9940 : /*
9941 : * In binary upgrade of inheritance child tables, must have a
9942 : * constraint name that we can UPDATE later; same if there's a
9943 : * comment on the constraint.
9944 : */
9945 6966 : if ((dopt->binary_upgrade &&
9946 642 : !tbinfo->ispartition &&
9947 7452 : !tbinfo->notnull_islocal) ||
9948 6966 : !PQgetisnull(res, r, i_notnull_comment))
9949 : {
9950 116 : tbinfo->notnull_constrs[j] =
9951 116 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9952 : }
9953 : else
9954 : {
9955 : char *default_name;
9956 :
9957 : /* XXX should match ChooseConstraintName better */
9958 6850 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
9959 6850 : tbinfo->attnames[j]);
9960 6850 : if (strcmp(default_name,
9961 6850 : PQgetvalue(res, r, i_notnull_name)) == 0)
9962 4546 : tbinfo->notnull_constrs[j] = "";
9963 : else
9964 : {
9965 2304 : tbinfo->notnull_constrs[j] =
9966 2304 : pstrdup(PQgetvalue(res, r, i_notnull_name));
9967 : }
9968 6850 : free(default_name);
9969 : }
9970 : }
9971 : }
9972 : }
9973 :
9974 : /*
9975 : * Test whether a column should be printed as part of table's CREATE TABLE.
9976 : * Column number is zero-based.
9977 : *
9978 : * Normally this is always true, but it's false for dropped columns, as well
9979 : * as those that were inherited without any local definition. (If we print
9980 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
9981 : * For partitions, it's always true, because we want the partitions to be
9982 : * created independently and ATTACH PARTITION used afterwards.
9983 : *
9984 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
9985 : * attisdropped state later, so as to keep control of the physical column
9986 : * order.
9987 : *
9988 : * This function exists because there are scattered nonobvious places that
9989 : * must be kept in sync with this decision.
9990 : */
9991 : bool
9992 117436 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
9993 : {
9994 117436 : if (dopt->binary_upgrade)
9995 12340 : return true;
9996 105096 : if (tbinfo->attisdropped[colno])
9997 2132 : return false;
9998 102964 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
9999 : }
10000 :
10001 :
10002 : /*
10003 : * getTSParsers:
10004 : * get information about all text search parsers in the system catalogs
10005 : */
10006 : void
10007 468 : getTSParsers(Archive *fout)
10008 : {
10009 : PGresult *res;
10010 : int ntups;
10011 : int i;
10012 : PQExpBuffer query;
10013 : TSParserInfo *prsinfo;
10014 : int i_tableoid;
10015 : int i_oid;
10016 : int i_prsname;
10017 : int i_prsnamespace;
10018 : int i_prsstart;
10019 : int i_prstoken;
10020 : int i_prsend;
10021 : int i_prsheadline;
10022 : int i_prslextype;
10023 :
10024 468 : query = createPQExpBuffer();
10025 :
10026 : /*
10027 : * find all text search objects, including builtin ones; we filter out
10028 : * system-defined objects at dump-out time.
10029 : */
10030 :
10031 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10032 : "prsstart::oid, prstoken::oid, "
10033 : "prsend::oid, prsheadline::oid, prslextype::oid "
10034 : "FROM pg_ts_parser");
10035 :
10036 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10037 :
10038 468 : ntups = PQntuples(res);
10039 :
10040 468 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10041 :
10042 468 : i_tableoid = PQfnumber(res, "tableoid");
10043 468 : i_oid = PQfnumber(res, "oid");
10044 468 : i_prsname = PQfnumber(res, "prsname");
10045 468 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10046 468 : i_prsstart = PQfnumber(res, "prsstart");
10047 468 : i_prstoken = PQfnumber(res, "prstoken");
10048 468 : i_prsend = PQfnumber(res, "prsend");
10049 468 : i_prsheadline = PQfnumber(res, "prsheadline");
10050 468 : i_prslextype = PQfnumber(res, "prslextype");
10051 :
10052 1034 : for (i = 0; i < ntups; i++)
10053 : {
10054 566 : prsinfo[i].dobj.objType = DO_TSPARSER;
10055 566 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10056 566 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10057 566 : AssignDumpId(&prsinfo[i].dobj);
10058 566 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10059 1132 : prsinfo[i].dobj.namespace =
10060 566 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10061 566 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10062 566 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10063 566 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10064 566 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10065 566 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10066 :
10067 : /* Decide whether we want to dump it */
10068 566 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10069 : }
10070 :
10071 468 : PQclear(res);
10072 :
10073 468 : destroyPQExpBuffer(query);
10074 468 : }
10075 :
10076 : /*
10077 : * getTSDictionaries:
10078 : * get information about all text search dictionaries in the system catalogs
10079 : */
10080 : void
10081 468 : getTSDictionaries(Archive *fout)
10082 : {
10083 : PGresult *res;
10084 : int ntups;
10085 : int i;
10086 : PQExpBuffer query;
10087 : TSDictInfo *dictinfo;
10088 : int i_tableoid;
10089 : int i_oid;
10090 : int i_dictname;
10091 : int i_dictnamespace;
10092 : int i_dictowner;
10093 : int i_dicttemplate;
10094 : int i_dictinitoption;
10095 :
10096 468 : query = createPQExpBuffer();
10097 :
10098 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10099 : "dictnamespace, dictowner, "
10100 : "dicttemplate, dictinitoption "
10101 : "FROM pg_ts_dict");
10102 :
10103 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10104 :
10105 468 : ntups = PQntuples(res);
10106 :
10107 468 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10108 :
10109 468 : i_tableoid = PQfnumber(res, "tableoid");
10110 468 : i_oid = PQfnumber(res, "oid");
10111 468 : i_dictname = PQfnumber(res, "dictname");
10112 468 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10113 468 : i_dictowner = PQfnumber(res, "dictowner");
10114 468 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10115 468 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10116 :
10117 14786 : for (i = 0; i < ntups; i++)
10118 : {
10119 14318 : dictinfo[i].dobj.objType = DO_TSDICT;
10120 14318 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10121 14318 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10122 14318 : AssignDumpId(&dictinfo[i].dobj);
10123 14318 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10124 28636 : dictinfo[i].dobj.namespace =
10125 14318 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10126 14318 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10127 14318 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10128 14318 : if (PQgetisnull(res, i, i_dictinitoption))
10129 566 : dictinfo[i].dictinitoption = NULL;
10130 : else
10131 13752 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10132 :
10133 : /* Decide whether we want to dump it */
10134 14318 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10135 : }
10136 :
10137 468 : PQclear(res);
10138 :
10139 468 : destroyPQExpBuffer(query);
10140 468 : }
10141 :
10142 : /*
10143 : * getTSTemplates:
10144 : * get information about all text search templates in the system catalogs
10145 : */
10146 : void
10147 468 : getTSTemplates(Archive *fout)
10148 : {
10149 : PGresult *res;
10150 : int ntups;
10151 : int i;
10152 : PQExpBuffer query;
10153 : TSTemplateInfo *tmplinfo;
10154 : int i_tableoid;
10155 : int i_oid;
10156 : int i_tmplname;
10157 : int i_tmplnamespace;
10158 : int i_tmplinit;
10159 : int i_tmpllexize;
10160 :
10161 468 : query = createPQExpBuffer();
10162 :
10163 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10164 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10165 : "FROM pg_ts_template");
10166 :
10167 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10168 :
10169 468 : ntups = PQntuples(res);
10170 :
10171 468 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10172 :
10173 468 : i_tableoid = PQfnumber(res, "tableoid");
10174 468 : i_oid = PQfnumber(res, "oid");
10175 468 : i_tmplname = PQfnumber(res, "tmplname");
10176 468 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10177 468 : i_tmplinit = PQfnumber(res, "tmplinit");
10178 468 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10179 :
10180 2906 : for (i = 0; i < ntups; i++)
10181 : {
10182 2438 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10183 2438 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10184 2438 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10185 2438 : AssignDumpId(&tmplinfo[i].dobj);
10186 2438 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10187 4876 : tmplinfo[i].dobj.namespace =
10188 2438 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10189 2438 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10190 2438 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10191 :
10192 : /* Decide whether we want to dump it */
10193 2438 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10194 : }
10195 :
10196 468 : PQclear(res);
10197 :
10198 468 : destroyPQExpBuffer(query);
10199 468 : }
10200 :
10201 : /*
10202 : * getTSConfigurations:
10203 : * get information about all text search configurations
10204 : */
10205 : void
10206 468 : getTSConfigurations(Archive *fout)
10207 : {
10208 : PGresult *res;
10209 : int ntups;
10210 : int i;
10211 : PQExpBuffer query;
10212 : TSConfigInfo *cfginfo;
10213 : int i_tableoid;
10214 : int i_oid;
10215 : int i_cfgname;
10216 : int i_cfgnamespace;
10217 : int i_cfgowner;
10218 : int i_cfgparser;
10219 :
10220 468 : query = createPQExpBuffer();
10221 :
10222 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10223 : "cfgnamespace, cfgowner, cfgparser "
10224 : "FROM pg_ts_config");
10225 :
10226 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10227 :
10228 468 : ntups = PQntuples(res);
10229 :
10230 468 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10231 :
10232 468 : i_tableoid = PQfnumber(res, "tableoid");
10233 468 : i_oid = PQfnumber(res, "oid");
10234 468 : i_cfgname = PQfnumber(res, "cfgname");
10235 468 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10236 468 : i_cfgowner = PQfnumber(res, "cfgowner");
10237 468 : i_cfgparser = PQfnumber(res, "cfgparser");
10238 :
10239 14686 : for (i = 0; i < ntups; i++)
10240 : {
10241 14218 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10242 14218 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10243 14218 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10244 14218 : AssignDumpId(&cfginfo[i].dobj);
10245 14218 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10246 28436 : cfginfo[i].dobj.namespace =
10247 14218 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10248 14218 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10249 14218 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10250 :
10251 : /* Decide whether we want to dump it */
10252 14218 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10253 : }
10254 :
10255 468 : PQclear(res);
10256 :
10257 468 : destroyPQExpBuffer(query);
10258 468 : }
10259 :
10260 : /*
10261 : * getForeignDataWrappers:
10262 : * get information about all foreign-data wrappers in the system catalogs
10263 : */
10264 : void
10265 468 : getForeignDataWrappers(Archive *fout)
10266 : {
10267 : PGresult *res;
10268 : int ntups;
10269 : int i;
10270 : PQExpBuffer query;
10271 : FdwInfo *fdwinfo;
10272 : int i_tableoid;
10273 : int i_oid;
10274 : int i_fdwname;
10275 : int i_fdwowner;
10276 : int i_fdwhandler;
10277 : int i_fdwvalidator;
10278 : int i_fdwacl;
10279 : int i_acldefault;
10280 : int i_fdwoptions;
10281 :
10282 468 : query = createPQExpBuffer();
10283 :
10284 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10285 : "fdwowner, "
10286 : "fdwhandler::pg_catalog.regproc, "
10287 : "fdwvalidator::pg_catalog.regproc, "
10288 : "fdwacl, "
10289 : "acldefault('F', fdwowner) AS acldefault, "
10290 : "array_to_string(ARRAY("
10291 : "SELECT quote_ident(option_name) || ' ' || "
10292 : "quote_literal(option_value) "
10293 : "FROM pg_options_to_table(fdwoptions) "
10294 : "ORDER BY option_name"
10295 : "), E',\n ') AS fdwoptions "
10296 : "FROM pg_foreign_data_wrapper");
10297 :
10298 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10299 :
10300 468 : ntups = PQntuples(res);
10301 :
10302 468 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10303 :
10304 468 : i_tableoid = PQfnumber(res, "tableoid");
10305 468 : i_oid = PQfnumber(res, "oid");
10306 468 : i_fdwname = PQfnumber(res, "fdwname");
10307 468 : i_fdwowner = PQfnumber(res, "fdwowner");
10308 468 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10309 468 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10310 468 : i_fdwacl = PQfnumber(res, "fdwacl");
10311 468 : i_acldefault = PQfnumber(res, "acldefault");
10312 468 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10313 :
10314 624 : for (i = 0; i < ntups; i++)
10315 : {
10316 156 : fdwinfo[i].dobj.objType = DO_FDW;
10317 156 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10318 156 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10319 156 : AssignDumpId(&fdwinfo[i].dobj);
10320 156 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10321 156 : fdwinfo[i].dobj.namespace = NULL;
10322 156 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10323 156 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10324 156 : fdwinfo[i].dacl.privtype = 0;
10325 156 : fdwinfo[i].dacl.initprivs = NULL;
10326 156 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10327 156 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10328 156 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10329 156 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10330 :
10331 : /* Decide whether we want to dump it */
10332 156 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10333 :
10334 : /* Mark whether FDW has an ACL */
10335 156 : if (!PQgetisnull(res, i, i_fdwacl))
10336 98 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10337 : }
10338 :
10339 468 : PQclear(res);
10340 :
10341 468 : destroyPQExpBuffer(query);
10342 468 : }
10343 :
10344 : /*
10345 : * getForeignServers:
10346 : * get information about all foreign servers in the system catalogs
10347 : */
10348 : void
10349 468 : getForeignServers(Archive *fout)
10350 : {
10351 : PGresult *res;
10352 : int ntups;
10353 : int i;
10354 : PQExpBuffer query;
10355 : ForeignServerInfo *srvinfo;
10356 : int i_tableoid;
10357 : int i_oid;
10358 : int i_srvname;
10359 : int i_srvowner;
10360 : int i_srvfdw;
10361 : int i_srvtype;
10362 : int i_srvversion;
10363 : int i_srvacl;
10364 : int i_acldefault;
10365 : int i_srvoptions;
10366 :
10367 468 : query = createPQExpBuffer();
10368 :
10369 468 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10370 : "srvowner, "
10371 : "srvfdw, srvtype, srvversion, srvacl, "
10372 : "acldefault('S', srvowner) AS acldefault, "
10373 : "array_to_string(ARRAY("
10374 : "SELECT quote_ident(option_name) || ' ' || "
10375 : "quote_literal(option_value) "
10376 : "FROM pg_options_to_table(srvoptions) "
10377 : "ORDER BY option_name"
10378 : "), E',\n ') AS srvoptions "
10379 : "FROM pg_foreign_server");
10380 :
10381 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10382 :
10383 468 : ntups = PQntuples(res);
10384 :
10385 468 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10386 :
10387 468 : i_tableoid = PQfnumber(res, "tableoid");
10388 468 : i_oid = PQfnumber(res, "oid");
10389 468 : i_srvname = PQfnumber(res, "srvname");
10390 468 : i_srvowner = PQfnumber(res, "srvowner");
10391 468 : i_srvfdw = PQfnumber(res, "srvfdw");
10392 468 : i_srvtype = PQfnumber(res, "srvtype");
10393 468 : i_srvversion = PQfnumber(res, "srvversion");
10394 468 : i_srvacl = PQfnumber(res, "srvacl");
10395 468 : i_acldefault = PQfnumber(res, "acldefault");
10396 468 : i_srvoptions = PQfnumber(res, "srvoptions");
10397 :
10398 632 : for (i = 0; i < ntups; i++)
10399 : {
10400 164 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10401 164 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10402 164 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10403 164 : AssignDumpId(&srvinfo[i].dobj);
10404 164 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10405 164 : srvinfo[i].dobj.namespace = NULL;
10406 164 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10407 164 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10408 164 : srvinfo[i].dacl.privtype = 0;
10409 164 : srvinfo[i].dacl.initprivs = NULL;
10410 164 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10411 164 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10412 164 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10413 164 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10414 164 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10415 :
10416 : /* Decide whether we want to dump it */
10417 164 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10418 :
10419 : /* Servers have user mappings */
10420 164 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10421 :
10422 : /* Mark whether server has an ACL */
10423 164 : if (!PQgetisnull(res, i, i_srvacl))
10424 98 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10425 : }
10426 :
10427 468 : PQclear(res);
10428 :
10429 468 : destroyPQExpBuffer(query);
10430 468 : }
10431 :
10432 : /*
10433 : * getDefaultACLs:
10434 : * get information about all default ACL information in the system catalogs
10435 : */
10436 : void
10437 468 : getDefaultACLs(Archive *fout)
10438 : {
10439 468 : DumpOptions *dopt = fout->dopt;
10440 : DefaultACLInfo *daclinfo;
10441 : PQExpBuffer query;
10442 : PGresult *res;
10443 : int i_oid;
10444 : int i_tableoid;
10445 : int i_defaclrole;
10446 : int i_defaclnamespace;
10447 : int i_defaclobjtype;
10448 : int i_defaclacl;
10449 : int i_acldefault;
10450 : int i,
10451 : ntups;
10452 :
10453 468 : query = createPQExpBuffer();
10454 :
10455 : /*
10456 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10457 : * ACL for their object type. We should dump them as deltas from the
10458 : * default ACL, since that will be used as a starting point for
10459 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10460 : * non-global entries can only add privileges not revoke them. We must
10461 : * dump those as-is (i.e., as deltas from an empty ACL).
10462 : *
10463 : * We can use defaclobjtype as the object type for acldefault(), except
10464 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10465 : * 's'.
10466 : */
10467 468 : appendPQExpBufferStr(query,
10468 : "SELECT oid, tableoid, "
10469 : "defaclrole, "
10470 : "defaclnamespace, "
10471 : "defaclobjtype, "
10472 : "defaclacl, "
10473 : "CASE WHEN defaclnamespace = 0 THEN "
10474 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10475 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10476 : "defaclrole) ELSE '{}' END AS acldefault "
10477 : "FROM pg_default_acl");
10478 :
10479 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10480 :
10481 468 : ntups = PQntuples(res);
10482 :
10483 468 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10484 :
10485 468 : i_oid = PQfnumber(res, "oid");
10486 468 : i_tableoid = PQfnumber(res, "tableoid");
10487 468 : i_defaclrole = PQfnumber(res, "defaclrole");
10488 468 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10489 468 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10490 468 : i_defaclacl = PQfnumber(res, "defaclacl");
10491 468 : i_acldefault = PQfnumber(res, "acldefault");
10492 :
10493 860 : for (i = 0; i < ntups; i++)
10494 : {
10495 392 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10496 :
10497 392 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10498 392 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10499 392 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10500 392 : AssignDumpId(&daclinfo[i].dobj);
10501 : /* cheesy ... is it worth coming up with a better object name? */
10502 392 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10503 :
10504 392 : if (nspid != InvalidOid)
10505 196 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10506 : else
10507 196 : daclinfo[i].dobj.namespace = NULL;
10508 :
10509 392 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10510 392 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10511 392 : daclinfo[i].dacl.privtype = 0;
10512 392 : daclinfo[i].dacl.initprivs = NULL;
10513 392 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10514 392 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10515 :
10516 : /* Default ACLs are ACLs, of course */
10517 392 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10518 :
10519 : /* Decide whether we want to dump it */
10520 392 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10521 : }
10522 :
10523 468 : PQclear(res);
10524 :
10525 468 : destroyPQExpBuffer(query);
10526 468 : }
10527 :
10528 : /*
10529 : * getRoleName -- look up the name of a role, given its OID
10530 : *
10531 : * In current usage, we don't expect failures, so error out for a bad OID.
10532 : */
10533 : static const char *
10534 1481320 : getRoleName(const char *roleoid_str)
10535 : {
10536 1481320 : Oid roleoid = atooid(roleoid_str);
10537 :
10538 : /*
10539 : * Do binary search to find the appropriate item.
10540 : */
10541 1481320 : if (nrolenames > 0)
10542 : {
10543 1481320 : RoleNameItem *low = &rolenames[0];
10544 1481320 : RoleNameItem *high = &rolenames[nrolenames - 1];
10545 :
10546 5925814 : while (low <= high)
10547 : {
10548 5925814 : RoleNameItem *middle = low + (high - low) / 2;
10549 :
10550 5925814 : if (roleoid < middle->roleoid)
10551 4441462 : high = middle - 1;
10552 1484352 : else if (roleoid > middle->roleoid)
10553 3032 : low = middle + 1;
10554 : else
10555 1481320 : return middle->rolename; /* found a match */
10556 : }
10557 : }
10558 :
10559 0 : pg_fatal("role with OID %u does not exist", roleoid);
10560 : return NULL; /* keep compiler quiet */
10561 : }
10562 :
10563 : /*
10564 : * collectRoleNames --
10565 : *
10566 : * Construct a table of all known roles.
10567 : * The table is sorted by OID for speed in lookup.
10568 : */
10569 : static void
10570 470 : collectRoleNames(Archive *fout)
10571 : {
10572 : PGresult *res;
10573 : const char *query;
10574 : int i;
10575 :
10576 470 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10577 :
10578 470 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10579 :
10580 470 : nrolenames = PQntuples(res);
10581 :
10582 470 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10583 :
10584 10180 : for (i = 0; i < nrolenames; i++)
10585 : {
10586 9710 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10587 9710 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10588 : }
10589 :
10590 470 : PQclear(res);
10591 470 : }
10592 :
10593 : /*
10594 : * getAdditionalACLs
10595 : *
10596 : * We have now created all the DumpableObjects, and collected the ACL data
10597 : * that appears in the directly-associated catalog entries. However, there's
10598 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10599 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10600 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10601 : * Also, in versions having the pg_init_privs catalog, read that and load the
10602 : * information into the relevant DumpableObjects.
10603 : */
10604 : static void
10605 464 : getAdditionalACLs(Archive *fout)
10606 : {
10607 464 : PQExpBuffer query = createPQExpBuffer();
10608 : PGresult *res;
10609 : int ntups,
10610 : i;
10611 :
10612 : /* Check for per-column ACLs */
10613 464 : appendPQExpBufferStr(query,
10614 : "SELECT DISTINCT attrelid FROM pg_attribute "
10615 : "WHERE attacl IS NOT NULL");
10616 :
10617 464 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10618 :
10619 464 : ntups = PQntuples(res);
10620 1304 : for (i = 0; i < ntups; i++)
10621 : {
10622 840 : Oid relid = atooid(PQgetvalue(res, i, 0));
10623 : TableInfo *tblinfo;
10624 :
10625 840 : tblinfo = findTableByOid(relid);
10626 : /* OK to ignore tables we haven't got a DumpableObject for */
10627 840 : if (tblinfo)
10628 : {
10629 840 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10630 840 : tblinfo->hascolumnACLs = true;
10631 : }
10632 : }
10633 464 : PQclear(res);
10634 :
10635 : /* Fetch initial-privileges data */
10636 464 : if (fout->remoteVersion >= 90600)
10637 : {
10638 464 : printfPQExpBuffer(query,
10639 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10640 : "FROM pg_init_privs");
10641 :
10642 464 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10643 :
10644 464 : ntups = PQntuples(res);
10645 108728 : for (i = 0; i < ntups; i++)
10646 : {
10647 108264 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10648 108264 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10649 108264 : int objsubid = atoi(PQgetvalue(res, i, 2));
10650 108264 : char privtype = *(PQgetvalue(res, i, 3));
10651 108264 : char *initprivs = PQgetvalue(res, i, 4);
10652 : CatalogId objId;
10653 : DumpableObject *dobj;
10654 :
10655 108264 : objId.tableoid = classoid;
10656 108264 : objId.oid = objoid;
10657 108264 : dobj = findObjectByCatalogId(objId);
10658 : /* OK to ignore entries we haven't got a DumpableObject for */
10659 108264 : if (dobj)
10660 : {
10661 : /* Cope with sub-object initprivs */
10662 77272 : if (objsubid != 0)
10663 : {
10664 8400 : if (dobj->objType == DO_TABLE)
10665 : {
10666 : /* For a column initprivs, set the table's ACL flags */
10667 8400 : dobj->components |= DUMP_COMPONENT_ACL;
10668 8400 : ((TableInfo *) dobj)->hascolumnACLs = true;
10669 : }
10670 : else
10671 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10672 : classoid, objoid, objsubid);
10673 8856 : continue;
10674 : }
10675 :
10676 : /*
10677 : * We ignore any pg_init_privs.initprivs entry for the public
10678 : * schema, as explained in getNamespaces().
10679 : */
10680 68872 : if (dobj->objType == DO_NAMESPACE &&
10681 920 : strcmp(dobj->name, "public") == 0)
10682 456 : continue;
10683 :
10684 : /* Else it had better be of a type we think has ACLs */
10685 68416 : if (dobj->objType == DO_NAMESPACE ||
10686 67952 : dobj->objType == DO_TYPE ||
10687 67904 : dobj->objType == DO_FUNC ||
10688 67712 : dobj->objType == DO_AGG ||
10689 67664 : dobj->objType == DO_TABLE ||
10690 0 : dobj->objType == DO_PROCLANG ||
10691 0 : dobj->objType == DO_FDW ||
10692 0 : dobj->objType == DO_FOREIGN_SERVER)
10693 68416 : {
10694 68416 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10695 :
10696 68416 : daobj->dacl.privtype = privtype;
10697 68416 : daobj->dacl.initprivs = pstrdup(initprivs);
10698 : }
10699 : else
10700 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10701 : classoid, objoid, objsubid);
10702 : }
10703 : }
10704 464 : PQclear(res);
10705 : }
10706 :
10707 464 : destroyPQExpBuffer(query);
10708 464 : }
10709 :
10710 : /*
10711 : * dumpCommentExtended --
10712 : *
10713 : * This routine is used to dump any comments associated with the
10714 : * object handed to this routine. The routine takes the object type
10715 : * and object name (ready to print, except for schema decoration), plus
10716 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10717 : * plus catalog ID and subid which are the lookup key for pg_description,
10718 : * plus the dump ID for the object (for setting a dependency).
10719 : * If a matching pg_description entry is found, it is dumped.
10720 : *
10721 : * Note: in some cases, such as comments for triggers and rules, the "type"
10722 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10723 : * but it doesn't seem worth complicating the API for all callers to make
10724 : * it cleaner.
10725 : *
10726 : * Note: although this routine takes a dumpId for dependency purposes,
10727 : * that purpose is just to mark the dependency in the emitted dump file
10728 : * for possible future use by pg_restore. We do NOT use it for determining
10729 : * ordering of the comment in the dump file, because this routine is called
10730 : * after dependency sorting occurs. This routine should be called just after
10731 : * calling ArchiveEntry() for the specified object.
10732 : */
10733 : static void
10734 13182 : dumpCommentExtended(Archive *fout, const char *type,
10735 : const char *name, const char *namespace,
10736 : const char *owner, CatalogId catalogId,
10737 : int subid, DumpId dumpId,
10738 : const char *initdb_comment)
10739 : {
10740 13182 : DumpOptions *dopt = fout->dopt;
10741 : CommentItem *comments;
10742 : int ncomments;
10743 :
10744 : /* do nothing, if --no-comments is supplied */
10745 13182 : if (dopt->no_comments)
10746 0 : return;
10747 :
10748 : /* Comments are schema not data ... except LO comments are data */
10749 13182 : if (strcmp(type, "LARGE OBJECT") != 0)
10750 : {
10751 13060 : if (!dopt->dumpSchema)
10752 0 : return;
10753 : }
10754 : else
10755 : {
10756 : /* We do dump LO comments in binary-upgrade mode */
10757 122 : if (!dopt->dumpData && !dopt->binary_upgrade)
10758 0 : return;
10759 : }
10760 :
10761 : /* Search for comments associated with catalogId, using table */
10762 13182 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10763 : &comments);
10764 :
10765 : /* Is there one matching the subid? */
10766 13182 : while (ncomments > 0)
10767 : {
10768 13086 : if (comments->objsubid == subid)
10769 13086 : break;
10770 0 : comments++;
10771 0 : ncomments--;
10772 : }
10773 :
10774 13182 : if (initdb_comment != NULL)
10775 : {
10776 : static CommentItem empty_comment = {.descr = ""};
10777 :
10778 : /*
10779 : * initdb creates this object with a comment. Skip dumping the
10780 : * initdb-provided comment, which would complicate matters for
10781 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10782 : * comment, replicate that.
10783 : */
10784 340 : if (ncomments == 0)
10785 : {
10786 8 : comments = &empty_comment;
10787 8 : ncomments = 1;
10788 : }
10789 332 : else if (strcmp(comments->descr, initdb_comment) == 0)
10790 332 : ncomments = 0;
10791 : }
10792 :
10793 : /* If a comment exists, build COMMENT ON statement */
10794 13182 : if (ncomments > 0)
10795 : {
10796 12762 : PQExpBuffer query = createPQExpBuffer();
10797 12762 : PQExpBuffer tag = createPQExpBuffer();
10798 :
10799 12762 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10800 12762 : if (namespace && *namespace)
10801 12404 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10802 12762 : appendPQExpBuffer(query, "%s IS ", name);
10803 12762 : appendStringLiteralAH(query, comments->descr, fout);
10804 12762 : appendPQExpBufferStr(query, ";\n");
10805 :
10806 12762 : appendPQExpBuffer(tag, "%s %s", type, name);
10807 :
10808 : /*
10809 : * We mark comments as SECTION_NONE because they really belong in the
10810 : * same section as their parent, whether that is pre-data or
10811 : * post-data.
10812 : */
10813 12762 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10814 12762 : ARCHIVE_OPTS(.tag = tag->data,
10815 : .namespace = namespace,
10816 : .owner = owner,
10817 : .description = "COMMENT",
10818 : .section = SECTION_NONE,
10819 : .createStmt = query->data,
10820 : .deps = &dumpId,
10821 : .nDeps = 1));
10822 :
10823 12762 : destroyPQExpBuffer(query);
10824 12762 : destroyPQExpBuffer(tag);
10825 : }
10826 : }
10827 :
10828 : /*
10829 : * dumpComment --
10830 : *
10831 : * Typical simplification of the above function.
10832 : */
10833 : static inline void
10834 12774 : dumpComment(Archive *fout, const char *type,
10835 : const char *name, const char *namespace,
10836 : const char *owner, CatalogId catalogId,
10837 : int subid, DumpId dumpId)
10838 : {
10839 12774 : dumpCommentExtended(fout, type, name, namespace, owner,
10840 : catalogId, subid, dumpId, NULL);
10841 12774 : }
10842 :
10843 : /*
10844 : * appendNamedArgument --
10845 : *
10846 : * Convenience routine for constructing parameters of the form:
10847 : * 'paraname', 'value'::type
10848 : */
10849 : static void
10850 11290 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10851 : const char *argtype, const char *argval)
10852 : {
10853 11290 : appendPQExpBufferStr(out, ",\n\t");
10854 :
10855 11290 : appendStringLiteralAH(out, argname, fout);
10856 11290 : appendPQExpBufferStr(out, ", ");
10857 :
10858 11290 : appendStringLiteralAH(out, argval, fout);
10859 11290 : appendPQExpBuffer(out, "::%s", argtype);
10860 11290 : }
10861 :
10862 : /*
10863 : * fetchAttributeStats --
10864 : *
10865 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10866 : */
10867 : static PGresult *
10868 2242 : fetchAttributeStats(Archive *fout)
10869 : {
10870 2242 : ArchiveHandle *AH = (ArchiveHandle *) fout;
10871 2242 : PQExpBuffer nspnames = createPQExpBuffer();
10872 2242 : PQExpBuffer relnames = createPQExpBuffer();
10873 2242 : int count = 0;
10874 2242 : PGresult *res = NULL;
10875 : static TocEntry *te;
10876 : static bool restarted;
10877 2242 : int max_rels = MAX_ATTR_STATS_RELS;
10878 :
10879 : /*
10880 : * Our query for retrieving statistics for multiple relations uses WITH
10881 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
10882 : * in v9.4. For older versions, we resort to gathering statistics for a
10883 : * single relation at a time.
10884 : */
10885 2242 : if (fout->remoteVersion < 90400)
10886 0 : max_rels = 1;
10887 :
10888 : /* If we're just starting, set our TOC pointer. */
10889 2242 : if (!te)
10890 106 : te = AH->toc->next;
10891 :
10892 : /*
10893 : * We can't easily avoid a second TOC scan for the tar format because it
10894 : * writes restore.sql separately, which means we must execute the queries
10895 : * twice. This feels risky, but there is no known reason it should
10896 : * generate different output than the first pass. Even if it does, the
10897 : * worst-case scenario is that restore.sql might have different statistics
10898 : * data than the archive.
10899 : */
10900 2242 : if (!restarted && te == AH->toc && AH->format == archTar)
10901 : {
10902 2 : te = AH->toc->next;
10903 2 : restarted = true;
10904 : }
10905 :
10906 2242 : appendPQExpBufferChar(nspnames, '{');
10907 2242 : appendPQExpBufferChar(relnames, '{');
10908 :
10909 : /*
10910 : * Scan the TOC for the next set of relevant stats entries. We assume
10911 : * that statistics are dumped in the order they are listed in the TOC.
10912 : * This is perhaps not the sturdiest assumption, so we verify it matches
10913 : * reality in dumpRelationStats_dumper().
10914 : */
10915 33520 : for (; te != AH->toc && count < max_rels; te = te->next)
10916 : {
10917 31278 : if ((te->reqs & REQ_STATS) != 0 &&
10918 6922 : strcmp(te->desc, "STATISTICS DATA") == 0)
10919 : {
10920 6922 : appendPGArray(nspnames, te->namespace);
10921 6922 : appendPGArray(relnames, te->tag);
10922 6922 : count++;
10923 : }
10924 : }
10925 :
10926 2242 : appendPQExpBufferChar(nspnames, '}');
10927 2242 : appendPQExpBufferChar(relnames, '}');
10928 :
10929 : /* Execute the query for the next batch of relations. */
10930 2242 : if (count > 0)
10931 : {
10932 200 : PQExpBuffer query = createPQExpBuffer();
10933 :
10934 200 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
10935 200 : appendStringLiteralAH(query, nspnames->data, fout);
10936 200 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
10937 200 : appendStringLiteralAH(query, relnames->data, fout);
10938 200 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
10939 200 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10940 200 : destroyPQExpBuffer(query);
10941 : }
10942 :
10943 2242 : destroyPQExpBuffer(nspnames);
10944 2242 : destroyPQExpBuffer(relnames);
10945 2242 : return res;
10946 : }
10947 :
10948 : /*
10949 : * dumpRelationStats_dumper --
10950 : *
10951 : * Generate command to import stats into the relation on the new database.
10952 : * This routine is called by the Archiver when it wants the statistics to be
10953 : * dumped.
10954 : */
10955 : static char *
10956 6922 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
10957 : {
10958 6922 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
10959 : static PGresult *res;
10960 : static int rownum;
10961 : PQExpBuffer query;
10962 : PQExpBufferData out_data;
10963 6922 : PQExpBuffer out = &out_data;
10964 : int i_schemaname;
10965 : int i_tablename;
10966 : int i_attname;
10967 : int i_inherited;
10968 : int i_null_frac;
10969 : int i_avg_width;
10970 : int i_n_distinct;
10971 : int i_most_common_vals;
10972 : int i_most_common_freqs;
10973 : int i_histogram_bounds;
10974 : int i_correlation;
10975 : int i_most_common_elems;
10976 : int i_most_common_elem_freqs;
10977 : int i_elem_count_histogram;
10978 : int i_range_length_histogram;
10979 : int i_range_empty_frac;
10980 : int i_range_bounds_histogram;
10981 : static TocEntry *expected_te;
10982 :
10983 : /*
10984 : * fetchAttributeStats() assumes that the statistics are dumped in the
10985 : * order they are listed in the TOC. We verify that here for safety.
10986 : */
10987 6922 : if (!expected_te)
10988 106 : expected_te = ((ArchiveHandle *) fout)->toc;
10989 :
10990 6922 : expected_te = expected_te->next;
10991 27518 : while ((expected_te->reqs & REQ_STATS) == 0 ||
10992 6922 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
10993 20596 : expected_te = expected_te->next;
10994 :
10995 6922 : if (te != expected_te)
10996 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
10997 : te->dumpId, te->desc, te->tag,
10998 : expected_te->dumpId, expected_te->desc, expected_te->tag);
10999 :
11000 6922 : query = createPQExpBuffer();
11001 6922 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11002 : {
11003 106 : appendPQExpBufferStr(query,
11004 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11005 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11006 : "s.null_frac, s.avg_width, s.n_distinct, "
11007 : "s.most_common_vals, s.most_common_freqs, "
11008 : "s.histogram_bounds, s.correlation, "
11009 : "s.most_common_elems, s.most_common_elem_freqs, "
11010 : "s.elem_count_histogram, ");
11011 :
11012 106 : if (fout->remoteVersion >= 170000)
11013 106 : appendPQExpBufferStr(query,
11014 : "s.range_length_histogram, "
11015 : "s.range_empty_frac, "
11016 : "s.range_bounds_histogram ");
11017 : else
11018 0 : appendPQExpBufferStr(query,
11019 : "NULL AS range_length_histogram,"
11020 : "NULL AS range_empty_frac,"
11021 : "NULL AS range_bounds_histogram ");
11022 :
11023 : /*
11024 : * The results must be in the order of the relations supplied in the
11025 : * parameters to ensure we remain in sync as we walk through the TOC.
11026 : * The redundant filter clause on s.tablename = ANY(...) seems
11027 : * sufficient to convince the planner to use
11028 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11029 : * This may not work for all versions.
11030 : *
11031 : * Our query for retrieving statistics for multiple relations uses
11032 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11033 : * introduced in v9.4. For older versions, we resort to gathering
11034 : * statistics for a single relation at a time.
11035 : */
11036 106 : if (fout->remoteVersion >= 90400)
11037 106 : appendPQExpBufferStr(query,
11038 : "FROM pg_catalog.pg_stats s "
11039 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11040 : "ON s.schemaname = u.schemaname "
11041 : "AND s.tablename = u.tablename "
11042 : "WHERE s.tablename = ANY($2) "
11043 : "ORDER BY u.ord, s.attname, s.inherited");
11044 : else
11045 0 : appendPQExpBufferStr(query,
11046 : "FROM pg_catalog.pg_stats s "
11047 : "WHERE s.schemaname = $1[1] "
11048 : "AND s.tablename = $2[1] "
11049 : "ORDER BY s.attname, s.inherited");
11050 :
11051 106 : ExecuteSqlStatement(fout, query->data);
11052 :
11053 106 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11054 106 : resetPQExpBuffer(query);
11055 : }
11056 :
11057 6922 : initPQExpBuffer(out);
11058 :
11059 : /* restore relation stats */
11060 6922 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11061 6922 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11062 : fout->remoteVersion);
11063 6922 : appendPQExpBufferStr(out, "\t'schemaname', ");
11064 6922 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11065 6922 : appendPQExpBufferStr(out, ",\n");
11066 6922 : appendPQExpBufferStr(out, "\t'relname', ");
11067 6922 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11068 6922 : appendPQExpBufferStr(out, ",\n");
11069 6922 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11070 :
11071 : /*
11072 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11073 : * the relation is empty, or it could mean that it hadn't yet been
11074 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11075 : * This ambiguity allegedly can cause the planner to choose inefficient
11076 : * plans after restoring to v18 or newer. To deal with this, let's just
11077 : * set reltuples to -1 in that case.
11078 : */
11079 6922 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11080 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11081 : else
11082 6922 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11083 :
11084 6922 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11085 6922 : rsinfo->relallvisible);
11086 :
11087 6922 : if (fout->remoteVersion >= 180000)
11088 6922 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11089 :
11090 6922 : appendPQExpBufferStr(out, "\n);\n");
11091 :
11092 : /* Fetch the next batch of attribute statistics if needed. */
11093 6922 : if (rownum >= PQntuples(res))
11094 : {
11095 2242 : PQclear(res);
11096 2242 : res = fetchAttributeStats(fout);
11097 2242 : rownum = 0;
11098 : }
11099 :
11100 6922 : i_schemaname = PQfnumber(res, "schemaname");
11101 6922 : i_tablename = PQfnumber(res, "tablename");
11102 6922 : i_attname = PQfnumber(res, "attname");
11103 6922 : i_inherited = PQfnumber(res, "inherited");
11104 6922 : i_null_frac = PQfnumber(res, "null_frac");
11105 6922 : i_avg_width = PQfnumber(res, "avg_width");
11106 6922 : i_n_distinct = PQfnumber(res, "n_distinct");
11107 6922 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11108 6922 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11109 6922 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11110 6922 : i_correlation = PQfnumber(res, "correlation");
11111 6922 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11112 6922 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11113 6922 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11114 6922 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11115 6922 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11116 6922 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11117 :
11118 : /* restore attribute stats */
11119 8620 : for (; rownum < PQntuples(res); rownum++)
11120 : {
11121 : const char *attname;
11122 :
11123 : /* Stop if the next stat row in our cache isn't for this relation. */
11124 6378 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11125 1698 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11126 : break;
11127 :
11128 1698 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11129 1698 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11130 : fout->remoteVersion);
11131 1698 : appendPQExpBufferStr(out, "\t'schemaname', ");
11132 1698 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11133 1698 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11134 1698 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11135 :
11136 1698 : if (PQgetisnull(res, rownum, i_attname))
11137 0 : pg_fatal("unexpected null attname");
11138 1698 : attname = PQgetvalue(res, rownum, i_attname);
11139 :
11140 : /*
11141 : * Indexes look up attname in indAttNames to derive attnum, all others
11142 : * use attname directly. We must specify attnum for indexes, since
11143 : * their attnames are not necessarily stable across dump/reload.
11144 : */
11145 1698 : if (rsinfo->nindAttNames == 0)
11146 : {
11147 1616 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11148 1616 : appendStringLiteralAH(out, attname, fout);
11149 : }
11150 : else
11151 : {
11152 82 : bool found = false;
11153 :
11154 152 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11155 : {
11156 152 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11157 : {
11158 82 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11159 : i + 1);
11160 82 : found = true;
11161 82 : break;
11162 : }
11163 : }
11164 :
11165 82 : if (!found)
11166 0 : pg_fatal("could not find index attname \"%s\"", attname);
11167 : }
11168 :
11169 1698 : if (!PQgetisnull(res, rownum, i_inherited))
11170 1698 : appendNamedArgument(out, fout, "inherited", "boolean",
11171 1698 : PQgetvalue(res, rownum, i_inherited));
11172 1698 : if (!PQgetisnull(res, rownum, i_null_frac))
11173 1698 : appendNamedArgument(out, fout, "null_frac", "real",
11174 1698 : PQgetvalue(res, rownum, i_null_frac));
11175 1698 : if (!PQgetisnull(res, rownum, i_avg_width))
11176 1698 : appendNamedArgument(out, fout, "avg_width", "integer",
11177 1698 : PQgetvalue(res, rownum, i_avg_width));
11178 1698 : if (!PQgetisnull(res, rownum, i_n_distinct))
11179 1698 : appendNamedArgument(out, fout, "n_distinct", "real",
11180 1698 : PQgetvalue(res, rownum, i_n_distinct));
11181 1698 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11182 874 : appendNamedArgument(out, fout, "most_common_vals", "text",
11183 874 : PQgetvalue(res, rownum, i_most_common_vals));
11184 1698 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11185 874 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11186 874 : PQgetvalue(res, rownum, i_most_common_freqs));
11187 1698 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11188 1062 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11189 1062 : PQgetvalue(res, rownum, i_histogram_bounds));
11190 1698 : if (!PQgetisnull(res, rownum, i_correlation))
11191 1618 : appendNamedArgument(out, fout, "correlation", "real",
11192 1618 : PQgetvalue(res, rownum, i_correlation));
11193 1698 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11194 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11195 16 : PQgetvalue(res, rownum, i_most_common_elems));
11196 1698 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11197 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11198 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11199 1698 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11200 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11201 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11202 1698 : if (fout->remoteVersion >= 170000)
11203 : {
11204 1698 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11205 8 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11206 8 : PQgetvalue(res, rownum, i_range_length_histogram));
11207 1698 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11208 8 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11209 8 : PQgetvalue(res, rownum, i_range_empty_frac));
11210 1698 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11211 8 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11212 8 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11213 : }
11214 1698 : appendPQExpBufferStr(out, "\n);\n");
11215 : }
11216 :
11217 6922 : destroyPQExpBuffer(query);
11218 6922 : return out->data;
11219 : }
11220 :
11221 : /*
11222 : * dumpRelationStats --
11223 : *
11224 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11225 : * care of gathering the statistics and generating the restore commands when
11226 : * they are needed.
11227 : */
11228 : static void
11229 7060 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11230 : {
11231 7060 : const DumpableObject *dobj = &rsinfo->dobj;
11232 :
11233 : /* nothing to do if we are not dumping statistics */
11234 7060 : if (!fout->dopt->dumpStatistics)
11235 0 : return;
11236 :
11237 7060 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11238 7060 : ARCHIVE_OPTS(.tag = dobj->name,
11239 : .namespace = dobj->namespace->dobj.name,
11240 : .description = "STATISTICS DATA",
11241 : .section = rsinfo->section,
11242 : .defnFn = dumpRelationStats_dumper,
11243 : .defnArg = rsinfo,
11244 : .deps = dobj->dependencies,
11245 : .nDeps = dobj->nDeps));
11246 : }
11247 :
11248 : /*
11249 : * dumpTableComment --
11250 : *
11251 : * As above, but dump comments for both the specified table (or view)
11252 : * and its columns.
11253 : */
11254 : static void
11255 176 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11256 : const char *reltypename)
11257 : {
11258 176 : DumpOptions *dopt = fout->dopt;
11259 : CommentItem *comments;
11260 : int ncomments;
11261 : PQExpBuffer query;
11262 : PQExpBuffer tag;
11263 :
11264 : /* do nothing, if --no-comments is supplied */
11265 176 : if (dopt->no_comments)
11266 0 : return;
11267 :
11268 : /* Comments are SCHEMA not data */
11269 176 : if (!dopt->dumpSchema)
11270 0 : return;
11271 :
11272 : /* Search for comments associated with relation, using table */
11273 176 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11274 176 : tbinfo->dobj.catId.oid,
11275 : &comments);
11276 :
11277 : /* If comments exist, build COMMENT ON statements */
11278 176 : if (ncomments <= 0)
11279 0 : return;
11280 :
11281 176 : query = createPQExpBuffer();
11282 176 : tag = createPQExpBuffer();
11283 :
11284 496 : while (ncomments > 0)
11285 : {
11286 320 : const char *descr = comments->descr;
11287 320 : int objsubid = comments->objsubid;
11288 :
11289 320 : if (objsubid == 0)
11290 : {
11291 72 : resetPQExpBuffer(tag);
11292 72 : appendPQExpBuffer(tag, "%s %s", reltypename,
11293 72 : fmtId(tbinfo->dobj.name));
11294 :
11295 72 : resetPQExpBuffer(query);
11296 72 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11297 72 : fmtQualifiedDumpable(tbinfo));
11298 72 : appendStringLiteralAH(query, descr, fout);
11299 72 : appendPQExpBufferStr(query, ";\n");
11300 :
11301 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11302 72 : ARCHIVE_OPTS(.tag = tag->data,
11303 : .namespace = tbinfo->dobj.namespace->dobj.name,
11304 : .owner = tbinfo->rolname,
11305 : .description = "COMMENT",
11306 : .section = SECTION_NONE,
11307 : .createStmt = query->data,
11308 : .deps = &(tbinfo->dobj.dumpId),
11309 : .nDeps = 1));
11310 : }
11311 248 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11312 : {
11313 248 : resetPQExpBuffer(tag);
11314 248 : appendPQExpBuffer(tag, "COLUMN %s.",
11315 248 : fmtId(tbinfo->dobj.name));
11316 248 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11317 :
11318 248 : resetPQExpBuffer(query);
11319 248 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11320 248 : fmtQualifiedDumpable(tbinfo));
11321 248 : appendPQExpBuffer(query, "%s IS ",
11322 248 : fmtId(tbinfo->attnames[objsubid - 1]));
11323 248 : appendStringLiteralAH(query, descr, fout);
11324 248 : appendPQExpBufferStr(query, ";\n");
11325 :
11326 248 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11327 248 : ARCHIVE_OPTS(.tag = tag->data,
11328 : .namespace = tbinfo->dobj.namespace->dobj.name,
11329 : .owner = tbinfo->rolname,
11330 : .description = "COMMENT",
11331 : .section = SECTION_NONE,
11332 : .createStmt = query->data,
11333 : .deps = &(tbinfo->dobj.dumpId),
11334 : .nDeps = 1));
11335 : }
11336 :
11337 320 : comments++;
11338 320 : ncomments--;
11339 : }
11340 :
11341 176 : destroyPQExpBuffer(query);
11342 176 : destroyPQExpBuffer(tag);
11343 : }
11344 :
11345 : /*
11346 : * findComments --
11347 : *
11348 : * Find the comment(s), if any, associated with the given object. All the
11349 : * objsubid values associated with the given classoid/objoid are found with
11350 : * one search.
11351 : */
11352 : static int
11353 13430 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11354 : {
11355 13430 : CommentItem *middle = NULL;
11356 : CommentItem *low;
11357 : CommentItem *high;
11358 : int nmatch;
11359 :
11360 : /*
11361 : * Do binary search to find some item matching the object.
11362 : */
11363 13430 : low = &comments[0];
11364 13430 : high = &comments[ncomments - 1];
11365 133776 : while (low <= high)
11366 : {
11367 133680 : middle = low + (high - low) / 2;
11368 :
11369 133680 : if (classoid < middle->classoid)
11370 16228 : high = middle - 1;
11371 117452 : else if (classoid > middle->classoid)
11372 14820 : low = middle + 1;
11373 102632 : else if (objoid < middle->objoid)
11374 43182 : high = middle - 1;
11375 59450 : else if (objoid > middle->objoid)
11376 46116 : low = middle + 1;
11377 : else
11378 13334 : break; /* found a match */
11379 : }
11380 :
11381 13430 : if (low > high) /* no matches */
11382 : {
11383 96 : *items = NULL;
11384 96 : return 0;
11385 : }
11386 :
11387 : /*
11388 : * Now determine how many items match the object. The search loop
11389 : * invariant still holds: only items between low and high inclusive could
11390 : * match.
11391 : */
11392 13334 : nmatch = 1;
11393 13478 : while (middle > low)
11394 : {
11395 6204 : if (classoid != middle[-1].classoid ||
11396 5930 : objoid != middle[-1].objoid)
11397 : break;
11398 144 : middle--;
11399 144 : nmatch++;
11400 : }
11401 :
11402 13334 : *items = middle;
11403 :
11404 13334 : middle += nmatch;
11405 13334 : while (middle <= high)
11406 : {
11407 7212 : if (classoid != middle->classoid ||
11408 6470 : objoid != middle->objoid)
11409 : break;
11410 0 : middle++;
11411 0 : nmatch++;
11412 : }
11413 :
11414 13334 : return nmatch;
11415 : }
11416 :
11417 : /*
11418 : * collectComments --
11419 : *
11420 : * Construct a table of all comments available for database objects;
11421 : * also set the has-comment component flag for each relevant object.
11422 : *
11423 : * We used to do per-object queries for the comments, but it's much faster
11424 : * to pull them all over at once, and on most databases the memory cost
11425 : * isn't high.
11426 : *
11427 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11428 : */
11429 : static void
11430 468 : collectComments(Archive *fout)
11431 : {
11432 : PGresult *res;
11433 : PQExpBuffer query;
11434 : int i_description;
11435 : int i_classoid;
11436 : int i_objoid;
11437 : int i_objsubid;
11438 : int ntups;
11439 : int i;
11440 : DumpableObject *dobj;
11441 :
11442 468 : query = createPQExpBuffer();
11443 :
11444 468 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11445 : "FROM pg_catalog.pg_description "
11446 : "ORDER BY classoid, objoid, objsubid");
11447 :
11448 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11449 :
11450 : /* Construct lookup table containing OIDs in numeric form */
11451 :
11452 468 : i_description = PQfnumber(res, "description");
11453 468 : i_classoid = PQfnumber(res, "classoid");
11454 468 : i_objoid = PQfnumber(res, "objoid");
11455 468 : i_objsubid = PQfnumber(res, "objsubid");
11456 :
11457 468 : ntups = PQntuples(res);
11458 :
11459 468 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11460 468 : ncomments = 0;
11461 468 : dobj = NULL;
11462 :
11463 2492852 : for (i = 0; i < ntups; i++)
11464 : {
11465 : CatalogId objId;
11466 : int subid;
11467 :
11468 2492384 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11469 2492384 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11470 2492384 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11471 :
11472 : /* We needn't remember comments that don't match any dumpable object */
11473 2492384 : if (dobj == NULL ||
11474 897686 : dobj->catId.tableoid != objId.tableoid ||
11475 892090 : dobj->catId.oid != objId.oid)
11476 2492188 : dobj = findObjectByCatalogId(objId);
11477 2492384 : if (dobj == NULL)
11478 1594246 : continue;
11479 :
11480 : /*
11481 : * Comments on columns of composite types are linked to the type's
11482 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11483 : * in the type's own DumpableObject.
11484 : */
11485 898138 : if (subid != 0 && dobj->objType == DO_TABLE &&
11486 432 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11487 98 : {
11488 : TypeInfo *cTypeInfo;
11489 :
11490 98 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11491 98 : if (cTypeInfo)
11492 98 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11493 : }
11494 : else
11495 898040 : dobj->components |= DUMP_COMPONENT_COMMENT;
11496 :
11497 898138 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11498 898138 : comments[ncomments].classoid = objId.tableoid;
11499 898138 : comments[ncomments].objoid = objId.oid;
11500 898138 : comments[ncomments].objsubid = subid;
11501 898138 : ncomments++;
11502 : }
11503 :
11504 468 : PQclear(res);
11505 468 : destroyPQExpBuffer(query);
11506 468 : }
11507 :
11508 : /*
11509 : * dumpDumpableObject
11510 : *
11511 : * This routine and its subsidiaries are responsible for creating
11512 : * ArchiveEntries (TOC objects) for each object to be dumped.
11513 : */
11514 : static void
11515 1740206 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11516 : {
11517 : /*
11518 : * Clear any dump-request bits for components that don't exist for this
11519 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11520 : * request for every kind of object.)
11521 : */
11522 1740206 : dobj->dump &= dobj->components;
11523 :
11524 : /* Now, short-circuit if there's nothing to be done here. */
11525 1740206 : if (dobj->dump == 0)
11526 1542682 : return;
11527 :
11528 197524 : switch (dobj->objType)
11529 : {
11530 1250 : case DO_NAMESPACE:
11531 1250 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11532 1250 : break;
11533 38 : case DO_EXTENSION:
11534 38 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11535 38 : break;
11536 2170 : case DO_TYPE:
11537 2170 : dumpType(fout, (const TypeInfo *) dobj);
11538 2170 : break;
11539 196 : case DO_SHELL_TYPE:
11540 196 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11541 196 : break;
11542 5376 : case DO_FUNC:
11543 5376 : dumpFunc(fout, (const FuncInfo *) dobj);
11544 5376 : break;
11545 874 : case DO_AGG:
11546 874 : dumpAgg(fout, (const AggInfo *) dobj);
11547 874 : break;
11548 5098 : case DO_OPERATOR:
11549 5098 : dumpOpr(fout, (const OprInfo *) dobj);
11550 5098 : break;
11551 182 : case DO_ACCESS_METHOD:
11552 182 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11553 182 : break;
11554 1362 : case DO_OPCLASS:
11555 1362 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11556 1362 : break;
11557 1156 : case DO_OPFAMILY:
11558 1156 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11559 1156 : break;
11560 5108 : case DO_COLLATION:
11561 5108 : dumpCollation(fout, (const CollInfo *) dobj);
11562 5108 : break;
11563 852 : case DO_CONVERSION:
11564 852 : dumpConversion(fout, (const ConvInfo *) dobj);
11565 852 : break;
11566 81182 : case DO_TABLE:
11567 81182 : dumpTable(fout, (const TableInfo *) dobj);
11568 81182 : break;
11569 3900 : case DO_TABLE_ATTACH:
11570 3900 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11571 3900 : break;
11572 2916 : case DO_ATTRDEF:
11573 2916 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11574 2916 : break;
11575 6986 : case DO_INDEX:
11576 6986 : dumpIndex(fout, (const IndxInfo *) dobj);
11577 6986 : break;
11578 1480 : case DO_INDEX_ATTACH:
11579 1480 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11580 1480 : break;
11581 314 : case DO_STATSEXT:
11582 314 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11583 314 : break;
11584 852 : case DO_REFRESH_MATVIEW:
11585 852 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11586 852 : break;
11587 2990 : case DO_RULE:
11588 2990 : dumpRule(fout, (const RuleInfo *) dobj);
11589 2990 : break;
11590 1476 : case DO_TRIGGER:
11591 1476 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11592 1476 : break;
11593 98 : case DO_EVENT_TRIGGER:
11594 98 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11595 98 : break;
11596 6078 : case DO_CONSTRAINT:
11597 6078 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11598 6078 : break;
11599 472 : case DO_FK_CONSTRAINT:
11600 472 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11601 472 : break;
11602 180 : case DO_PROCLANG:
11603 180 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11604 180 : break;
11605 178 : case DO_CAST:
11606 178 : dumpCast(fout, (const CastInfo *) dobj);
11607 178 : break;
11608 98 : case DO_TRANSFORM:
11609 98 : dumpTransform(fout, (const TransformInfo *) dobj);
11610 98 : break;
11611 1136 : case DO_SEQUENCE_SET:
11612 1136 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11613 1136 : break;
11614 12594 : case DO_TABLE_DATA:
11615 12594 : dumpTableData(fout, (const TableDataInfo *) dobj);
11616 12594 : break;
11617 38910 : case DO_DUMMY_TYPE:
11618 : /* table rowtypes and array types are never dumped separately */
11619 38910 : break;
11620 90 : case DO_TSPARSER:
11621 90 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11622 90 : break;
11623 408 : case DO_TSDICT:
11624 408 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11625 408 : break;
11626 114 : case DO_TSTEMPLATE:
11627 114 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11628 114 : break;
11629 328 : case DO_TSCONFIG:
11630 328 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11631 328 : break;
11632 118 : case DO_FDW:
11633 118 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11634 118 : break;
11635 126 : case DO_FOREIGN_SERVER:
11636 126 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11637 126 : break;
11638 332 : case DO_DEFAULT_ACL:
11639 332 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11640 332 : break;
11641 162 : case DO_LARGE_OBJECT:
11642 162 : dumpLO(fout, (const LoInfo *) dobj);
11643 162 : break;
11644 164 : case DO_LARGE_OBJECT_DATA:
11645 164 : if (dobj->dump & DUMP_COMPONENT_DATA)
11646 : {
11647 : LoInfo *loinfo;
11648 : TocEntry *te;
11649 :
11650 164 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11651 164 : if (loinfo == NULL)
11652 0 : pg_fatal("missing metadata for large objects \"%s\"",
11653 : dobj->name);
11654 :
11655 164 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11656 164 : ARCHIVE_OPTS(.tag = dobj->name,
11657 : .owner = loinfo->rolname,
11658 : .description = "BLOBS",
11659 : .section = SECTION_DATA,
11660 : .deps = dobj->dependencies,
11661 : .nDeps = dobj->nDeps,
11662 : .dumpFn = dumpLOs,
11663 : .dumpArg = loinfo));
11664 :
11665 : /*
11666 : * Set the TocEntry's dataLength in case we are doing a
11667 : * parallel dump and want to order dump jobs by table size.
11668 : * (We need some size estimate for every TocEntry with a
11669 : * DataDumper function.) We don't currently have any cheap
11670 : * way to estimate the size of LOs, but fortunately it doesn't
11671 : * matter too much as long as we get large batches of LOs
11672 : * processed reasonably early. Assume 8K per blob.
11673 : */
11674 164 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11675 : }
11676 164 : break;
11677 780 : case DO_POLICY:
11678 780 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11679 780 : break;
11680 412 : case DO_PUBLICATION:
11681 412 : dumpPublication(fout, (const PublicationInfo *) dobj);
11682 412 : break;
11683 574 : case DO_PUBLICATION_REL:
11684 574 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11685 574 : break;
11686 164 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11687 164 : dumpPublicationNamespace(fout,
11688 : (const PublicationSchemaInfo *) dobj);
11689 164 : break;
11690 250 : case DO_SUBSCRIPTION:
11691 250 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11692 250 : break;
11693 4 : case DO_SUBSCRIPTION_REL:
11694 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11695 4 : break;
11696 7060 : case DO_REL_STATS:
11697 7060 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11698 7060 : break;
11699 936 : case DO_PRE_DATA_BOUNDARY:
11700 : case DO_POST_DATA_BOUNDARY:
11701 : /* never dumped, nothing to do */
11702 936 : break;
11703 : }
11704 : }
11705 :
11706 : /*
11707 : * dumpNamespace
11708 : * writes out to fout the queries to recreate a user-defined namespace
11709 : */
11710 : static void
11711 1250 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11712 : {
11713 1250 : DumpOptions *dopt = fout->dopt;
11714 : PQExpBuffer q;
11715 : PQExpBuffer delq;
11716 : char *qnspname;
11717 :
11718 : /* Do nothing if not dumping schema */
11719 1250 : if (!dopt->dumpSchema)
11720 56 : return;
11721 :
11722 1194 : q = createPQExpBuffer();
11723 1194 : delq = createPQExpBuffer();
11724 :
11725 1194 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11726 :
11727 1194 : if (nspinfo->create)
11728 : {
11729 796 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11730 796 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11731 : }
11732 : else
11733 : {
11734 : /* see selectDumpableNamespace() */
11735 398 : appendPQExpBufferStr(delq,
11736 : "-- *not* dropping schema, since initdb creates it\n");
11737 398 : appendPQExpBufferStr(q,
11738 : "-- *not* creating schema, since initdb creates it\n");
11739 : }
11740 :
11741 1194 : if (dopt->binary_upgrade)
11742 156 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11743 : "SCHEMA", qnspname, NULL);
11744 :
11745 1194 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11746 466 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11747 466 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11748 : .owner = nspinfo->rolname,
11749 : .description = "SCHEMA",
11750 : .section = SECTION_PRE_DATA,
11751 : .createStmt = q->data,
11752 : .dropStmt = delq->data));
11753 :
11754 : /* Dump Schema Comments and Security Labels */
11755 1194 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11756 : {
11757 408 : const char *initdb_comment = NULL;
11758 :
11759 408 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11760 340 : initdb_comment = "standard public schema";
11761 408 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11762 408 : NULL, nspinfo->rolname,
11763 408 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11764 : initdb_comment);
11765 : }
11766 :
11767 1194 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11768 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11769 0 : NULL, nspinfo->rolname,
11770 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11771 :
11772 1194 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11773 936 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11774 : qnspname, NULL, NULL,
11775 936 : NULL, nspinfo->rolname, &nspinfo->dacl);
11776 :
11777 1194 : free(qnspname);
11778 :
11779 1194 : destroyPQExpBuffer(q);
11780 1194 : destroyPQExpBuffer(delq);
11781 : }
11782 :
11783 : /*
11784 : * dumpExtension
11785 : * writes out to fout the queries to recreate an extension
11786 : */
11787 : static void
11788 38 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11789 : {
11790 38 : DumpOptions *dopt = fout->dopt;
11791 : PQExpBuffer q;
11792 : PQExpBuffer delq;
11793 : char *qextname;
11794 :
11795 : /* Do nothing if not dumping schema */
11796 38 : if (!dopt->dumpSchema)
11797 2 : return;
11798 :
11799 36 : q = createPQExpBuffer();
11800 36 : delq = createPQExpBuffer();
11801 :
11802 36 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11803 :
11804 36 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11805 :
11806 36 : if (!dopt->binary_upgrade)
11807 : {
11808 : /*
11809 : * In a regular dump, we simply create the extension, intentionally
11810 : * not specifying a version, so that the destination installation's
11811 : * default version is used.
11812 : *
11813 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11814 : * types; but there are various scenarios in which it's convenient to
11815 : * manually create the desired extension before restoring, so we
11816 : * prefer to allow it to exist already.
11817 : */
11818 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11819 34 : qextname, fmtId(extinfo->namespace));
11820 : }
11821 : else
11822 : {
11823 : /*
11824 : * In binary-upgrade mode, it's critical to reproduce the state of the
11825 : * database exactly, so our procedure is to create an empty extension,
11826 : * restore all the contained objects normally, and add them to the
11827 : * extension one by one. This function performs just the first of
11828 : * those steps. binary_upgrade_extension_member() takes care of
11829 : * adding member objects as they're created.
11830 : */
11831 : int i;
11832 : int n;
11833 :
11834 2 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11835 :
11836 : /*
11837 : * We unconditionally create the extension, so we must drop it if it
11838 : * exists. This could happen if the user deleted 'plpgsql' and then
11839 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11840 : */
11841 2 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11842 :
11843 2 : appendPQExpBufferStr(q,
11844 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11845 2 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11846 2 : appendPQExpBufferStr(q, ", ");
11847 2 : appendStringLiteralAH(q, extinfo->namespace, fout);
11848 2 : appendPQExpBufferStr(q, ", ");
11849 2 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11850 2 : appendStringLiteralAH(q, extinfo->extversion, fout);
11851 2 : appendPQExpBufferStr(q, ", ");
11852 :
11853 : /*
11854 : * Note that we're pushing extconfig (an OID array) back into
11855 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11856 : * preserved in binary upgrade.
11857 : */
11858 2 : if (strlen(extinfo->extconfig) > 2)
11859 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11860 : else
11861 0 : appendPQExpBufferStr(q, "NULL");
11862 2 : appendPQExpBufferStr(q, ", ");
11863 2 : if (strlen(extinfo->extcondition) > 2)
11864 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11865 : else
11866 0 : appendPQExpBufferStr(q, "NULL");
11867 2 : appendPQExpBufferStr(q, ", ");
11868 2 : appendPQExpBufferStr(q, "ARRAY[");
11869 2 : n = 0;
11870 4 : for (i = 0; i < extinfo->dobj.nDeps; i++)
11871 : {
11872 : DumpableObject *extobj;
11873 :
11874 2 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
11875 2 : if (extobj && extobj->objType == DO_EXTENSION)
11876 : {
11877 0 : if (n++ > 0)
11878 0 : appendPQExpBufferChar(q, ',');
11879 0 : appendStringLiteralAH(q, extobj->name, fout);
11880 : }
11881 : }
11882 2 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
11883 2 : appendPQExpBufferStr(q, ");\n");
11884 : }
11885 :
11886 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11887 36 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
11888 36 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
11889 : .description = "EXTENSION",
11890 : .section = SECTION_PRE_DATA,
11891 : .createStmt = q->data,
11892 : .dropStmt = delq->data));
11893 :
11894 : /* Dump Extension Comments and Security Labels */
11895 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11896 36 : dumpComment(fout, "EXTENSION", qextname,
11897 : NULL, "",
11898 36 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11899 :
11900 36 : if (extinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11901 0 : dumpSecLabel(fout, "EXTENSION", qextname,
11902 : NULL, "",
11903 0 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
11904 :
11905 36 : free(qextname);
11906 :
11907 36 : destroyPQExpBuffer(q);
11908 36 : destroyPQExpBuffer(delq);
11909 : }
11910 :
11911 : /*
11912 : * dumpType
11913 : * writes out to fout the queries to recreate a user-defined type
11914 : */
11915 : static void
11916 2170 : dumpType(Archive *fout, const TypeInfo *tyinfo)
11917 : {
11918 2170 : DumpOptions *dopt = fout->dopt;
11919 :
11920 : /* Do nothing if not dumping schema */
11921 2170 : if (!dopt->dumpSchema)
11922 86 : return;
11923 :
11924 : /* Dump out in proper style */
11925 2084 : if (tyinfo->typtype == TYPTYPE_BASE)
11926 610 : dumpBaseType(fout, tyinfo);
11927 1474 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
11928 456 : dumpDomain(fout, tyinfo);
11929 1018 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
11930 364 : dumpCompositeType(fout, tyinfo);
11931 654 : else if (tyinfo->typtype == TYPTYPE_ENUM)
11932 140 : dumpEnumType(fout, tyinfo);
11933 514 : else if (tyinfo->typtype == TYPTYPE_RANGE)
11934 276 : dumpRangeType(fout, tyinfo);
11935 238 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
11936 88 : dumpUndefinedType(fout, tyinfo);
11937 : else
11938 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
11939 : tyinfo->dobj.name);
11940 : }
11941 :
11942 : /*
11943 : * dumpEnumType
11944 : * writes out to fout the queries to recreate a user-defined enum type
11945 : */
11946 : static void
11947 140 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
11948 : {
11949 140 : DumpOptions *dopt = fout->dopt;
11950 140 : PQExpBuffer q = createPQExpBuffer();
11951 140 : PQExpBuffer delq = createPQExpBuffer();
11952 140 : PQExpBuffer query = createPQExpBuffer();
11953 : PGresult *res;
11954 : int num,
11955 : i;
11956 : Oid enum_oid;
11957 : char *qtypname;
11958 : char *qualtypname;
11959 : char *label;
11960 : int i_enumlabel;
11961 : int i_oid;
11962 :
11963 140 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
11964 : {
11965 : /* Set up query for enum-specific details */
11966 92 : appendPQExpBufferStr(query,
11967 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
11968 : "SELECT oid, enumlabel "
11969 : "FROM pg_catalog.pg_enum "
11970 : "WHERE enumtypid = $1 "
11971 : "ORDER BY enumsortorder");
11972 :
11973 92 : ExecuteSqlStatement(fout, query->data);
11974 :
11975 92 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
11976 : }
11977 :
11978 140 : printfPQExpBuffer(query,
11979 : "EXECUTE dumpEnumType('%u')",
11980 140 : tyinfo->dobj.catId.oid);
11981 :
11982 140 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11983 :
11984 140 : num = PQntuples(res);
11985 :
11986 140 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
11987 140 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
11988 :
11989 : /*
11990 : * CASCADE shouldn't be required here as for normal types since the I/O
11991 : * functions are generic and do not get dropped.
11992 : */
11993 140 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
11994 :
11995 140 : if (dopt->binary_upgrade)
11996 10 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
11997 10 : tyinfo->dobj.catId.oid,
11998 : false, false);
11999 :
12000 140 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12001 : qualtypname);
12002 :
12003 140 : if (!dopt->binary_upgrade)
12004 : {
12005 130 : i_enumlabel = PQfnumber(res, "enumlabel");
12006 :
12007 : /* Labels with server-assigned oids */
12008 1080 : for (i = 0; i < num; i++)
12009 : {
12010 950 : label = PQgetvalue(res, i, i_enumlabel);
12011 950 : if (i > 0)
12012 820 : appendPQExpBufferChar(q, ',');
12013 950 : appendPQExpBufferStr(q, "\n ");
12014 950 : appendStringLiteralAH(q, label, fout);
12015 : }
12016 : }
12017 :
12018 140 : appendPQExpBufferStr(q, "\n);\n");
12019 :
12020 140 : if (dopt->binary_upgrade)
12021 : {
12022 10 : i_oid = PQfnumber(res, "oid");
12023 10 : i_enumlabel = PQfnumber(res, "enumlabel");
12024 :
12025 : /* Labels with dump-assigned (preserved) oids */
12026 116 : for (i = 0; i < num; i++)
12027 : {
12028 106 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12029 106 : label = PQgetvalue(res, i, i_enumlabel);
12030 :
12031 106 : if (i == 0)
12032 10 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12033 106 : appendPQExpBuffer(q,
12034 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12035 : enum_oid);
12036 106 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12037 106 : appendStringLiteralAH(q, label, fout);
12038 106 : appendPQExpBufferStr(q, ";\n\n");
12039 : }
12040 : }
12041 :
12042 140 : if (dopt->binary_upgrade)
12043 10 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12044 : "TYPE", qtypname,
12045 10 : tyinfo->dobj.namespace->dobj.name);
12046 :
12047 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12048 140 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12049 140 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12050 : .namespace = tyinfo->dobj.namespace->dobj.name,
12051 : .owner = tyinfo->rolname,
12052 : .description = "TYPE",
12053 : .section = SECTION_PRE_DATA,
12054 : .createStmt = q->data,
12055 : .dropStmt = delq->data));
12056 :
12057 : /* Dump Type Comments and Security Labels */
12058 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12059 72 : dumpComment(fout, "TYPE", qtypname,
12060 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12061 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12062 :
12063 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12064 0 : dumpSecLabel(fout, "TYPE", qtypname,
12065 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12066 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12067 :
12068 140 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12069 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12070 : qtypname, NULL,
12071 72 : tyinfo->dobj.namespace->dobj.name,
12072 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12073 :
12074 140 : PQclear(res);
12075 140 : destroyPQExpBuffer(q);
12076 140 : destroyPQExpBuffer(delq);
12077 140 : destroyPQExpBuffer(query);
12078 140 : free(qtypname);
12079 140 : free(qualtypname);
12080 140 : }
12081 :
12082 : /*
12083 : * dumpRangeType
12084 : * writes out to fout the queries to recreate a user-defined range type
12085 : */
12086 : static void
12087 276 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12088 : {
12089 276 : DumpOptions *dopt = fout->dopt;
12090 276 : PQExpBuffer q = createPQExpBuffer();
12091 276 : PQExpBuffer delq = createPQExpBuffer();
12092 276 : PQExpBuffer query = createPQExpBuffer();
12093 : PGresult *res;
12094 : Oid collationOid;
12095 : char *qtypname;
12096 : char *qualtypname;
12097 : char *procname;
12098 :
12099 276 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12100 : {
12101 : /* Set up query for range-specific details */
12102 94 : appendPQExpBufferStr(query,
12103 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12104 :
12105 94 : appendPQExpBufferStr(query,
12106 : "SELECT ");
12107 :
12108 94 : if (fout->remoteVersion >= 140000)
12109 94 : appendPQExpBufferStr(query,
12110 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12111 : else
12112 0 : appendPQExpBufferStr(query,
12113 : "NULL AS rngmultitype, ");
12114 :
12115 94 : appendPQExpBufferStr(query,
12116 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12117 : "opc.opcname AS opcname, "
12118 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12119 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12120 : "opc.opcdefault, "
12121 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12122 : " ELSE rngcollation END AS collation, "
12123 : "rngcanonical, rngsubdiff "
12124 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12125 : " pg_catalog.pg_opclass opc "
12126 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12127 : "rngtypid = $1");
12128 :
12129 94 : ExecuteSqlStatement(fout, query->data);
12130 :
12131 94 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12132 : }
12133 :
12134 276 : printfPQExpBuffer(query,
12135 : "EXECUTE dumpRangeType('%u')",
12136 276 : tyinfo->dobj.catId.oid);
12137 :
12138 276 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12139 :
12140 276 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12141 276 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12142 :
12143 : /*
12144 : * CASCADE shouldn't be required here as for normal types since the I/O
12145 : * functions are generic and do not get dropped.
12146 : */
12147 276 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12148 :
12149 276 : if (dopt->binary_upgrade)
12150 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12151 16 : tyinfo->dobj.catId.oid,
12152 : false, true);
12153 :
12154 276 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12155 : qualtypname);
12156 :
12157 276 : appendPQExpBuffer(q, "\n subtype = %s",
12158 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12159 :
12160 276 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12161 276 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12162 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12163 :
12164 : /* print subtype_opclass only if not default for subtype */
12165 276 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12166 : {
12167 72 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12168 72 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12169 :
12170 72 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12171 : fmtId(nspname));
12172 72 : appendPQExpBufferStr(q, fmtId(opcname));
12173 : }
12174 :
12175 276 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12176 276 : if (OidIsValid(collationOid))
12177 : {
12178 88 : CollInfo *coll = findCollationByOid(collationOid);
12179 :
12180 88 : if (coll)
12181 88 : appendPQExpBuffer(q, ",\n collation = %s",
12182 88 : fmtQualifiedDumpable(coll));
12183 : }
12184 :
12185 276 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12186 276 : if (strcmp(procname, "-") != 0)
12187 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12188 :
12189 276 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12190 276 : if (strcmp(procname, "-") != 0)
12191 52 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12192 :
12193 276 : appendPQExpBufferStr(q, "\n);\n");
12194 :
12195 276 : if (dopt->binary_upgrade)
12196 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12197 : "TYPE", qtypname,
12198 16 : tyinfo->dobj.namespace->dobj.name);
12199 :
12200 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12201 276 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12202 276 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12203 : .namespace = tyinfo->dobj.namespace->dobj.name,
12204 : .owner = tyinfo->rolname,
12205 : .description = "TYPE",
12206 : .section = SECTION_PRE_DATA,
12207 : .createStmt = q->data,
12208 : .dropStmt = delq->data));
12209 :
12210 : /* Dump Type Comments and Security Labels */
12211 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12212 108 : dumpComment(fout, "TYPE", qtypname,
12213 108 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12214 108 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12215 :
12216 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12217 0 : dumpSecLabel(fout, "TYPE", qtypname,
12218 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12219 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12220 :
12221 276 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12222 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12223 : qtypname, NULL,
12224 72 : tyinfo->dobj.namespace->dobj.name,
12225 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12226 :
12227 276 : PQclear(res);
12228 276 : destroyPQExpBuffer(q);
12229 276 : destroyPQExpBuffer(delq);
12230 276 : destroyPQExpBuffer(query);
12231 276 : free(qtypname);
12232 276 : free(qualtypname);
12233 276 : }
12234 :
12235 : /*
12236 : * dumpUndefinedType
12237 : * writes out to fout the queries to recreate a !typisdefined type
12238 : *
12239 : * This is a shell type, but we use different terminology to distinguish
12240 : * this case from where we have to emit a shell type definition to break
12241 : * circular dependencies. An undefined type shouldn't ever have anything
12242 : * depending on it.
12243 : */
12244 : static void
12245 88 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12246 : {
12247 88 : DumpOptions *dopt = fout->dopt;
12248 88 : PQExpBuffer q = createPQExpBuffer();
12249 88 : PQExpBuffer delq = createPQExpBuffer();
12250 : char *qtypname;
12251 : char *qualtypname;
12252 :
12253 88 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12254 88 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12255 :
12256 88 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12257 :
12258 88 : if (dopt->binary_upgrade)
12259 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12260 4 : tyinfo->dobj.catId.oid,
12261 : false, false);
12262 :
12263 88 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12264 : qualtypname);
12265 :
12266 88 : if (dopt->binary_upgrade)
12267 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12268 : "TYPE", qtypname,
12269 4 : tyinfo->dobj.namespace->dobj.name);
12270 :
12271 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12272 88 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12273 88 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12274 : .namespace = tyinfo->dobj.namespace->dobj.name,
12275 : .owner = tyinfo->rolname,
12276 : .description = "TYPE",
12277 : .section = SECTION_PRE_DATA,
12278 : .createStmt = q->data,
12279 : .dropStmt = delq->data));
12280 :
12281 : /* Dump Type Comments and Security Labels */
12282 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12283 72 : dumpComment(fout, "TYPE", qtypname,
12284 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12285 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12286 :
12287 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12288 0 : dumpSecLabel(fout, "TYPE", qtypname,
12289 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12290 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12291 :
12292 88 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12293 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12294 : qtypname, NULL,
12295 0 : tyinfo->dobj.namespace->dobj.name,
12296 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12297 :
12298 88 : destroyPQExpBuffer(q);
12299 88 : destroyPQExpBuffer(delq);
12300 88 : free(qtypname);
12301 88 : free(qualtypname);
12302 88 : }
12303 :
12304 : /*
12305 : * dumpBaseType
12306 : * writes out to fout the queries to recreate a user-defined base type
12307 : */
12308 : static void
12309 610 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12310 : {
12311 610 : DumpOptions *dopt = fout->dopt;
12312 610 : PQExpBuffer q = createPQExpBuffer();
12313 610 : PQExpBuffer delq = createPQExpBuffer();
12314 610 : PQExpBuffer query = createPQExpBuffer();
12315 : PGresult *res;
12316 : char *qtypname;
12317 : char *qualtypname;
12318 : char *typlen;
12319 : char *typinput;
12320 : char *typoutput;
12321 : char *typreceive;
12322 : char *typsend;
12323 : char *typmodin;
12324 : char *typmodout;
12325 : char *typanalyze;
12326 : char *typsubscript;
12327 : Oid typreceiveoid;
12328 : Oid typsendoid;
12329 : Oid typmodinoid;
12330 : Oid typmodoutoid;
12331 : Oid typanalyzeoid;
12332 : Oid typsubscriptoid;
12333 : char *typcategory;
12334 : char *typispreferred;
12335 : char *typdelim;
12336 : char *typbyval;
12337 : char *typalign;
12338 : char *typstorage;
12339 : char *typcollatable;
12340 : char *typdefault;
12341 610 : bool typdefault_is_literal = false;
12342 :
12343 610 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12344 : {
12345 : /* Set up query for type-specific details */
12346 94 : appendPQExpBufferStr(query,
12347 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12348 : "SELECT typlen, "
12349 : "typinput, typoutput, typreceive, typsend, "
12350 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12351 : "typsend::pg_catalog.oid AS typsendoid, "
12352 : "typanalyze, "
12353 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12354 : "typdelim, typbyval, typalign, typstorage, "
12355 : "typmodin, typmodout, "
12356 : "typmodin::pg_catalog.oid AS typmodinoid, "
12357 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12358 : "typcategory, typispreferred, "
12359 : "(typcollation <> 0) AS typcollatable, "
12360 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12361 :
12362 94 : if (fout->remoteVersion >= 140000)
12363 94 : appendPQExpBufferStr(query,
12364 : "typsubscript, "
12365 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12366 : else
12367 0 : appendPQExpBufferStr(query,
12368 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12369 :
12370 94 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12371 : "WHERE oid = $1");
12372 :
12373 94 : ExecuteSqlStatement(fout, query->data);
12374 :
12375 94 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12376 : }
12377 :
12378 610 : printfPQExpBuffer(query,
12379 : "EXECUTE dumpBaseType('%u')",
12380 610 : tyinfo->dobj.catId.oid);
12381 :
12382 610 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12383 :
12384 610 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12385 610 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12386 610 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12387 610 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12388 610 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12389 610 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12390 610 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12391 610 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12392 610 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12393 610 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12394 610 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12395 610 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12396 610 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12397 610 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12398 610 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12399 610 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12400 610 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12401 610 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12402 610 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12403 610 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12404 610 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12405 610 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12406 610 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12407 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12408 610 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12409 : {
12410 104 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12411 104 : typdefault_is_literal = true; /* it needs quotes */
12412 : }
12413 : else
12414 506 : typdefault = NULL;
12415 :
12416 610 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12417 610 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12418 :
12419 : /*
12420 : * The reason we include CASCADE is that the circular dependency between
12421 : * the type and its I/O functions makes it impossible to drop the type any
12422 : * other way.
12423 : */
12424 610 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12425 :
12426 : /*
12427 : * We might already have a shell type, but setting pg_type_oid is
12428 : * harmless, and in any case we'd better set the array type OID.
12429 : */
12430 610 : if (dopt->binary_upgrade)
12431 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12432 16 : tyinfo->dobj.catId.oid,
12433 : false, false);
12434 :
12435 610 : appendPQExpBuffer(q,
12436 : "CREATE TYPE %s (\n"
12437 : " INTERNALLENGTH = %s",
12438 : qualtypname,
12439 610 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12440 :
12441 : /* regproc result is sufficiently quoted already */
12442 610 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12443 610 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12444 610 : if (OidIsValid(typreceiveoid))
12445 414 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12446 610 : if (OidIsValid(typsendoid))
12447 414 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12448 610 : if (OidIsValid(typmodinoid))
12449 76 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12450 610 : if (OidIsValid(typmodoutoid))
12451 76 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12452 610 : if (OidIsValid(typanalyzeoid))
12453 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12454 :
12455 610 : if (strcmp(typcollatable, "t") == 0)
12456 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12457 :
12458 610 : if (typdefault != NULL)
12459 : {
12460 104 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12461 104 : if (typdefault_is_literal)
12462 104 : appendStringLiteralAH(q, typdefault, fout);
12463 : else
12464 0 : appendPQExpBufferStr(q, typdefault);
12465 : }
12466 :
12467 610 : if (OidIsValid(typsubscriptoid))
12468 64 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12469 :
12470 610 : if (OidIsValid(tyinfo->typelem))
12471 58 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12472 58 : getFormattedTypeName(fout, tyinfo->typelem,
12473 : zeroIsError));
12474 :
12475 610 : if (strcmp(typcategory, "U") != 0)
12476 : {
12477 322 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12478 322 : appendStringLiteralAH(q, typcategory, fout);
12479 : }
12480 :
12481 610 : if (strcmp(typispreferred, "t") == 0)
12482 64 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12483 :
12484 610 : if (typdelim && strcmp(typdelim, ",") != 0)
12485 : {
12486 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12487 6 : appendStringLiteralAH(q, typdelim, fout);
12488 : }
12489 :
12490 610 : if (*typalign == TYPALIGN_CHAR)
12491 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12492 586 : else if (*typalign == TYPALIGN_SHORT)
12493 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12494 574 : else if (*typalign == TYPALIGN_INT)
12495 406 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12496 168 : else if (*typalign == TYPALIGN_DOUBLE)
12497 168 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12498 :
12499 610 : if (*typstorage == TYPSTORAGE_PLAIN)
12500 460 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12501 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12502 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12503 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12504 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12505 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12506 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12507 :
12508 610 : if (strcmp(typbyval, "t") == 0)
12509 294 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12510 :
12511 610 : appendPQExpBufferStr(q, "\n);\n");
12512 :
12513 610 : if (dopt->binary_upgrade)
12514 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12515 : "TYPE", qtypname,
12516 16 : tyinfo->dobj.namespace->dobj.name);
12517 :
12518 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12519 610 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12520 610 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12521 : .namespace = tyinfo->dobj.namespace->dobj.name,
12522 : .owner = tyinfo->rolname,
12523 : .description = "TYPE",
12524 : .section = SECTION_PRE_DATA,
12525 : .createStmt = q->data,
12526 : .dropStmt = delq->data));
12527 :
12528 : /* Dump Type Comments and Security Labels */
12529 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12530 498 : dumpComment(fout, "TYPE", qtypname,
12531 498 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12532 498 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12533 :
12534 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12535 0 : dumpSecLabel(fout, "TYPE", qtypname,
12536 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12537 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12538 :
12539 610 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12540 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12541 : qtypname, NULL,
12542 72 : tyinfo->dobj.namespace->dobj.name,
12543 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12544 :
12545 610 : PQclear(res);
12546 610 : destroyPQExpBuffer(q);
12547 610 : destroyPQExpBuffer(delq);
12548 610 : destroyPQExpBuffer(query);
12549 610 : free(qtypname);
12550 610 : free(qualtypname);
12551 610 : }
12552 :
12553 : /*
12554 : * dumpDomain
12555 : * writes out to fout the queries to recreate a user-defined domain
12556 : */
12557 : static void
12558 456 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12559 : {
12560 456 : DumpOptions *dopt = fout->dopt;
12561 456 : PQExpBuffer q = createPQExpBuffer();
12562 456 : PQExpBuffer delq = createPQExpBuffer();
12563 456 : PQExpBuffer query = createPQExpBuffer();
12564 : PGresult *res;
12565 : int i;
12566 : char *qtypname;
12567 : char *qualtypname;
12568 : char *typnotnull;
12569 : char *typdefn;
12570 : char *typdefault;
12571 : Oid typcollation;
12572 456 : bool typdefault_is_literal = false;
12573 :
12574 456 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12575 : {
12576 : /* Set up query for domain-specific details */
12577 88 : appendPQExpBufferStr(query,
12578 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12579 :
12580 88 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12581 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12582 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12583 : "t.typdefault, "
12584 : "CASE WHEN t.typcollation <> u.typcollation "
12585 : "THEN t.typcollation ELSE 0 END AS typcollation "
12586 : "FROM pg_catalog.pg_type t "
12587 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12588 : "WHERE t.oid = $1");
12589 :
12590 88 : ExecuteSqlStatement(fout, query->data);
12591 :
12592 88 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12593 : }
12594 :
12595 456 : printfPQExpBuffer(query,
12596 : "EXECUTE dumpDomain('%u')",
12597 456 : tyinfo->dobj.catId.oid);
12598 :
12599 456 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12600 :
12601 456 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12602 456 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12603 456 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12604 88 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12605 368 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12606 : {
12607 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12608 0 : typdefault_is_literal = true; /* it needs quotes */
12609 : }
12610 : else
12611 368 : typdefault = NULL;
12612 456 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12613 :
12614 456 : if (dopt->binary_upgrade)
12615 50 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12616 50 : tyinfo->dobj.catId.oid,
12617 : true, /* force array type */
12618 : false); /* force multirange type */
12619 :
12620 456 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12621 456 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12622 :
12623 456 : appendPQExpBuffer(q,
12624 : "CREATE DOMAIN %s AS %s",
12625 : qualtypname,
12626 : typdefn);
12627 :
12628 : /* Print collation only if different from base type's collation */
12629 456 : if (OidIsValid(typcollation))
12630 : {
12631 : CollInfo *coll;
12632 :
12633 72 : coll = findCollationByOid(typcollation);
12634 72 : if (coll)
12635 72 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12636 : }
12637 :
12638 : /*
12639 : * Print a not-null constraint if there's one. In servers older than 17
12640 : * these don't have names, so just print it unadorned; in newer ones they
12641 : * do, but most of the time it's going to be the standard generated one,
12642 : * so omit the name in that case also.
12643 : */
12644 456 : if (typnotnull[0] == 't')
12645 : {
12646 120 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12647 0 : appendPQExpBufferStr(q, " NOT NULL");
12648 : else
12649 : {
12650 120 : ConstraintInfo *notnull = tyinfo->notnull;
12651 :
12652 120 : if (!notnull->separate)
12653 : {
12654 : char *default_name;
12655 :
12656 : /* XXX should match ChooseConstraintName better */
12657 120 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12658 :
12659 120 : if (strcmp(default_name, notnull->dobj.name) == 0)
12660 48 : appendPQExpBufferStr(q, " NOT NULL");
12661 : else
12662 72 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12663 72 : fmtId(notnull->dobj.name), notnull->condef);
12664 120 : free(default_name);
12665 : }
12666 : }
12667 : }
12668 :
12669 456 : if (typdefault != NULL)
12670 : {
12671 88 : appendPQExpBufferStr(q, " DEFAULT ");
12672 88 : if (typdefault_is_literal)
12673 0 : appendStringLiteralAH(q, typdefault, fout);
12674 : else
12675 88 : appendPQExpBufferStr(q, typdefault);
12676 : }
12677 :
12678 456 : PQclear(res);
12679 :
12680 : /*
12681 : * Add any CHECK constraints for the domain
12682 : */
12683 768 : for (i = 0; i < tyinfo->nDomChecks; i++)
12684 : {
12685 312 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12686 :
12687 312 : if (!domcheck->separate && domcheck->contype == 'c')
12688 296 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12689 296 : fmtId(domcheck->dobj.name), domcheck->condef);
12690 : }
12691 :
12692 456 : appendPQExpBufferStr(q, ";\n");
12693 :
12694 456 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12695 :
12696 456 : if (dopt->binary_upgrade)
12697 50 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12698 : "DOMAIN", qtypname,
12699 50 : tyinfo->dobj.namespace->dobj.name);
12700 :
12701 456 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12702 456 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12703 456 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12704 : .namespace = tyinfo->dobj.namespace->dobj.name,
12705 : .owner = tyinfo->rolname,
12706 : .description = "DOMAIN",
12707 : .section = SECTION_PRE_DATA,
12708 : .createStmt = q->data,
12709 : .dropStmt = delq->data));
12710 :
12711 : /* Dump Domain Comments and Security Labels */
12712 456 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12713 0 : dumpComment(fout, "DOMAIN", qtypname,
12714 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12715 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12716 :
12717 456 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12718 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12719 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12720 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12721 :
12722 456 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12723 72 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12724 : qtypname, NULL,
12725 72 : tyinfo->dobj.namespace->dobj.name,
12726 72 : NULL, tyinfo->rolname, &tyinfo->dacl);
12727 :
12728 : /* Dump any per-constraint comments */
12729 768 : for (i = 0; i < tyinfo->nDomChecks; i++)
12730 : {
12731 312 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12732 : PQExpBuffer conprefix;
12733 :
12734 : /* but only if the constraint itself was dumped here */
12735 312 : if (domcheck->separate)
12736 16 : continue;
12737 :
12738 296 : conprefix = createPQExpBuffer();
12739 296 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12740 296 : fmtId(domcheck->dobj.name));
12741 :
12742 296 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12743 72 : dumpComment(fout, conprefix->data, qtypname,
12744 72 : tyinfo->dobj.namespace->dobj.name,
12745 72 : tyinfo->rolname,
12746 72 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12747 :
12748 296 : destroyPQExpBuffer(conprefix);
12749 : }
12750 :
12751 : /*
12752 : * And a comment on the not-null constraint, if there's one -- but only if
12753 : * the constraint itself was dumped here
12754 : */
12755 456 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12756 : {
12757 120 : PQExpBuffer conprefix = createPQExpBuffer();
12758 :
12759 120 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12760 120 : fmtId(tyinfo->notnull->dobj.name));
12761 :
12762 120 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12763 72 : dumpComment(fout, conprefix->data, qtypname,
12764 72 : tyinfo->dobj.namespace->dobj.name,
12765 72 : tyinfo->rolname,
12766 72 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12767 120 : destroyPQExpBuffer(conprefix);
12768 : }
12769 :
12770 456 : destroyPQExpBuffer(q);
12771 456 : destroyPQExpBuffer(delq);
12772 456 : destroyPQExpBuffer(query);
12773 456 : free(qtypname);
12774 456 : free(qualtypname);
12775 456 : }
12776 :
12777 : /*
12778 : * dumpCompositeType
12779 : * writes out to fout the queries to recreate a user-defined stand-alone
12780 : * composite type
12781 : */
12782 : static void
12783 364 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12784 : {
12785 364 : DumpOptions *dopt = fout->dopt;
12786 364 : PQExpBuffer q = createPQExpBuffer();
12787 364 : PQExpBuffer dropped = createPQExpBuffer();
12788 364 : PQExpBuffer delq = createPQExpBuffer();
12789 364 : PQExpBuffer query = createPQExpBuffer();
12790 : PGresult *res;
12791 : char *qtypname;
12792 : char *qualtypname;
12793 : int ntups;
12794 : int i_attname;
12795 : int i_atttypdefn;
12796 : int i_attlen;
12797 : int i_attalign;
12798 : int i_attisdropped;
12799 : int i_attcollation;
12800 : int i;
12801 : int actual_atts;
12802 :
12803 364 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12804 : {
12805 : /*
12806 : * Set up query for type-specific details.
12807 : *
12808 : * Since we only want to dump COLLATE clauses for attributes whose
12809 : * collation is different from their type's default, we use a CASE
12810 : * here to suppress uninteresting attcollations cheaply. atttypid
12811 : * will be 0 for dropped columns; collation does not matter for those.
12812 : */
12813 124 : appendPQExpBufferStr(query,
12814 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12815 : "SELECT a.attname, a.attnum, "
12816 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12817 : "a.attlen, a.attalign, a.attisdropped, "
12818 : "CASE WHEN a.attcollation <> at.typcollation "
12819 : "THEN a.attcollation ELSE 0 END AS attcollation "
12820 : "FROM pg_catalog.pg_type ct "
12821 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12822 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12823 : "WHERE ct.oid = $1 "
12824 : "ORDER BY a.attnum");
12825 :
12826 124 : ExecuteSqlStatement(fout, query->data);
12827 :
12828 124 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12829 : }
12830 :
12831 364 : printfPQExpBuffer(query,
12832 : "EXECUTE dumpCompositeType('%u')",
12833 364 : tyinfo->dobj.catId.oid);
12834 :
12835 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12836 :
12837 364 : ntups = PQntuples(res);
12838 :
12839 364 : i_attname = PQfnumber(res, "attname");
12840 364 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12841 364 : i_attlen = PQfnumber(res, "attlen");
12842 364 : i_attalign = PQfnumber(res, "attalign");
12843 364 : i_attisdropped = PQfnumber(res, "attisdropped");
12844 364 : i_attcollation = PQfnumber(res, "attcollation");
12845 :
12846 364 : if (dopt->binary_upgrade)
12847 : {
12848 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12849 36 : tyinfo->dobj.catId.oid,
12850 : false, false);
12851 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12852 : }
12853 :
12854 364 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12855 364 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12856 :
12857 364 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12858 : qualtypname);
12859 :
12860 364 : actual_atts = 0;
12861 1180 : for (i = 0; i < ntups; i++)
12862 : {
12863 : char *attname;
12864 : char *atttypdefn;
12865 : char *attlen;
12866 : char *attalign;
12867 : bool attisdropped;
12868 : Oid attcollation;
12869 :
12870 816 : attname = PQgetvalue(res, i, i_attname);
12871 816 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12872 816 : attlen = PQgetvalue(res, i, i_attlen);
12873 816 : attalign = PQgetvalue(res, i, i_attalign);
12874 816 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
12875 816 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
12876 :
12877 816 : if (attisdropped && !dopt->binary_upgrade)
12878 24 : continue;
12879 :
12880 : /* Format properly if not first attr */
12881 792 : if (actual_atts++ > 0)
12882 428 : appendPQExpBufferChar(q, ',');
12883 792 : appendPQExpBufferStr(q, "\n\t");
12884 :
12885 792 : if (!attisdropped)
12886 : {
12887 788 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
12888 :
12889 : /* Add collation if not default for the column type */
12890 788 : if (OidIsValid(attcollation))
12891 : {
12892 : CollInfo *coll;
12893 :
12894 0 : coll = findCollationByOid(attcollation);
12895 0 : if (coll)
12896 0 : appendPQExpBuffer(q, " COLLATE %s",
12897 0 : fmtQualifiedDumpable(coll));
12898 : }
12899 : }
12900 : else
12901 : {
12902 : /*
12903 : * This is a dropped attribute and we're in binary_upgrade mode.
12904 : * Insert a placeholder for it in the CREATE TYPE command, and set
12905 : * length and alignment with direct UPDATE to the catalogs
12906 : * afterwards. See similar code in dumpTableSchema().
12907 : */
12908 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
12909 :
12910 : /* stash separately for insertion after the CREATE TYPE */
12911 4 : appendPQExpBufferStr(dropped,
12912 : "\n-- For binary upgrade, recreate dropped column.\n");
12913 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
12914 : "SET attlen = %s, "
12915 : "attalign = '%s', attbyval = false\n"
12916 : "WHERE attname = ", attlen, attalign);
12917 4 : appendStringLiteralAH(dropped, attname, fout);
12918 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
12919 4 : appendStringLiteralAH(dropped, qualtypname, fout);
12920 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
12921 :
12922 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
12923 : qualtypname);
12924 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
12925 : fmtId(attname));
12926 : }
12927 : }
12928 364 : appendPQExpBufferStr(q, "\n);\n");
12929 364 : appendPQExpBufferStr(q, dropped->data);
12930 :
12931 364 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12932 :
12933 364 : if (dopt->binary_upgrade)
12934 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12935 : "TYPE", qtypname,
12936 36 : tyinfo->dobj.namespace->dobj.name);
12937 :
12938 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12939 330 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12940 330 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12941 : .namespace = tyinfo->dobj.namespace->dobj.name,
12942 : .owner = tyinfo->rolname,
12943 : .description = "TYPE",
12944 : .section = SECTION_PRE_DATA,
12945 : .createStmt = q->data,
12946 : .dropStmt = delq->data));
12947 :
12948 :
12949 : /* Dump Type Comments and Security Labels */
12950 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12951 72 : dumpComment(fout, "TYPE", qtypname,
12952 72 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12953 72 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12954 :
12955 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12956 0 : dumpSecLabel(fout, "TYPE", qtypname,
12957 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12958 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12959 :
12960 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12961 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12962 : qtypname, NULL,
12963 36 : tyinfo->dobj.namespace->dobj.name,
12964 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
12965 :
12966 : /* Dump any per-column comments */
12967 364 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12968 72 : dumpCompositeTypeColComments(fout, tyinfo, res);
12969 :
12970 364 : PQclear(res);
12971 364 : destroyPQExpBuffer(q);
12972 364 : destroyPQExpBuffer(dropped);
12973 364 : destroyPQExpBuffer(delq);
12974 364 : destroyPQExpBuffer(query);
12975 364 : free(qtypname);
12976 364 : free(qualtypname);
12977 364 : }
12978 :
12979 : /*
12980 : * dumpCompositeTypeColComments
12981 : * writes out to fout the queries to recreate comments on the columns of
12982 : * a user-defined stand-alone composite type.
12983 : *
12984 : * The caller has already made a query to collect the names and attnums
12985 : * of the type's columns, so we just pass that result into here rather
12986 : * than reading them again.
12987 : */
12988 : static void
12989 72 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
12990 : PGresult *res)
12991 : {
12992 : CommentItem *comments;
12993 : int ncomments;
12994 : PQExpBuffer query;
12995 : PQExpBuffer target;
12996 : int i;
12997 : int ntups;
12998 : int i_attname;
12999 : int i_attnum;
13000 : int i_attisdropped;
13001 :
13002 : /* do nothing, if --no-comments is supplied */
13003 72 : if (fout->dopt->no_comments)
13004 0 : return;
13005 :
13006 : /* Search for comments associated with type's pg_class OID */
13007 72 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13008 : &comments);
13009 :
13010 : /* If no comments exist, we're done */
13011 72 : if (ncomments <= 0)
13012 0 : return;
13013 :
13014 : /* Build COMMENT ON statements */
13015 72 : query = createPQExpBuffer();
13016 72 : target = createPQExpBuffer();
13017 :
13018 72 : ntups = PQntuples(res);
13019 72 : i_attnum = PQfnumber(res, "attnum");
13020 72 : i_attname = PQfnumber(res, "attname");
13021 72 : i_attisdropped = PQfnumber(res, "attisdropped");
13022 144 : while (ncomments > 0)
13023 : {
13024 : const char *attname;
13025 :
13026 72 : attname = NULL;
13027 72 : for (i = 0; i < ntups; i++)
13028 : {
13029 72 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13030 72 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13031 : {
13032 72 : attname = PQgetvalue(res, i, i_attname);
13033 72 : break;
13034 : }
13035 : }
13036 72 : if (attname) /* just in case we don't find it */
13037 : {
13038 72 : const char *descr = comments->descr;
13039 :
13040 72 : resetPQExpBuffer(target);
13041 72 : appendPQExpBuffer(target, "COLUMN %s.",
13042 72 : fmtId(tyinfo->dobj.name));
13043 72 : appendPQExpBufferStr(target, fmtId(attname));
13044 :
13045 72 : resetPQExpBuffer(query);
13046 72 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13047 72 : fmtQualifiedDumpable(tyinfo));
13048 72 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13049 72 : appendStringLiteralAH(query, descr, fout);
13050 72 : appendPQExpBufferStr(query, ";\n");
13051 :
13052 72 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13053 72 : ARCHIVE_OPTS(.tag = target->data,
13054 : .namespace = tyinfo->dobj.namespace->dobj.name,
13055 : .owner = tyinfo->rolname,
13056 : .description = "COMMENT",
13057 : .section = SECTION_NONE,
13058 : .createStmt = query->data,
13059 : .deps = &(tyinfo->dobj.dumpId),
13060 : .nDeps = 1));
13061 : }
13062 :
13063 72 : comments++;
13064 72 : ncomments--;
13065 : }
13066 :
13067 72 : destroyPQExpBuffer(query);
13068 72 : destroyPQExpBuffer(target);
13069 : }
13070 :
13071 : /*
13072 : * dumpShellType
13073 : * writes out to fout the queries to create a shell type
13074 : *
13075 : * We dump a shell definition in advance of the I/O functions for the type.
13076 : */
13077 : static void
13078 196 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13079 : {
13080 196 : DumpOptions *dopt = fout->dopt;
13081 : PQExpBuffer q;
13082 :
13083 : /* Do nothing if not dumping schema */
13084 196 : if (!dopt->dumpSchema)
13085 12 : return;
13086 :
13087 184 : q = createPQExpBuffer();
13088 :
13089 : /*
13090 : * Note the lack of a DROP command for the shell type; any required DROP
13091 : * is driven off the base type entry, instead. This interacts with
13092 : * _printTocEntry()'s use of the presence of a DROP command to decide
13093 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13094 : * the shell type's owner immediately on creation; that should happen only
13095 : * after it's filled in, otherwise the backend complains.
13096 : */
13097 :
13098 184 : if (dopt->binary_upgrade)
13099 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13100 16 : stinfo->baseType->dobj.catId.oid,
13101 : false, false);
13102 :
13103 184 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13104 184 : fmtQualifiedDumpable(stinfo));
13105 :
13106 184 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13107 184 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13108 184 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13109 : .namespace = stinfo->dobj.namespace->dobj.name,
13110 : .owner = stinfo->baseType->rolname,
13111 : .description = "SHELL TYPE",
13112 : .section = SECTION_PRE_DATA,
13113 : .createStmt = q->data));
13114 :
13115 184 : destroyPQExpBuffer(q);
13116 : }
13117 :
13118 : /*
13119 : * dumpProcLang
13120 : * writes out to fout the queries to recreate a user-defined
13121 : * procedural language
13122 : */
13123 : static void
13124 180 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13125 : {
13126 180 : DumpOptions *dopt = fout->dopt;
13127 : PQExpBuffer defqry;
13128 : PQExpBuffer delqry;
13129 : bool useParams;
13130 : char *qlanname;
13131 : FuncInfo *funcInfo;
13132 180 : FuncInfo *inlineInfo = NULL;
13133 180 : FuncInfo *validatorInfo = NULL;
13134 :
13135 : /* Do nothing if not dumping schema */
13136 180 : if (!dopt->dumpSchema)
13137 26 : return;
13138 :
13139 : /*
13140 : * Try to find the support function(s). It is not an error if we don't
13141 : * find them --- if the functions are in the pg_catalog schema, as is
13142 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13143 : * we will emit a parameterless CREATE LANGUAGE command, which will
13144 : * require PL template knowledge in the backend to reload.)
13145 : */
13146 :
13147 154 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13148 154 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13149 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
13150 :
13151 154 : if (OidIsValid(plang->laninline))
13152 : {
13153 84 : inlineInfo = findFuncByOid(plang->laninline);
13154 84 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13155 2 : inlineInfo = NULL;
13156 : }
13157 :
13158 154 : if (OidIsValid(plang->lanvalidator))
13159 : {
13160 84 : validatorInfo = findFuncByOid(plang->lanvalidator);
13161 84 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13162 2 : validatorInfo = NULL;
13163 : }
13164 :
13165 : /*
13166 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13167 : * parameters. Otherwise, we'll write a parameterless command, which will
13168 : * be interpreted as CREATE EXTENSION.
13169 : */
13170 68 : useParams = (funcInfo != NULL &&
13171 290 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13172 68 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13173 :
13174 154 : defqry = createPQExpBuffer();
13175 154 : delqry = createPQExpBuffer();
13176 :
13177 154 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13178 :
13179 154 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13180 : qlanname);
13181 :
13182 154 : if (useParams)
13183 : {
13184 68 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13185 68 : plang->lanpltrusted ? "TRUSTED " : "",
13186 : qlanname);
13187 68 : appendPQExpBuffer(defqry, " HANDLER %s",
13188 68 : fmtQualifiedDumpable(funcInfo));
13189 68 : if (OidIsValid(plang->laninline))
13190 0 : appendPQExpBuffer(defqry, " INLINE %s",
13191 0 : fmtQualifiedDumpable(inlineInfo));
13192 68 : if (OidIsValid(plang->lanvalidator))
13193 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13194 0 : fmtQualifiedDumpable(validatorInfo));
13195 : }
13196 : else
13197 : {
13198 : /*
13199 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13200 : * command will not fail if the language is preinstalled in the target
13201 : * database.
13202 : *
13203 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13204 : * EXISTS; perhaps we should emit that instead? But it might just add
13205 : * confusion.
13206 : */
13207 86 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13208 : qlanname);
13209 : }
13210 154 : appendPQExpBufferStr(defqry, ";\n");
13211 :
13212 154 : if (dopt->binary_upgrade)
13213 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13214 : "LANGUAGE", qlanname, NULL);
13215 :
13216 154 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13217 70 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13218 70 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13219 : .owner = plang->lanowner,
13220 : .description = "PROCEDURAL LANGUAGE",
13221 : .section = SECTION_PRE_DATA,
13222 : .createStmt = defqry->data,
13223 : .dropStmt = delqry->data,
13224 : ));
13225 :
13226 : /* Dump Proc Lang Comments and Security Labels */
13227 154 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13228 0 : dumpComment(fout, "LANGUAGE", qlanname,
13229 0 : NULL, plang->lanowner,
13230 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13231 :
13232 154 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13233 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13234 0 : NULL, plang->lanowner,
13235 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13236 :
13237 154 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13238 84 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13239 : qlanname, NULL, NULL,
13240 84 : NULL, plang->lanowner, &plang->dacl);
13241 :
13242 154 : free(qlanname);
13243 :
13244 154 : destroyPQExpBuffer(defqry);
13245 154 : destroyPQExpBuffer(delqry);
13246 : }
13247 :
13248 : /*
13249 : * format_function_arguments: generate function name and argument list
13250 : *
13251 : * This is used when we can rely on pg_get_function_arguments to format
13252 : * the argument list. Note, however, that pg_get_function_arguments
13253 : * does not special-case zero-argument aggregates.
13254 : */
13255 : static char *
13256 12224 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13257 : {
13258 : PQExpBufferData fn;
13259 :
13260 12224 : initPQExpBuffer(&fn);
13261 12224 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13262 12224 : if (is_agg && finfo->nargs == 0)
13263 256 : appendPQExpBufferStr(&fn, "(*)");
13264 : else
13265 11968 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13266 12224 : return fn.data;
13267 : }
13268 :
13269 : /*
13270 : * format_function_signature: generate function name and argument list
13271 : *
13272 : * Only a minimal list of input argument types is generated; this is
13273 : * sufficient to reference the function, but not to define it.
13274 : *
13275 : * If honor_quotes is false then the function name is never quoted.
13276 : * This is appropriate for use in TOC tags, but not in SQL commands.
13277 : */
13278 : static char *
13279 6370 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13280 : {
13281 : PQExpBufferData fn;
13282 : int j;
13283 :
13284 6370 : initPQExpBuffer(&fn);
13285 6370 : if (honor_quotes)
13286 1118 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13287 : else
13288 5252 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13289 11838 : for (j = 0; j < finfo->nargs; j++)
13290 : {
13291 5468 : if (j > 0)
13292 1348 : appendPQExpBufferStr(&fn, ", ");
13293 :
13294 5468 : appendPQExpBufferStr(&fn,
13295 5468 : getFormattedTypeName(fout, finfo->argtypes[j],
13296 : zeroIsError));
13297 : }
13298 6370 : appendPQExpBufferChar(&fn, ')');
13299 6370 : return fn.data;
13300 : }
13301 :
13302 :
13303 : /*
13304 : * dumpFunc:
13305 : * dump out one function
13306 : */
13307 : static void
13308 5376 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13309 : {
13310 5376 : DumpOptions *dopt = fout->dopt;
13311 : PQExpBuffer query;
13312 : PQExpBuffer q;
13313 : PQExpBuffer delqry;
13314 : PQExpBuffer asPart;
13315 : PGresult *res;
13316 : char *funcsig; /* identity signature */
13317 5376 : char *funcfullsig = NULL; /* full signature */
13318 : char *funcsig_tag;
13319 : char *qual_funcsig;
13320 : char *proretset;
13321 : char *prosrc;
13322 : char *probin;
13323 : char *prosqlbody;
13324 : char *funcargs;
13325 : char *funciargs;
13326 : char *funcresult;
13327 : char *protrftypes;
13328 : char *prokind;
13329 : char *provolatile;
13330 : char *proisstrict;
13331 : char *prosecdef;
13332 : char *proleakproof;
13333 : char *proconfig;
13334 : char *procost;
13335 : char *prorows;
13336 : char *prosupport;
13337 : char *proparallel;
13338 : char *lanname;
13339 5376 : char **configitems = NULL;
13340 5376 : int nconfigitems = 0;
13341 : const char *keyword;
13342 :
13343 : /* Do nothing if not dumping schema */
13344 5376 : if (!dopt->dumpSchema)
13345 124 : return;
13346 :
13347 5252 : query = createPQExpBuffer();
13348 5252 : q = createPQExpBuffer();
13349 5252 : delqry = createPQExpBuffer();
13350 5252 : asPart = createPQExpBuffer();
13351 :
13352 5252 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13353 : {
13354 : /* Set up query for function-specific details */
13355 150 : appendPQExpBufferStr(query,
13356 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13357 :
13358 150 : appendPQExpBufferStr(query,
13359 : "SELECT\n"
13360 : "proretset,\n"
13361 : "prosrc,\n"
13362 : "probin,\n"
13363 : "provolatile,\n"
13364 : "proisstrict,\n"
13365 : "prosecdef,\n"
13366 : "lanname,\n"
13367 : "proconfig,\n"
13368 : "procost,\n"
13369 : "prorows,\n"
13370 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13371 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13372 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13373 : "proleakproof,\n");
13374 :
13375 150 : if (fout->remoteVersion >= 90500)
13376 150 : appendPQExpBufferStr(query,
13377 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13378 : else
13379 0 : appendPQExpBufferStr(query,
13380 : "NULL AS protrftypes,\n");
13381 :
13382 150 : if (fout->remoteVersion >= 90600)
13383 150 : appendPQExpBufferStr(query,
13384 : "proparallel,\n");
13385 : else
13386 0 : appendPQExpBufferStr(query,
13387 : "'u' AS proparallel,\n");
13388 :
13389 150 : if (fout->remoteVersion >= 110000)
13390 150 : appendPQExpBufferStr(query,
13391 : "prokind,\n");
13392 : else
13393 0 : appendPQExpBufferStr(query,
13394 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13395 :
13396 150 : if (fout->remoteVersion >= 120000)
13397 150 : appendPQExpBufferStr(query,
13398 : "prosupport,\n");
13399 : else
13400 0 : appendPQExpBufferStr(query,
13401 : "'-' AS prosupport,\n");
13402 :
13403 150 : if (fout->remoteVersion >= 140000)
13404 150 : appendPQExpBufferStr(query,
13405 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13406 : else
13407 0 : appendPQExpBufferStr(query,
13408 : "NULL AS prosqlbody\n");
13409 :
13410 150 : appendPQExpBufferStr(query,
13411 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13412 : "WHERE p.oid = $1 "
13413 : "AND l.oid = p.prolang");
13414 :
13415 150 : ExecuteSqlStatement(fout, query->data);
13416 :
13417 150 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13418 : }
13419 :
13420 5252 : printfPQExpBuffer(query,
13421 : "EXECUTE dumpFunc('%u')",
13422 5252 : finfo->dobj.catId.oid);
13423 :
13424 5252 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13425 :
13426 5252 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13427 5252 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13428 : {
13429 5130 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13430 5130 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13431 5130 : prosqlbody = NULL;
13432 : }
13433 : else
13434 : {
13435 122 : prosrc = NULL;
13436 122 : probin = NULL;
13437 122 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13438 : }
13439 5252 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13440 5252 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13441 5252 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13442 5252 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13443 5252 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13444 5252 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13445 5252 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13446 5252 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13447 5252 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13448 5252 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13449 5252 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13450 5252 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13451 5252 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13452 5252 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13453 5252 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13454 :
13455 : /*
13456 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13457 : * is used.
13458 : */
13459 5252 : if (prosqlbody)
13460 : {
13461 122 : appendPQExpBufferStr(asPart, prosqlbody);
13462 : }
13463 5130 : else if (probin[0] != '\0')
13464 : {
13465 392 : appendPQExpBufferStr(asPart, "AS ");
13466 392 : appendStringLiteralAH(asPart, probin, fout);
13467 392 : if (prosrc[0] != '\0')
13468 : {
13469 392 : appendPQExpBufferStr(asPart, ", ");
13470 :
13471 : /*
13472 : * where we have bin, use dollar quoting if allowed and src
13473 : * contains quote or backslash; else use regular quoting.
13474 : */
13475 392 : if (dopt->disable_dollar_quoting ||
13476 392 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13477 392 : appendStringLiteralAH(asPart, prosrc, fout);
13478 : else
13479 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13480 : }
13481 : }
13482 : else
13483 : {
13484 4738 : appendPQExpBufferStr(asPart, "AS ");
13485 : /* with no bin, dollar quote src unconditionally if allowed */
13486 4738 : if (dopt->disable_dollar_quoting)
13487 0 : appendStringLiteralAH(asPart, prosrc, fout);
13488 : else
13489 4738 : appendStringLiteralDQ(asPart, prosrc, NULL);
13490 : }
13491 :
13492 5252 : if (*proconfig)
13493 : {
13494 48 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13495 0 : pg_fatal("could not parse %s array", "proconfig");
13496 : }
13497 : else
13498 : {
13499 5204 : configitems = NULL;
13500 5204 : nconfigitems = 0;
13501 : }
13502 :
13503 5252 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13504 5252 : funcsig = format_function_arguments(finfo, funciargs, false);
13505 :
13506 5252 : funcsig_tag = format_function_signature(fout, finfo, false);
13507 :
13508 5252 : qual_funcsig = psprintf("%s.%s",
13509 5252 : fmtId(finfo->dobj.namespace->dobj.name),
13510 : funcsig);
13511 :
13512 5252 : if (prokind[0] == PROKIND_PROCEDURE)
13513 264 : keyword = "PROCEDURE";
13514 : else
13515 4988 : keyword = "FUNCTION"; /* works for window functions too */
13516 :
13517 5252 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13518 : keyword, qual_funcsig);
13519 :
13520 10504 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13521 : keyword,
13522 5252 : fmtId(finfo->dobj.namespace->dobj.name),
13523 : funcfullsig ? funcfullsig :
13524 : funcsig);
13525 :
13526 5252 : if (prokind[0] == PROKIND_PROCEDURE)
13527 : /* no result type to output */ ;
13528 4988 : else if (funcresult)
13529 4988 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13530 : else
13531 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13532 0 : (proretset[0] == 't') ? "SETOF " : "",
13533 0 : getFormattedTypeName(fout, finfo->prorettype,
13534 : zeroIsError));
13535 :
13536 5252 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13537 :
13538 5252 : if (*protrftypes)
13539 : {
13540 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13541 : int i;
13542 :
13543 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13544 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13545 0 : for (i = 0; typeids[i]; i++)
13546 : {
13547 0 : if (i != 0)
13548 0 : appendPQExpBufferStr(q, ", ");
13549 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13550 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13551 : }
13552 :
13553 0 : free(typeids);
13554 : }
13555 :
13556 5252 : if (prokind[0] == PROKIND_WINDOW)
13557 16 : appendPQExpBufferStr(q, " WINDOW");
13558 :
13559 5252 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13560 : {
13561 1054 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13562 988 : appendPQExpBufferStr(q, " IMMUTABLE");
13563 66 : else if (provolatile[0] == PROVOLATILE_STABLE)
13564 66 : appendPQExpBufferStr(q, " STABLE");
13565 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13566 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13567 : finfo->dobj.name);
13568 : }
13569 :
13570 5252 : if (proisstrict[0] == 't')
13571 1030 : appendPQExpBufferStr(q, " STRICT");
13572 :
13573 5252 : if (prosecdef[0] == 't')
13574 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13575 :
13576 5252 : if (proleakproof[0] == 't')
13577 32 : appendPQExpBufferStr(q, " LEAKPROOF");
13578 :
13579 : /*
13580 : * COST and ROWS are emitted only if present and not default, so as not to
13581 : * break backwards-compatibility of the dump without need. Keep this code
13582 : * in sync with the defaults in functioncmds.c.
13583 : */
13584 5252 : if (strcmp(procost, "0") != 0)
13585 : {
13586 5252 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13587 : {
13588 : /* default cost is 1 */
13589 996 : if (strcmp(procost, "1") != 0)
13590 0 : appendPQExpBuffer(q, " COST %s", procost);
13591 : }
13592 : else
13593 : {
13594 : /* default cost is 100 */
13595 4256 : if (strcmp(procost, "100") != 0)
13596 18 : appendPQExpBuffer(q, " COST %s", procost);
13597 : }
13598 : }
13599 5252 : if (proretset[0] == 't' &&
13600 568 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13601 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13602 :
13603 5252 : if (strcmp(prosupport, "-") != 0)
13604 : {
13605 : /* We rely on regprocout to provide quoting and qualification */
13606 104 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13607 : }
13608 :
13609 5252 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13610 : {
13611 302 : if (proparallel[0] == PROPARALLEL_SAFE)
13612 286 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13613 16 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13614 16 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13615 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13616 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13617 : finfo->dobj.name);
13618 : }
13619 :
13620 5364 : for (int i = 0; i < nconfigitems; i++)
13621 : {
13622 : /* we feel free to scribble on configitems[] here */
13623 112 : char *configitem = configitems[i];
13624 : char *pos;
13625 :
13626 112 : pos = strchr(configitem, '=');
13627 112 : if (pos == NULL)
13628 0 : continue;
13629 112 : *pos++ = '\0';
13630 112 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13631 :
13632 : /*
13633 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13634 : * by flatten_set_variable_args() before they were put into the
13635 : * proconfig array. However, because the quoting rules used there
13636 : * aren't exactly like SQL's, we have to break the list value apart
13637 : * and then quote the elements as string literals. (The elements may
13638 : * be double-quoted as-is, but we can't just feed them to the SQL
13639 : * parser; it would do the wrong thing with elements that are
13640 : * zero-length or longer than NAMEDATALEN.)
13641 : *
13642 : * Variables that are not so marked should just be emitted as simple
13643 : * string literals. If the variable is not known to
13644 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13645 : * to use GUC_LIST_QUOTE for extension variables.
13646 : */
13647 112 : if (variable_is_guc_list_quote(configitem))
13648 : {
13649 : char **namelist;
13650 : char **nameptr;
13651 :
13652 : /* Parse string into list of identifiers */
13653 : /* this shouldn't fail really */
13654 32 : if (SplitGUCList(pos, ',', &namelist))
13655 : {
13656 112 : for (nameptr = namelist; *nameptr; nameptr++)
13657 : {
13658 80 : if (nameptr != namelist)
13659 48 : appendPQExpBufferStr(q, ", ");
13660 80 : appendStringLiteralAH(q, *nameptr, fout);
13661 : }
13662 : }
13663 32 : pg_free(namelist);
13664 : }
13665 : else
13666 80 : appendStringLiteralAH(q, pos, fout);
13667 : }
13668 :
13669 5252 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13670 :
13671 5252 : append_depends_on_extension(fout, q, &finfo->dobj,
13672 : "pg_catalog.pg_proc", keyword,
13673 : qual_funcsig);
13674 :
13675 5252 : if (dopt->binary_upgrade)
13676 576 : binary_upgrade_extension_member(q, &finfo->dobj,
13677 : keyword, funcsig,
13678 576 : finfo->dobj.namespace->dobj.name);
13679 :
13680 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13681 5044 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13682 5044 : ARCHIVE_OPTS(.tag = funcsig_tag,
13683 : .namespace = finfo->dobj.namespace->dobj.name,
13684 : .owner = finfo->rolname,
13685 : .description = keyword,
13686 : .section = finfo->postponed_def ?
13687 : SECTION_POST_DATA : SECTION_PRE_DATA,
13688 : .createStmt = q->data,
13689 : .dropStmt = delqry->data));
13690 :
13691 : /* Dump Function Comments and Security Labels */
13692 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13693 18 : dumpComment(fout, keyword, funcsig,
13694 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13695 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13696 :
13697 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13698 0 : dumpSecLabel(fout, keyword, funcsig,
13699 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13700 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13701 :
13702 5252 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13703 230 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13704 : funcsig, NULL,
13705 230 : finfo->dobj.namespace->dobj.name,
13706 230 : NULL, finfo->rolname, &finfo->dacl);
13707 :
13708 5252 : PQclear(res);
13709 :
13710 5252 : destroyPQExpBuffer(query);
13711 5252 : destroyPQExpBuffer(q);
13712 5252 : destroyPQExpBuffer(delqry);
13713 5252 : destroyPQExpBuffer(asPart);
13714 5252 : free(funcsig);
13715 5252 : free(funcfullsig);
13716 5252 : free(funcsig_tag);
13717 5252 : free(qual_funcsig);
13718 5252 : free(configitems);
13719 : }
13720 :
13721 :
13722 : /*
13723 : * Dump a user-defined cast
13724 : */
13725 : static void
13726 178 : dumpCast(Archive *fout, const CastInfo *cast)
13727 : {
13728 178 : DumpOptions *dopt = fout->dopt;
13729 : PQExpBuffer defqry;
13730 : PQExpBuffer delqry;
13731 : PQExpBuffer labelq;
13732 : PQExpBuffer castargs;
13733 178 : FuncInfo *funcInfo = NULL;
13734 : const char *sourceType;
13735 : const char *targetType;
13736 :
13737 : /* Do nothing if not dumping schema */
13738 178 : if (!dopt->dumpSchema)
13739 12 : return;
13740 :
13741 : /* Cannot dump if we don't have the cast function's info */
13742 166 : if (OidIsValid(cast->castfunc))
13743 : {
13744 86 : funcInfo = findFuncByOid(cast->castfunc);
13745 86 : if (funcInfo == NULL)
13746 0 : pg_fatal("could not find function definition for function with OID %u",
13747 : cast->castfunc);
13748 : }
13749 :
13750 166 : defqry = createPQExpBuffer();
13751 166 : delqry = createPQExpBuffer();
13752 166 : labelq = createPQExpBuffer();
13753 166 : castargs = createPQExpBuffer();
13754 :
13755 166 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13756 166 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13757 166 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13758 : sourceType, targetType);
13759 :
13760 166 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13761 : sourceType, targetType);
13762 :
13763 166 : switch (cast->castmethod)
13764 : {
13765 80 : case COERCION_METHOD_BINARY:
13766 80 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13767 80 : break;
13768 0 : case COERCION_METHOD_INOUT:
13769 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13770 0 : break;
13771 86 : case COERCION_METHOD_FUNCTION:
13772 86 : if (funcInfo)
13773 : {
13774 86 : char *fsig = format_function_signature(fout, funcInfo, true);
13775 :
13776 : /*
13777 : * Always qualify the function name (format_function_signature
13778 : * won't qualify it).
13779 : */
13780 86 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13781 86 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13782 86 : free(fsig);
13783 : }
13784 : else
13785 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13786 86 : break;
13787 0 : default:
13788 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13789 : }
13790 :
13791 166 : if (cast->castcontext == 'a')
13792 70 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13793 96 : else if (cast->castcontext == 'i')
13794 32 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13795 166 : appendPQExpBufferStr(defqry, ";\n");
13796 :
13797 166 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13798 : sourceType, targetType);
13799 :
13800 166 : appendPQExpBuffer(castargs, "(%s AS %s)",
13801 : sourceType, targetType);
13802 :
13803 166 : if (dopt->binary_upgrade)
13804 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13805 14 : "CAST", castargs->data, NULL);
13806 :
13807 166 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13808 166 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13809 166 : ARCHIVE_OPTS(.tag = labelq->data,
13810 : .description = "CAST",
13811 : .section = SECTION_PRE_DATA,
13812 : .createStmt = defqry->data,
13813 : .dropStmt = delqry->data));
13814 :
13815 : /* Dump Cast Comments */
13816 166 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13817 0 : dumpComment(fout, "CAST", castargs->data,
13818 : NULL, "",
13819 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13820 :
13821 166 : destroyPQExpBuffer(defqry);
13822 166 : destroyPQExpBuffer(delqry);
13823 166 : destroyPQExpBuffer(labelq);
13824 166 : destroyPQExpBuffer(castargs);
13825 : }
13826 :
13827 : /*
13828 : * Dump a transform
13829 : */
13830 : static void
13831 98 : dumpTransform(Archive *fout, const TransformInfo *transform)
13832 : {
13833 98 : DumpOptions *dopt = fout->dopt;
13834 : PQExpBuffer defqry;
13835 : PQExpBuffer delqry;
13836 : PQExpBuffer labelq;
13837 : PQExpBuffer transformargs;
13838 98 : FuncInfo *fromsqlFuncInfo = NULL;
13839 98 : FuncInfo *tosqlFuncInfo = NULL;
13840 : char *lanname;
13841 : const char *transformType;
13842 :
13843 : /* Do nothing if not dumping schema */
13844 98 : if (!dopt->dumpSchema)
13845 12 : return;
13846 :
13847 : /* Cannot dump if we don't have the transform functions' info */
13848 86 : if (OidIsValid(transform->trffromsql))
13849 : {
13850 86 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13851 86 : if (fromsqlFuncInfo == NULL)
13852 0 : pg_fatal("could not find function definition for function with OID %u",
13853 : transform->trffromsql);
13854 : }
13855 86 : if (OidIsValid(transform->trftosql))
13856 : {
13857 86 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13858 86 : if (tosqlFuncInfo == NULL)
13859 0 : pg_fatal("could not find function definition for function with OID %u",
13860 : transform->trftosql);
13861 : }
13862 :
13863 86 : defqry = createPQExpBuffer();
13864 86 : delqry = createPQExpBuffer();
13865 86 : labelq = createPQExpBuffer();
13866 86 : transformargs = createPQExpBuffer();
13867 :
13868 86 : lanname = get_language_name(fout, transform->trflang);
13869 86 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
13870 :
13871 86 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
13872 : transformType, lanname);
13873 :
13874 86 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
13875 : transformType, lanname);
13876 :
13877 86 : if (!transform->trffromsql && !transform->trftosql)
13878 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
13879 :
13880 86 : if (transform->trffromsql)
13881 : {
13882 86 : if (fromsqlFuncInfo)
13883 : {
13884 86 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
13885 :
13886 : /*
13887 : * Always qualify the function name (format_function_signature
13888 : * won't qualify it).
13889 : */
13890 86 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
13891 86 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
13892 86 : free(fsig);
13893 : }
13894 : else
13895 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
13896 : }
13897 :
13898 86 : if (transform->trftosql)
13899 : {
13900 86 : if (transform->trffromsql)
13901 86 : appendPQExpBufferStr(defqry, ", ");
13902 :
13903 86 : if (tosqlFuncInfo)
13904 : {
13905 86 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
13906 :
13907 : /*
13908 : * Always qualify the function name (format_function_signature
13909 : * won't qualify it).
13910 : */
13911 86 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
13912 86 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
13913 86 : free(fsig);
13914 : }
13915 : else
13916 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
13917 : }
13918 :
13919 86 : appendPQExpBufferStr(defqry, ");\n");
13920 :
13921 86 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
13922 : transformType, lanname);
13923 :
13924 86 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
13925 : transformType, lanname);
13926 :
13927 86 : if (dopt->binary_upgrade)
13928 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
13929 4 : "TRANSFORM", transformargs->data, NULL);
13930 :
13931 86 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
13932 86 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
13933 86 : ARCHIVE_OPTS(.tag = labelq->data,
13934 : .description = "TRANSFORM",
13935 : .section = SECTION_PRE_DATA,
13936 : .createStmt = defqry->data,
13937 : .dropStmt = delqry->data,
13938 : .deps = transform->dobj.dependencies,
13939 : .nDeps = transform->dobj.nDeps));
13940 :
13941 : /* Dump Transform Comments */
13942 86 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
13943 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
13944 : NULL, "",
13945 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
13946 :
13947 86 : free(lanname);
13948 86 : destroyPQExpBuffer(defqry);
13949 86 : destroyPQExpBuffer(delqry);
13950 86 : destroyPQExpBuffer(labelq);
13951 86 : destroyPQExpBuffer(transformargs);
13952 : }
13953 :
13954 :
13955 : /*
13956 : * dumpOpr
13957 : * write out a single operator definition
13958 : */
13959 : static void
13960 5098 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
13961 : {
13962 5098 : DumpOptions *dopt = fout->dopt;
13963 : PQExpBuffer query;
13964 : PQExpBuffer q;
13965 : PQExpBuffer delq;
13966 : PQExpBuffer oprid;
13967 : PQExpBuffer details;
13968 : PGresult *res;
13969 : int i_oprkind;
13970 : int i_oprcode;
13971 : int i_oprleft;
13972 : int i_oprright;
13973 : int i_oprcom;
13974 : int i_oprnegate;
13975 : int i_oprrest;
13976 : int i_oprjoin;
13977 : int i_oprcanmerge;
13978 : int i_oprcanhash;
13979 : char *oprkind;
13980 : char *oprcode;
13981 : char *oprleft;
13982 : char *oprright;
13983 : char *oprcom;
13984 : char *oprnegate;
13985 : char *oprrest;
13986 : char *oprjoin;
13987 : char *oprcanmerge;
13988 : char *oprcanhash;
13989 : char *oprregproc;
13990 : char *oprref;
13991 :
13992 : /* Do nothing if not dumping schema */
13993 5098 : if (!dopt->dumpSchema)
13994 12 : return;
13995 :
13996 : /*
13997 : * some operators are invalid because they were the result of user
13998 : * defining operators before commutators exist
13999 : */
14000 5086 : if (!OidIsValid(oprinfo->oprcode))
14001 44 : return;
14002 :
14003 5042 : query = createPQExpBuffer();
14004 5042 : q = createPQExpBuffer();
14005 5042 : delq = createPQExpBuffer();
14006 5042 : oprid = createPQExpBuffer();
14007 5042 : details = createPQExpBuffer();
14008 :
14009 5042 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14010 : {
14011 : /* Set up query for operator-specific details */
14012 94 : appendPQExpBufferStr(query,
14013 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14014 : "SELECT oprkind, "
14015 : "oprcode::pg_catalog.regprocedure, "
14016 : "oprleft::pg_catalog.regtype, "
14017 : "oprright::pg_catalog.regtype, "
14018 : "oprcom, "
14019 : "oprnegate, "
14020 : "oprrest::pg_catalog.regprocedure, "
14021 : "oprjoin::pg_catalog.regprocedure, "
14022 : "oprcanmerge, oprcanhash "
14023 : "FROM pg_catalog.pg_operator "
14024 : "WHERE oid = $1");
14025 :
14026 94 : ExecuteSqlStatement(fout, query->data);
14027 :
14028 94 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14029 : }
14030 :
14031 5042 : printfPQExpBuffer(query,
14032 : "EXECUTE dumpOpr('%u')",
14033 5042 : oprinfo->dobj.catId.oid);
14034 :
14035 5042 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14036 :
14037 5042 : i_oprkind = PQfnumber(res, "oprkind");
14038 5042 : i_oprcode = PQfnumber(res, "oprcode");
14039 5042 : i_oprleft = PQfnumber(res, "oprleft");
14040 5042 : i_oprright = PQfnumber(res, "oprright");
14041 5042 : i_oprcom = PQfnumber(res, "oprcom");
14042 5042 : i_oprnegate = PQfnumber(res, "oprnegate");
14043 5042 : i_oprrest = PQfnumber(res, "oprrest");
14044 5042 : i_oprjoin = PQfnumber(res, "oprjoin");
14045 5042 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14046 5042 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14047 :
14048 5042 : oprkind = PQgetvalue(res, 0, i_oprkind);
14049 5042 : oprcode = PQgetvalue(res, 0, i_oprcode);
14050 5042 : oprleft = PQgetvalue(res, 0, i_oprleft);
14051 5042 : oprright = PQgetvalue(res, 0, i_oprright);
14052 5042 : oprcom = PQgetvalue(res, 0, i_oprcom);
14053 5042 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14054 5042 : oprrest = PQgetvalue(res, 0, i_oprrest);
14055 5042 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14056 5042 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14057 5042 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14058 :
14059 : /* In PG14 upwards postfix operator support does not exist anymore. */
14060 5042 : if (strcmp(oprkind, "r") == 0)
14061 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14062 : oprcode);
14063 :
14064 5042 : oprregproc = convertRegProcReference(oprcode);
14065 5042 : if (oprregproc)
14066 : {
14067 5042 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14068 5042 : free(oprregproc);
14069 : }
14070 :
14071 5042 : appendPQExpBuffer(oprid, "%s (",
14072 5042 : oprinfo->dobj.name);
14073 :
14074 : /*
14075 : * right unary means there's a left arg and left unary means there's a
14076 : * right arg. (Although the "r" case is dead code for PG14 and later,
14077 : * continue to support it in case we're dumping from an old server.)
14078 : */
14079 5042 : if (strcmp(oprkind, "r") == 0 ||
14080 5042 : strcmp(oprkind, "b") == 0)
14081 : {
14082 4732 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14083 4732 : appendPQExpBufferStr(oprid, oprleft);
14084 : }
14085 : else
14086 310 : appendPQExpBufferStr(oprid, "NONE");
14087 :
14088 5042 : if (strcmp(oprkind, "l") == 0 ||
14089 4732 : strcmp(oprkind, "b") == 0)
14090 : {
14091 5042 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14092 5042 : appendPQExpBuffer(oprid, ", %s)", oprright);
14093 : }
14094 : else
14095 0 : appendPQExpBufferStr(oprid, ", NONE)");
14096 :
14097 5042 : oprref = getFormattedOperatorName(oprcom);
14098 5042 : if (oprref)
14099 : {
14100 3346 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14101 3346 : free(oprref);
14102 : }
14103 :
14104 5042 : oprref = getFormattedOperatorName(oprnegate);
14105 5042 : if (oprref)
14106 : {
14107 2332 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14108 2332 : free(oprref);
14109 : }
14110 :
14111 5042 : if (strcmp(oprcanmerge, "t") == 0)
14112 394 : appendPQExpBufferStr(details, ",\n MERGES");
14113 :
14114 5042 : if (strcmp(oprcanhash, "t") == 0)
14115 276 : appendPQExpBufferStr(details, ",\n HASHES");
14116 :
14117 5042 : oprregproc = convertRegProcReference(oprrest);
14118 5042 : if (oprregproc)
14119 : {
14120 3052 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14121 3052 : free(oprregproc);
14122 : }
14123 :
14124 5042 : oprregproc = convertRegProcReference(oprjoin);
14125 5042 : if (oprregproc)
14126 : {
14127 3052 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14128 3052 : free(oprregproc);
14129 : }
14130 :
14131 5042 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14132 5042 : fmtId(oprinfo->dobj.namespace->dobj.name),
14133 : oprid->data);
14134 :
14135 5042 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14136 5042 : fmtId(oprinfo->dobj.namespace->dobj.name),
14137 5042 : oprinfo->dobj.name, details->data);
14138 :
14139 5042 : if (dopt->binary_upgrade)
14140 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14141 24 : "OPERATOR", oprid->data,
14142 24 : oprinfo->dobj.namespace->dobj.name);
14143 :
14144 5042 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14145 5042 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14146 5042 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14147 : .namespace = oprinfo->dobj.namespace->dobj.name,
14148 : .owner = oprinfo->rolname,
14149 : .description = "OPERATOR",
14150 : .section = SECTION_PRE_DATA,
14151 : .createStmt = q->data,
14152 : .dropStmt = delq->data));
14153 :
14154 : /* Dump Operator Comments */
14155 5042 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14156 4794 : dumpComment(fout, "OPERATOR", oprid->data,
14157 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14158 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14159 :
14160 5042 : PQclear(res);
14161 :
14162 5042 : destroyPQExpBuffer(query);
14163 5042 : destroyPQExpBuffer(q);
14164 5042 : destroyPQExpBuffer(delq);
14165 5042 : destroyPQExpBuffer(oprid);
14166 5042 : destroyPQExpBuffer(details);
14167 : }
14168 :
14169 : /*
14170 : * Convert a function reference obtained from pg_operator
14171 : *
14172 : * Returns allocated string of what to print, or NULL if function references
14173 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14174 : *
14175 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14176 : * part.
14177 : */
14178 : static char *
14179 15126 : convertRegProcReference(const char *proc)
14180 : {
14181 : char *name;
14182 : char *paren;
14183 : bool inquote;
14184 :
14185 : /* In all cases "-" means a null reference */
14186 15126 : if (strcmp(proc, "-") == 0)
14187 3980 : return NULL;
14188 :
14189 11146 : name = pg_strdup(proc);
14190 : /* find non-double-quoted left paren */
14191 11146 : inquote = false;
14192 134312 : for (paren = name; *paren; paren++)
14193 : {
14194 134312 : if (*paren == '(' && !inquote)
14195 : {
14196 11146 : *paren = '\0';
14197 11146 : break;
14198 : }
14199 123166 : if (*paren == '"')
14200 100 : inquote = !inquote;
14201 : }
14202 11146 : return name;
14203 : }
14204 :
14205 : /*
14206 : * getFormattedOperatorName - retrieve the operator name for the
14207 : * given operator OID (presented in string form).
14208 : *
14209 : * Returns an allocated string, or NULL if the given OID is invalid.
14210 : * Caller is responsible for free'ing result string.
14211 : *
14212 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14213 : * useful in commands where the operator's argument types can be inferred from
14214 : * context. We always schema-qualify the name, though. The predecessor to
14215 : * this code tried to skip the schema qualification if possible, but that led
14216 : * to wrong results in corner cases, such as if an operator and its negator
14217 : * are in different schemas.
14218 : */
14219 : static char *
14220 10944 : getFormattedOperatorName(const char *oproid)
14221 : {
14222 : OprInfo *oprInfo;
14223 :
14224 : /* In all cases "0" means a null reference */
14225 10944 : if (strcmp(oproid, "0") == 0)
14226 5266 : return NULL;
14227 :
14228 5678 : oprInfo = findOprByOid(atooid(oproid));
14229 5678 : if (oprInfo == NULL)
14230 : {
14231 0 : pg_log_warning("could not find operator with OID %s",
14232 : oproid);
14233 0 : return NULL;
14234 : }
14235 :
14236 5678 : return psprintf("OPERATOR(%s.%s)",
14237 5678 : fmtId(oprInfo->dobj.namespace->dobj.name),
14238 : oprInfo->dobj.name);
14239 : }
14240 :
14241 : /*
14242 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14243 : *
14244 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14245 : * argument lists of these functions are predetermined. Note that the
14246 : * caller should ensure we are in the proper schema, because the results
14247 : * are search path dependent!
14248 : */
14249 : static char *
14250 450 : convertTSFunction(Archive *fout, Oid funcOid)
14251 : {
14252 : char *result;
14253 : char query[128];
14254 : PGresult *res;
14255 :
14256 450 : snprintf(query, sizeof(query),
14257 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14258 450 : res = ExecuteSqlQueryForSingleRow(fout, query);
14259 :
14260 450 : result = pg_strdup(PQgetvalue(res, 0, 0));
14261 :
14262 450 : PQclear(res);
14263 :
14264 450 : return result;
14265 : }
14266 :
14267 : /*
14268 : * dumpAccessMethod
14269 : * write out a single access method definition
14270 : */
14271 : static void
14272 182 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14273 : {
14274 182 : DumpOptions *dopt = fout->dopt;
14275 : PQExpBuffer q;
14276 : PQExpBuffer delq;
14277 : char *qamname;
14278 :
14279 : /* Do nothing if not dumping schema */
14280 182 : if (!dopt->dumpSchema)
14281 24 : return;
14282 :
14283 158 : q = createPQExpBuffer();
14284 158 : delq = createPQExpBuffer();
14285 :
14286 158 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14287 :
14288 158 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14289 :
14290 158 : switch (aminfo->amtype)
14291 : {
14292 72 : case AMTYPE_INDEX:
14293 72 : appendPQExpBufferStr(q, "TYPE INDEX ");
14294 72 : break;
14295 86 : case AMTYPE_TABLE:
14296 86 : appendPQExpBufferStr(q, "TYPE TABLE ");
14297 86 : break;
14298 0 : default:
14299 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14300 : aminfo->amtype, qamname);
14301 0 : destroyPQExpBuffer(q);
14302 0 : destroyPQExpBuffer(delq);
14303 0 : free(qamname);
14304 0 : return;
14305 : }
14306 :
14307 158 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14308 :
14309 158 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14310 : qamname);
14311 :
14312 158 : if (dopt->binary_upgrade)
14313 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14314 : "ACCESS METHOD", qamname, NULL);
14315 :
14316 158 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14317 158 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14318 158 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14319 : .description = "ACCESS METHOD",
14320 : .section = SECTION_PRE_DATA,
14321 : .createStmt = q->data,
14322 : .dropStmt = delq->data));
14323 :
14324 : /* Dump Access Method Comments */
14325 158 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14326 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14327 : NULL, "",
14328 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14329 :
14330 158 : destroyPQExpBuffer(q);
14331 158 : destroyPQExpBuffer(delq);
14332 158 : free(qamname);
14333 : }
14334 :
14335 : /*
14336 : * dumpOpclass
14337 : * write out a single operator class definition
14338 : */
14339 : static void
14340 1362 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14341 : {
14342 1362 : DumpOptions *dopt = fout->dopt;
14343 : PQExpBuffer query;
14344 : PQExpBuffer q;
14345 : PQExpBuffer delq;
14346 : PQExpBuffer nameusing;
14347 : PGresult *res;
14348 : int ntups;
14349 : int i_opcintype;
14350 : int i_opckeytype;
14351 : int i_opcdefault;
14352 : int i_opcfamily;
14353 : int i_opcfamilyname;
14354 : int i_opcfamilynsp;
14355 : int i_amname;
14356 : int i_amopstrategy;
14357 : int i_amopopr;
14358 : int i_sortfamily;
14359 : int i_sortfamilynsp;
14360 : int i_amprocnum;
14361 : int i_amproc;
14362 : int i_amproclefttype;
14363 : int i_amprocrighttype;
14364 : char *opcintype;
14365 : char *opckeytype;
14366 : char *opcdefault;
14367 : char *opcfamily;
14368 : char *opcfamilyname;
14369 : char *opcfamilynsp;
14370 : char *amname;
14371 : char *amopstrategy;
14372 : char *amopopr;
14373 : char *sortfamily;
14374 : char *sortfamilynsp;
14375 : char *amprocnum;
14376 : char *amproc;
14377 : char *amproclefttype;
14378 : char *amprocrighttype;
14379 : bool needComma;
14380 : int i;
14381 :
14382 : /* Do nothing if not dumping schema */
14383 1362 : if (!dopt->dumpSchema)
14384 36 : return;
14385 :
14386 1326 : query = createPQExpBuffer();
14387 1326 : q = createPQExpBuffer();
14388 1326 : delq = createPQExpBuffer();
14389 1326 : nameusing = createPQExpBuffer();
14390 :
14391 : /* Get additional fields from the pg_opclass row */
14392 1326 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14393 : "opckeytype::pg_catalog.regtype, "
14394 : "opcdefault, opcfamily, "
14395 : "opfname AS opcfamilyname, "
14396 : "nspname AS opcfamilynsp, "
14397 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14398 : "FROM pg_catalog.pg_opclass c "
14399 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14400 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14401 : "WHERE c.oid = '%u'::pg_catalog.oid",
14402 1326 : opcinfo->dobj.catId.oid);
14403 :
14404 1326 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14405 :
14406 1326 : i_opcintype = PQfnumber(res, "opcintype");
14407 1326 : i_opckeytype = PQfnumber(res, "opckeytype");
14408 1326 : i_opcdefault = PQfnumber(res, "opcdefault");
14409 1326 : i_opcfamily = PQfnumber(res, "opcfamily");
14410 1326 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14411 1326 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14412 1326 : i_amname = PQfnumber(res, "amname");
14413 :
14414 : /* opcintype may still be needed after we PQclear res */
14415 1326 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14416 1326 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14417 1326 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14418 : /* opcfamily will still be needed after we PQclear res */
14419 1326 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14420 1326 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14421 1326 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14422 : /* amname will still be needed after we PQclear res */
14423 1326 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14424 :
14425 1326 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14426 1326 : fmtQualifiedDumpable(opcinfo));
14427 1326 : appendPQExpBuffer(delq, " USING %s;\n",
14428 : fmtId(amname));
14429 :
14430 : /* Build the fixed portion of the CREATE command */
14431 1326 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14432 1326 : fmtQualifiedDumpable(opcinfo));
14433 1326 : if (strcmp(opcdefault, "t") == 0)
14434 714 : appendPQExpBufferStr(q, "DEFAULT ");
14435 1326 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14436 : opcintype,
14437 : fmtId(amname));
14438 1326 : if (strlen(opcfamilyname) > 0)
14439 : {
14440 1326 : appendPQExpBufferStr(q, " FAMILY ");
14441 1326 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14442 1326 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14443 : }
14444 1326 : appendPQExpBufferStr(q, " AS\n ");
14445 :
14446 1326 : needComma = false;
14447 :
14448 1326 : if (strcmp(opckeytype, "-") != 0)
14449 : {
14450 504 : appendPQExpBuffer(q, "STORAGE %s",
14451 : opckeytype);
14452 504 : needComma = true;
14453 : }
14454 :
14455 1326 : PQclear(res);
14456 :
14457 : /*
14458 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14459 : *
14460 : * Print only those opfamily members that are tied to the opclass by
14461 : * pg_depend entries.
14462 : */
14463 1326 : resetPQExpBuffer(query);
14464 1326 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14465 : "amopopr::pg_catalog.regoperator, "
14466 : "opfname AS sortfamily, "
14467 : "nspname AS sortfamilynsp "
14468 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14469 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14470 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14471 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14472 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14473 : "AND refobjid = '%u'::pg_catalog.oid "
14474 : "AND amopfamily = '%s'::pg_catalog.oid "
14475 : "ORDER BY amopstrategy",
14476 1326 : opcinfo->dobj.catId.oid,
14477 : opcfamily);
14478 :
14479 1326 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14480 :
14481 1326 : ntups = PQntuples(res);
14482 :
14483 1326 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14484 1326 : i_amopopr = PQfnumber(res, "amopopr");
14485 1326 : i_sortfamily = PQfnumber(res, "sortfamily");
14486 1326 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14487 :
14488 1790 : for (i = 0; i < ntups; i++)
14489 : {
14490 464 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14491 464 : amopopr = PQgetvalue(res, i, i_amopopr);
14492 464 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14493 464 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14494 :
14495 464 : if (needComma)
14496 288 : appendPQExpBufferStr(q, " ,\n ");
14497 :
14498 464 : appendPQExpBuffer(q, "OPERATOR %s %s",
14499 : amopstrategy, amopopr);
14500 :
14501 464 : if (strlen(sortfamily) > 0)
14502 : {
14503 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14504 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14505 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14506 : }
14507 :
14508 464 : needComma = true;
14509 : }
14510 :
14511 1326 : PQclear(res);
14512 :
14513 : /*
14514 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14515 : *
14516 : * Print only those opfamily members that are tied to the opclass by
14517 : * pg_depend entries.
14518 : *
14519 : * We print the amproclefttype/amprocrighttype even though in most cases
14520 : * the backend could deduce the right values, because of the corner case
14521 : * of a btree sort support function for a cross-type comparison.
14522 : */
14523 1326 : resetPQExpBuffer(query);
14524 :
14525 1326 : appendPQExpBuffer(query, "SELECT amprocnum, "
14526 : "amproc::pg_catalog.regprocedure, "
14527 : "amproclefttype::pg_catalog.regtype, "
14528 : "amprocrighttype::pg_catalog.regtype "
14529 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14530 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14531 : "AND refobjid = '%u'::pg_catalog.oid "
14532 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14533 : "AND objid = ap.oid "
14534 : "ORDER BY amprocnum",
14535 1326 : opcinfo->dobj.catId.oid);
14536 :
14537 1326 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14538 :
14539 1326 : ntups = PQntuples(res);
14540 :
14541 1326 : i_amprocnum = PQfnumber(res, "amprocnum");
14542 1326 : i_amproc = PQfnumber(res, "amproc");
14543 1326 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14544 1326 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14545 :
14546 1398 : for (i = 0; i < ntups; i++)
14547 : {
14548 72 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14549 72 : amproc = PQgetvalue(res, i, i_amproc);
14550 72 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14551 72 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14552 :
14553 72 : if (needComma)
14554 72 : appendPQExpBufferStr(q, " ,\n ");
14555 :
14556 72 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14557 :
14558 72 : if (*amproclefttype && *amprocrighttype)
14559 72 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14560 :
14561 72 : appendPQExpBuffer(q, " %s", amproc);
14562 :
14563 72 : needComma = true;
14564 : }
14565 :
14566 1326 : PQclear(res);
14567 :
14568 : /*
14569 : * If needComma is still false it means we haven't added anything after
14570 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14571 : * clause with the same datatype. This isn't sanctioned by the
14572 : * documentation, but actually DefineOpClass will treat it as a no-op.
14573 : */
14574 1326 : if (!needComma)
14575 646 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14576 :
14577 1326 : appendPQExpBufferStr(q, ";\n");
14578 :
14579 1326 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14580 1326 : appendPQExpBuffer(nameusing, " USING %s",
14581 : fmtId(amname));
14582 :
14583 1326 : if (dopt->binary_upgrade)
14584 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14585 12 : "OPERATOR CLASS", nameusing->data,
14586 12 : opcinfo->dobj.namespace->dobj.name);
14587 :
14588 1326 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14589 1326 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14590 1326 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14591 : .namespace = opcinfo->dobj.namespace->dobj.name,
14592 : .owner = opcinfo->rolname,
14593 : .description = "OPERATOR CLASS",
14594 : .section = SECTION_PRE_DATA,
14595 : .createStmt = q->data,
14596 : .dropStmt = delq->data));
14597 :
14598 : /* Dump Operator Class Comments */
14599 1326 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14600 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14601 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14602 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14603 :
14604 1326 : free(opcintype);
14605 1326 : free(opcfamily);
14606 1326 : free(amname);
14607 1326 : destroyPQExpBuffer(query);
14608 1326 : destroyPQExpBuffer(q);
14609 1326 : destroyPQExpBuffer(delq);
14610 1326 : destroyPQExpBuffer(nameusing);
14611 : }
14612 :
14613 : /*
14614 : * dumpOpfamily
14615 : * write out a single operator family definition
14616 : *
14617 : * Note: this also dumps any "loose" operator members that aren't bound to a
14618 : * specific opclass within the opfamily.
14619 : */
14620 : static void
14621 1156 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14622 : {
14623 1156 : DumpOptions *dopt = fout->dopt;
14624 : PQExpBuffer query;
14625 : PQExpBuffer q;
14626 : PQExpBuffer delq;
14627 : PQExpBuffer nameusing;
14628 : PGresult *res;
14629 : PGresult *res_ops;
14630 : PGresult *res_procs;
14631 : int ntups;
14632 : int i_amname;
14633 : int i_amopstrategy;
14634 : int i_amopopr;
14635 : int i_sortfamily;
14636 : int i_sortfamilynsp;
14637 : int i_amprocnum;
14638 : int i_amproc;
14639 : int i_amproclefttype;
14640 : int i_amprocrighttype;
14641 : char *amname;
14642 : char *amopstrategy;
14643 : char *amopopr;
14644 : char *sortfamily;
14645 : char *sortfamilynsp;
14646 : char *amprocnum;
14647 : char *amproc;
14648 : char *amproclefttype;
14649 : char *amprocrighttype;
14650 : bool needComma;
14651 : int i;
14652 :
14653 : /* Do nothing if not dumping schema */
14654 1156 : if (!dopt->dumpSchema)
14655 24 : return;
14656 :
14657 1132 : query = createPQExpBuffer();
14658 1132 : q = createPQExpBuffer();
14659 1132 : delq = createPQExpBuffer();
14660 1132 : nameusing = createPQExpBuffer();
14661 :
14662 : /*
14663 : * Fetch only those opfamily members that are tied directly to the
14664 : * opfamily by pg_depend entries.
14665 : */
14666 1132 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14667 : "amopopr::pg_catalog.regoperator, "
14668 : "opfname AS sortfamily, "
14669 : "nspname AS sortfamilynsp "
14670 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14671 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14672 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14673 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14674 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14675 : "AND refobjid = '%u'::pg_catalog.oid "
14676 : "AND amopfamily = '%u'::pg_catalog.oid "
14677 : "ORDER BY amopstrategy",
14678 1132 : opfinfo->dobj.catId.oid,
14679 1132 : opfinfo->dobj.catId.oid);
14680 :
14681 1132 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14682 :
14683 1132 : resetPQExpBuffer(query);
14684 :
14685 1132 : appendPQExpBuffer(query, "SELECT amprocnum, "
14686 : "amproc::pg_catalog.regprocedure, "
14687 : "amproclefttype::pg_catalog.regtype, "
14688 : "amprocrighttype::pg_catalog.regtype "
14689 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14690 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14691 : "AND refobjid = '%u'::pg_catalog.oid "
14692 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14693 : "AND objid = ap.oid "
14694 : "ORDER BY amprocnum",
14695 1132 : opfinfo->dobj.catId.oid);
14696 :
14697 1132 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14698 :
14699 : /* Get additional fields from the pg_opfamily row */
14700 1132 : resetPQExpBuffer(query);
14701 :
14702 1132 : appendPQExpBuffer(query, "SELECT "
14703 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14704 : "FROM pg_catalog.pg_opfamily "
14705 : "WHERE oid = '%u'::pg_catalog.oid",
14706 1132 : opfinfo->dobj.catId.oid);
14707 :
14708 1132 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14709 :
14710 1132 : i_amname = PQfnumber(res, "amname");
14711 :
14712 : /* amname will still be needed after we PQclear res */
14713 1132 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14714 :
14715 1132 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14716 1132 : fmtQualifiedDumpable(opfinfo));
14717 1132 : appendPQExpBuffer(delq, " USING %s;\n",
14718 : fmtId(amname));
14719 :
14720 : /* Build the fixed portion of the CREATE command */
14721 1132 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14722 1132 : fmtQualifiedDumpable(opfinfo));
14723 1132 : appendPQExpBuffer(q, " USING %s;\n",
14724 : fmtId(amname));
14725 :
14726 1132 : PQclear(res);
14727 :
14728 : /* Do we need an ALTER to add loose members? */
14729 1132 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14730 : {
14731 120 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14732 120 : fmtQualifiedDumpable(opfinfo));
14733 120 : appendPQExpBuffer(q, " USING %s ADD\n ",
14734 : fmtId(amname));
14735 :
14736 120 : needComma = false;
14737 :
14738 : /*
14739 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14740 : */
14741 120 : ntups = PQntuples(res_ops);
14742 :
14743 120 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14744 120 : i_amopopr = PQfnumber(res_ops, "amopopr");
14745 120 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14746 120 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14747 :
14748 480 : for (i = 0; i < ntups; i++)
14749 : {
14750 360 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14751 360 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14752 360 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14753 360 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14754 :
14755 360 : if (needComma)
14756 288 : appendPQExpBufferStr(q, " ,\n ");
14757 :
14758 360 : appendPQExpBuffer(q, "OPERATOR %s %s",
14759 : amopstrategy, amopopr);
14760 :
14761 360 : if (strlen(sortfamily) > 0)
14762 : {
14763 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14764 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14765 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14766 : }
14767 :
14768 360 : needComma = true;
14769 : }
14770 :
14771 : /*
14772 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14773 : */
14774 120 : ntups = PQntuples(res_procs);
14775 :
14776 120 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14777 120 : i_amproc = PQfnumber(res_procs, "amproc");
14778 120 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14779 120 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14780 :
14781 528 : for (i = 0; i < ntups; i++)
14782 : {
14783 408 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14784 408 : amproc = PQgetvalue(res_procs, i, i_amproc);
14785 408 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14786 408 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14787 :
14788 408 : if (needComma)
14789 360 : appendPQExpBufferStr(q, " ,\n ");
14790 :
14791 408 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14792 : amprocnum, amproclefttype, amprocrighttype,
14793 : amproc);
14794 :
14795 408 : needComma = true;
14796 : }
14797 :
14798 120 : appendPQExpBufferStr(q, ";\n");
14799 : }
14800 :
14801 1132 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14802 1132 : appendPQExpBuffer(nameusing, " USING %s",
14803 : fmtId(amname));
14804 :
14805 1132 : if (dopt->binary_upgrade)
14806 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14807 18 : "OPERATOR FAMILY", nameusing->data,
14808 18 : opfinfo->dobj.namespace->dobj.name);
14809 :
14810 1132 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14811 1132 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14812 1132 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14813 : .namespace = opfinfo->dobj.namespace->dobj.name,
14814 : .owner = opfinfo->rolname,
14815 : .description = "OPERATOR FAMILY",
14816 : .section = SECTION_PRE_DATA,
14817 : .createStmt = q->data,
14818 : .dropStmt = delq->data));
14819 :
14820 : /* Dump Operator Family Comments */
14821 1132 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14822 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14823 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14824 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14825 :
14826 1132 : free(amname);
14827 1132 : PQclear(res_ops);
14828 1132 : PQclear(res_procs);
14829 1132 : destroyPQExpBuffer(query);
14830 1132 : destroyPQExpBuffer(q);
14831 1132 : destroyPQExpBuffer(delq);
14832 1132 : destroyPQExpBuffer(nameusing);
14833 : }
14834 :
14835 : /*
14836 : * dumpCollation
14837 : * write out a single collation definition
14838 : */
14839 : static void
14840 5108 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14841 : {
14842 5108 : DumpOptions *dopt = fout->dopt;
14843 : PQExpBuffer query;
14844 : PQExpBuffer q;
14845 : PQExpBuffer delq;
14846 : char *qcollname;
14847 : PGresult *res;
14848 : int i_collprovider;
14849 : int i_collisdeterministic;
14850 : int i_collcollate;
14851 : int i_collctype;
14852 : int i_colllocale;
14853 : int i_collicurules;
14854 : const char *collprovider;
14855 : const char *collcollate;
14856 : const char *collctype;
14857 : const char *colllocale;
14858 : const char *collicurules;
14859 :
14860 : /* Do nothing if not dumping schema */
14861 5108 : if (!dopt->dumpSchema)
14862 24 : return;
14863 :
14864 5084 : query = createPQExpBuffer();
14865 5084 : q = createPQExpBuffer();
14866 5084 : delq = createPQExpBuffer();
14867 :
14868 5084 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
14869 :
14870 : /* Get collation-specific details */
14871 5084 : appendPQExpBufferStr(query, "SELECT ");
14872 :
14873 5084 : if (fout->remoteVersion >= 100000)
14874 5084 : appendPQExpBufferStr(query,
14875 : "collprovider, "
14876 : "collversion, ");
14877 : else
14878 0 : appendPQExpBufferStr(query,
14879 : "'c' AS collprovider, "
14880 : "NULL AS collversion, ");
14881 :
14882 5084 : if (fout->remoteVersion >= 120000)
14883 5084 : appendPQExpBufferStr(query,
14884 : "collisdeterministic, ");
14885 : else
14886 0 : appendPQExpBufferStr(query,
14887 : "true AS collisdeterministic, ");
14888 :
14889 5084 : if (fout->remoteVersion >= 170000)
14890 5084 : appendPQExpBufferStr(query,
14891 : "colllocale, ");
14892 0 : else if (fout->remoteVersion >= 150000)
14893 0 : appendPQExpBufferStr(query,
14894 : "colliculocale AS colllocale, ");
14895 : else
14896 0 : appendPQExpBufferStr(query,
14897 : "NULL AS colllocale, ");
14898 :
14899 5084 : if (fout->remoteVersion >= 160000)
14900 5084 : appendPQExpBufferStr(query,
14901 : "collicurules, ");
14902 : else
14903 0 : appendPQExpBufferStr(query,
14904 : "NULL AS collicurules, ");
14905 :
14906 5084 : appendPQExpBuffer(query,
14907 : "collcollate, "
14908 : "collctype "
14909 : "FROM pg_catalog.pg_collation c "
14910 : "WHERE c.oid = '%u'::pg_catalog.oid",
14911 5084 : collinfo->dobj.catId.oid);
14912 :
14913 5084 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14914 :
14915 5084 : i_collprovider = PQfnumber(res, "collprovider");
14916 5084 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
14917 5084 : i_collcollate = PQfnumber(res, "collcollate");
14918 5084 : i_collctype = PQfnumber(res, "collctype");
14919 5084 : i_colllocale = PQfnumber(res, "colllocale");
14920 5084 : i_collicurules = PQfnumber(res, "collicurules");
14921 :
14922 5084 : collprovider = PQgetvalue(res, 0, i_collprovider);
14923 :
14924 5084 : if (!PQgetisnull(res, 0, i_collcollate))
14925 100 : collcollate = PQgetvalue(res, 0, i_collcollate);
14926 : else
14927 4984 : collcollate = NULL;
14928 :
14929 5084 : if (!PQgetisnull(res, 0, i_collctype))
14930 100 : collctype = PQgetvalue(res, 0, i_collctype);
14931 : else
14932 4984 : collctype = NULL;
14933 :
14934 : /*
14935 : * Before version 15, collcollate and collctype were of type NAME and
14936 : * non-nullable. Treat empty strings as NULL for consistency.
14937 : */
14938 5084 : if (fout->remoteVersion < 150000)
14939 : {
14940 0 : if (collcollate[0] == '\0')
14941 0 : collcollate = NULL;
14942 0 : if (collctype[0] == '\0')
14943 0 : collctype = NULL;
14944 : }
14945 :
14946 5084 : if (!PQgetisnull(res, 0, i_colllocale))
14947 4978 : colllocale = PQgetvalue(res, 0, i_colllocale);
14948 : else
14949 106 : colllocale = NULL;
14950 :
14951 5084 : if (!PQgetisnull(res, 0, i_collicurules))
14952 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
14953 : else
14954 5084 : collicurules = NULL;
14955 :
14956 5084 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
14957 5084 : fmtQualifiedDumpable(collinfo));
14958 :
14959 5084 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
14960 5084 : fmtQualifiedDumpable(collinfo));
14961 :
14962 5084 : appendPQExpBufferStr(q, "provider = ");
14963 5084 : if (collprovider[0] == 'b')
14964 50 : appendPQExpBufferStr(q, "builtin");
14965 5034 : else if (collprovider[0] == 'c')
14966 100 : appendPQExpBufferStr(q, "libc");
14967 4934 : else if (collprovider[0] == 'i')
14968 4928 : appendPQExpBufferStr(q, "icu");
14969 6 : else if (collprovider[0] == 'd')
14970 : /* to allow dumping pg_catalog; not accepted on input */
14971 6 : appendPQExpBufferStr(q, "default");
14972 : else
14973 0 : pg_fatal("unrecognized collation provider: %s",
14974 : collprovider);
14975 :
14976 5084 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
14977 0 : appendPQExpBufferStr(q, ", deterministic = false");
14978 :
14979 5084 : if (collprovider[0] == 'd')
14980 : {
14981 6 : if (collcollate || collctype || colllocale || collicurules)
14982 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14983 :
14984 : /* no locale -- the default collation cannot be reloaded anyway */
14985 : }
14986 5078 : else if (collprovider[0] == 'b')
14987 : {
14988 50 : if (collcollate || collctype || !colllocale || collicurules)
14989 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
14990 :
14991 50 : appendPQExpBufferStr(q, ", locale = ");
14992 50 : appendStringLiteralAH(q, colllocale ? colllocale : "",
14993 : fout);
14994 : }
14995 5028 : else if (collprovider[0] == 'i')
14996 : {
14997 4928 : if (fout->remoteVersion >= 150000)
14998 : {
14999 4928 : if (collcollate || collctype || !colllocale)
15000 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15001 :
15002 4928 : appendPQExpBufferStr(q, ", locale = ");
15003 4928 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15004 : fout);
15005 : }
15006 : else
15007 : {
15008 0 : if (!collcollate || !collctype || colllocale ||
15009 0 : strcmp(collcollate, collctype) != 0)
15010 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15011 :
15012 0 : appendPQExpBufferStr(q, ", locale = ");
15013 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15014 : }
15015 :
15016 4928 : if (collicurules)
15017 : {
15018 0 : appendPQExpBufferStr(q, ", rules = ");
15019 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15020 : }
15021 : }
15022 100 : else if (collprovider[0] == 'c')
15023 : {
15024 100 : if (colllocale || collicurules || !collcollate || !collctype)
15025 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15026 :
15027 100 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15028 : {
15029 100 : appendPQExpBufferStr(q, ", locale = ");
15030 100 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15031 : }
15032 : else
15033 : {
15034 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15035 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15036 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15037 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15038 : }
15039 : }
15040 : else
15041 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15042 :
15043 : /*
15044 : * For binary upgrade, carry over the collation version. For normal
15045 : * dump/restore, omit the version, so that it is computed upon restore.
15046 : */
15047 5084 : if (dopt->binary_upgrade)
15048 : {
15049 : int i_collversion;
15050 :
15051 10 : i_collversion = PQfnumber(res, "collversion");
15052 10 : if (!PQgetisnull(res, 0, i_collversion))
15053 : {
15054 8 : appendPQExpBufferStr(q, ", version = ");
15055 8 : appendStringLiteralAH(q,
15056 : PQgetvalue(res, 0, i_collversion),
15057 : fout);
15058 : }
15059 : }
15060 :
15061 5084 : appendPQExpBufferStr(q, ");\n");
15062 :
15063 5084 : if (dopt->binary_upgrade)
15064 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
15065 : "COLLATION", qcollname,
15066 10 : collinfo->dobj.namespace->dobj.name);
15067 :
15068 5084 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15069 5084 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15070 5084 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15071 : .namespace = collinfo->dobj.namespace->dobj.name,
15072 : .owner = collinfo->rolname,
15073 : .description = "COLLATION",
15074 : .section = SECTION_PRE_DATA,
15075 : .createStmt = q->data,
15076 : .dropStmt = delq->data));
15077 :
15078 : /* Dump Collation Comments */
15079 5084 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15080 4870 : dumpComment(fout, "COLLATION", qcollname,
15081 4870 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15082 4870 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15083 :
15084 5084 : PQclear(res);
15085 :
15086 5084 : destroyPQExpBuffer(query);
15087 5084 : destroyPQExpBuffer(q);
15088 5084 : destroyPQExpBuffer(delq);
15089 5084 : free(qcollname);
15090 : }
15091 :
15092 : /*
15093 : * dumpConversion
15094 : * write out a single conversion definition
15095 : */
15096 : static void
15097 852 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15098 : {
15099 852 : DumpOptions *dopt = fout->dopt;
15100 : PQExpBuffer query;
15101 : PQExpBuffer q;
15102 : PQExpBuffer delq;
15103 : char *qconvname;
15104 : PGresult *res;
15105 : int i_conforencoding;
15106 : int i_contoencoding;
15107 : int i_conproc;
15108 : int i_condefault;
15109 : const char *conforencoding;
15110 : const char *contoencoding;
15111 : const char *conproc;
15112 : bool condefault;
15113 :
15114 : /* Do nothing if not dumping schema */
15115 852 : if (!dopt->dumpSchema)
15116 12 : return;
15117 :
15118 840 : query = createPQExpBuffer();
15119 840 : q = createPQExpBuffer();
15120 840 : delq = createPQExpBuffer();
15121 :
15122 840 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15123 :
15124 : /* Get conversion-specific details */
15125 840 : appendPQExpBuffer(query, "SELECT "
15126 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15127 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15128 : "conproc, condefault "
15129 : "FROM pg_catalog.pg_conversion c "
15130 : "WHERE c.oid = '%u'::pg_catalog.oid",
15131 840 : convinfo->dobj.catId.oid);
15132 :
15133 840 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15134 :
15135 840 : i_conforencoding = PQfnumber(res, "conforencoding");
15136 840 : i_contoencoding = PQfnumber(res, "contoencoding");
15137 840 : i_conproc = PQfnumber(res, "conproc");
15138 840 : i_condefault = PQfnumber(res, "condefault");
15139 :
15140 840 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15141 840 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15142 840 : conproc = PQgetvalue(res, 0, i_conproc);
15143 840 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15144 :
15145 840 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15146 840 : fmtQualifiedDumpable(convinfo));
15147 :
15148 840 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15149 : (condefault) ? "DEFAULT " : "",
15150 840 : fmtQualifiedDumpable(convinfo));
15151 840 : appendStringLiteralAH(q, conforencoding, fout);
15152 840 : appendPQExpBufferStr(q, " TO ");
15153 840 : appendStringLiteralAH(q, contoencoding, fout);
15154 : /* regproc output is already sufficiently quoted */
15155 840 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15156 :
15157 840 : if (dopt->binary_upgrade)
15158 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
15159 : "CONVERSION", qconvname,
15160 2 : convinfo->dobj.namespace->dobj.name);
15161 :
15162 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15163 840 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15164 840 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15165 : .namespace = convinfo->dobj.namespace->dobj.name,
15166 : .owner = convinfo->rolname,
15167 : .description = "CONVERSION",
15168 : .section = SECTION_PRE_DATA,
15169 : .createStmt = q->data,
15170 : .dropStmt = delq->data));
15171 :
15172 : /* Dump Conversion Comments */
15173 840 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15174 840 : dumpComment(fout, "CONVERSION", qconvname,
15175 840 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15176 840 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15177 :
15178 840 : PQclear(res);
15179 :
15180 840 : destroyPQExpBuffer(query);
15181 840 : destroyPQExpBuffer(q);
15182 840 : destroyPQExpBuffer(delq);
15183 840 : free(qconvname);
15184 : }
15185 :
15186 : /*
15187 : * format_aggregate_signature: generate aggregate name and argument list
15188 : *
15189 : * The argument type names are qualified if needed. The aggregate name
15190 : * is never qualified.
15191 : */
15192 : static char *
15193 860 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15194 : {
15195 : PQExpBufferData buf;
15196 : int j;
15197 :
15198 860 : initPQExpBuffer(&buf);
15199 860 : if (honor_quotes)
15200 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15201 : else
15202 860 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15203 :
15204 860 : if (agginfo->aggfn.nargs == 0)
15205 128 : appendPQExpBufferStr(&buf, "(*)");
15206 : else
15207 : {
15208 732 : appendPQExpBufferChar(&buf, '(');
15209 1608 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15210 876 : appendPQExpBuffer(&buf, "%s%s",
15211 : (j > 0) ? ", " : "",
15212 : getFormattedTypeName(fout,
15213 876 : agginfo->aggfn.argtypes[j],
15214 : zeroIsError));
15215 732 : appendPQExpBufferChar(&buf, ')');
15216 : }
15217 860 : return buf.data;
15218 : }
15219 :
15220 : /*
15221 : * dumpAgg
15222 : * write out a single aggregate definition
15223 : */
15224 : static void
15225 874 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15226 : {
15227 874 : DumpOptions *dopt = fout->dopt;
15228 : PQExpBuffer query;
15229 : PQExpBuffer q;
15230 : PQExpBuffer delq;
15231 : PQExpBuffer details;
15232 : char *aggsig; /* identity signature */
15233 874 : char *aggfullsig = NULL; /* full signature */
15234 : char *aggsig_tag;
15235 : PGresult *res;
15236 : int i_agginitval;
15237 : int i_aggminitval;
15238 : const char *aggtransfn;
15239 : const char *aggfinalfn;
15240 : const char *aggcombinefn;
15241 : const char *aggserialfn;
15242 : const char *aggdeserialfn;
15243 : const char *aggmtransfn;
15244 : const char *aggminvtransfn;
15245 : const char *aggmfinalfn;
15246 : bool aggfinalextra;
15247 : bool aggmfinalextra;
15248 : char aggfinalmodify;
15249 : char aggmfinalmodify;
15250 : const char *aggsortop;
15251 : char *aggsortconvop;
15252 : char aggkind;
15253 : const char *aggtranstype;
15254 : const char *aggtransspace;
15255 : const char *aggmtranstype;
15256 : const char *aggmtransspace;
15257 : const char *agginitval;
15258 : const char *aggminitval;
15259 : const char *proparallel;
15260 : char defaultfinalmodify;
15261 :
15262 : /* Do nothing if not dumping schema */
15263 874 : if (!dopt->dumpSchema)
15264 14 : return;
15265 :
15266 860 : query = createPQExpBuffer();
15267 860 : q = createPQExpBuffer();
15268 860 : delq = createPQExpBuffer();
15269 860 : details = createPQExpBuffer();
15270 :
15271 860 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15272 : {
15273 : /* Set up query for aggregate-specific details */
15274 124 : appendPQExpBufferStr(query,
15275 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15276 :
15277 124 : appendPQExpBufferStr(query,
15278 : "SELECT "
15279 : "aggtransfn,\n"
15280 : "aggfinalfn,\n"
15281 : "aggtranstype::pg_catalog.regtype,\n"
15282 : "agginitval,\n"
15283 : "aggsortop,\n"
15284 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15285 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15286 :
15287 124 : if (fout->remoteVersion >= 90400)
15288 124 : appendPQExpBufferStr(query,
15289 : "aggkind,\n"
15290 : "aggmtransfn,\n"
15291 : "aggminvtransfn,\n"
15292 : "aggmfinalfn,\n"
15293 : "aggmtranstype::pg_catalog.regtype,\n"
15294 : "aggfinalextra,\n"
15295 : "aggmfinalextra,\n"
15296 : "aggtransspace,\n"
15297 : "aggmtransspace,\n"
15298 : "aggminitval,\n");
15299 : else
15300 0 : appendPQExpBufferStr(query,
15301 : "'n' AS aggkind,\n"
15302 : "'-' AS aggmtransfn,\n"
15303 : "'-' AS aggminvtransfn,\n"
15304 : "'-' AS aggmfinalfn,\n"
15305 : "0 AS aggmtranstype,\n"
15306 : "false AS aggfinalextra,\n"
15307 : "false AS aggmfinalextra,\n"
15308 : "0 AS aggtransspace,\n"
15309 : "0 AS aggmtransspace,\n"
15310 : "NULL AS aggminitval,\n");
15311 :
15312 124 : if (fout->remoteVersion >= 90600)
15313 124 : appendPQExpBufferStr(query,
15314 : "aggcombinefn,\n"
15315 : "aggserialfn,\n"
15316 : "aggdeserialfn,\n"
15317 : "proparallel,\n");
15318 : else
15319 0 : appendPQExpBufferStr(query,
15320 : "'-' AS aggcombinefn,\n"
15321 : "'-' AS aggserialfn,\n"
15322 : "'-' AS aggdeserialfn,\n"
15323 : "'u' AS proparallel,\n");
15324 :
15325 124 : if (fout->remoteVersion >= 110000)
15326 124 : appendPQExpBufferStr(query,
15327 : "aggfinalmodify,\n"
15328 : "aggmfinalmodify\n");
15329 : else
15330 0 : appendPQExpBufferStr(query,
15331 : "'0' AS aggfinalmodify,\n"
15332 : "'0' AS aggmfinalmodify\n");
15333 :
15334 124 : appendPQExpBufferStr(query,
15335 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15336 : "WHERE a.aggfnoid = p.oid "
15337 : "AND p.oid = $1");
15338 :
15339 124 : ExecuteSqlStatement(fout, query->data);
15340 :
15341 124 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15342 : }
15343 :
15344 860 : printfPQExpBuffer(query,
15345 : "EXECUTE dumpAgg('%u')",
15346 860 : agginfo->aggfn.dobj.catId.oid);
15347 :
15348 860 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15349 :
15350 860 : i_agginitval = PQfnumber(res, "agginitval");
15351 860 : i_aggminitval = PQfnumber(res, "aggminitval");
15352 :
15353 860 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15354 860 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15355 860 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15356 860 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15357 860 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15358 860 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15359 860 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15360 860 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15361 860 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15362 860 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15363 860 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15364 860 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15365 860 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15366 860 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15367 860 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15368 860 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15369 860 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15370 860 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15371 860 : agginitval = PQgetvalue(res, 0, i_agginitval);
15372 860 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15373 860 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15374 :
15375 : {
15376 : char *funcargs;
15377 : char *funciargs;
15378 :
15379 860 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15380 860 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15381 860 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15382 860 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15383 : }
15384 :
15385 860 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15386 :
15387 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15388 860 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15389 : /* replace omitted flags for old versions */
15390 860 : if (aggfinalmodify == '0')
15391 0 : aggfinalmodify = defaultfinalmodify;
15392 860 : if (aggmfinalmodify == '0')
15393 0 : aggmfinalmodify = defaultfinalmodify;
15394 :
15395 : /* regproc and regtype output is already sufficiently quoted */
15396 860 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15397 : aggtransfn, aggtranstype);
15398 :
15399 860 : if (strcmp(aggtransspace, "0") != 0)
15400 : {
15401 16 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15402 : aggtransspace);
15403 : }
15404 :
15405 860 : if (!PQgetisnull(res, 0, i_agginitval))
15406 : {
15407 632 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15408 632 : appendStringLiteralAH(details, agginitval, fout);
15409 : }
15410 :
15411 860 : if (strcmp(aggfinalfn, "-") != 0)
15412 : {
15413 392 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15414 : aggfinalfn);
15415 392 : if (aggfinalextra)
15416 32 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15417 392 : if (aggfinalmodify != defaultfinalmodify)
15418 : {
15419 72 : switch (aggfinalmodify)
15420 : {
15421 0 : case AGGMODIFY_READ_ONLY:
15422 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15423 0 : break;
15424 72 : case AGGMODIFY_SHAREABLE:
15425 72 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15426 72 : break;
15427 0 : case AGGMODIFY_READ_WRITE:
15428 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15429 0 : break;
15430 0 : default:
15431 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15432 : agginfo->aggfn.dobj.name);
15433 : break;
15434 : }
15435 : }
15436 : }
15437 :
15438 860 : if (strcmp(aggcombinefn, "-") != 0)
15439 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15440 :
15441 860 : if (strcmp(aggserialfn, "-") != 0)
15442 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15443 :
15444 860 : if (strcmp(aggdeserialfn, "-") != 0)
15445 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15446 :
15447 860 : if (strcmp(aggmtransfn, "-") != 0)
15448 : {
15449 96 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15450 : aggmtransfn,
15451 : aggminvtransfn,
15452 : aggmtranstype);
15453 : }
15454 :
15455 860 : if (strcmp(aggmtransspace, "0") != 0)
15456 : {
15457 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15458 : aggmtransspace);
15459 : }
15460 :
15461 860 : if (!PQgetisnull(res, 0, i_aggminitval))
15462 : {
15463 32 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15464 32 : appendStringLiteralAH(details, aggminitval, fout);
15465 : }
15466 :
15467 860 : if (strcmp(aggmfinalfn, "-") != 0)
15468 : {
15469 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15470 : aggmfinalfn);
15471 0 : if (aggmfinalextra)
15472 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15473 0 : if (aggmfinalmodify != defaultfinalmodify)
15474 : {
15475 0 : switch (aggmfinalmodify)
15476 : {
15477 0 : case AGGMODIFY_READ_ONLY:
15478 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15479 0 : break;
15480 0 : case AGGMODIFY_SHAREABLE:
15481 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15482 0 : break;
15483 0 : case AGGMODIFY_READ_WRITE:
15484 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15485 0 : break;
15486 0 : default:
15487 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15488 : agginfo->aggfn.dobj.name);
15489 : break;
15490 : }
15491 : }
15492 : }
15493 :
15494 860 : aggsortconvop = getFormattedOperatorName(aggsortop);
15495 860 : if (aggsortconvop)
15496 : {
15497 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15498 : aggsortconvop);
15499 0 : free(aggsortconvop);
15500 : }
15501 :
15502 860 : if (aggkind == AGGKIND_HYPOTHETICAL)
15503 16 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15504 :
15505 860 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15506 : {
15507 16 : if (proparallel[0] == PROPARALLEL_SAFE)
15508 16 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15509 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15510 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15511 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15512 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15513 : agginfo->aggfn.dobj.name);
15514 : }
15515 :
15516 860 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15517 860 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15518 : aggsig);
15519 :
15520 1720 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15521 860 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15522 : aggfullsig ? aggfullsig : aggsig, details->data);
15523 :
15524 860 : if (dopt->binary_upgrade)
15525 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15526 : "AGGREGATE", aggsig,
15527 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15528 :
15529 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15530 826 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15531 826 : agginfo->aggfn.dobj.dumpId,
15532 826 : ARCHIVE_OPTS(.tag = aggsig_tag,
15533 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15534 : .owner = agginfo->aggfn.rolname,
15535 : .description = "AGGREGATE",
15536 : .section = SECTION_PRE_DATA,
15537 : .createStmt = q->data,
15538 : .dropStmt = delq->data));
15539 :
15540 : /* Dump Aggregate Comments */
15541 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15542 32 : dumpComment(fout, "AGGREGATE", aggsig,
15543 32 : agginfo->aggfn.dobj.namespace->dobj.name,
15544 32 : agginfo->aggfn.rolname,
15545 32 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15546 :
15547 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15548 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15549 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15550 0 : agginfo->aggfn.rolname,
15551 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15552 :
15553 : /*
15554 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15555 : * command look like a function's GRANT; in particular this affects the
15556 : * syntax for zero-argument aggregates and ordered-set aggregates.
15557 : */
15558 860 : free(aggsig);
15559 :
15560 860 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15561 :
15562 860 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15563 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15564 : "FUNCTION", aggsig, NULL,
15565 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15566 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15567 :
15568 860 : free(aggsig);
15569 860 : free(aggfullsig);
15570 860 : free(aggsig_tag);
15571 :
15572 860 : PQclear(res);
15573 :
15574 860 : destroyPQExpBuffer(query);
15575 860 : destroyPQExpBuffer(q);
15576 860 : destroyPQExpBuffer(delq);
15577 860 : destroyPQExpBuffer(details);
15578 : }
15579 :
15580 : /*
15581 : * dumpTSParser
15582 : * write out a single text search parser
15583 : */
15584 : static void
15585 90 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15586 : {
15587 90 : DumpOptions *dopt = fout->dopt;
15588 : PQExpBuffer q;
15589 : PQExpBuffer delq;
15590 : char *qprsname;
15591 :
15592 : /* Do nothing if not dumping schema */
15593 90 : if (!dopt->dumpSchema)
15594 12 : return;
15595 :
15596 78 : q = createPQExpBuffer();
15597 78 : delq = createPQExpBuffer();
15598 :
15599 78 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15600 :
15601 78 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15602 78 : fmtQualifiedDumpable(prsinfo));
15603 :
15604 78 : appendPQExpBuffer(q, " START = %s,\n",
15605 78 : convertTSFunction(fout, prsinfo->prsstart));
15606 78 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15607 78 : convertTSFunction(fout, prsinfo->prstoken));
15608 78 : appendPQExpBuffer(q, " END = %s,\n",
15609 78 : convertTSFunction(fout, prsinfo->prsend));
15610 78 : if (prsinfo->prsheadline != InvalidOid)
15611 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15612 6 : convertTSFunction(fout, prsinfo->prsheadline));
15613 78 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15614 78 : convertTSFunction(fout, prsinfo->prslextype));
15615 :
15616 78 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15617 78 : fmtQualifiedDumpable(prsinfo));
15618 :
15619 78 : if (dopt->binary_upgrade)
15620 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15621 : "TEXT SEARCH PARSER", qprsname,
15622 2 : prsinfo->dobj.namespace->dobj.name);
15623 :
15624 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15625 78 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15626 78 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15627 : .namespace = prsinfo->dobj.namespace->dobj.name,
15628 : .description = "TEXT SEARCH PARSER",
15629 : .section = SECTION_PRE_DATA,
15630 : .createStmt = q->data,
15631 : .dropStmt = delq->data));
15632 :
15633 : /* Dump Parser Comments */
15634 78 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15635 78 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15636 78 : prsinfo->dobj.namespace->dobj.name, "",
15637 78 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15638 :
15639 78 : destroyPQExpBuffer(q);
15640 78 : destroyPQExpBuffer(delq);
15641 78 : free(qprsname);
15642 : }
15643 :
15644 : /*
15645 : * dumpTSDictionary
15646 : * write out a single text search dictionary
15647 : */
15648 : static void
15649 408 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15650 : {
15651 408 : DumpOptions *dopt = fout->dopt;
15652 : PQExpBuffer q;
15653 : PQExpBuffer delq;
15654 : PQExpBuffer query;
15655 : char *qdictname;
15656 : PGresult *res;
15657 : char *nspname;
15658 : char *tmplname;
15659 :
15660 : /* Do nothing if not dumping schema */
15661 408 : if (!dopt->dumpSchema)
15662 12 : return;
15663 :
15664 396 : q = createPQExpBuffer();
15665 396 : delq = createPQExpBuffer();
15666 396 : query = createPQExpBuffer();
15667 :
15668 396 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15669 :
15670 : /* Fetch name and namespace of the dictionary's template */
15671 396 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15672 : "FROM pg_ts_template p, pg_namespace n "
15673 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15674 396 : dictinfo->dicttemplate);
15675 396 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15676 396 : nspname = PQgetvalue(res, 0, 0);
15677 396 : tmplname = PQgetvalue(res, 0, 1);
15678 :
15679 396 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15680 396 : fmtQualifiedDumpable(dictinfo));
15681 :
15682 396 : appendPQExpBufferStr(q, " TEMPLATE = ");
15683 396 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15684 396 : appendPQExpBufferStr(q, fmtId(tmplname));
15685 :
15686 396 : PQclear(res);
15687 :
15688 : /* the dictinitoption can be dumped straight into the command */
15689 396 : if (dictinfo->dictinitoption)
15690 318 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15691 :
15692 396 : appendPQExpBufferStr(q, " );\n");
15693 :
15694 396 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15695 396 : fmtQualifiedDumpable(dictinfo));
15696 :
15697 396 : if (dopt->binary_upgrade)
15698 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15699 : "TEXT SEARCH DICTIONARY", qdictname,
15700 20 : dictinfo->dobj.namespace->dobj.name);
15701 :
15702 396 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15703 396 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15704 396 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15705 : .namespace = dictinfo->dobj.namespace->dobj.name,
15706 : .owner = dictinfo->rolname,
15707 : .description = "TEXT SEARCH DICTIONARY",
15708 : .section = SECTION_PRE_DATA,
15709 : .createStmt = q->data,
15710 : .dropStmt = delq->data));
15711 :
15712 : /* Dump Dictionary Comments */
15713 396 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15714 252 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15715 252 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15716 252 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15717 :
15718 396 : destroyPQExpBuffer(q);
15719 396 : destroyPQExpBuffer(delq);
15720 396 : destroyPQExpBuffer(query);
15721 396 : free(qdictname);
15722 : }
15723 :
15724 : /*
15725 : * dumpTSTemplate
15726 : * write out a single text search template
15727 : */
15728 : static void
15729 114 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15730 : {
15731 114 : DumpOptions *dopt = fout->dopt;
15732 : PQExpBuffer q;
15733 : PQExpBuffer delq;
15734 : char *qtmplname;
15735 :
15736 : /* Do nothing if not dumping schema */
15737 114 : if (!dopt->dumpSchema)
15738 12 : return;
15739 :
15740 102 : q = createPQExpBuffer();
15741 102 : delq = createPQExpBuffer();
15742 :
15743 102 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15744 :
15745 102 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15746 102 : fmtQualifiedDumpable(tmplinfo));
15747 :
15748 102 : if (tmplinfo->tmplinit != InvalidOid)
15749 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15750 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15751 102 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15752 102 : convertTSFunction(fout, tmplinfo->tmpllexize));
15753 :
15754 102 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15755 102 : fmtQualifiedDumpable(tmplinfo));
15756 :
15757 102 : if (dopt->binary_upgrade)
15758 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15759 : "TEXT SEARCH TEMPLATE", qtmplname,
15760 2 : tmplinfo->dobj.namespace->dobj.name);
15761 :
15762 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15763 102 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15764 102 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15765 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15766 : .description = "TEXT SEARCH TEMPLATE",
15767 : .section = SECTION_PRE_DATA,
15768 : .createStmt = q->data,
15769 : .dropStmt = delq->data));
15770 :
15771 : /* Dump Template Comments */
15772 102 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15773 102 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15774 102 : tmplinfo->dobj.namespace->dobj.name, "",
15775 102 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15776 :
15777 102 : destroyPQExpBuffer(q);
15778 102 : destroyPQExpBuffer(delq);
15779 102 : free(qtmplname);
15780 : }
15781 :
15782 : /*
15783 : * dumpTSConfig
15784 : * write out a single text search configuration
15785 : */
15786 : static void
15787 328 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15788 : {
15789 328 : DumpOptions *dopt = fout->dopt;
15790 : PQExpBuffer q;
15791 : PQExpBuffer delq;
15792 : PQExpBuffer query;
15793 : char *qcfgname;
15794 : PGresult *res;
15795 : char *nspname;
15796 : char *prsname;
15797 : int ntups,
15798 : i;
15799 : int i_tokenname;
15800 : int i_dictname;
15801 :
15802 : /* Do nothing if not dumping schema */
15803 328 : if (!dopt->dumpSchema)
15804 12 : return;
15805 :
15806 316 : q = createPQExpBuffer();
15807 316 : delq = createPQExpBuffer();
15808 316 : query = createPQExpBuffer();
15809 :
15810 316 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15811 :
15812 : /* Fetch name and namespace of the config's parser */
15813 316 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15814 : "FROM pg_ts_parser p, pg_namespace n "
15815 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15816 316 : cfginfo->cfgparser);
15817 316 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15818 316 : nspname = PQgetvalue(res, 0, 0);
15819 316 : prsname = PQgetvalue(res, 0, 1);
15820 :
15821 316 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15822 316 : fmtQualifiedDumpable(cfginfo));
15823 :
15824 316 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15825 316 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15826 :
15827 316 : PQclear(res);
15828 :
15829 316 : resetPQExpBuffer(query);
15830 316 : appendPQExpBuffer(query,
15831 : "SELECT\n"
15832 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15833 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15834 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15835 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15836 : "WHERE m.mapcfg = '%u'\n"
15837 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15838 316 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15839 :
15840 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15841 316 : ntups = PQntuples(res);
15842 :
15843 316 : i_tokenname = PQfnumber(res, "tokenname");
15844 316 : i_dictname = PQfnumber(res, "dictname");
15845 :
15846 6752 : for (i = 0; i < ntups; i++)
15847 : {
15848 6436 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15849 6436 : char *dictname = PQgetvalue(res, i, i_dictname);
15850 :
15851 6436 : if (i == 0 ||
15852 6120 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15853 : {
15854 : /* starting a new token type, so start a new command */
15855 6004 : if (i > 0)
15856 5688 : appendPQExpBufferStr(q, ";\n");
15857 6004 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15858 6004 : fmtQualifiedDumpable(cfginfo));
15859 : /* tokenname needs quoting, dictname does NOT */
15860 6004 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15861 : fmtId(tokenname), dictname);
15862 : }
15863 : else
15864 432 : appendPQExpBuffer(q, ", %s", dictname);
15865 : }
15866 :
15867 316 : if (ntups > 0)
15868 316 : appendPQExpBufferStr(q, ";\n");
15869 :
15870 316 : PQclear(res);
15871 :
15872 316 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
15873 316 : fmtQualifiedDumpable(cfginfo));
15874 :
15875 316 : if (dopt->binary_upgrade)
15876 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
15877 : "TEXT SEARCH CONFIGURATION", qcfgname,
15878 10 : cfginfo->dobj.namespace->dobj.name);
15879 :
15880 316 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15881 316 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
15882 316 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
15883 : .namespace = cfginfo->dobj.namespace->dobj.name,
15884 : .owner = cfginfo->rolname,
15885 : .description = "TEXT SEARCH CONFIGURATION",
15886 : .section = SECTION_PRE_DATA,
15887 : .createStmt = q->data,
15888 : .dropStmt = delq->data));
15889 :
15890 : /* Dump Configuration Comments */
15891 316 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15892 252 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
15893 252 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
15894 252 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
15895 :
15896 316 : destroyPQExpBuffer(q);
15897 316 : destroyPQExpBuffer(delq);
15898 316 : destroyPQExpBuffer(query);
15899 316 : free(qcfgname);
15900 : }
15901 :
15902 : /*
15903 : * dumpForeignDataWrapper
15904 : * write out a single foreign-data wrapper definition
15905 : */
15906 : static void
15907 118 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
15908 : {
15909 118 : DumpOptions *dopt = fout->dopt;
15910 : PQExpBuffer q;
15911 : PQExpBuffer delq;
15912 : char *qfdwname;
15913 :
15914 : /* Do nothing if not dumping schema */
15915 118 : if (!dopt->dumpSchema)
15916 14 : return;
15917 :
15918 104 : q = createPQExpBuffer();
15919 104 : delq = createPQExpBuffer();
15920 :
15921 104 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
15922 :
15923 104 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
15924 : qfdwname);
15925 :
15926 104 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
15927 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
15928 :
15929 104 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
15930 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
15931 :
15932 104 : if (strlen(fdwinfo->fdwoptions) > 0)
15933 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
15934 :
15935 104 : appendPQExpBufferStr(q, ";\n");
15936 :
15937 104 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
15938 : qfdwname);
15939 :
15940 104 : if (dopt->binary_upgrade)
15941 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
15942 : "FOREIGN DATA WRAPPER", qfdwname,
15943 : NULL);
15944 :
15945 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15946 104 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
15947 104 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
15948 : .owner = fdwinfo->rolname,
15949 : .description = "FOREIGN DATA WRAPPER",
15950 : .section = SECTION_PRE_DATA,
15951 : .createStmt = q->data,
15952 : .dropStmt = delq->data));
15953 :
15954 : /* Dump Foreign Data Wrapper Comments */
15955 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15956 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
15957 0 : NULL, fdwinfo->rolname,
15958 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
15959 :
15960 : /* Handle the ACL */
15961 104 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
15962 70 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
15963 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
15964 70 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
15965 :
15966 104 : free(qfdwname);
15967 :
15968 104 : destroyPQExpBuffer(q);
15969 104 : destroyPQExpBuffer(delq);
15970 : }
15971 :
15972 : /*
15973 : * dumpForeignServer
15974 : * write out a foreign server definition
15975 : */
15976 : static void
15977 126 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
15978 : {
15979 126 : DumpOptions *dopt = fout->dopt;
15980 : PQExpBuffer q;
15981 : PQExpBuffer delq;
15982 : PQExpBuffer query;
15983 : PGresult *res;
15984 : char *qsrvname;
15985 : char *fdwname;
15986 :
15987 : /* Do nothing if not dumping schema */
15988 126 : if (!dopt->dumpSchema)
15989 18 : return;
15990 :
15991 108 : q = createPQExpBuffer();
15992 108 : delq = createPQExpBuffer();
15993 108 : query = createPQExpBuffer();
15994 :
15995 108 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
15996 :
15997 : /* look up the foreign-data wrapper */
15998 108 : appendPQExpBuffer(query, "SELECT fdwname "
15999 : "FROM pg_foreign_data_wrapper w "
16000 : "WHERE w.oid = '%u'",
16001 108 : srvinfo->srvfdw);
16002 108 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16003 108 : fdwname = PQgetvalue(res, 0, 0);
16004 :
16005 108 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16006 108 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16007 : {
16008 0 : appendPQExpBufferStr(q, " TYPE ");
16009 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16010 : }
16011 108 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16012 : {
16013 0 : appendPQExpBufferStr(q, " VERSION ");
16014 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16015 : }
16016 :
16017 108 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16018 108 : appendPQExpBufferStr(q, fmtId(fdwname));
16019 :
16020 108 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16021 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16022 :
16023 108 : appendPQExpBufferStr(q, ";\n");
16024 :
16025 108 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16026 : qsrvname);
16027 :
16028 108 : if (dopt->binary_upgrade)
16029 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16030 : "SERVER", qsrvname, NULL);
16031 :
16032 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16033 108 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16034 108 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16035 : .owner = srvinfo->rolname,
16036 : .description = "SERVER",
16037 : .section = SECTION_PRE_DATA,
16038 : .createStmt = q->data,
16039 : .dropStmt = delq->data));
16040 :
16041 : /* Dump Foreign Server Comments */
16042 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16043 0 : dumpComment(fout, "SERVER", qsrvname,
16044 0 : NULL, srvinfo->rolname,
16045 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16046 :
16047 : /* Handle the ACL */
16048 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16049 70 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16050 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16051 70 : NULL, srvinfo->rolname, &srvinfo->dacl);
16052 :
16053 : /* Dump user mappings */
16054 108 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16055 108 : dumpUserMappings(fout,
16056 108 : srvinfo->dobj.name, NULL,
16057 108 : srvinfo->rolname,
16058 108 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16059 :
16060 108 : PQclear(res);
16061 :
16062 108 : free(qsrvname);
16063 :
16064 108 : destroyPQExpBuffer(q);
16065 108 : destroyPQExpBuffer(delq);
16066 108 : destroyPQExpBuffer(query);
16067 : }
16068 :
16069 : /*
16070 : * dumpUserMappings
16071 : *
16072 : * This routine is used to dump any user mappings associated with the
16073 : * server handed to this routine. Should be called after ArchiveEntry()
16074 : * for the server.
16075 : */
16076 : static void
16077 108 : dumpUserMappings(Archive *fout,
16078 : const char *servername, const char *namespace,
16079 : const char *owner,
16080 : CatalogId catalogId, DumpId dumpId)
16081 : {
16082 : PQExpBuffer q;
16083 : PQExpBuffer delq;
16084 : PQExpBuffer query;
16085 : PQExpBuffer tag;
16086 : PGresult *res;
16087 : int ntups;
16088 : int i_usename;
16089 : int i_umoptions;
16090 : int i;
16091 :
16092 108 : q = createPQExpBuffer();
16093 108 : tag = createPQExpBuffer();
16094 108 : delq = createPQExpBuffer();
16095 108 : query = createPQExpBuffer();
16096 :
16097 : /*
16098 : * We read from the publicly accessible view pg_user_mappings, so as not
16099 : * to fail if run by a non-superuser. Note that the view will show
16100 : * umoptions as null if the user hasn't got privileges for the associated
16101 : * server; this means that pg_dump will dump such a mapping, but with no
16102 : * OPTIONS clause. A possible alternative is to skip such mappings
16103 : * altogether, but it's not clear that that's an improvement.
16104 : */
16105 108 : appendPQExpBuffer(query,
16106 : "SELECT usename, "
16107 : "array_to_string(ARRAY("
16108 : "SELECT quote_ident(option_name) || ' ' || "
16109 : "quote_literal(option_value) "
16110 : "FROM pg_options_to_table(umoptions) "
16111 : "ORDER BY option_name"
16112 : "), E',\n ') AS umoptions "
16113 : "FROM pg_user_mappings "
16114 : "WHERE srvid = '%u' "
16115 : "ORDER BY usename",
16116 : catalogId.oid);
16117 :
16118 108 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16119 :
16120 108 : ntups = PQntuples(res);
16121 108 : i_usename = PQfnumber(res, "usename");
16122 108 : i_umoptions = PQfnumber(res, "umoptions");
16123 :
16124 178 : for (i = 0; i < ntups; i++)
16125 : {
16126 : char *usename;
16127 : char *umoptions;
16128 :
16129 70 : usename = PQgetvalue(res, i, i_usename);
16130 70 : umoptions = PQgetvalue(res, i, i_umoptions);
16131 :
16132 70 : resetPQExpBuffer(q);
16133 70 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16134 70 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16135 :
16136 70 : if (umoptions && strlen(umoptions) > 0)
16137 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16138 :
16139 70 : appendPQExpBufferStr(q, ";\n");
16140 :
16141 70 : resetPQExpBuffer(delq);
16142 70 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16143 70 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16144 :
16145 70 : resetPQExpBuffer(tag);
16146 70 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16147 : usename, servername);
16148 :
16149 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16150 70 : ARCHIVE_OPTS(.tag = tag->data,
16151 : .namespace = namespace,
16152 : .owner = owner,
16153 : .description = "USER MAPPING",
16154 : .section = SECTION_PRE_DATA,
16155 : .createStmt = q->data,
16156 : .dropStmt = delq->data));
16157 : }
16158 :
16159 108 : PQclear(res);
16160 :
16161 108 : destroyPQExpBuffer(query);
16162 108 : destroyPQExpBuffer(delq);
16163 108 : destroyPQExpBuffer(tag);
16164 108 : destroyPQExpBuffer(q);
16165 108 : }
16166 :
16167 : /*
16168 : * Write out default privileges information
16169 : */
16170 : static void
16171 332 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16172 : {
16173 332 : DumpOptions *dopt = fout->dopt;
16174 : PQExpBuffer q;
16175 : PQExpBuffer tag;
16176 : const char *type;
16177 :
16178 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16179 332 : if (!dopt->dumpSchema || dopt->aclsSkip)
16180 56 : return;
16181 :
16182 276 : q = createPQExpBuffer();
16183 276 : tag = createPQExpBuffer();
16184 :
16185 276 : switch (daclinfo->defaclobjtype)
16186 : {
16187 138 : case DEFACLOBJ_RELATION:
16188 138 : type = "TABLES";
16189 138 : break;
16190 0 : case DEFACLOBJ_SEQUENCE:
16191 0 : type = "SEQUENCES";
16192 0 : break;
16193 138 : case DEFACLOBJ_FUNCTION:
16194 138 : type = "FUNCTIONS";
16195 138 : break;
16196 0 : case DEFACLOBJ_TYPE:
16197 0 : type = "TYPES";
16198 0 : break;
16199 0 : case DEFACLOBJ_NAMESPACE:
16200 0 : type = "SCHEMAS";
16201 0 : break;
16202 0 : case DEFACLOBJ_LARGEOBJECT:
16203 0 : type = "LARGE OBJECTS";
16204 0 : break;
16205 0 : default:
16206 : /* shouldn't get here */
16207 0 : pg_fatal("unrecognized object type in default privileges: %d",
16208 : (int) daclinfo->defaclobjtype);
16209 : type = ""; /* keep compiler quiet */
16210 : }
16211 :
16212 276 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16213 :
16214 : /* build the actual command(s) for this tuple */
16215 276 : if (!buildDefaultACLCommands(type,
16216 276 : daclinfo->dobj.namespace != NULL ?
16217 140 : daclinfo->dobj.namespace->dobj.name : NULL,
16218 276 : daclinfo->dacl.acl,
16219 276 : daclinfo->dacl.acldefault,
16220 276 : daclinfo->defaclrole,
16221 : fout->remoteVersion,
16222 : q))
16223 0 : pg_fatal("could not parse default ACL list (%s)",
16224 : daclinfo->dacl.acl);
16225 :
16226 276 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16227 276 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16228 276 : ARCHIVE_OPTS(.tag = tag->data,
16229 : .namespace = daclinfo->dobj.namespace ?
16230 : daclinfo->dobj.namespace->dobj.name : NULL,
16231 : .owner = daclinfo->defaclrole,
16232 : .description = "DEFAULT ACL",
16233 : .section = SECTION_POST_DATA,
16234 : .createStmt = q->data));
16235 :
16236 276 : destroyPQExpBuffer(tag);
16237 276 : destroyPQExpBuffer(q);
16238 : }
16239 :
16240 : /*----------
16241 : * Write out grant/revoke information
16242 : *
16243 : * 'objDumpId' is the dump ID of the underlying object.
16244 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16245 : * or InvalidDumpId if there is no need for a second dependency.
16246 : * 'type' must be one of
16247 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16248 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16249 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16250 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16251 : * (Currently we assume that subname is only provided for table columns.)
16252 : * 'nspname' is the namespace the object is in (NULL if none).
16253 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16254 : * to use the default for the object type.
16255 : * 'owner' is the owner, NULL if there is no owner (for languages).
16256 : * 'dacl' is the DumpableAcl struct for the object.
16257 : *
16258 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16259 : * no ACL entry was created.
16260 : *----------
16261 : */
16262 : static DumpId
16263 72874 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16264 : const char *type, const char *name, const char *subname,
16265 : const char *nspname, const char *tag, const char *owner,
16266 : const DumpableAcl *dacl)
16267 : {
16268 72874 : DumpId aclDumpId = InvalidDumpId;
16269 72874 : DumpOptions *dopt = fout->dopt;
16270 72874 : const char *acls = dacl->acl;
16271 72874 : const char *acldefault = dacl->acldefault;
16272 72874 : char privtype = dacl->privtype;
16273 72874 : const char *initprivs = dacl->initprivs;
16274 : const char *baseacls;
16275 : PQExpBuffer sql;
16276 :
16277 : /* Do nothing if ACL dump is not enabled */
16278 72874 : if (dopt->aclsSkip)
16279 648 : return InvalidDumpId;
16280 :
16281 : /* --data-only skips ACLs *except* large object ACLs */
16282 72226 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16283 0 : return InvalidDumpId;
16284 :
16285 72226 : sql = createPQExpBuffer();
16286 :
16287 : /*
16288 : * In binary upgrade mode, we don't run an extension's script but instead
16289 : * dump out the objects independently and then recreate them. To preserve
16290 : * any initial privileges which were set on extension objects, we need to
16291 : * compute the set of GRANT and REVOKE commands necessary to get from the
16292 : * default privileges of an object to its initial privileges as recorded
16293 : * in pg_init_privs.
16294 : *
16295 : * At restore time, we apply these commands after having called
16296 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16297 : * copy the results into pg_init_privs. This is how we preserve the
16298 : * contents of that catalog across binary upgrades.
16299 : */
16300 72226 : if (dopt->binary_upgrade && privtype == 'e' &&
16301 26 : initprivs && *initprivs != '\0')
16302 : {
16303 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16304 26 : if (!buildACLCommands(name, subname, nspname, type,
16305 : initprivs, acldefault, owner,
16306 : "", fout->remoteVersion, sql))
16307 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16308 : initprivs, acldefault, name, type);
16309 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16310 : }
16311 :
16312 : /*
16313 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16314 : * actual current ACL, starting from the initprivs if given, else from the
16315 : * object-type-specific default. Also, while buildACLCommands will assume
16316 : * that a NULL/empty acls string means it needn't do anything, what that
16317 : * actually represents is the object-type-specific default; so we need to
16318 : * substitute the acldefault string to get the right results in that case.
16319 : */
16320 72226 : if (initprivs && *initprivs != '\0')
16321 : {
16322 68150 : baseacls = initprivs;
16323 68150 : if (acls == NULL || *acls == '\0')
16324 34 : acls = acldefault;
16325 : }
16326 : else
16327 4076 : baseacls = acldefault;
16328 :
16329 72226 : if (!buildACLCommands(name, subname, nspname, type,
16330 : acls, baseacls, owner,
16331 : "", fout->remoteVersion, sql))
16332 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16333 : acls, baseacls, name, type);
16334 :
16335 72226 : if (sql->len > 0)
16336 : {
16337 4136 : PQExpBuffer tagbuf = createPQExpBuffer();
16338 : DumpId aclDeps[2];
16339 4136 : int nDeps = 0;
16340 :
16341 4136 : if (tag)
16342 0 : appendPQExpBufferStr(tagbuf, tag);
16343 4136 : else if (subname)
16344 2368 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16345 : else
16346 1768 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16347 :
16348 4136 : aclDeps[nDeps++] = objDumpId;
16349 4136 : if (altDumpId != InvalidDumpId)
16350 2174 : aclDeps[nDeps++] = altDumpId;
16351 :
16352 4136 : aclDumpId = createDumpId();
16353 :
16354 4136 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16355 4136 : ARCHIVE_OPTS(.tag = tagbuf->data,
16356 : .namespace = nspname,
16357 : .owner = owner,
16358 : .description = "ACL",
16359 : .section = SECTION_NONE,
16360 : .createStmt = sql->data,
16361 : .deps = aclDeps,
16362 : .nDeps = nDeps));
16363 :
16364 4136 : destroyPQExpBuffer(tagbuf);
16365 : }
16366 :
16367 72226 : destroyPQExpBuffer(sql);
16368 :
16369 72226 : return aclDumpId;
16370 : }
16371 :
16372 : /*
16373 : * dumpSecLabel
16374 : *
16375 : * This routine is used to dump any security labels associated with the
16376 : * object handed to this routine. The routine takes the object type
16377 : * and object name (ready to print, except for schema decoration), plus
16378 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16379 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16380 : * plus the dump ID for the object (for setting a dependency).
16381 : * If a matching pg_seclabel entry is found, it is dumped.
16382 : *
16383 : * Note: although this routine takes a dumpId for dependency purposes,
16384 : * that purpose is just to mark the dependency in the emitted dump file
16385 : * for possible future use by pg_restore. We do NOT use it for determining
16386 : * ordering of the label in the dump file, because this routine is called
16387 : * after dependency sorting occurs. This routine should be called just after
16388 : * calling ArchiveEntry() for the specified object.
16389 : */
16390 : static void
16391 0 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16392 : const char *namespace, const char *owner,
16393 : CatalogId catalogId, int subid, DumpId dumpId)
16394 : {
16395 0 : DumpOptions *dopt = fout->dopt;
16396 : SecLabelItem *labels;
16397 : int nlabels;
16398 : int i;
16399 : PQExpBuffer query;
16400 :
16401 : /* do nothing, if --no-security-labels is supplied */
16402 0 : if (dopt->no_security_labels)
16403 0 : return;
16404 :
16405 : /*
16406 : * Security labels are schema not data ... except large object labels are
16407 : * data
16408 : */
16409 0 : if (strcmp(type, "LARGE OBJECT") != 0)
16410 : {
16411 0 : if (!dopt->dumpSchema)
16412 0 : return;
16413 : }
16414 : else
16415 : {
16416 : /* We do dump large object security labels in binary-upgrade mode */
16417 0 : if (!dopt->dumpData && !dopt->binary_upgrade)
16418 0 : return;
16419 : }
16420 :
16421 : /* Search for security labels associated with catalogId, using table */
16422 0 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16423 :
16424 0 : query = createPQExpBuffer();
16425 :
16426 0 : for (i = 0; i < nlabels; i++)
16427 : {
16428 : /*
16429 : * Ignore label entries for which the subid doesn't match.
16430 : */
16431 0 : if (labels[i].objsubid != subid)
16432 0 : continue;
16433 :
16434 0 : appendPQExpBuffer(query,
16435 : "SECURITY LABEL FOR %s ON %s ",
16436 0 : fmtId(labels[i].provider), type);
16437 0 : if (namespace && *namespace)
16438 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16439 0 : appendPQExpBuffer(query, "%s IS ", name);
16440 0 : appendStringLiteralAH(query, labels[i].label, fout);
16441 0 : appendPQExpBufferStr(query, ";\n");
16442 : }
16443 :
16444 0 : if (query->len > 0)
16445 : {
16446 0 : PQExpBuffer tag = createPQExpBuffer();
16447 :
16448 0 : appendPQExpBuffer(tag, "%s %s", type, name);
16449 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16450 0 : ARCHIVE_OPTS(.tag = tag->data,
16451 : .namespace = namespace,
16452 : .owner = owner,
16453 : .description = "SECURITY LABEL",
16454 : .section = SECTION_NONE,
16455 : .createStmt = query->data,
16456 : .deps = &dumpId,
16457 : .nDeps = 1));
16458 0 : destroyPQExpBuffer(tag);
16459 : }
16460 :
16461 0 : destroyPQExpBuffer(query);
16462 : }
16463 :
16464 : /*
16465 : * dumpTableSecLabel
16466 : *
16467 : * As above, but dump security label for both the specified table (or view)
16468 : * and its columns.
16469 : */
16470 : static void
16471 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16472 : {
16473 0 : DumpOptions *dopt = fout->dopt;
16474 : SecLabelItem *labels;
16475 : int nlabels;
16476 : int i;
16477 : PQExpBuffer query;
16478 : PQExpBuffer target;
16479 :
16480 : /* do nothing, if --no-security-labels is supplied */
16481 0 : if (dopt->no_security_labels)
16482 0 : return;
16483 :
16484 : /* SecLabel are SCHEMA not data */
16485 0 : if (!dopt->dumpSchema)
16486 0 : return;
16487 :
16488 : /* Search for comments associated with relation, using table */
16489 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16490 0 : tbinfo->dobj.catId.oid,
16491 : &labels);
16492 :
16493 : /* If security labels exist, build SECURITY LABEL statements */
16494 0 : if (nlabels <= 0)
16495 0 : return;
16496 :
16497 0 : query = createPQExpBuffer();
16498 0 : target = createPQExpBuffer();
16499 :
16500 0 : for (i = 0; i < nlabels; i++)
16501 : {
16502 : const char *colname;
16503 0 : const char *provider = labels[i].provider;
16504 0 : const char *label = labels[i].label;
16505 0 : int objsubid = labels[i].objsubid;
16506 :
16507 0 : resetPQExpBuffer(target);
16508 0 : if (objsubid == 0)
16509 : {
16510 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16511 0 : fmtQualifiedDumpable(tbinfo));
16512 : }
16513 : else
16514 : {
16515 0 : colname = getAttrName(objsubid, tbinfo);
16516 : /* first fmtXXX result must be consumed before calling again */
16517 0 : appendPQExpBuffer(target, "COLUMN %s",
16518 0 : fmtQualifiedDumpable(tbinfo));
16519 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16520 : }
16521 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16522 : fmtId(provider), target->data);
16523 0 : appendStringLiteralAH(query, label, fout);
16524 0 : appendPQExpBufferStr(query, ";\n");
16525 : }
16526 0 : if (query->len > 0)
16527 : {
16528 0 : resetPQExpBuffer(target);
16529 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16530 0 : fmtId(tbinfo->dobj.name));
16531 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16532 0 : ARCHIVE_OPTS(.tag = target->data,
16533 : .namespace = tbinfo->dobj.namespace->dobj.name,
16534 : .owner = tbinfo->rolname,
16535 : .description = "SECURITY LABEL",
16536 : .section = SECTION_NONE,
16537 : .createStmt = query->data,
16538 : .deps = &(tbinfo->dobj.dumpId),
16539 : .nDeps = 1));
16540 : }
16541 0 : destroyPQExpBuffer(query);
16542 0 : destroyPQExpBuffer(target);
16543 : }
16544 :
16545 : /*
16546 : * findSecLabels
16547 : *
16548 : * Find the security label(s), if any, associated with the given object.
16549 : * All the objsubid values associated with the given classoid/objoid are
16550 : * found with one search.
16551 : */
16552 : static int
16553 0 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16554 : {
16555 0 : SecLabelItem *middle = NULL;
16556 : SecLabelItem *low;
16557 : SecLabelItem *high;
16558 : int nmatch;
16559 :
16560 0 : if (nseclabels <= 0) /* no labels, so no match is possible */
16561 : {
16562 0 : *items = NULL;
16563 0 : return 0;
16564 : }
16565 :
16566 : /*
16567 : * Do binary search to find some item matching the object.
16568 : */
16569 0 : low = &seclabels[0];
16570 0 : high = &seclabels[nseclabels - 1];
16571 0 : while (low <= high)
16572 : {
16573 0 : middle = low + (high - low) / 2;
16574 :
16575 0 : if (classoid < middle->classoid)
16576 0 : high = middle - 1;
16577 0 : else if (classoid > middle->classoid)
16578 0 : low = middle + 1;
16579 0 : else if (objoid < middle->objoid)
16580 0 : high = middle - 1;
16581 0 : else if (objoid > middle->objoid)
16582 0 : low = middle + 1;
16583 : else
16584 0 : break; /* found a match */
16585 : }
16586 :
16587 0 : if (low > high) /* no matches */
16588 : {
16589 0 : *items = NULL;
16590 0 : return 0;
16591 : }
16592 :
16593 : /*
16594 : * Now determine how many items match the object. The search loop
16595 : * invariant still holds: only items between low and high inclusive could
16596 : * match.
16597 : */
16598 0 : nmatch = 1;
16599 0 : while (middle > low)
16600 : {
16601 0 : if (classoid != middle[-1].classoid ||
16602 0 : objoid != middle[-1].objoid)
16603 : break;
16604 0 : middle--;
16605 0 : nmatch++;
16606 : }
16607 :
16608 0 : *items = middle;
16609 :
16610 0 : middle += nmatch;
16611 0 : while (middle <= high)
16612 : {
16613 0 : if (classoid != middle->classoid ||
16614 0 : objoid != middle->objoid)
16615 : break;
16616 0 : middle++;
16617 0 : nmatch++;
16618 : }
16619 :
16620 0 : return nmatch;
16621 : }
16622 :
16623 : /*
16624 : * collectSecLabels
16625 : *
16626 : * Construct a table of all security labels available for database objects;
16627 : * also set the has-seclabel component flag for each relevant object.
16628 : *
16629 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16630 : */
16631 : static void
16632 468 : collectSecLabels(Archive *fout)
16633 : {
16634 : PGresult *res;
16635 : PQExpBuffer query;
16636 : int i_label;
16637 : int i_provider;
16638 : int i_classoid;
16639 : int i_objoid;
16640 : int i_objsubid;
16641 : int ntups;
16642 : int i;
16643 : DumpableObject *dobj;
16644 :
16645 468 : query = createPQExpBuffer();
16646 :
16647 468 : appendPQExpBufferStr(query,
16648 : "SELECT label, provider, classoid, objoid, objsubid "
16649 : "FROM pg_catalog.pg_seclabel "
16650 : "ORDER BY classoid, objoid, objsubid");
16651 :
16652 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16653 :
16654 : /* Construct lookup table containing OIDs in numeric form */
16655 468 : i_label = PQfnumber(res, "label");
16656 468 : i_provider = PQfnumber(res, "provider");
16657 468 : i_classoid = PQfnumber(res, "classoid");
16658 468 : i_objoid = PQfnumber(res, "objoid");
16659 468 : i_objsubid = PQfnumber(res, "objsubid");
16660 :
16661 468 : ntups = PQntuples(res);
16662 :
16663 468 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16664 468 : nseclabels = 0;
16665 468 : dobj = NULL;
16666 :
16667 468 : for (i = 0; i < ntups; i++)
16668 : {
16669 : CatalogId objId;
16670 : int subid;
16671 :
16672 0 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16673 0 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16674 0 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16675 :
16676 : /* We needn't remember labels that don't match any dumpable object */
16677 0 : if (dobj == NULL ||
16678 0 : dobj->catId.tableoid != objId.tableoid ||
16679 0 : dobj->catId.oid != objId.oid)
16680 0 : dobj = findObjectByCatalogId(objId);
16681 0 : if (dobj == NULL)
16682 0 : continue;
16683 :
16684 : /*
16685 : * Labels on columns of composite types are linked to the type's
16686 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16687 : * in the type's own DumpableObject.
16688 : */
16689 0 : if (subid != 0 && dobj->objType == DO_TABLE &&
16690 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16691 0 : {
16692 : TypeInfo *cTypeInfo;
16693 :
16694 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16695 0 : if (cTypeInfo)
16696 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16697 : }
16698 : else
16699 0 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16700 :
16701 0 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16702 0 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16703 0 : seclabels[nseclabels].classoid = objId.tableoid;
16704 0 : seclabels[nseclabels].objoid = objId.oid;
16705 0 : seclabels[nseclabels].objsubid = subid;
16706 0 : nseclabels++;
16707 : }
16708 :
16709 468 : PQclear(res);
16710 468 : destroyPQExpBuffer(query);
16711 468 : }
16712 :
16713 : /*
16714 : * dumpTable
16715 : * write out to fout the declarations (not data) of a user-defined table
16716 : */
16717 : static void
16718 81182 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16719 : {
16720 81182 : DumpOptions *dopt = fout->dopt;
16721 81182 : DumpId tableAclDumpId = InvalidDumpId;
16722 : char *namecopy;
16723 :
16724 : /* Do nothing if not dumping schema */
16725 81182 : if (!dopt->dumpSchema)
16726 3000 : return;
16727 :
16728 78182 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16729 : {
16730 18716 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16731 1106 : dumpSequence(fout, tbinfo);
16732 : else
16733 17610 : dumpTableSchema(fout, tbinfo);
16734 : }
16735 :
16736 : /* Handle the ACL here */
16737 78182 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16738 78182 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16739 : {
16740 61040 : const char *objtype =
16741 61040 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16742 :
16743 : tableAclDumpId =
16744 61040 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16745 : objtype, namecopy, NULL,
16746 61040 : tbinfo->dobj.namespace->dobj.name,
16747 61040 : NULL, tbinfo->rolname, &tbinfo->dacl);
16748 : }
16749 :
16750 : /*
16751 : * Handle column ACLs, if any. Note: we pull these with a separate query
16752 : * rather than trying to fetch them during getTableAttrs, so that we won't
16753 : * miss ACLs on system columns. Doing it this way also allows us to dump
16754 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16755 : */
16756 78182 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16757 : {
16758 694 : PQExpBuffer query = createPQExpBuffer();
16759 : PGresult *res;
16760 : int i;
16761 :
16762 694 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16763 : {
16764 : /* Set up query for column ACLs */
16765 416 : appendPQExpBufferStr(query,
16766 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16767 :
16768 416 : if (fout->remoteVersion >= 90600)
16769 : {
16770 : /*
16771 : * In principle we should call acldefault('c', relowner) to
16772 : * get the default ACL for a column. However, we don't
16773 : * currently store the numeric OID of the relowner in
16774 : * TableInfo. We could convert the owner name using regrole,
16775 : * but that creates a risk of failure due to concurrent role
16776 : * renames. Given that the default ACL for columns is empty
16777 : * and is likely to stay that way, it's not worth extra cycles
16778 : * and risk to avoid hard-wiring that knowledge here.
16779 : */
16780 416 : appendPQExpBufferStr(query,
16781 : "SELECT at.attname, "
16782 : "at.attacl, "
16783 : "'{}' AS acldefault, "
16784 : "pip.privtype, pip.initprivs "
16785 : "FROM pg_catalog.pg_attribute at "
16786 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16787 : "(at.attrelid = pip.objoid "
16788 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16789 : "AND at.attnum = pip.objsubid) "
16790 : "WHERE at.attrelid = $1 AND "
16791 : "NOT at.attisdropped "
16792 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16793 : "ORDER BY at.attnum");
16794 : }
16795 : else
16796 : {
16797 0 : appendPQExpBufferStr(query,
16798 : "SELECT attname, attacl, '{}' AS acldefault, "
16799 : "NULL AS privtype, NULL AS initprivs "
16800 : "FROM pg_catalog.pg_attribute "
16801 : "WHERE attrelid = $1 AND NOT attisdropped "
16802 : "AND attacl IS NOT NULL "
16803 : "ORDER BY attnum");
16804 : }
16805 :
16806 416 : ExecuteSqlStatement(fout, query->data);
16807 :
16808 416 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16809 : }
16810 :
16811 694 : printfPQExpBuffer(query,
16812 : "EXECUTE getColumnACLs('%u')",
16813 694 : tbinfo->dobj.catId.oid);
16814 :
16815 694 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16816 :
16817 10442 : for (i = 0; i < PQntuples(res); i++)
16818 : {
16819 9748 : char *attname = PQgetvalue(res, i, 0);
16820 9748 : char *attacl = PQgetvalue(res, i, 1);
16821 9748 : char *acldefault = PQgetvalue(res, i, 2);
16822 9748 : char privtype = *(PQgetvalue(res, i, 3));
16823 9748 : char *initprivs = PQgetvalue(res, i, 4);
16824 : DumpableAcl coldacl;
16825 : char *attnamecopy;
16826 :
16827 9748 : coldacl.acl = attacl;
16828 9748 : coldacl.acldefault = acldefault;
16829 9748 : coldacl.privtype = privtype;
16830 9748 : coldacl.initprivs = initprivs;
16831 9748 : attnamecopy = pg_strdup(fmtId(attname));
16832 :
16833 : /*
16834 : * Column's GRANT type is always TABLE. Each column ACL depends
16835 : * on the table-level ACL, since we can restore column ACLs in
16836 : * parallel but the table-level ACL has to be done first.
16837 : */
16838 9748 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16839 : "TABLE", namecopy, attnamecopy,
16840 9748 : tbinfo->dobj.namespace->dobj.name,
16841 9748 : NULL, tbinfo->rolname, &coldacl);
16842 9748 : free(attnamecopy);
16843 : }
16844 694 : PQclear(res);
16845 694 : destroyPQExpBuffer(query);
16846 : }
16847 :
16848 78182 : free(namecopy);
16849 : }
16850 :
16851 : /*
16852 : * Create the AS clause for a view or materialized view. The semicolon is
16853 : * stripped because a materialized view must add a WITH NO DATA clause.
16854 : *
16855 : * This returns a new buffer which must be freed by the caller.
16856 : */
16857 : static PQExpBuffer
16858 2222 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16859 : {
16860 2222 : PQExpBuffer query = createPQExpBuffer();
16861 2222 : PQExpBuffer result = createPQExpBuffer();
16862 : PGresult *res;
16863 : int len;
16864 :
16865 : /* Fetch the view definition */
16866 2222 : appendPQExpBuffer(query,
16867 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16868 2222 : tbinfo->dobj.catId.oid);
16869 :
16870 2222 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16871 :
16872 2222 : if (PQntuples(res) != 1)
16873 : {
16874 0 : if (PQntuples(res) < 1)
16875 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
16876 : tbinfo->dobj.name);
16877 : else
16878 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
16879 : tbinfo->dobj.name);
16880 : }
16881 :
16882 2222 : len = PQgetlength(res, 0, 0);
16883 :
16884 2222 : if (len == 0)
16885 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
16886 : tbinfo->dobj.name);
16887 :
16888 : /* Strip off the trailing semicolon so that other things may follow. */
16889 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
16890 2222 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
16891 :
16892 2222 : PQclear(res);
16893 2222 : destroyPQExpBuffer(query);
16894 :
16895 2222 : return result;
16896 : }
16897 :
16898 : /*
16899 : * Create a dummy AS clause for a view. This is used when the real view
16900 : * definition has to be postponed because of circular dependencies.
16901 : * We must duplicate the view's external properties -- column names and types
16902 : * (including collation) -- so that it works for subsequent references.
16903 : *
16904 : * This returns a new buffer which must be freed by the caller.
16905 : */
16906 : static PQExpBuffer
16907 64 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
16908 : {
16909 64 : PQExpBuffer result = createPQExpBuffer();
16910 : int j;
16911 :
16912 64 : appendPQExpBufferStr(result, "SELECT");
16913 :
16914 128 : for (j = 0; j < tbinfo->numatts; j++)
16915 : {
16916 64 : if (j > 0)
16917 32 : appendPQExpBufferChar(result, ',');
16918 64 : appendPQExpBufferStr(result, "\n ");
16919 :
16920 64 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
16921 :
16922 : /*
16923 : * Must add collation if not default for the type, because CREATE OR
16924 : * REPLACE VIEW won't change it
16925 : */
16926 64 : if (OidIsValid(tbinfo->attcollation[j]))
16927 : {
16928 : CollInfo *coll;
16929 :
16930 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
16931 0 : if (coll)
16932 0 : appendPQExpBuffer(result, " COLLATE %s",
16933 0 : fmtQualifiedDumpable(coll));
16934 : }
16935 :
16936 64 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
16937 : }
16938 :
16939 64 : return result;
16940 : }
16941 :
16942 : /*
16943 : * dumpTableSchema
16944 : * write the declaration (not data) of one user-defined table or view
16945 : */
16946 : static void
16947 17610 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
16948 : {
16949 17610 : DumpOptions *dopt = fout->dopt;
16950 17610 : PQExpBuffer q = createPQExpBuffer();
16951 17610 : PQExpBuffer delq = createPQExpBuffer();
16952 17610 : PQExpBuffer extra = createPQExpBuffer();
16953 : char *qrelname;
16954 : char *qualrelname;
16955 : int numParents;
16956 : TableInfo **parents;
16957 : int actual_atts; /* number of attrs in this CREATE statement */
16958 : const char *reltypename;
16959 : char *storage;
16960 : int j,
16961 : k;
16962 :
16963 : /* We had better have loaded per-column details about this table */
16964 : Assert(tbinfo->interesting);
16965 :
16966 17610 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
16967 17610 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
16968 :
16969 17610 : if (tbinfo->hasoids)
16970 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
16971 : qrelname);
16972 :
16973 17610 : if (dopt->binary_upgrade)
16974 1704 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
16975 :
16976 : /* Is it a table or a view? */
16977 17610 : if (tbinfo->relkind == RELKIND_VIEW)
16978 : {
16979 : PQExpBuffer result;
16980 :
16981 : /*
16982 : * Note: keep this code in sync with the is_view case in dumpRule()
16983 : */
16984 :
16985 1374 : reltypename = "VIEW";
16986 :
16987 1374 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
16988 :
16989 1374 : if (dopt->binary_upgrade)
16990 104 : binary_upgrade_set_pg_class_oids(fout, q,
16991 104 : tbinfo->dobj.catId.oid);
16992 :
16993 1374 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
16994 :
16995 1374 : if (tbinfo->dummy_view)
16996 32 : result = createDummyViewAsClause(fout, tbinfo);
16997 : else
16998 : {
16999 1342 : if (nonemptyReloptions(tbinfo->reloptions))
17000 : {
17001 154 : appendPQExpBufferStr(q, " WITH (");
17002 154 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17003 154 : appendPQExpBufferChar(q, ')');
17004 : }
17005 1342 : result = createViewAsClause(fout, tbinfo);
17006 : }
17007 1374 : appendPQExpBuffer(q, " AS\n%s", result->data);
17008 1374 : destroyPQExpBuffer(result);
17009 :
17010 1374 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17011 72 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17012 1374 : appendPQExpBufferStr(q, ";\n");
17013 : }
17014 : else
17015 : {
17016 16236 : char *partkeydef = NULL;
17017 16236 : char *ftoptions = NULL;
17018 16236 : char *srvname = NULL;
17019 16236 : const char *foreign = "";
17020 :
17021 : /*
17022 : * Set reltypename, and collect any relkind-specific data that we
17023 : * didn't fetch during getTables().
17024 : */
17025 16236 : switch (tbinfo->relkind)
17026 : {
17027 1616 : case RELKIND_PARTITIONED_TABLE:
17028 : {
17029 1616 : PQExpBuffer query = createPQExpBuffer();
17030 : PGresult *res;
17031 :
17032 1616 : reltypename = "TABLE";
17033 :
17034 : /* retrieve partition key definition */
17035 1616 : appendPQExpBuffer(query,
17036 : "SELECT pg_get_partkeydef('%u')",
17037 1616 : tbinfo->dobj.catId.oid);
17038 1616 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17039 1616 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17040 1616 : PQclear(res);
17041 1616 : destroyPQExpBuffer(query);
17042 1616 : break;
17043 : }
17044 76 : case RELKIND_FOREIGN_TABLE:
17045 : {
17046 76 : PQExpBuffer query = createPQExpBuffer();
17047 : PGresult *res;
17048 : int i_srvname;
17049 : int i_ftoptions;
17050 :
17051 76 : reltypename = "FOREIGN TABLE";
17052 :
17053 : /* retrieve name of foreign server and generic options */
17054 76 : appendPQExpBuffer(query,
17055 : "SELECT fs.srvname, "
17056 : "pg_catalog.array_to_string(ARRAY("
17057 : "SELECT pg_catalog.quote_ident(option_name) || "
17058 : "' ' || pg_catalog.quote_literal(option_value) "
17059 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17060 : "ORDER BY option_name"
17061 : "), E',\n ') AS ftoptions "
17062 : "FROM pg_catalog.pg_foreign_table ft "
17063 : "JOIN pg_catalog.pg_foreign_server fs "
17064 : "ON (fs.oid = ft.ftserver) "
17065 : "WHERE ft.ftrelid = '%u'",
17066 76 : tbinfo->dobj.catId.oid);
17067 76 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17068 76 : i_srvname = PQfnumber(res, "srvname");
17069 76 : i_ftoptions = PQfnumber(res, "ftoptions");
17070 76 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17071 76 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17072 76 : PQclear(res);
17073 76 : destroyPQExpBuffer(query);
17074 :
17075 76 : foreign = "FOREIGN ";
17076 76 : break;
17077 : }
17078 848 : case RELKIND_MATVIEW:
17079 848 : reltypename = "MATERIALIZED VIEW";
17080 848 : break;
17081 13696 : default:
17082 13696 : reltypename = "TABLE";
17083 13696 : break;
17084 : }
17085 :
17086 16236 : numParents = tbinfo->numParents;
17087 16236 : parents = tbinfo->parents;
17088 :
17089 16236 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17090 :
17091 16236 : if (dopt->binary_upgrade)
17092 1600 : binary_upgrade_set_pg_class_oids(fout, q,
17093 1600 : tbinfo->dobj.catId.oid);
17094 :
17095 : /*
17096 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17097 : * ignore it when dumping if it was set in this case.
17098 : */
17099 16236 : appendPQExpBuffer(q, "CREATE %s%s %s",
17100 16236 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17101 64 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17102 : "UNLOGGED " : "",
17103 : reltypename,
17104 : qualrelname);
17105 :
17106 : /*
17107 : * Attach to type, if reloftype; except in case of a binary upgrade,
17108 : * we dump the table normally and attach it to the type afterward.
17109 : */
17110 16236 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17111 84 : appendPQExpBuffer(q, " OF %s",
17112 84 : getFormattedTypeName(fout, tbinfo->reloftype,
17113 : zeroIsError));
17114 :
17115 16236 : if (tbinfo->relkind != RELKIND_MATVIEW)
17116 : {
17117 : /* Dump the attributes */
17118 15388 : actual_atts = 0;
17119 73520 : for (j = 0; j < tbinfo->numatts; j++)
17120 : {
17121 : /*
17122 : * Normally, dump if it's locally defined in this table, and
17123 : * not dropped. But for binary upgrade, we'll dump all the
17124 : * columns, and then fix up the dropped and nonlocal cases
17125 : * below.
17126 : */
17127 58132 : if (shouldPrintColumn(dopt, tbinfo, j))
17128 : {
17129 : bool print_default;
17130 : bool print_notnull;
17131 :
17132 : /*
17133 : * Default value --- suppress if to be printed separately
17134 : * or not at all.
17135 : */
17136 113336 : print_default = (tbinfo->attrdefs[j] != NULL &&
17137 58020 : tbinfo->attrdefs[j]->dobj.dump &&
17138 2830 : !tbinfo->attrdefs[j]->separate);
17139 :
17140 : /*
17141 : * Not Null constraint --- print it if it is locally
17142 : * defined, or if binary upgrade. (In the latter case, we
17143 : * reset conislocal below.)
17144 : */
17145 61246 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17146 6056 : (tbinfo->notnull_islocal[j] ||
17147 1640 : dopt->binary_upgrade ||
17148 1472 : tbinfo->ispartition));
17149 :
17150 : /*
17151 : * Skip column if fully defined by reloftype, except in
17152 : * binary upgrade
17153 : */
17154 55190 : if (OidIsValid(tbinfo->reloftype) &&
17155 160 : !print_default && !print_notnull &&
17156 96 : !dopt->binary_upgrade)
17157 84 : continue;
17158 :
17159 : /* Format properly if not first attr */
17160 55106 : if (actual_atts == 0)
17161 14516 : appendPQExpBufferStr(q, " (");
17162 : else
17163 40590 : appendPQExpBufferChar(q, ',');
17164 55106 : appendPQExpBufferStr(q, "\n ");
17165 55106 : actual_atts++;
17166 :
17167 : /* Attribute name */
17168 55106 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17169 :
17170 55106 : if (tbinfo->attisdropped[j])
17171 : {
17172 : /*
17173 : * ALTER TABLE DROP COLUMN clears
17174 : * pg_attribute.atttypid, so we will not have gotten a
17175 : * valid type name; insert INTEGER as a stopgap. We'll
17176 : * clean things up later.
17177 : */
17178 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17179 : /* and skip to the next column */
17180 168 : continue;
17181 : }
17182 :
17183 : /*
17184 : * Attribute type; print it except when creating a typed
17185 : * table ('OF type_name'), but in binary-upgrade mode,
17186 : * print it in that case too.
17187 : */
17188 54938 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17189 : {
17190 54882 : appendPQExpBuffer(q, " %s",
17191 54882 : tbinfo->atttypnames[j]);
17192 : }
17193 :
17194 54938 : if (print_default)
17195 : {
17196 2476 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17197 788 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17198 788 : tbinfo->attrdefs[j]->adef_expr);
17199 1688 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17200 676 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17201 676 : tbinfo->attrdefs[j]->adef_expr);
17202 : else
17203 1012 : appendPQExpBuffer(q, " DEFAULT %s",
17204 1012 : tbinfo->attrdefs[j]->adef_expr);
17205 : }
17206 :
17207 54938 : if (print_notnull)
17208 : {
17209 5986 : if (tbinfo->notnull_constrs[j][0] == '\0')
17210 4268 : appendPQExpBufferStr(q, " NOT NULL");
17211 : else
17212 1718 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17213 1718 : fmtId(tbinfo->notnull_constrs[j]));
17214 :
17215 5986 : if (tbinfo->notnull_noinh[j])
17216 0 : appendPQExpBufferStr(q, " NO INHERIT");
17217 : }
17218 :
17219 : /* Add collation if not default for the type */
17220 54938 : if (OidIsValid(tbinfo->attcollation[j]))
17221 : {
17222 : CollInfo *coll;
17223 :
17224 400 : coll = findCollationByOid(tbinfo->attcollation[j]);
17225 400 : if (coll)
17226 400 : appendPQExpBuffer(q, " COLLATE %s",
17227 400 : fmtQualifiedDumpable(coll));
17228 : }
17229 : }
17230 :
17231 : /*
17232 : * On the other hand, if we choose not to print a column
17233 : * (likely because it is created by inheritance), but the
17234 : * column has a locally-defined not-null constraint, we need
17235 : * to dump the constraint as a standalone object.
17236 : *
17237 : * This syntax isn't SQL-conforming, but if you wanted
17238 : * standard output you wouldn't be creating non-standard
17239 : * objects to begin with.
17240 : */
17241 57880 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17242 2942 : !tbinfo->attisdropped[j] &&
17243 1876 : tbinfo->notnull_constrs[j] != NULL &&
17244 490 : tbinfo->notnull_islocal[j])
17245 : {
17246 : /* Format properly if not first attr */
17247 154 : if (actual_atts == 0)
17248 140 : appendPQExpBufferStr(q, " (");
17249 : else
17250 14 : appendPQExpBufferChar(q, ',');
17251 154 : appendPQExpBufferStr(q, "\n ");
17252 154 : actual_atts++;
17253 :
17254 154 : if (tbinfo->notnull_constrs[j][0] == '\0')
17255 14 : appendPQExpBuffer(q, "NOT NULL %s",
17256 14 : fmtId(tbinfo->attnames[j]));
17257 : else
17258 280 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17259 140 : tbinfo->notnull_constrs[j],
17260 140 : fmtId(tbinfo->attnames[j]));
17261 : }
17262 : }
17263 :
17264 : /*
17265 : * Add non-inherited CHECK constraints, if any.
17266 : *
17267 : * For partitions, we need to include check constraints even if
17268 : * they're not defined locally, because the ALTER TABLE ATTACH
17269 : * PARTITION that we'll emit later expects the constraint to be
17270 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17271 : */
17272 16940 : for (j = 0; j < tbinfo->ncheck; j++)
17273 : {
17274 1552 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17275 :
17276 1552 : if (constr->separate ||
17277 1328 : (!constr->conislocal && !tbinfo->ispartition))
17278 312 : continue;
17279 :
17280 1240 : if (actual_atts == 0)
17281 56 : appendPQExpBufferStr(q, " (\n ");
17282 : else
17283 1184 : appendPQExpBufferStr(q, ",\n ");
17284 :
17285 1240 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17286 1240 : fmtId(constr->dobj.name));
17287 1240 : appendPQExpBufferStr(q, constr->condef);
17288 :
17289 1240 : actual_atts++;
17290 : }
17291 :
17292 15388 : if (actual_atts)
17293 14712 : appendPQExpBufferStr(q, "\n)");
17294 676 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17295 : {
17296 : /*
17297 : * No attributes? we must have a parenthesized attribute list,
17298 : * even though empty, when not using the OF TYPE syntax.
17299 : */
17300 634 : appendPQExpBufferStr(q, " (\n)");
17301 : }
17302 :
17303 : /*
17304 : * Emit the INHERITS clause (not for partitions), except in
17305 : * binary-upgrade mode.
17306 : */
17307 15388 : if (numParents > 0 && !tbinfo->ispartition &&
17308 1358 : !dopt->binary_upgrade)
17309 : {
17310 1232 : appendPQExpBufferStr(q, "\nINHERITS (");
17311 2674 : for (k = 0; k < numParents; k++)
17312 : {
17313 1442 : TableInfo *parentRel = parents[k];
17314 :
17315 1442 : if (k > 0)
17316 210 : appendPQExpBufferStr(q, ", ");
17317 1442 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17318 : }
17319 1232 : appendPQExpBufferChar(q, ')');
17320 : }
17321 :
17322 15388 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17323 1616 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17324 :
17325 15388 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17326 76 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17327 : }
17328 :
17329 32048 : if (nonemptyReloptions(tbinfo->reloptions) ||
17330 15812 : nonemptyReloptions(tbinfo->toast_reloptions))
17331 : {
17332 424 : bool addcomma = false;
17333 :
17334 424 : appendPQExpBufferStr(q, "\nWITH (");
17335 424 : if (nonemptyReloptions(tbinfo->reloptions))
17336 : {
17337 424 : addcomma = true;
17338 424 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17339 : }
17340 424 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17341 : {
17342 16 : if (addcomma)
17343 16 : appendPQExpBufferStr(q, ", ");
17344 16 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17345 : fout);
17346 : }
17347 424 : appendPQExpBufferChar(q, ')');
17348 : }
17349 :
17350 : /* Dump generic options if any */
17351 16236 : if (ftoptions && ftoptions[0])
17352 72 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17353 :
17354 : /*
17355 : * For materialized views, create the AS clause just like a view. At
17356 : * this point, we always mark the view as not populated.
17357 : */
17358 16236 : if (tbinfo->relkind == RELKIND_MATVIEW)
17359 : {
17360 : PQExpBuffer result;
17361 :
17362 848 : result = createViewAsClause(fout, tbinfo);
17363 848 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17364 : result->data);
17365 848 : destroyPQExpBuffer(result);
17366 : }
17367 : else
17368 15388 : appendPQExpBufferStr(q, ";\n");
17369 :
17370 : /* Materialized views can depend on extensions */
17371 16236 : if (tbinfo->relkind == RELKIND_MATVIEW)
17372 848 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17373 : "pg_catalog.pg_class",
17374 : "MATERIALIZED VIEW",
17375 : qualrelname);
17376 :
17377 : /*
17378 : * in binary upgrade mode, update the catalog with any missing values
17379 : * that might be present.
17380 : */
17381 16236 : if (dopt->binary_upgrade)
17382 : {
17383 7836 : for (j = 0; j < tbinfo->numatts; j++)
17384 : {
17385 6236 : if (tbinfo->attmissingval[j][0] != '\0')
17386 : {
17387 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17388 4 : appendPQExpBufferStr(q,
17389 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17390 4 : appendStringLiteralAH(q, qualrelname, fout);
17391 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17392 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17393 4 : appendPQExpBufferChar(q, ',');
17394 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17395 4 : appendPQExpBufferStr(q, ");\n\n");
17396 : }
17397 : }
17398 : }
17399 :
17400 : /*
17401 : * To create binary-compatible heap files, we have to ensure the same
17402 : * physical column order, including dropped columns, as in the
17403 : * original. Therefore, we create dropped columns above and drop them
17404 : * here, also updating their attlen/attalign values so that the
17405 : * dropped column can be skipped properly. (We do not bother with
17406 : * restoring the original attbyval setting.) Also, inheritance
17407 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17408 : * using an INHERITS clause --- the latter would possibly mess up the
17409 : * column order. That also means we have to take care about setting
17410 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17411 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17412 : *
17413 : * We process foreign and partitioned tables here, even though they
17414 : * lack heap storage, because they can participate in inheritance
17415 : * relationships and we want this stuff to be consistent across the
17416 : * inheritance tree. We can exclude indexes, toast tables, sequences
17417 : * and matviews, even though they have storage, because we don't
17418 : * support altering or dropping columns in them, nor can they be part
17419 : * of inheritance trees.
17420 : */
17421 16236 : if (dopt->binary_upgrade &&
17422 1600 : (tbinfo->relkind == RELKIND_RELATION ||
17423 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17424 216 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17425 : {
17426 : bool firstitem;
17427 : bool firstitem_extra;
17428 :
17429 : /*
17430 : * Drop any dropped columns. Merge the pg_attribute manipulations
17431 : * into a single SQL command, so that we don't cause repeated
17432 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17433 : * relcache bloat while dropping N columns.
17434 : */
17435 1564 : resetPQExpBuffer(extra);
17436 1564 : firstitem = true;
17437 7756 : for (j = 0; j < tbinfo->numatts; j++)
17438 : {
17439 6192 : if (tbinfo->attisdropped[j])
17440 : {
17441 168 : if (firstitem)
17442 : {
17443 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17444 : "UPDATE pg_catalog.pg_attribute\n"
17445 : "SET attlen = v.dlen, "
17446 : "attalign = v.dalign, "
17447 : "attbyval = false\n"
17448 : "FROM (VALUES ");
17449 76 : firstitem = false;
17450 : }
17451 : else
17452 92 : appendPQExpBufferStr(q, ",\n ");
17453 168 : appendPQExpBufferChar(q, '(');
17454 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17455 168 : appendPQExpBuffer(q, ", %d, '%c')",
17456 168 : tbinfo->attlen[j],
17457 168 : tbinfo->attalign[j]);
17458 : /* The ALTER ... DROP COLUMN commands must come after */
17459 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17460 : foreign, qualrelname);
17461 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17462 168 : fmtId(tbinfo->attnames[j]));
17463 : }
17464 : }
17465 1564 : if (!firstitem)
17466 : {
17467 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17468 : "WHERE attrelid = ");
17469 76 : appendStringLiteralAH(q, qualrelname, fout);
17470 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17471 : " AND attname = v.dname;\n");
17472 : /* Now we can issue the actual DROP COLUMN commands */
17473 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17474 : }
17475 :
17476 : /*
17477 : * Fix up inherited columns. As above, do the pg_attribute
17478 : * manipulations in a single SQL command.
17479 : */
17480 1564 : firstitem = true;
17481 7756 : for (j = 0; j < tbinfo->numatts; j++)
17482 : {
17483 6192 : if (!tbinfo->attisdropped[j] &&
17484 6024 : !tbinfo->attislocal[j])
17485 : {
17486 1202 : if (firstitem)
17487 : {
17488 528 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17489 528 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17490 : "SET attislocal = false\n"
17491 : "WHERE attrelid = ");
17492 528 : appendStringLiteralAH(q, qualrelname, fout);
17493 528 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17494 : " AND attname IN (");
17495 528 : firstitem = false;
17496 : }
17497 : else
17498 674 : appendPQExpBufferStr(q, ", ");
17499 1202 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17500 : }
17501 : }
17502 1564 : if (!firstitem)
17503 528 : appendPQExpBufferStr(q, ");\n");
17504 :
17505 : /*
17506 : * Fix up not-null constraints that come from inheritance. As
17507 : * above, do the pg_constraint manipulations in a single SQL
17508 : * command. (Actually, two in special cases, if we're doing an
17509 : * upgrade from < 18).
17510 : */
17511 1564 : firstitem = true;
17512 1564 : firstitem_extra = true;
17513 1564 : resetPQExpBuffer(extra);
17514 7756 : for (j = 0; j < tbinfo->numatts; j++)
17515 : {
17516 : /*
17517 : * If a not-null constraint comes from inheritance, reset
17518 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17519 : * below. Special hack: in versions < 18, columns with no
17520 : * local definition need their constraint to be matched by
17521 : * column number in conkeys instead of by constraint name,
17522 : * because the latter is not available. (We distinguish the
17523 : * case because the constraint name is the empty string.)
17524 : */
17525 6192 : if (tbinfo->notnull_constrs[j] != NULL &&
17526 580 : !tbinfo->notnull_islocal[j])
17527 : {
17528 168 : if (tbinfo->notnull_constrs[j][0] != '\0')
17529 : {
17530 142 : if (firstitem)
17531 : {
17532 122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17533 : "SET conislocal = false\n"
17534 : "WHERE contype = 'n' AND conrelid = ");
17535 122 : appendStringLiteralAH(q, qualrelname, fout);
17536 122 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17537 : "conname IN (");
17538 122 : firstitem = false;
17539 : }
17540 : else
17541 20 : appendPQExpBufferStr(q, ", ");
17542 142 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17543 : }
17544 : else
17545 : {
17546 26 : if (firstitem_extra)
17547 : {
17548 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17549 : "SET conislocal = false\n"
17550 : "WHERE contype = 'n' AND conrelid = ");
17551 26 : appendStringLiteralAH(extra, qualrelname, fout);
17552 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17553 : "conkey IN (");
17554 26 : firstitem_extra = false;
17555 : }
17556 : else
17557 0 : appendPQExpBufferStr(extra, ", ");
17558 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17559 : }
17560 : }
17561 : }
17562 1564 : if (!firstitem)
17563 122 : appendPQExpBufferStr(q, ");\n");
17564 1564 : if (!firstitem_extra)
17565 26 : appendPQExpBufferStr(extra, ");\n");
17566 :
17567 1564 : if (extra->len > 0)
17568 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17569 :
17570 : /*
17571 : * Add inherited CHECK constraints, if any.
17572 : *
17573 : * For partitions, they were already dumped, and conislocal
17574 : * doesn't need fixing.
17575 : *
17576 : * As above, issue only one direct manipulation of pg_constraint.
17577 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17578 : * commands into one as well, refrain for now due to concern about
17579 : * possible backend memory bloat if there are many such
17580 : * constraints.
17581 : */
17582 1564 : resetPQExpBuffer(extra);
17583 1564 : firstitem = true;
17584 1692 : for (k = 0; k < tbinfo->ncheck; k++)
17585 : {
17586 128 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17587 :
17588 128 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17589 124 : continue;
17590 :
17591 4 : if (firstitem)
17592 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17593 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17594 : foreign, qualrelname,
17595 4 : fmtId(constr->dobj.name),
17596 : constr->condef);
17597 : /* Update pg_constraint after all the ALTER TABLEs */
17598 4 : if (firstitem)
17599 : {
17600 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17601 : "SET conislocal = false\n"
17602 : "WHERE contype = 'c' AND conrelid = ");
17603 4 : appendStringLiteralAH(extra, qualrelname, fout);
17604 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17605 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17606 4 : firstitem = false;
17607 : }
17608 : else
17609 0 : appendPQExpBufferStr(extra, ", ");
17610 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17611 : }
17612 1564 : if (!firstitem)
17613 : {
17614 4 : appendPQExpBufferStr(extra, ");\n");
17615 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17616 : }
17617 :
17618 1564 : if (numParents > 0 && !tbinfo->ispartition)
17619 : {
17620 126 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17621 274 : for (k = 0; k < numParents; k++)
17622 : {
17623 148 : TableInfo *parentRel = parents[k];
17624 :
17625 148 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17626 : qualrelname,
17627 148 : fmtQualifiedDumpable(parentRel));
17628 : }
17629 : }
17630 :
17631 1564 : if (OidIsValid(tbinfo->reloftype))
17632 : {
17633 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17634 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17635 : qualrelname,
17636 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17637 : zeroIsError));
17638 : }
17639 : }
17640 :
17641 : /*
17642 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17643 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17644 : * TOAST tables semi-independently, here we see them only as children
17645 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17646 : * child toast table is handled below.)
17647 : */
17648 16236 : if (dopt->binary_upgrade &&
17649 1600 : (tbinfo->relkind == RELKIND_RELATION ||
17650 218 : tbinfo->relkind == RELKIND_MATVIEW))
17651 : {
17652 1418 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17653 1418 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17654 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17655 : "WHERE oid = ",
17656 1418 : tbinfo->frozenxid, tbinfo->minmxid);
17657 1418 : appendStringLiteralAH(q, qualrelname, fout);
17658 1418 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17659 :
17660 1418 : if (tbinfo->toast_oid)
17661 : {
17662 : /*
17663 : * The toast table will have the same OID at restore, so we
17664 : * can safely target it by OID.
17665 : */
17666 560 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17667 560 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17668 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17669 : "WHERE oid = '%u';\n",
17670 560 : tbinfo->toast_frozenxid,
17671 560 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17672 : }
17673 : }
17674 :
17675 : /*
17676 : * In binary_upgrade mode, restore matviews' populated status by
17677 : * poking pg_class directly. This is pretty ugly, but we can't use
17678 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17679 : * matview is not populated even though this matview is; in any case,
17680 : * we want to transfer the matview's heap storage, not run REFRESH.
17681 : */
17682 16236 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17683 36 : tbinfo->relispopulated)
17684 : {
17685 32 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17686 32 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17687 : "SET relispopulated = 't'\n"
17688 : "WHERE oid = ");
17689 32 : appendStringLiteralAH(q, qualrelname, fout);
17690 32 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17691 : }
17692 :
17693 : /*
17694 : * Dump additional per-column properties that we can't handle in the
17695 : * main CREATE TABLE command.
17696 : */
17697 75392 : for (j = 0; j < tbinfo->numatts; j++)
17698 : {
17699 : /* None of this applies to dropped columns */
17700 59156 : if (tbinfo->attisdropped[j])
17701 1234 : continue;
17702 :
17703 : /*
17704 : * Dump per-column statistics information. We only issue an ALTER
17705 : * TABLE statement if the attstattarget entry for this column is
17706 : * not the default value.
17707 : */
17708 57922 : if (tbinfo->attstattarget[j] >= 0)
17709 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17710 : foreign, qualrelname,
17711 72 : fmtId(tbinfo->attnames[j]),
17712 72 : tbinfo->attstattarget[j]);
17713 :
17714 : /*
17715 : * Dump per-column storage information. The statement is only
17716 : * dumped if the storage has been changed from the type's default.
17717 : */
17718 57922 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17719 : {
17720 192 : switch (tbinfo->attstorage[j])
17721 : {
17722 32 : case TYPSTORAGE_PLAIN:
17723 32 : storage = "PLAIN";
17724 32 : break;
17725 88 : case TYPSTORAGE_EXTERNAL:
17726 88 : storage = "EXTERNAL";
17727 88 : break;
17728 0 : case TYPSTORAGE_EXTENDED:
17729 0 : storage = "EXTENDED";
17730 0 : break;
17731 72 : case TYPSTORAGE_MAIN:
17732 72 : storage = "MAIN";
17733 72 : break;
17734 0 : default:
17735 0 : storage = NULL;
17736 : }
17737 :
17738 : /*
17739 : * Only dump the statement if it's a storage type we recognize
17740 : */
17741 192 : if (storage != NULL)
17742 192 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17743 : foreign, qualrelname,
17744 192 : fmtId(tbinfo->attnames[j]),
17745 : storage);
17746 : }
17747 :
17748 : /*
17749 : * Dump per-column compression, if it's been set.
17750 : */
17751 57922 : if (!dopt->no_toast_compression)
17752 : {
17753 : const char *cmname;
17754 :
17755 57732 : switch (tbinfo->attcompression[j])
17756 : {
17757 198 : case 'p':
17758 198 : cmname = "pglz";
17759 198 : break;
17760 220 : case 'l':
17761 220 : cmname = "lz4";
17762 220 : break;
17763 57314 : default:
17764 57314 : cmname = NULL;
17765 57314 : break;
17766 : }
17767 :
17768 57732 : if (cmname != NULL)
17769 418 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17770 : foreign, qualrelname,
17771 418 : fmtId(tbinfo->attnames[j]),
17772 : cmname);
17773 : }
17774 :
17775 : /*
17776 : * Dump per-column attributes.
17777 : */
17778 57922 : if (tbinfo->attoptions[j][0] != '\0')
17779 72 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17780 : foreign, qualrelname,
17781 72 : fmtId(tbinfo->attnames[j]),
17782 72 : tbinfo->attoptions[j]);
17783 :
17784 : /*
17785 : * Dump per-column fdw options.
17786 : */
17787 57922 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17788 76 : tbinfo->attfdwoptions[j][0] != '\0')
17789 72 : appendPQExpBuffer(q,
17790 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17791 : " %s\n"
17792 : ");\n",
17793 : qualrelname,
17794 72 : fmtId(tbinfo->attnames[j]),
17795 72 : tbinfo->attfdwoptions[j]);
17796 : } /* end loop over columns */
17797 :
17798 16236 : free(partkeydef);
17799 16236 : free(ftoptions);
17800 16236 : free(srvname);
17801 : }
17802 :
17803 : /*
17804 : * dump properties we only have ALTER TABLE syntax for
17805 : */
17806 17610 : if ((tbinfo->relkind == RELKIND_RELATION ||
17807 3914 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17808 2298 : tbinfo->relkind == RELKIND_MATVIEW) &&
17809 16160 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17810 : {
17811 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17812 : {
17813 : /* nothing to do, will be set when the index is dumped */
17814 : }
17815 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17816 : {
17817 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17818 : qualrelname);
17819 : }
17820 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17821 : {
17822 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17823 : qualrelname);
17824 : }
17825 : }
17826 :
17827 17610 : if (tbinfo->forcerowsec)
17828 16 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17829 : qualrelname);
17830 :
17831 17610 : if (dopt->binary_upgrade)
17832 1704 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17833 : reltypename, qrelname,
17834 1704 : tbinfo->dobj.namespace->dobj.name);
17835 :
17836 17610 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17837 : {
17838 17610 : char *tablespace = NULL;
17839 17610 : char *tableam = NULL;
17840 :
17841 : /*
17842 : * _selectTablespace() relies on tablespace-enabled objects in the
17843 : * default tablespace to have a tablespace of "" (empty string) versus
17844 : * non-tablespace-enabled objects to have a tablespace of NULL.
17845 : * getTables() sets tbinfo->reltablespace to "" for the default
17846 : * tablespace (not NULL).
17847 : */
17848 17610 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17849 16160 : tablespace = tbinfo->reltablespace;
17850 :
17851 17610 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17852 3066 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17853 16160 : tableam = tbinfo->amname;
17854 :
17855 17610 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17856 17610 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17857 : .namespace = tbinfo->dobj.namespace->dobj.name,
17858 : .tablespace = tablespace,
17859 : .tableam = tableam,
17860 : .relkind = tbinfo->relkind,
17861 : .owner = tbinfo->rolname,
17862 : .description = reltypename,
17863 : .section = tbinfo->postponed_def ?
17864 : SECTION_POST_DATA : SECTION_PRE_DATA,
17865 : .createStmt = q->data,
17866 : .dropStmt = delq->data));
17867 : }
17868 :
17869 : /* Dump Table Comments */
17870 17610 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
17871 176 : dumpTableComment(fout, tbinfo, reltypename);
17872 :
17873 : /* Dump Table Security Labels */
17874 17610 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
17875 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
17876 :
17877 : /*
17878 : * Dump comments for not-null constraints that aren't to be dumped
17879 : * separately (those are processed by collectComments/dumpComment).
17880 : */
17881 17610 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
17882 17610 : fout->remoteVersion >= 180000)
17883 : {
17884 17610 : PQExpBuffer comment = NULL;
17885 17610 : PQExpBuffer tag = NULL;
17886 :
17887 84460 : for (j = 0; j < tbinfo->numatts; j++)
17888 : {
17889 66850 : if (tbinfo->notnull_constrs[j] != NULL &&
17890 6546 : tbinfo->notnull_comment[j] != NULL)
17891 : {
17892 104 : if (comment == NULL)
17893 : {
17894 104 : comment = createPQExpBuffer();
17895 104 : tag = createPQExpBuffer();
17896 : }
17897 : else
17898 : {
17899 0 : resetPQExpBuffer(comment);
17900 0 : resetPQExpBuffer(tag);
17901 : }
17902 :
17903 104 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
17904 104 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
17905 104 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
17906 104 : appendPQExpBufferStr(comment, ";\n");
17907 :
17908 104 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
17909 104 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
17910 :
17911 104 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
17912 104 : ARCHIVE_OPTS(.tag = tag->data,
17913 : .namespace = tbinfo->dobj.namespace->dobj.name,
17914 : .owner = tbinfo->rolname,
17915 : .description = "COMMENT",
17916 : .section = SECTION_NONE,
17917 : .createStmt = comment->data,
17918 : .deps = &(tbinfo->dobj.dumpId),
17919 : .nDeps = 1));
17920 : }
17921 : }
17922 :
17923 17610 : destroyPQExpBuffer(comment);
17924 17610 : destroyPQExpBuffer(tag);
17925 : }
17926 :
17927 : /* Dump comments on inlined table constraints */
17928 19162 : for (j = 0; j < tbinfo->ncheck; j++)
17929 : {
17930 1552 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17931 :
17932 1552 : if (constr->separate || !constr->conislocal)
17933 624 : continue;
17934 :
17935 928 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
17936 88 : dumpTableConstraintComment(fout, constr);
17937 : }
17938 :
17939 17610 : destroyPQExpBuffer(q);
17940 17610 : destroyPQExpBuffer(delq);
17941 17610 : destroyPQExpBuffer(extra);
17942 17610 : free(qrelname);
17943 17610 : free(qualrelname);
17944 17610 : }
17945 :
17946 : /*
17947 : * dumpTableAttach
17948 : * write to fout the commands to attach a child partition
17949 : *
17950 : * Child partitions are always made by creating them separately
17951 : * and then using ATTACH PARTITION, rather than using
17952 : * CREATE TABLE ... PARTITION OF. This is important for preserving
17953 : * any possible discrepancy in column layout, to allow assigning the
17954 : * correct tablespace if different, and so that it's possible to restore
17955 : * a partition without restoring its parent. (You'll get an error from
17956 : * the ATTACH PARTITION command, but that can be ignored, or skipped
17957 : * using "pg_restore -L" if you prefer.) The last point motivates
17958 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
17959 : * rather than emitting it within the child partition's ArchiveEntry.
17960 : */
17961 : static void
17962 3900 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
17963 : {
17964 3900 : DumpOptions *dopt = fout->dopt;
17965 : PQExpBuffer q;
17966 : PGresult *res;
17967 : char *partbound;
17968 :
17969 : /* Do nothing if not dumping schema */
17970 3900 : if (!dopt->dumpSchema)
17971 84 : return;
17972 :
17973 3816 : q = createPQExpBuffer();
17974 :
17975 3816 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
17976 : {
17977 : /* Set up query for partbound details */
17978 100 : appendPQExpBufferStr(q,
17979 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
17980 :
17981 100 : appendPQExpBufferStr(q,
17982 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
17983 : "FROM pg_class c "
17984 : "WHERE c.oid = $1");
17985 :
17986 100 : ExecuteSqlStatement(fout, q->data);
17987 :
17988 100 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
17989 : }
17990 :
17991 3816 : printfPQExpBuffer(q,
17992 : "EXECUTE dumpTableAttach('%u')",
17993 3816 : attachinfo->partitionTbl->dobj.catId.oid);
17994 :
17995 3816 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
17996 3816 : partbound = PQgetvalue(res, 0, 0);
17997 :
17998 : /* Perform ALTER TABLE on the parent */
17999 3816 : printfPQExpBuffer(q,
18000 : "ALTER TABLE ONLY %s ",
18001 3816 : fmtQualifiedDumpable(attachinfo->parentTbl));
18002 3816 : appendPQExpBuffer(q,
18003 : "ATTACH PARTITION %s %s;\n",
18004 3816 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18005 : partbound);
18006 :
18007 : /*
18008 : * There is no point in creating a drop query as the drop is done by table
18009 : * drop. (If you think to change this, see also _printTocEntry().)
18010 : * Although this object doesn't really have ownership as such, set the
18011 : * owner field anyway to ensure that the command is run by the correct
18012 : * role at restore time.
18013 : */
18014 3816 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18015 3816 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18016 : .namespace = attachinfo->dobj.namespace->dobj.name,
18017 : .owner = attachinfo->partitionTbl->rolname,
18018 : .description = "TABLE ATTACH",
18019 : .section = SECTION_PRE_DATA,
18020 : .createStmt = q->data));
18021 :
18022 3816 : PQclear(res);
18023 3816 : destroyPQExpBuffer(q);
18024 : }
18025 :
18026 : /*
18027 : * dumpAttrDef --- dump an attribute's default-value declaration
18028 : */
18029 : static void
18030 2916 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18031 : {
18032 2916 : DumpOptions *dopt = fout->dopt;
18033 2916 : TableInfo *tbinfo = adinfo->adtable;
18034 2916 : int adnum = adinfo->adnum;
18035 : PQExpBuffer q;
18036 : PQExpBuffer delq;
18037 : char *qualrelname;
18038 : char *tag;
18039 : char *foreign;
18040 :
18041 : /* Do nothing if not dumping schema */
18042 2916 : if (!dopt->dumpSchema)
18043 0 : return;
18044 :
18045 : /* Skip if not "separate"; it was dumped in the table's definition */
18046 2916 : if (!adinfo->separate)
18047 2476 : return;
18048 :
18049 440 : q = createPQExpBuffer();
18050 440 : delq = createPQExpBuffer();
18051 :
18052 440 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18053 :
18054 440 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18055 :
18056 440 : appendPQExpBuffer(q,
18057 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18058 440 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18059 440 : adinfo->adef_expr);
18060 :
18061 440 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18062 : foreign, qualrelname,
18063 440 : fmtId(tbinfo->attnames[adnum - 1]));
18064 :
18065 440 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18066 :
18067 440 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18068 440 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18069 440 : ARCHIVE_OPTS(.tag = tag,
18070 : .namespace = tbinfo->dobj.namespace->dobj.name,
18071 : .owner = tbinfo->rolname,
18072 : .description = "DEFAULT",
18073 : .section = SECTION_PRE_DATA,
18074 : .createStmt = q->data,
18075 : .dropStmt = delq->data));
18076 :
18077 440 : free(tag);
18078 440 : destroyPQExpBuffer(q);
18079 440 : destroyPQExpBuffer(delq);
18080 440 : free(qualrelname);
18081 : }
18082 :
18083 : /*
18084 : * getAttrName: extract the correct name for an attribute
18085 : *
18086 : * The array tblInfo->attnames[] only provides names of user attributes;
18087 : * if a system attribute number is supplied, we have to fake it.
18088 : * We also do a little bit of bounds checking for safety's sake.
18089 : */
18090 : static const char *
18091 5220 : getAttrName(int attrnum, const TableInfo *tblInfo)
18092 : {
18093 5220 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18094 5220 : return tblInfo->attnames[attrnum - 1];
18095 0 : switch (attrnum)
18096 : {
18097 0 : case SelfItemPointerAttributeNumber:
18098 0 : return "ctid";
18099 0 : case MinTransactionIdAttributeNumber:
18100 0 : return "xmin";
18101 0 : case MinCommandIdAttributeNumber:
18102 0 : return "cmin";
18103 0 : case MaxTransactionIdAttributeNumber:
18104 0 : return "xmax";
18105 0 : case MaxCommandIdAttributeNumber:
18106 0 : return "cmax";
18107 0 : case TableOidAttributeNumber:
18108 0 : return "tableoid";
18109 : }
18110 0 : pg_fatal("invalid column number %d for table \"%s\"",
18111 : attrnum, tblInfo->dobj.name);
18112 : return NULL; /* keep compiler quiet */
18113 : }
18114 :
18115 : /*
18116 : * dumpIndex
18117 : * write out to fout a user-defined index
18118 : */
18119 : static void
18120 6986 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18121 : {
18122 6986 : DumpOptions *dopt = fout->dopt;
18123 6986 : TableInfo *tbinfo = indxinfo->indextable;
18124 6986 : bool is_constraint = (indxinfo->indexconstraint != 0);
18125 : PQExpBuffer q;
18126 : PQExpBuffer delq;
18127 : char *qindxname;
18128 : char *qqindxname;
18129 :
18130 : /* Do nothing if not dumping schema */
18131 6986 : if (!dopt->dumpSchema)
18132 234 : return;
18133 :
18134 6752 : q = createPQExpBuffer();
18135 6752 : delq = createPQExpBuffer();
18136 :
18137 6752 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18138 6752 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18139 :
18140 : /*
18141 : * If there's an associated constraint, don't dump the index per se, but
18142 : * do dump any comment for it. (This is safe because dependency ordering
18143 : * will have ensured the constraint is emitted first.) Note that the
18144 : * emitted comment has to be shown as depending on the constraint, not the
18145 : * index, in such cases.
18146 : */
18147 6752 : if (!is_constraint)
18148 : {
18149 3014 : char *indstatcols = indxinfo->indstatcols;
18150 3014 : char *indstatvals = indxinfo->indstatvals;
18151 3014 : char **indstatcolsarray = NULL;
18152 3014 : char **indstatvalsarray = NULL;
18153 3014 : int nstatcols = 0;
18154 3014 : int nstatvals = 0;
18155 :
18156 3014 : if (dopt->binary_upgrade)
18157 312 : binary_upgrade_set_pg_class_oids(fout, q,
18158 312 : indxinfo->dobj.catId.oid);
18159 :
18160 : /* Plain secondary index */
18161 3014 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18162 :
18163 : /*
18164 : * Append ALTER TABLE commands as needed to set properties that we
18165 : * only have ALTER TABLE syntax for. Keep this in sync with the
18166 : * similar code in dumpConstraint!
18167 : */
18168 :
18169 : /* If the index is clustered, we need to record that. */
18170 3014 : if (indxinfo->indisclustered)
18171 : {
18172 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18173 0 : fmtQualifiedDumpable(tbinfo));
18174 : /* index name is not qualified in this syntax */
18175 0 : appendPQExpBuffer(q, " ON %s;\n",
18176 : qindxname);
18177 : }
18178 :
18179 : /*
18180 : * If the index has any statistics on some of its columns, generate
18181 : * the associated ALTER INDEX queries.
18182 : */
18183 3014 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18184 : {
18185 : int j;
18186 :
18187 72 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18188 0 : pg_fatal("could not parse index statistic columns");
18189 72 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18190 0 : pg_fatal("could not parse index statistic values");
18191 72 : if (nstatcols != nstatvals)
18192 0 : pg_fatal("mismatched number of columns and values for index statistics");
18193 :
18194 216 : for (j = 0; j < nstatcols; j++)
18195 : {
18196 144 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18197 :
18198 : /*
18199 : * Note that this is a column number, so no quotes should be
18200 : * used.
18201 : */
18202 144 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18203 144 : indstatcolsarray[j]);
18204 144 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18205 144 : indstatvalsarray[j]);
18206 : }
18207 : }
18208 :
18209 : /* Indexes can depend on extensions */
18210 3014 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18211 : "pg_catalog.pg_class",
18212 : "INDEX", qqindxname);
18213 :
18214 : /* If the index defines identity, we need to record that. */
18215 3014 : if (indxinfo->indisreplident)
18216 : {
18217 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18218 0 : fmtQualifiedDumpable(tbinfo));
18219 : /* index name is not qualified in this syntax */
18220 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18221 : qindxname);
18222 : }
18223 :
18224 : /*
18225 : * If this index is a member of a partitioned index, the backend will
18226 : * not allow us to drop it separately, so don't try. It will go away
18227 : * automatically when we drop either the index's table or the
18228 : * partitioned index. (If, in a selective restore with --clean, we
18229 : * drop neither of those, then this index will not be dropped either.
18230 : * But that's fine, and even if you think it's not, the backend won't
18231 : * let us do differently.)
18232 : */
18233 3014 : if (indxinfo->parentidx == 0)
18234 2558 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18235 :
18236 3014 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18237 3014 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18238 3014 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18239 : .namespace = tbinfo->dobj.namespace->dobj.name,
18240 : .tablespace = indxinfo->tablespace,
18241 : .owner = tbinfo->rolname,
18242 : .description = "INDEX",
18243 : .section = SECTION_POST_DATA,
18244 : .createStmt = q->data,
18245 : .dropStmt = delq->data));
18246 :
18247 3014 : free(indstatcolsarray);
18248 3014 : free(indstatvalsarray);
18249 : }
18250 :
18251 : /* Dump Index Comments */
18252 6752 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18253 48 : dumpComment(fout, "INDEX", qindxname,
18254 48 : tbinfo->dobj.namespace->dobj.name,
18255 : tbinfo->rolname,
18256 : indxinfo->dobj.catId, 0,
18257 : is_constraint ? indxinfo->indexconstraint :
18258 : indxinfo->dobj.dumpId);
18259 :
18260 6752 : destroyPQExpBuffer(q);
18261 6752 : destroyPQExpBuffer(delq);
18262 6752 : free(qindxname);
18263 6752 : free(qqindxname);
18264 : }
18265 :
18266 : /*
18267 : * dumpIndexAttach
18268 : * write out to fout a partitioned-index attachment clause
18269 : */
18270 : static void
18271 1480 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18272 : {
18273 : /* Do nothing if not dumping schema */
18274 1480 : if (!fout->dopt->dumpSchema)
18275 96 : return;
18276 :
18277 1384 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18278 : {
18279 1384 : PQExpBuffer q = createPQExpBuffer();
18280 :
18281 1384 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18282 1384 : fmtQualifiedDumpable(attachinfo->parentIdx));
18283 1384 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18284 1384 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18285 :
18286 : /*
18287 : * There is no need for a dropStmt since the drop is done implicitly
18288 : * when we drop either the index's table or the partitioned index.
18289 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18290 : * there's no way to do it anyway. (If you think to change this,
18291 : * consider also what to do with --if-exists.)
18292 : *
18293 : * Although this object doesn't really have ownership as such, set the
18294 : * owner field anyway to ensure that the command is run by the correct
18295 : * role at restore time.
18296 : */
18297 1384 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18298 1384 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18299 : .namespace = attachinfo->dobj.namespace->dobj.name,
18300 : .owner = attachinfo->parentIdx->indextable->rolname,
18301 : .description = "INDEX ATTACH",
18302 : .section = SECTION_POST_DATA,
18303 : .createStmt = q->data));
18304 :
18305 1384 : destroyPQExpBuffer(q);
18306 : }
18307 : }
18308 :
18309 : /*
18310 : * dumpStatisticsExt
18311 : * write out to fout an extended statistics object
18312 : */
18313 : static void
18314 314 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18315 : {
18316 314 : DumpOptions *dopt = fout->dopt;
18317 : PQExpBuffer q;
18318 : PQExpBuffer delq;
18319 : PQExpBuffer query;
18320 : char *qstatsextname;
18321 : PGresult *res;
18322 : char *stxdef;
18323 :
18324 : /* Do nothing if not dumping schema */
18325 314 : if (!dopt->dumpSchema)
18326 36 : return;
18327 :
18328 278 : q = createPQExpBuffer();
18329 278 : delq = createPQExpBuffer();
18330 278 : query = createPQExpBuffer();
18331 :
18332 278 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18333 :
18334 278 : appendPQExpBuffer(query, "SELECT "
18335 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18336 278 : statsextinfo->dobj.catId.oid);
18337 :
18338 278 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18339 :
18340 278 : stxdef = PQgetvalue(res, 0, 0);
18341 :
18342 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18343 278 : appendPQExpBuffer(q, "%s;\n", stxdef);
18344 :
18345 : /*
18346 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18347 : * for this statistics object is not the default value.
18348 : */
18349 278 : if (statsextinfo->stattarget >= 0)
18350 : {
18351 72 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18352 72 : fmtQualifiedDumpable(statsextinfo));
18353 72 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18354 72 : statsextinfo->stattarget);
18355 : }
18356 :
18357 278 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18358 278 : fmtQualifiedDumpable(statsextinfo));
18359 :
18360 278 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18361 278 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18362 278 : statsextinfo->dobj.dumpId,
18363 278 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18364 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18365 : .owner = statsextinfo->rolname,
18366 : .description = "STATISTICS",
18367 : .section = SECTION_POST_DATA,
18368 : .createStmt = q->data,
18369 : .dropStmt = delq->data));
18370 :
18371 : /* Dump Statistics Comments */
18372 278 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18373 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18374 0 : statsextinfo->dobj.namespace->dobj.name,
18375 0 : statsextinfo->rolname,
18376 : statsextinfo->dobj.catId, 0,
18377 0 : statsextinfo->dobj.dumpId);
18378 :
18379 278 : PQclear(res);
18380 278 : destroyPQExpBuffer(q);
18381 278 : destroyPQExpBuffer(delq);
18382 278 : destroyPQExpBuffer(query);
18383 278 : free(qstatsextname);
18384 : }
18385 :
18386 : /*
18387 : * dumpConstraint
18388 : * write out to fout a user-defined constraint
18389 : */
18390 : static void
18391 6550 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18392 : {
18393 6550 : DumpOptions *dopt = fout->dopt;
18394 6550 : TableInfo *tbinfo = coninfo->contable;
18395 : PQExpBuffer q;
18396 : PQExpBuffer delq;
18397 6550 : char *tag = NULL;
18398 : char *foreign;
18399 :
18400 : /* Do nothing if not dumping schema */
18401 6550 : if (!dopt->dumpSchema)
18402 196 : return;
18403 :
18404 6354 : q = createPQExpBuffer();
18405 6354 : delq = createPQExpBuffer();
18406 :
18407 12276 : foreign = tbinfo &&
18408 6354 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18409 :
18410 6354 : if (coninfo->contype == 'p' ||
18411 3156 : coninfo->contype == 'u' ||
18412 2648 : coninfo->contype == 'x')
18413 3738 : {
18414 : /* Index-related constraint */
18415 : IndxInfo *indxinfo;
18416 : int k;
18417 :
18418 3738 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18419 :
18420 3738 : if (indxinfo == NULL)
18421 0 : pg_fatal("missing index for constraint \"%s\"",
18422 : coninfo->dobj.name);
18423 :
18424 3738 : if (dopt->binary_upgrade)
18425 292 : binary_upgrade_set_pg_class_oids(fout, q,
18426 : indxinfo->dobj.catId.oid);
18427 :
18428 3738 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18429 3738 : fmtQualifiedDumpable(tbinfo));
18430 3738 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18431 3738 : fmtId(coninfo->dobj.name));
18432 :
18433 3738 : if (coninfo->condef)
18434 : {
18435 : /* pg_get_constraintdef should have provided everything */
18436 32 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18437 : }
18438 : else
18439 : {
18440 3706 : appendPQExpBufferStr(q,
18441 3706 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18442 :
18443 : /*
18444 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18445 : * indexes. Being able to create this was fixed, but we need to
18446 : * make the index distinct in order to be able to restore the
18447 : * dump.
18448 : */
18449 3706 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18450 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18451 3706 : appendPQExpBufferStr(q, " (");
18452 8798 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18453 : {
18454 5092 : int indkey = (int) indxinfo->indkeys[k];
18455 : const char *attname;
18456 :
18457 5092 : if (indkey == InvalidAttrNumber)
18458 0 : break;
18459 5092 : attname = getAttrName(indkey, tbinfo);
18460 :
18461 5092 : appendPQExpBuffer(q, "%s%s",
18462 : (k == 0) ? "" : ", ",
18463 : fmtId(attname));
18464 : }
18465 3706 : if (coninfo->conperiod)
18466 272 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18467 :
18468 3706 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18469 64 : appendPQExpBufferStr(q, ") INCLUDE (");
18470 :
18471 3834 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18472 : {
18473 128 : int indkey = (int) indxinfo->indkeys[k];
18474 : const char *attname;
18475 :
18476 128 : if (indkey == InvalidAttrNumber)
18477 0 : break;
18478 128 : attname = getAttrName(indkey, tbinfo);
18479 :
18480 256 : appendPQExpBuffer(q, "%s%s",
18481 128 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18482 : fmtId(attname));
18483 : }
18484 :
18485 3706 : appendPQExpBufferChar(q, ')');
18486 :
18487 3706 : if (nonemptyReloptions(indxinfo->indreloptions))
18488 : {
18489 0 : appendPQExpBufferStr(q, " WITH (");
18490 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18491 0 : appendPQExpBufferChar(q, ')');
18492 : }
18493 :
18494 3706 : if (coninfo->condeferrable)
18495 : {
18496 80 : appendPQExpBufferStr(q, " DEFERRABLE");
18497 80 : if (coninfo->condeferred)
18498 48 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18499 : }
18500 :
18501 3706 : appendPQExpBufferStr(q, ";\n");
18502 : }
18503 :
18504 : /*
18505 : * Append ALTER TABLE commands as needed to set properties that we
18506 : * only have ALTER TABLE syntax for. Keep this in sync with the
18507 : * similar code in dumpIndex!
18508 : */
18509 :
18510 : /* If the index is clustered, we need to record that. */
18511 3738 : if (indxinfo->indisclustered)
18512 : {
18513 72 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18514 72 : fmtQualifiedDumpable(tbinfo));
18515 : /* index name is not qualified in this syntax */
18516 72 : appendPQExpBuffer(q, " ON %s;\n",
18517 72 : fmtId(indxinfo->dobj.name));
18518 : }
18519 :
18520 : /* If the index defines identity, we need to record that. */
18521 3738 : if (indxinfo->indisreplident)
18522 : {
18523 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18524 0 : fmtQualifiedDumpable(tbinfo));
18525 : /* index name is not qualified in this syntax */
18526 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18527 0 : fmtId(indxinfo->dobj.name));
18528 : }
18529 :
18530 : /* Indexes can depend on extensions */
18531 3738 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18532 : "pg_catalog.pg_class", "INDEX",
18533 3738 : fmtQualifiedDumpable(indxinfo));
18534 :
18535 3738 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18536 3738 : fmtQualifiedDumpable(tbinfo));
18537 3738 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18538 3738 : fmtId(coninfo->dobj.name));
18539 :
18540 3738 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18541 :
18542 3738 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18543 3738 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18544 3738 : ARCHIVE_OPTS(.tag = tag,
18545 : .namespace = tbinfo->dobj.namespace->dobj.name,
18546 : .tablespace = indxinfo->tablespace,
18547 : .owner = tbinfo->rolname,
18548 : .description = "CONSTRAINT",
18549 : .section = SECTION_POST_DATA,
18550 : .createStmt = q->data,
18551 : .dropStmt = delq->data));
18552 : }
18553 2616 : else if (coninfo->contype == 'f')
18554 : {
18555 : char *only;
18556 :
18557 : /*
18558 : * Foreign keys on partitioned tables are always declared as
18559 : * inheriting to partitions; for all other cases, emit them as
18560 : * applying ONLY directly to the named table, because that's how they
18561 : * work for regular inherited tables.
18562 : */
18563 448 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18564 :
18565 : /*
18566 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18567 : * current table data is not processed
18568 : */
18569 448 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18570 448 : only, fmtQualifiedDumpable(tbinfo));
18571 448 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18572 448 : fmtId(coninfo->dobj.name),
18573 448 : coninfo->condef);
18574 :
18575 448 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18576 448 : only, fmtQualifiedDumpable(tbinfo));
18577 448 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18578 448 : fmtId(coninfo->dobj.name));
18579 :
18580 448 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18581 :
18582 448 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18583 448 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18584 448 : ARCHIVE_OPTS(.tag = tag,
18585 : .namespace = tbinfo->dobj.namespace->dobj.name,
18586 : .owner = tbinfo->rolname,
18587 : .description = "FK CONSTRAINT",
18588 : .section = SECTION_POST_DATA,
18589 : .createStmt = q->data,
18590 : .dropStmt = delq->data));
18591 : }
18592 2168 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18593 : {
18594 : /* CHECK or invalid not-null constraint on a table */
18595 :
18596 : /* Ignore if not to be dumped separately, or if it was inherited */
18597 1736 : if (coninfo->separate && coninfo->conislocal)
18598 : {
18599 : const char *keyword;
18600 :
18601 312 : if (coninfo->contype == 'c')
18602 144 : keyword = "CHECK CONSTRAINT";
18603 : else
18604 168 : keyword = "CONSTRAINT";
18605 :
18606 : /* not ONLY since we want it to propagate to children */
18607 312 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18608 312 : fmtQualifiedDumpable(tbinfo));
18609 312 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18610 312 : fmtId(coninfo->dobj.name),
18611 312 : coninfo->condef);
18612 :
18613 312 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18614 312 : fmtQualifiedDumpable(tbinfo));
18615 312 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18616 312 : fmtId(coninfo->dobj.name));
18617 :
18618 312 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18619 :
18620 312 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18621 312 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18622 312 : ARCHIVE_OPTS(.tag = tag,
18623 : .namespace = tbinfo->dobj.namespace->dobj.name,
18624 : .owner = tbinfo->rolname,
18625 : .description = keyword,
18626 : .section = SECTION_POST_DATA,
18627 : .createStmt = q->data,
18628 : .dropStmt = delq->data));
18629 : }
18630 : }
18631 432 : else if (tbinfo == NULL)
18632 : {
18633 : /* CHECK, NOT NULL constraint on a domain */
18634 432 : TypeInfo *tyinfo = coninfo->condomain;
18635 :
18636 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18637 :
18638 : /* Ignore if not to be dumped separately */
18639 432 : if (coninfo->separate)
18640 : {
18641 : const char *keyword;
18642 :
18643 16 : if (coninfo->contype == 'c')
18644 16 : keyword = "CHECK CONSTRAINT";
18645 : else
18646 0 : keyword = "CONSTRAINT";
18647 :
18648 16 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18649 16 : fmtQualifiedDumpable(tyinfo));
18650 16 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18651 16 : fmtId(coninfo->dobj.name),
18652 16 : coninfo->condef);
18653 :
18654 16 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18655 16 : fmtQualifiedDumpable(tyinfo));
18656 16 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18657 16 : fmtId(coninfo->dobj.name));
18658 :
18659 16 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18660 :
18661 16 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18662 16 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18663 16 : ARCHIVE_OPTS(.tag = tag,
18664 : .namespace = tyinfo->dobj.namespace->dobj.name,
18665 : .owner = tyinfo->rolname,
18666 : .description = keyword,
18667 : .section = SECTION_POST_DATA,
18668 : .createStmt = q->data,
18669 : .dropStmt = delq->data));
18670 :
18671 16 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18672 : {
18673 16 : PQExpBuffer conprefix = createPQExpBuffer();
18674 16 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18675 :
18676 16 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18677 16 : fmtId(coninfo->dobj.name));
18678 :
18679 16 : dumpComment(fout, conprefix->data, qtypname,
18680 16 : tyinfo->dobj.namespace->dobj.name,
18681 : tyinfo->rolname,
18682 : coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
18683 16 : destroyPQExpBuffer(conprefix);
18684 16 : free(qtypname);
18685 : }
18686 : }
18687 : }
18688 : else
18689 : {
18690 0 : pg_fatal("unrecognized constraint type: %c",
18691 : coninfo->contype);
18692 : }
18693 :
18694 : /* Dump Constraint Comments --- only works for table constraints */
18695 6354 : if (tbinfo && coninfo->separate &&
18696 4594 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18697 120 : dumpTableConstraintComment(fout, coninfo);
18698 :
18699 6354 : free(tag);
18700 6354 : destroyPQExpBuffer(q);
18701 6354 : destroyPQExpBuffer(delq);
18702 : }
18703 :
18704 : /*
18705 : * dumpTableConstraintComment --- dump a constraint's comment if any
18706 : *
18707 : * This is split out because we need the function in two different places
18708 : * depending on whether the constraint is dumped as part of CREATE TABLE
18709 : * or as a separate ALTER command.
18710 : */
18711 : static void
18712 208 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18713 : {
18714 208 : TableInfo *tbinfo = coninfo->contable;
18715 208 : PQExpBuffer conprefix = createPQExpBuffer();
18716 : char *qtabname;
18717 :
18718 208 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18719 :
18720 208 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18721 208 : fmtId(coninfo->dobj.name));
18722 :
18723 208 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18724 208 : dumpComment(fout, conprefix->data, qtabname,
18725 208 : tbinfo->dobj.namespace->dobj.name,
18726 : tbinfo->rolname,
18727 : coninfo->dobj.catId, 0,
18728 208 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18729 :
18730 208 : destroyPQExpBuffer(conprefix);
18731 208 : free(qtabname);
18732 208 : }
18733 :
18734 : static inline SeqType
18735 1632 : parse_sequence_type(const char *name)
18736 : {
18737 3612 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18738 : {
18739 3612 : if (strcmp(SeqTypeNames[i], name) == 0)
18740 1632 : return (SeqType) i;
18741 : }
18742 :
18743 0 : pg_fatal("unrecognized sequence type: %s", name);
18744 : return (SeqType) 0; /* keep compiler quiet */
18745 : }
18746 :
18747 : /*
18748 : * bsearch() comparator for SequenceItem
18749 : */
18750 : static int
18751 9144 : SequenceItemCmp(const void *p1, const void *p2)
18752 : {
18753 9144 : SequenceItem v1 = *((const SequenceItem *) p1);
18754 9144 : SequenceItem v2 = *((const SequenceItem *) p2);
18755 :
18756 9144 : return pg_cmp_u32(v1.oid, v2.oid);
18757 : }
18758 :
18759 : /*
18760 : * collectSequences
18761 : *
18762 : * Construct a table of sequence information. This table is sorted by OID for
18763 : * speed in lookup.
18764 : */
18765 : static void
18766 468 : collectSequences(Archive *fout)
18767 : {
18768 : PGresult *res;
18769 : const char *query;
18770 :
18771 : /*
18772 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18773 : * some extra effort, we might be able to use the sorted table for those
18774 : * versions, but for now it seems unlikely to be worth it.
18775 : *
18776 : * Since version 18, we can gather the sequence data in this query with
18777 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18778 : */
18779 468 : if (fout->remoteVersion < 100000)
18780 0 : return;
18781 468 : else if (fout->remoteVersion < 180000 ||
18782 468 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18783 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18784 : "seqstart, seqincrement, "
18785 : "seqmax, seqmin, "
18786 : "seqcache, seqcycle, "
18787 : "NULL, 'f' "
18788 : "FROM pg_catalog.pg_sequence "
18789 : "ORDER BY seqrelid";
18790 : else
18791 452 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18792 : "seqstart, seqincrement, "
18793 : "seqmax, seqmin, "
18794 : "seqcache, seqcycle, "
18795 : "last_value, is_called "
18796 : "FROM pg_catalog.pg_sequence, "
18797 : "pg_get_sequence_data(seqrelid) "
18798 : "ORDER BY seqrelid;";
18799 :
18800 468 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18801 :
18802 468 : nsequences = PQntuples(res);
18803 468 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18804 :
18805 2100 : for (int i = 0; i < nsequences; i++)
18806 : {
18807 1632 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18808 1632 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18809 1632 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18810 1632 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18811 1632 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18812 1632 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18813 1632 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18814 1632 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18815 1632 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18816 1632 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18817 : }
18818 :
18819 468 : PQclear(res);
18820 : }
18821 :
18822 : /*
18823 : * dumpSequence
18824 : * write the declaration (not data) of one user-defined sequence
18825 : */
18826 : static void
18827 1106 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18828 : {
18829 1106 : DumpOptions *dopt = fout->dopt;
18830 : SequenceItem *seq;
18831 : bool is_ascending;
18832 : int64 default_minv,
18833 : default_maxv;
18834 1106 : PQExpBuffer query = createPQExpBuffer();
18835 1106 : PQExpBuffer delqry = createPQExpBuffer();
18836 : char *qseqname;
18837 1106 : TableInfo *owning_tab = NULL;
18838 :
18839 1106 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18840 :
18841 : /*
18842 : * For versions >= 10, the sequence information is gathered in a sorted
18843 : * table before any calls to dumpSequence(). See collectSequences() for
18844 : * more information.
18845 : */
18846 1106 : if (fout->remoteVersion >= 100000)
18847 : {
18848 1106 : SequenceItem key = {0};
18849 :
18850 : Assert(sequences);
18851 :
18852 1106 : key.oid = tbinfo->dobj.catId.oid;
18853 1106 : seq = bsearch(&key, sequences, nsequences,
18854 : sizeof(SequenceItem), SequenceItemCmp);
18855 : }
18856 : else
18857 : {
18858 : PGresult *res;
18859 :
18860 : /*
18861 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18862 : *
18863 : * Note: it might seem that 'bigint' potentially needs to be
18864 : * schema-qualified, but actually that's a keyword.
18865 : */
18866 0 : appendPQExpBuffer(query,
18867 : "SELECT 'bigint' AS sequence_type, "
18868 : "start_value, increment_by, max_value, min_value, "
18869 : "cache_value, is_cycled FROM %s",
18870 0 : fmtQualifiedDumpable(tbinfo));
18871 :
18872 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
18873 :
18874 0 : if (PQntuples(res) != 1)
18875 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
18876 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
18877 : PQntuples(res)),
18878 : tbinfo->dobj.name, PQntuples(res));
18879 :
18880 0 : seq = pg_malloc0(sizeof(SequenceItem));
18881 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
18882 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
18883 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
18884 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
18885 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
18886 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
18887 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
18888 :
18889 0 : PQclear(res);
18890 : }
18891 :
18892 : /* Calculate default limits for a sequence of this type */
18893 1106 : is_ascending = (seq->incby >= 0);
18894 1106 : if (seq->seqtype == SEQTYPE_SMALLINT)
18895 : {
18896 80 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
18897 80 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
18898 : }
18899 1026 : else if (seq->seqtype == SEQTYPE_INTEGER)
18900 : {
18901 814 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
18902 814 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
18903 : }
18904 212 : else if (seq->seqtype == SEQTYPE_BIGINT)
18905 : {
18906 212 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
18907 212 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
18908 : }
18909 : else
18910 : {
18911 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
18912 : default_minv = default_maxv = 0; /* keep compiler quiet */
18913 : }
18914 :
18915 : /*
18916 : * Identity sequences are not to be dropped separately.
18917 : */
18918 1106 : if (!tbinfo->is_identity_sequence)
18919 : {
18920 682 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
18921 682 : fmtQualifiedDumpable(tbinfo));
18922 : }
18923 :
18924 1106 : resetPQExpBuffer(query);
18925 :
18926 1106 : if (dopt->binary_upgrade)
18927 : {
18928 132 : binary_upgrade_set_pg_class_oids(fout, query,
18929 132 : tbinfo->dobj.catId.oid);
18930 :
18931 : /*
18932 : * In older PG versions a sequence will have a pg_type entry, but v14
18933 : * and up don't use that, so don't attempt to preserve the type OID.
18934 : */
18935 : }
18936 :
18937 1106 : if (tbinfo->is_identity_sequence)
18938 : {
18939 424 : owning_tab = findTableByOid(tbinfo->owning_tab);
18940 :
18941 424 : appendPQExpBuffer(query,
18942 : "ALTER TABLE %s ",
18943 424 : fmtQualifiedDumpable(owning_tab));
18944 424 : appendPQExpBuffer(query,
18945 : "ALTER COLUMN %s ADD GENERATED ",
18946 424 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
18947 424 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
18948 296 : appendPQExpBufferStr(query, "ALWAYS");
18949 128 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
18950 128 : appendPQExpBufferStr(query, "BY DEFAULT");
18951 424 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
18952 424 : fmtQualifiedDumpable(tbinfo));
18953 :
18954 : /*
18955 : * Emit persistence option only if it's different from the owning
18956 : * table's. This avoids using this new syntax unnecessarily.
18957 : */
18958 424 : if (tbinfo->relpersistence != owning_tab->relpersistence)
18959 32 : appendPQExpBuffer(query, " %s\n",
18960 32 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18961 : "UNLOGGED" : "LOGGED");
18962 : }
18963 : else
18964 : {
18965 682 : appendPQExpBuffer(query,
18966 : "CREATE %sSEQUENCE %s\n",
18967 682 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
18968 : "UNLOGGED " : "",
18969 682 : fmtQualifiedDumpable(tbinfo));
18970 :
18971 682 : if (seq->seqtype != SEQTYPE_BIGINT)
18972 518 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
18973 : }
18974 :
18975 1106 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
18976 :
18977 1106 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
18978 :
18979 1106 : if (seq->minv != default_minv)
18980 48 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
18981 : else
18982 1058 : appendPQExpBufferStr(query, " NO MINVALUE\n");
18983 :
18984 1106 : if (seq->maxv != default_maxv)
18985 48 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
18986 : else
18987 1058 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
18988 :
18989 1106 : appendPQExpBuffer(query,
18990 : " CACHE " INT64_FORMAT "%s",
18991 1106 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
18992 :
18993 1106 : if (tbinfo->is_identity_sequence)
18994 424 : appendPQExpBufferStr(query, "\n);\n");
18995 : else
18996 682 : appendPQExpBufferStr(query, ";\n");
18997 :
18998 : /* binary_upgrade: no need to clear TOAST table oid */
18999 :
19000 1106 : if (dopt->binary_upgrade)
19001 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19002 : "SEQUENCE", qseqname,
19003 132 : tbinfo->dobj.namespace->dobj.name);
19004 :
19005 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19006 1106 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19007 1106 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19008 : .namespace = tbinfo->dobj.namespace->dobj.name,
19009 : .owner = tbinfo->rolname,
19010 : .description = "SEQUENCE",
19011 : .section = SECTION_PRE_DATA,
19012 : .createStmt = query->data,
19013 : .dropStmt = delqry->data));
19014 :
19015 : /*
19016 : * If the sequence is owned by a table column, emit the ALTER for it as a
19017 : * separate TOC entry immediately following the sequence's own entry. It's
19018 : * OK to do this rather than using full sorting logic, because the
19019 : * dependency that tells us it's owned will have forced the table to be
19020 : * created first. We can't just include the ALTER in the TOC entry
19021 : * because it will fail if we haven't reassigned the sequence owner to
19022 : * match the table's owner.
19023 : *
19024 : * We need not schema-qualify the table reference because both sequence
19025 : * and table must be in the same schema.
19026 : */
19027 1106 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19028 : {
19029 374 : owning_tab = findTableByOid(tbinfo->owning_tab);
19030 :
19031 374 : if (owning_tab == NULL)
19032 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19033 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19034 :
19035 374 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19036 : {
19037 370 : resetPQExpBuffer(query);
19038 370 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19039 370 : fmtQualifiedDumpable(tbinfo));
19040 370 : appendPQExpBuffer(query, " OWNED BY %s",
19041 370 : fmtQualifiedDumpable(owning_tab));
19042 370 : appendPQExpBuffer(query, ".%s;\n",
19043 370 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19044 :
19045 370 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19046 370 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19047 370 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19048 : .namespace = tbinfo->dobj.namespace->dobj.name,
19049 : .owner = tbinfo->rolname,
19050 : .description = "SEQUENCE OWNED BY",
19051 : .section = SECTION_PRE_DATA,
19052 : .createStmt = query->data,
19053 : .deps = &(tbinfo->dobj.dumpId),
19054 : .nDeps = 1));
19055 : }
19056 : }
19057 :
19058 : /* Dump Sequence Comments and Security Labels */
19059 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19060 0 : dumpComment(fout, "SEQUENCE", qseqname,
19061 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19062 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19063 :
19064 1106 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19065 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19066 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19067 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19068 :
19069 1106 : if (fout->remoteVersion < 100000)
19070 0 : pg_free(seq);
19071 1106 : destroyPQExpBuffer(query);
19072 1106 : destroyPQExpBuffer(delqry);
19073 1106 : free(qseqname);
19074 1106 : }
19075 :
19076 : /*
19077 : * dumpSequenceData
19078 : * write the data of one user-defined sequence
19079 : */
19080 : static void
19081 1136 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19082 : {
19083 1136 : TableInfo *tbinfo = tdinfo->tdtable;
19084 : int64 last;
19085 : bool called;
19086 1136 : PQExpBuffer query = createPQExpBuffer();
19087 :
19088 : /*
19089 : * For versions >= 18, the sequence information is gathered in the sorted
19090 : * array before any calls to dumpSequenceData(). See collectSequences()
19091 : * for more information.
19092 : *
19093 : * For older versions, we have to query the sequence relations
19094 : * individually.
19095 : */
19096 1136 : if (fout->remoteVersion < 180000)
19097 : {
19098 : PGresult *res;
19099 :
19100 0 : appendPQExpBuffer(query,
19101 : "SELECT last_value, is_called FROM %s",
19102 0 : fmtQualifiedDumpable(tbinfo));
19103 :
19104 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19105 :
19106 0 : if (PQntuples(res) != 1)
19107 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19108 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19109 : PQntuples(res)),
19110 : tbinfo->dobj.name, PQntuples(res));
19111 :
19112 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19113 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19114 :
19115 0 : PQclear(res);
19116 : }
19117 : else
19118 : {
19119 1136 : SequenceItem key = {0};
19120 : SequenceItem *entry;
19121 :
19122 : Assert(sequences);
19123 : Assert(tbinfo->dobj.catId.oid);
19124 :
19125 1136 : key.oid = tbinfo->dobj.catId.oid;
19126 1136 : entry = bsearch(&key, sequences, nsequences,
19127 : sizeof(SequenceItem), SequenceItemCmp);
19128 :
19129 1136 : last = entry->last_value;
19130 1136 : called = entry->is_called;
19131 : }
19132 :
19133 1136 : resetPQExpBuffer(query);
19134 1136 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19135 1136 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19136 1136 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19137 : last, (called ? "true" : "false"));
19138 :
19139 1136 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19140 1136 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19141 1136 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19142 : .namespace = tbinfo->dobj.namespace->dobj.name,
19143 : .owner = tbinfo->rolname,
19144 : .description = "SEQUENCE SET",
19145 : .section = SECTION_DATA,
19146 : .createStmt = query->data,
19147 : .deps = &(tbinfo->dobj.dumpId),
19148 : .nDeps = 1));
19149 :
19150 1136 : destroyPQExpBuffer(query);
19151 1136 : }
19152 :
19153 : /*
19154 : * dumpTrigger
19155 : * write the declaration of one user-defined table trigger
19156 : */
19157 : static void
19158 1476 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19159 : {
19160 1476 : DumpOptions *dopt = fout->dopt;
19161 1476 : TableInfo *tbinfo = tginfo->tgtable;
19162 : PQExpBuffer query;
19163 : PQExpBuffer delqry;
19164 : PQExpBuffer trigprefix;
19165 : PQExpBuffer trigidentity;
19166 : char *qtabname;
19167 : char *tag;
19168 :
19169 : /* Do nothing if not dumping schema */
19170 1476 : if (!dopt->dumpSchema)
19171 62 : return;
19172 :
19173 1414 : query = createPQExpBuffer();
19174 1414 : delqry = createPQExpBuffer();
19175 1414 : trigprefix = createPQExpBuffer();
19176 1414 : trigidentity = createPQExpBuffer();
19177 :
19178 1414 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19179 :
19180 1414 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19181 1414 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19182 :
19183 1414 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19184 1414 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19185 :
19186 : /* Triggers can depend on extensions */
19187 1414 : append_depends_on_extension(fout, query, &tginfo->dobj,
19188 : "pg_catalog.pg_trigger", "TRIGGER",
19189 1414 : trigidentity->data);
19190 :
19191 1414 : if (tginfo->tgispartition)
19192 : {
19193 : Assert(tbinfo->ispartition);
19194 :
19195 : /*
19196 : * Partition triggers only appear here because their 'tgenabled' flag
19197 : * differs from its parent's. The trigger is created already, so
19198 : * remove the CREATE and replace it with an ALTER. (Clear out the
19199 : * DROP query too, so that pg_dump --create does not cause errors.)
19200 : */
19201 254 : resetPQExpBuffer(query);
19202 254 : resetPQExpBuffer(delqry);
19203 254 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19204 254 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19205 254 : fmtQualifiedDumpable(tbinfo));
19206 254 : switch (tginfo->tgenabled)
19207 : {
19208 90 : case 'f':
19209 : case 'D':
19210 90 : appendPQExpBufferStr(query, "DISABLE");
19211 90 : break;
19212 0 : case 't':
19213 : case 'O':
19214 0 : appendPQExpBufferStr(query, "ENABLE");
19215 0 : break;
19216 74 : case 'R':
19217 74 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19218 74 : break;
19219 90 : case 'A':
19220 90 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19221 90 : break;
19222 : }
19223 254 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19224 254 : fmtId(tginfo->dobj.name));
19225 : }
19226 1160 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19227 : {
19228 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19229 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19230 0 : fmtQualifiedDumpable(tbinfo));
19231 0 : switch (tginfo->tgenabled)
19232 : {
19233 0 : case 'D':
19234 : case 'f':
19235 0 : appendPQExpBufferStr(query, "DISABLE");
19236 0 : break;
19237 0 : case 'A':
19238 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19239 0 : break;
19240 0 : case 'R':
19241 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19242 0 : break;
19243 0 : default:
19244 0 : appendPQExpBufferStr(query, "ENABLE");
19245 0 : break;
19246 : }
19247 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19248 0 : fmtId(tginfo->dobj.name));
19249 : }
19250 :
19251 1414 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19252 1414 : fmtId(tginfo->dobj.name));
19253 :
19254 1414 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19255 :
19256 1414 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19257 1414 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19258 1414 : ARCHIVE_OPTS(.tag = tag,
19259 : .namespace = tbinfo->dobj.namespace->dobj.name,
19260 : .owner = tbinfo->rolname,
19261 : .description = "TRIGGER",
19262 : .section = SECTION_POST_DATA,
19263 : .createStmt = query->data,
19264 : .dropStmt = delqry->data));
19265 :
19266 1414 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19267 0 : dumpComment(fout, trigprefix->data, qtabname,
19268 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19269 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19270 :
19271 1414 : free(tag);
19272 1414 : destroyPQExpBuffer(query);
19273 1414 : destroyPQExpBuffer(delqry);
19274 1414 : destroyPQExpBuffer(trigprefix);
19275 1414 : destroyPQExpBuffer(trigidentity);
19276 1414 : free(qtabname);
19277 : }
19278 :
19279 : /*
19280 : * dumpEventTrigger
19281 : * write the declaration of one user-defined event trigger
19282 : */
19283 : static void
19284 98 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19285 : {
19286 98 : DumpOptions *dopt = fout->dopt;
19287 : PQExpBuffer query;
19288 : PQExpBuffer delqry;
19289 : char *qevtname;
19290 :
19291 : /* Do nothing if not dumping schema */
19292 98 : if (!dopt->dumpSchema)
19293 12 : return;
19294 :
19295 86 : query = createPQExpBuffer();
19296 86 : delqry = createPQExpBuffer();
19297 :
19298 86 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19299 :
19300 86 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19301 86 : appendPQExpBufferStr(query, qevtname);
19302 86 : appendPQExpBufferStr(query, " ON ");
19303 86 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19304 :
19305 86 : if (strcmp("", evtinfo->evttags) != 0)
19306 : {
19307 16 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19308 16 : appendPQExpBufferStr(query, evtinfo->evttags);
19309 16 : appendPQExpBufferChar(query, ')');
19310 : }
19311 :
19312 86 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19313 86 : appendPQExpBufferStr(query, evtinfo->evtfname);
19314 86 : appendPQExpBufferStr(query, "();\n");
19315 :
19316 86 : if (evtinfo->evtenabled != 'O')
19317 : {
19318 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19319 : qevtname);
19320 0 : switch (evtinfo->evtenabled)
19321 : {
19322 0 : case 'D':
19323 0 : appendPQExpBufferStr(query, "DISABLE");
19324 0 : break;
19325 0 : case 'A':
19326 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19327 0 : break;
19328 0 : case 'R':
19329 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19330 0 : break;
19331 0 : default:
19332 0 : appendPQExpBufferStr(query, "ENABLE");
19333 0 : break;
19334 : }
19335 0 : appendPQExpBufferStr(query, ";\n");
19336 : }
19337 :
19338 86 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19339 : qevtname);
19340 :
19341 86 : if (dopt->binary_upgrade)
19342 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19343 : "EVENT TRIGGER", qevtname, NULL);
19344 :
19345 86 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19346 86 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19347 86 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19348 : .owner = evtinfo->evtowner,
19349 : .description = "EVENT TRIGGER",
19350 : .section = SECTION_POST_DATA,
19351 : .createStmt = query->data,
19352 : .dropStmt = delqry->data));
19353 :
19354 86 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19355 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19356 0 : NULL, evtinfo->evtowner,
19357 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19358 :
19359 86 : destroyPQExpBuffer(query);
19360 86 : destroyPQExpBuffer(delqry);
19361 86 : free(qevtname);
19362 : }
19363 :
19364 : /*
19365 : * dumpRule
19366 : * Dump a rule
19367 : */
19368 : static void
19369 2990 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19370 : {
19371 2990 : DumpOptions *dopt = fout->dopt;
19372 2990 : TableInfo *tbinfo = rinfo->ruletable;
19373 : bool is_view;
19374 : PQExpBuffer query;
19375 : PQExpBuffer cmd;
19376 : PQExpBuffer delcmd;
19377 : PQExpBuffer ruleprefix;
19378 : char *qtabname;
19379 : PGresult *res;
19380 : char *tag;
19381 :
19382 : /* Do nothing if not dumping schema */
19383 2990 : if (!dopt->dumpSchema)
19384 132 : return;
19385 :
19386 : /*
19387 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19388 : * we do not want to dump it as a separate object.
19389 : */
19390 2858 : if (!rinfo->separate)
19391 2190 : return;
19392 :
19393 : /*
19394 : * If it's an ON SELECT rule, we want to print it as a view definition,
19395 : * instead of a rule.
19396 : */
19397 668 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19398 :
19399 668 : query = createPQExpBuffer();
19400 668 : cmd = createPQExpBuffer();
19401 668 : delcmd = createPQExpBuffer();
19402 668 : ruleprefix = createPQExpBuffer();
19403 :
19404 668 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19405 :
19406 668 : if (is_view)
19407 : {
19408 : PQExpBuffer result;
19409 :
19410 : /*
19411 : * We need OR REPLACE here because we'll be replacing a dummy view.
19412 : * Otherwise this should look largely like the regular view dump code.
19413 : */
19414 32 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19415 32 : fmtQualifiedDumpable(tbinfo));
19416 32 : if (nonemptyReloptions(tbinfo->reloptions))
19417 : {
19418 0 : appendPQExpBufferStr(cmd, " WITH (");
19419 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19420 0 : appendPQExpBufferChar(cmd, ')');
19421 : }
19422 32 : result = createViewAsClause(fout, tbinfo);
19423 32 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19424 32 : destroyPQExpBuffer(result);
19425 32 : if (tbinfo->checkoption != NULL)
19426 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19427 : tbinfo->checkoption);
19428 32 : appendPQExpBufferStr(cmd, ";\n");
19429 : }
19430 : else
19431 : {
19432 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19433 636 : appendPQExpBuffer(query,
19434 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19435 636 : rinfo->dobj.catId.oid);
19436 :
19437 636 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19438 :
19439 636 : if (PQntuples(res) != 1)
19440 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19441 : rinfo->dobj.name, tbinfo->dobj.name);
19442 :
19443 636 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19444 :
19445 636 : PQclear(res);
19446 : }
19447 :
19448 : /*
19449 : * Add the command to alter the rules replication firing semantics if it
19450 : * differs from the default.
19451 : */
19452 668 : if (rinfo->ev_enabled != 'O')
19453 : {
19454 48 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19455 48 : switch (rinfo->ev_enabled)
19456 : {
19457 0 : case 'A':
19458 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19459 0 : fmtId(rinfo->dobj.name));
19460 0 : break;
19461 0 : case 'R':
19462 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19463 0 : fmtId(rinfo->dobj.name));
19464 0 : break;
19465 48 : case 'D':
19466 48 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19467 48 : fmtId(rinfo->dobj.name));
19468 48 : break;
19469 : }
19470 : }
19471 :
19472 668 : if (is_view)
19473 : {
19474 : /*
19475 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19476 : * REPLACE VIEW to replace the rule with something with minimal
19477 : * dependencies.
19478 : */
19479 : PQExpBuffer result;
19480 :
19481 32 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19482 32 : fmtQualifiedDumpable(tbinfo));
19483 32 : result = createDummyViewAsClause(fout, tbinfo);
19484 32 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19485 32 : destroyPQExpBuffer(result);
19486 : }
19487 : else
19488 : {
19489 636 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19490 636 : fmtId(rinfo->dobj.name));
19491 636 : appendPQExpBuffer(delcmd, "ON %s;\n",
19492 636 : fmtQualifiedDumpable(tbinfo));
19493 : }
19494 :
19495 668 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19496 668 : fmtId(rinfo->dobj.name));
19497 :
19498 668 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19499 :
19500 668 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19501 668 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19502 668 : ARCHIVE_OPTS(.tag = tag,
19503 : .namespace = tbinfo->dobj.namespace->dobj.name,
19504 : .owner = tbinfo->rolname,
19505 : .description = "RULE",
19506 : .section = SECTION_POST_DATA,
19507 : .createStmt = cmd->data,
19508 : .dropStmt = delcmd->data));
19509 :
19510 : /* Dump rule comments */
19511 668 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19512 0 : dumpComment(fout, ruleprefix->data, qtabname,
19513 0 : tbinfo->dobj.namespace->dobj.name,
19514 : tbinfo->rolname,
19515 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19516 :
19517 668 : free(tag);
19518 668 : destroyPQExpBuffer(query);
19519 668 : destroyPQExpBuffer(cmd);
19520 668 : destroyPQExpBuffer(delcmd);
19521 668 : destroyPQExpBuffer(ruleprefix);
19522 668 : free(qtabname);
19523 : }
19524 :
19525 : /*
19526 : * getExtensionMembership --- obtain extension membership data
19527 : *
19528 : * We need to identify objects that are extension members as soon as they're
19529 : * loaded, so that we can correctly determine whether they need to be dumped.
19530 : * Generally speaking, extension member objects will get marked as *not* to
19531 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19532 : * command. However, in binary upgrade mode we still need to dump the members
19533 : * individually.
19534 : */
19535 : void
19536 470 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19537 : int numExtensions)
19538 : {
19539 : PQExpBuffer query;
19540 : PGresult *res;
19541 : int ntups,
19542 : i;
19543 : int i_classid,
19544 : i_objid,
19545 : i_refobjid;
19546 : ExtensionInfo *ext;
19547 :
19548 : /* Nothing to do if no extensions */
19549 470 : if (numExtensions == 0)
19550 0 : return;
19551 :
19552 470 : query = createPQExpBuffer();
19553 :
19554 : /* refclassid constraint is redundant but may speed the search */
19555 470 : appendPQExpBufferStr(query, "SELECT "
19556 : "classid, objid, refobjid "
19557 : "FROM pg_depend "
19558 : "WHERE refclassid = 'pg_extension'::regclass "
19559 : "AND deptype = 'e' "
19560 : "ORDER BY 3");
19561 :
19562 470 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19563 :
19564 470 : ntups = PQntuples(res);
19565 :
19566 470 : i_classid = PQfnumber(res, "classid");
19567 470 : i_objid = PQfnumber(res, "objid");
19568 470 : i_refobjid = PQfnumber(res, "refobjid");
19569 :
19570 : /*
19571 : * Since we ordered the SELECT by referenced ID, we can expect that
19572 : * multiple entries for the same extension will appear together; this
19573 : * saves on searches.
19574 : */
19575 470 : ext = NULL;
19576 :
19577 3550 : for (i = 0; i < ntups; i++)
19578 : {
19579 : CatalogId objId;
19580 : Oid extId;
19581 :
19582 3080 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19583 3080 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19584 3080 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19585 :
19586 3080 : if (ext == NULL ||
19587 2610 : ext->dobj.catId.oid != extId)
19588 520 : ext = findExtensionByOid(extId);
19589 :
19590 3080 : if (ext == NULL)
19591 : {
19592 : /* shouldn't happen */
19593 0 : pg_log_warning("could not find referenced extension %u", extId);
19594 0 : continue;
19595 : }
19596 :
19597 3080 : recordExtensionMembership(objId, ext);
19598 : }
19599 :
19600 470 : PQclear(res);
19601 :
19602 470 : destroyPQExpBuffer(query);
19603 : }
19604 :
19605 : /*
19606 : * processExtensionTables --- deal with extension configuration tables
19607 : *
19608 : * There are two parts to this process:
19609 : *
19610 : * 1. Identify and create dump records for extension configuration tables.
19611 : *
19612 : * Extensions can mark tables as "configuration", which means that the user
19613 : * is able and expected to modify those tables after the extension has been
19614 : * loaded. For these tables, we dump out only the data- the structure is
19615 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19616 : * foreign keys, which brings us to-
19617 : *
19618 : * 2. Record FK dependencies between configuration tables.
19619 : *
19620 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19621 : * the data is loaded, we have to work out what the best order for reloading
19622 : * the data is, to avoid FK violations when the tables are restored. This is
19623 : * not perfect- we can't handle circular dependencies and if any exist they
19624 : * will cause an invalid dump to be produced (though at least all of the data
19625 : * is included for a user to manually restore). This is currently documented
19626 : * but perhaps we can provide a better solution in the future.
19627 : */
19628 : void
19629 468 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19630 : int numExtensions)
19631 : {
19632 468 : DumpOptions *dopt = fout->dopt;
19633 : PQExpBuffer query;
19634 : PGresult *res;
19635 : int ntups,
19636 : i;
19637 : int i_conrelid,
19638 : i_confrelid;
19639 :
19640 : /* Nothing to do if no extensions */
19641 468 : if (numExtensions == 0)
19642 0 : return;
19643 :
19644 : /*
19645 : * Identify extension configuration tables and create TableDataInfo
19646 : * objects for them, ensuring their data will be dumped even though the
19647 : * tables themselves won't be.
19648 : *
19649 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19650 : * user data in a configuration table is treated like schema data. This
19651 : * seems appropriate since system data in a config table would get
19652 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19653 : * list of extensions to be included, none of its data is dumped.
19654 : */
19655 986 : for (i = 0; i < numExtensions; i++)
19656 : {
19657 518 : ExtensionInfo *curext = &(extinfo[i]);
19658 518 : char *extconfig = curext->extconfig;
19659 518 : char *extcondition = curext->extcondition;
19660 518 : char **extconfigarray = NULL;
19661 518 : char **extconditionarray = NULL;
19662 518 : int nconfigitems = 0;
19663 518 : int nconditionitems = 0;
19664 :
19665 : /*
19666 : * Check if this extension is listed as to include in the dump. If
19667 : * not, any table data associated with it is discarded.
19668 : */
19669 518 : if (extension_include_oids.head != NULL &&
19670 16 : !simple_oid_list_member(&extension_include_oids,
19671 : curext->dobj.catId.oid))
19672 12 : continue;
19673 :
19674 : /*
19675 : * Check if this extension is listed as to exclude in the dump. If
19676 : * yes, any table data associated with it is discarded.
19677 : */
19678 518 : if (extension_exclude_oids.head != NULL &&
19679 8 : simple_oid_list_member(&extension_exclude_oids,
19680 : curext->dobj.catId.oid))
19681 4 : continue;
19682 :
19683 506 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19684 : {
19685 : int j;
19686 :
19687 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19688 0 : pg_fatal("could not parse %s array", "extconfig");
19689 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19690 0 : pg_fatal("could not parse %s array", "extcondition");
19691 40 : if (nconfigitems != nconditionitems)
19692 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19693 :
19694 120 : for (j = 0; j < nconfigitems; j++)
19695 : {
19696 : TableInfo *configtbl;
19697 80 : Oid configtbloid = atooid(extconfigarray[j]);
19698 80 : bool dumpobj =
19699 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19700 :
19701 80 : configtbl = findTableByOid(configtbloid);
19702 80 : if (configtbl == NULL)
19703 0 : continue;
19704 :
19705 : /*
19706 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19707 : * unless the table or its schema is explicitly included
19708 : */
19709 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19710 : {
19711 : /* check table explicitly requested */
19712 4 : if (table_include_oids.head != NULL &&
19713 0 : simple_oid_list_member(&table_include_oids,
19714 : configtbloid))
19715 0 : dumpobj = true;
19716 :
19717 : /* check table's schema explicitly requested */
19718 4 : if (configtbl->dobj.namespace->dobj.dump &
19719 : DUMP_COMPONENT_DATA)
19720 4 : dumpobj = true;
19721 : }
19722 :
19723 : /* check table excluded by an exclusion switch */
19724 88 : if (table_exclude_oids.head != NULL &&
19725 8 : simple_oid_list_member(&table_exclude_oids,
19726 : configtbloid))
19727 2 : dumpobj = false;
19728 :
19729 : /* check schema excluded by an exclusion switch */
19730 80 : if (simple_oid_list_member(&schema_exclude_oids,
19731 80 : configtbl->dobj.namespace->dobj.catId.oid))
19732 0 : dumpobj = false;
19733 :
19734 80 : if (dumpobj)
19735 : {
19736 78 : makeTableDataInfo(dopt, configtbl);
19737 78 : if (configtbl->dataObj != NULL)
19738 : {
19739 78 : if (strlen(extconditionarray[j]) > 0)
19740 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19741 : }
19742 : }
19743 : }
19744 : }
19745 506 : if (extconfigarray)
19746 40 : free(extconfigarray);
19747 506 : if (extconditionarray)
19748 40 : free(extconditionarray);
19749 : }
19750 :
19751 : /*
19752 : * Now that all the TableDataInfo objects have been created for all the
19753 : * extensions, check their FK dependencies and register them to try and
19754 : * dump the data out in an order that they can be restored in.
19755 : *
19756 : * Note that this is not a problem for user tables as their FKs are
19757 : * recreated after the data has been loaded.
19758 : */
19759 :
19760 468 : query = createPQExpBuffer();
19761 :
19762 468 : printfPQExpBuffer(query,
19763 : "SELECT conrelid, confrelid "
19764 : "FROM pg_constraint "
19765 : "JOIN pg_depend ON (objid = confrelid) "
19766 : "WHERE contype = 'f' "
19767 : "AND refclassid = 'pg_extension'::regclass "
19768 : "AND classid = 'pg_class'::regclass;");
19769 :
19770 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19771 468 : ntups = PQntuples(res);
19772 :
19773 468 : i_conrelid = PQfnumber(res, "conrelid");
19774 468 : i_confrelid = PQfnumber(res, "confrelid");
19775 :
19776 : /* Now get the dependencies and register them */
19777 468 : for (i = 0; i < ntups; i++)
19778 : {
19779 : Oid conrelid,
19780 : confrelid;
19781 : TableInfo *reftable,
19782 : *contable;
19783 :
19784 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19785 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19786 0 : contable = findTableByOid(conrelid);
19787 0 : reftable = findTableByOid(confrelid);
19788 :
19789 0 : if (reftable == NULL ||
19790 0 : reftable->dataObj == NULL ||
19791 0 : contable == NULL ||
19792 0 : contable->dataObj == NULL)
19793 0 : continue;
19794 :
19795 : /*
19796 : * Make referencing TABLE_DATA object depend on the referenced table's
19797 : * TABLE_DATA object.
19798 : */
19799 0 : addObjectDependency(&contable->dataObj->dobj,
19800 0 : reftable->dataObj->dobj.dumpId);
19801 : }
19802 468 : PQclear(res);
19803 468 : destroyPQExpBuffer(query);
19804 : }
19805 :
19806 : /*
19807 : * getDependencies --- obtain available dependency data
19808 : */
19809 : static void
19810 468 : getDependencies(Archive *fout)
19811 : {
19812 : PQExpBuffer query;
19813 : PGresult *res;
19814 : int ntups,
19815 : i;
19816 : int i_classid,
19817 : i_objid,
19818 : i_refclassid,
19819 : i_refobjid,
19820 : i_deptype;
19821 : DumpableObject *dobj,
19822 : *refdobj;
19823 :
19824 468 : pg_log_info("reading dependency data");
19825 :
19826 468 : query = createPQExpBuffer();
19827 :
19828 : /*
19829 : * Messy query to collect the dependency data we need. Note that we
19830 : * ignore the sub-object column, so that dependencies of or on a column
19831 : * look the same as dependencies of or on a whole table.
19832 : *
19833 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19834 : * already processed by getExtensionMembership.
19835 : */
19836 468 : appendPQExpBufferStr(query, "SELECT "
19837 : "classid, objid, refclassid, refobjid, deptype "
19838 : "FROM pg_depend "
19839 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19840 :
19841 : /*
19842 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19843 : * have to translate their dependencies into dependencies of their parent
19844 : * opfamily. Ignore internal dependencies though, as those will point to
19845 : * their parent opclass, which we needn't consider here (and if we did,
19846 : * it'd just result in circular dependencies). Also, "loose" opfamily
19847 : * entries will have dependencies on their parent opfamily, which we
19848 : * should drop since they'd likewise become useless self-dependencies.
19849 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19850 : */
19851 468 : appendPQExpBufferStr(query, "UNION ALL\n"
19852 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19853 : "FROM pg_depend d, pg_amop o "
19854 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19855 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19856 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19857 :
19858 : /* Likewise for pg_amproc entries */
19859 468 : appendPQExpBufferStr(query, "UNION ALL\n"
19860 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19861 : "FROM pg_depend d, pg_amproc p "
19862 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19863 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
19864 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
19865 :
19866 : /* Sort the output for efficiency below */
19867 468 : appendPQExpBufferStr(query, "ORDER BY 1,2");
19868 :
19869 468 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19870 :
19871 468 : ntups = PQntuples(res);
19872 :
19873 468 : i_classid = PQfnumber(res, "classid");
19874 468 : i_objid = PQfnumber(res, "objid");
19875 468 : i_refclassid = PQfnumber(res, "refclassid");
19876 468 : i_refobjid = PQfnumber(res, "refobjid");
19877 468 : i_deptype = PQfnumber(res, "deptype");
19878 :
19879 : /*
19880 : * Since we ordered the SELECT by referencing ID, we can expect that
19881 : * multiple entries for the same object will appear together; this saves
19882 : * on searches.
19883 : */
19884 468 : dobj = NULL;
19885 :
19886 1012934 : for (i = 0; i < ntups; i++)
19887 : {
19888 : CatalogId objId;
19889 : CatalogId refobjId;
19890 : char deptype;
19891 :
19892 1012466 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19893 1012466 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19894 1012466 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
19895 1012466 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
19896 1012466 : deptype = *(PQgetvalue(res, i, i_deptype));
19897 :
19898 1012466 : if (dobj == NULL ||
19899 950154 : dobj->catId.tableoid != objId.tableoid ||
19900 945110 : dobj->catId.oid != objId.oid)
19901 444976 : dobj = findObjectByCatalogId(objId);
19902 :
19903 : /*
19904 : * Failure to find objects mentioned in pg_depend is not unexpected,
19905 : * since for example we don't collect info about TOAST tables.
19906 : */
19907 1012466 : if (dobj == NULL)
19908 : {
19909 : #ifdef NOT_USED
19910 : pg_log_warning("no referencing object %u %u",
19911 : objId.tableoid, objId.oid);
19912 : #endif
19913 64068 : continue;
19914 : }
19915 :
19916 950622 : refdobj = findObjectByCatalogId(refobjId);
19917 :
19918 950622 : if (refdobj == NULL)
19919 : {
19920 : #ifdef NOT_USED
19921 : pg_log_warning("no referenced object %u %u",
19922 : refobjId.tableoid, refobjId.oid);
19923 : #endif
19924 2224 : continue;
19925 : }
19926 :
19927 : /*
19928 : * For 'x' dependencies, mark the object for later; we still add the
19929 : * normal dependency, for possible ordering purposes. Currently
19930 : * pg_dump_sort.c knows to put extensions ahead of all object types
19931 : * that could possibly depend on them, but this is safer.
19932 : */
19933 948398 : if (deptype == 'x')
19934 88 : dobj->depends_on_ext = true;
19935 :
19936 : /*
19937 : * Ordinarily, table rowtypes have implicit dependencies on their
19938 : * tables. However, for a composite type the implicit dependency goes
19939 : * the other way in pg_depend; which is the right thing for DROP but
19940 : * it doesn't produce the dependency ordering we need. So in that one
19941 : * case, we reverse the direction of the dependency.
19942 : */
19943 948398 : if (deptype == 'i' &&
19944 267160 : dobj->objType == DO_TABLE &&
19945 3248 : refdobj->objType == DO_TYPE)
19946 468 : addObjectDependency(refdobj, dobj->dumpId);
19947 : else
19948 : /* normal case */
19949 947930 : addObjectDependency(dobj, refdobj->dumpId);
19950 : }
19951 :
19952 468 : PQclear(res);
19953 :
19954 468 : destroyPQExpBuffer(query);
19955 468 : }
19956 :
19957 :
19958 : /*
19959 : * createBoundaryObjects - create dummy DumpableObjects to represent
19960 : * dump section boundaries.
19961 : */
19962 : static DumpableObject *
19963 468 : createBoundaryObjects(void)
19964 : {
19965 : DumpableObject *dobjs;
19966 :
19967 468 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
19968 :
19969 468 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
19970 468 : dobjs[0].catId = nilCatalogId;
19971 468 : AssignDumpId(dobjs + 0);
19972 468 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
19973 :
19974 468 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
19975 468 : dobjs[1].catId = nilCatalogId;
19976 468 : AssignDumpId(dobjs + 1);
19977 468 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
19978 :
19979 468 : return dobjs;
19980 : }
19981 :
19982 : /*
19983 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
19984 : * section boundaries.
19985 : */
19986 : static void
19987 468 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
19988 : DumpableObject *boundaryObjs)
19989 : {
19990 468 : DumpableObject *preDataBound = boundaryObjs + 0;
19991 468 : DumpableObject *postDataBound = boundaryObjs + 1;
19992 : int i;
19993 :
19994 1740674 : for (i = 0; i < numObjs; i++)
19995 : {
19996 1740206 : DumpableObject *dobj = dobjs[i];
19997 :
19998 : /*
19999 : * The classification of object types here must match the SECTION_xxx
20000 : * values assigned during subsequent ArchiveEntry calls!
20001 : */
20002 1740206 : switch (dobj->objType)
20003 : {
20004 1625072 : case DO_NAMESPACE:
20005 : case DO_EXTENSION:
20006 : case DO_TYPE:
20007 : case DO_SHELL_TYPE:
20008 : case DO_FUNC:
20009 : case DO_AGG:
20010 : case DO_OPERATOR:
20011 : case DO_ACCESS_METHOD:
20012 : case DO_OPCLASS:
20013 : case DO_OPFAMILY:
20014 : case DO_COLLATION:
20015 : case DO_CONVERSION:
20016 : case DO_TABLE:
20017 : case DO_TABLE_ATTACH:
20018 : case DO_ATTRDEF:
20019 : case DO_PROCLANG:
20020 : case DO_CAST:
20021 : case DO_DUMMY_TYPE:
20022 : case DO_TSPARSER:
20023 : case DO_TSDICT:
20024 : case DO_TSTEMPLATE:
20025 : case DO_TSCONFIG:
20026 : case DO_FDW:
20027 : case DO_FOREIGN_SERVER:
20028 : case DO_TRANSFORM:
20029 : /* Pre-data objects: must come before the pre-data boundary */
20030 1625072 : addObjectDependency(preDataBound, dobj->dumpId);
20031 1625072 : break;
20032 14058 : case DO_TABLE_DATA:
20033 : case DO_SEQUENCE_SET:
20034 : case DO_LARGE_OBJECT:
20035 : case DO_LARGE_OBJECT_DATA:
20036 : /* Data objects: must come between the boundaries */
20037 14058 : addObjectDependency(dobj, preDataBound->dumpId);
20038 14058 : addObjectDependency(postDataBound, dobj->dumpId);
20039 14058 : break;
20040 14140 : case DO_INDEX:
20041 : case DO_INDEX_ATTACH:
20042 : case DO_STATSEXT:
20043 : case DO_REFRESH_MATVIEW:
20044 : case DO_TRIGGER:
20045 : case DO_EVENT_TRIGGER:
20046 : case DO_DEFAULT_ACL:
20047 : case DO_POLICY:
20048 : case DO_PUBLICATION:
20049 : case DO_PUBLICATION_REL:
20050 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20051 : case DO_SUBSCRIPTION:
20052 : case DO_SUBSCRIPTION_REL:
20053 : /* Post-data objects: must come after the post-data boundary */
20054 14140 : addObjectDependency(dobj, postDataBound->dumpId);
20055 14140 : break;
20056 72302 : case DO_RULE:
20057 : /* Rules are post-data, but only if dumped separately */
20058 72302 : if (((RuleInfo *) dobj)->separate)
20059 1716 : addObjectDependency(dobj, postDataBound->dumpId);
20060 72302 : break;
20061 6638 : case DO_CONSTRAINT:
20062 : case DO_FK_CONSTRAINT:
20063 : /* Constraints are post-data, but only if dumped separately */
20064 6638 : if (((ConstraintInfo *) dobj)->separate)
20065 4776 : addObjectDependency(dobj, postDataBound->dumpId);
20066 6638 : break;
20067 468 : case DO_PRE_DATA_BOUNDARY:
20068 : /* nothing to do */
20069 468 : break;
20070 468 : case DO_POST_DATA_BOUNDARY:
20071 : /* must come after the pre-data boundary */
20072 468 : addObjectDependency(dobj, preDataBound->dumpId);
20073 468 : break;
20074 7060 : case DO_REL_STATS:
20075 : /* stats section varies by parent object type, DATA or POST */
20076 7060 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20077 : {
20078 4456 : addObjectDependency(dobj, preDataBound->dumpId);
20079 4456 : addObjectDependency(postDataBound, dobj->dumpId);
20080 : }
20081 : else
20082 2604 : addObjectDependency(dobj, postDataBound->dumpId);
20083 7060 : break;
20084 : }
20085 : }
20086 468 : }
20087 :
20088 :
20089 : /*
20090 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20091 : *
20092 : * The raw dependency data obtained by getDependencies() is not terribly
20093 : * useful in an archive dump, because in many cases there are dependency
20094 : * chains linking through objects that don't appear explicitly in the dump.
20095 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20096 : * will depend on other objects --- but the rule will not appear as a separate
20097 : * object in the dump. We need to adjust the view's dependencies to include
20098 : * whatever the rule depends on that is included in the dump.
20099 : *
20100 : * Just to make things more complicated, there are also "special" dependencies
20101 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20102 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20103 : * its table. In these cases we must leave the dependencies strictly as-is
20104 : * even if they refer to not-to-be-dumped objects.
20105 : *
20106 : * To handle this, the convention is that "special" dependencies are created
20107 : * during ArchiveEntry calls, and an archive TOC item that has any such
20108 : * entries will not be touched here. Otherwise, we recursively search the
20109 : * DumpableObject data structures to build the correct dependencies for each
20110 : * archive TOC item.
20111 : */
20112 : static void
20113 206 : BuildArchiveDependencies(Archive *fout)
20114 : {
20115 206 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20116 : TocEntry *te;
20117 :
20118 : /* Scan all TOC entries in the archive */
20119 20736 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20120 : {
20121 : DumpableObject *dobj;
20122 : DumpId *dependencies;
20123 : int nDeps;
20124 : int allocDeps;
20125 :
20126 : /* No need to process entries that will not be dumped */
20127 20530 : if (te->reqs == 0)
20128 9208 : continue;
20129 : /* Ignore entries that already have "special" dependencies */
20130 20524 : if (te->nDeps > 0)
20131 7948 : continue;
20132 : /* Otherwise, look up the item's original DumpableObject, if any */
20133 12576 : dobj = findObjectByDumpId(te->dumpId);
20134 12576 : if (dobj == NULL)
20135 978 : continue;
20136 : /* No work if it has no dependencies */
20137 11598 : if (dobj->nDeps <= 0)
20138 276 : continue;
20139 : /* Set up work array */
20140 11322 : allocDeps = 64;
20141 11322 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20142 11322 : nDeps = 0;
20143 : /* Recursively find all dumpable dependencies */
20144 11322 : findDumpableDependencies(AH, dobj,
20145 : &dependencies, &nDeps, &allocDeps);
20146 : /* And save 'em ... */
20147 11322 : if (nDeps > 0)
20148 : {
20149 8076 : dependencies = (DumpId *) pg_realloc(dependencies,
20150 : nDeps * sizeof(DumpId));
20151 8076 : te->dependencies = dependencies;
20152 8076 : te->nDeps = nDeps;
20153 : }
20154 : else
20155 3246 : free(dependencies);
20156 : }
20157 206 : }
20158 :
20159 : /* Recursive search subroutine for BuildArchiveDependencies */
20160 : static void
20161 26718 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20162 : DumpId **dependencies, int *nDeps, int *allocDeps)
20163 : {
20164 : int i;
20165 :
20166 : /*
20167 : * Ignore section boundary objects: if we search through them, we'll
20168 : * report lots of bogus dependencies.
20169 : */
20170 26718 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20171 26684 : dobj->objType == DO_POST_DATA_BOUNDARY)
20172 4314 : return;
20173 :
20174 54764 : for (i = 0; i < dobj->nDeps; i++)
20175 : {
20176 32360 : DumpId depid = dobj->dependencies[i];
20177 :
20178 32360 : if (TocIDRequired(AH, depid) != 0)
20179 : {
20180 : /* Object will be dumped, so just reference it as a dependency */
20181 16964 : if (*nDeps >= *allocDeps)
20182 : {
20183 0 : *allocDeps *= 2;
20184 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20185 0 : *allocDeps * sizeof(DumpId));
20186 : }
20187 16964 : (*dependencies)[*nDeps] = depid;
20188 16964 : (*nDeps)++;
20189 : }
20190 : else
20191 : {
20192 : /*
20193 : * Object will not be dumped, so recursively consider its deps. We
20194 : * rely on the assumption that sortDumpableObjects already broke
20195 : * any dependency loops, else we might recurse infinitely.
20196 : */
20197 15396 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20198 :
20199 15396 : if (otherdobj)
20200 15396 : findDumpableDependencies(AH, otherdobj,
20201 : dependencies, nDeps, allocDeps);
20202 : }
20203 : }
20204 : }
20205 :
20206 :
20207 : /*
20208 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20209 : * given type OID.
20210 : *
20211 : * This does not guarantee to schema-qualify the output, so it should not
20212 : * be used to create the target object name for CREATE or ALTER commands.
20213 : *
20214 : * Note that the result is cached and must not be freed by the caller.
20215 : */
20216 : static const char *
20217 6916 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20218 : {
20219 : TypeInfo *typeInfo;
20220 : char *result;
20221 : PQExpBuffer query;
20222 : PGresult *res;
20223 :
20224 6916 : if (oid == 0)
20225 : {
20226 0 : if ((opts & zeroAsStar) != 0)
20227 0 : return "*";
20228 0 : else if ((opts & zeroAsNone) != 0)
20229 0 : return "NONE";
20230 : }
20231 :
20232 : /* see if we have the result cached in the type's TypeInfo record */
20233 6916 : typeInfo = findTypeByOid(oid);
20234 6916 : if (typeInfo && typeInfo->ftypname)
20235 5680 : return typeInfo->ftypname;
20236 :
20237 1236 : query = createPQExpBuffer();
20238 1236 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20239 : oid);
20240 :
20241 1236 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20242 :
20243 : /* result of format_type is already quoted */
20244 1236 : result = pg_strdup(PQgetvalue(res, 0, 0));
20245 :
20246 1236 : PQclear(res);
20247 1236 : destroyPQExpBuffer(query);
20248 :
20249 : /*
20250 : * Cache the result for re-use in later requests, if possible. If we
20251 : * don't have a TypeInfo for the type, the string will be leaked once the
20252 : * caller is done with it ... but that case really should not happen, so
20253 : * leaking if it does seems acceptable.
20254 : */
20255 1236 : if (typeInfo)
20256 1236 : typeInfo->ftypname = result;
20257 :
20258 1236 : return result;
20259 : }
20260 :
20261 : /*
20262 : * Return a column list clause for the given relation.
20263 : *
20264 : * Special case: if there are no undropped columns in the relation, return
20265 : * "", not an invalid "()" column list.
20266 : */
20267 : static const char *
20268 24704 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20269 : {
20270 24704 : int numatts = ti->numatts;
20271 24704 : char **attnames = ti->attnames;
20272 24704 : bool *attisdropped = ti->attisdropped;
20273 24704 : char *attgenerated = ti->attgenerated;
20274 : bool needComma;
20275 : int i;
20276 :
20277 24704 : appendPQExpBufferChar(buffer, '(');
20278 24704 : needComma = false;
20279 122556 : for (i = 0; i < numatts; i++)
20280 : {
20281 97852 : if (attisdropped[i])
20282 1744 : continue;
20283 96108 : if (attgenerated[i])
20284 3216 : continue;
20285 92892 : if (needComma)
20286 68720 : appendPQExpBufferStr(buffer, ", ");
20287 92892 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20288 92892 : needComma = true;
20289 : }
20290 :
20291 24704 : if (!needComma)
20292 532 : return ""; /* no undropped columns */
20293 :
20294 24172 : appendPQExpBufferChar(buffer, ')');
20295 24172 : return buffer->data;
20296 : }
20297 :
20298 : /*
20299 : * Check if a reloptions array is nonempty.
20300 : */
20301 : static bool
20302 37976 : nonemptyReloptions(const char *reloptions)
20303 : {
20304 : /* Don't want to print it if it's just "{}" */
20305 37976 : return (reloptions != NULL && strlen(reloptions) > 2);
20306 : }
20307 :
20308 : /*
20309 : * Format a reloptions array and append it to the given buffer.
20310 : *
20311 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20312 : */
20313 : static void
20314 594 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20315 : const char *prefix, Archive *fout)
20316 : {
20317 : bool res;
20318 :
20319 594 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20320 594 : fout->std_strings);
20321 594 : if (!res)
20322 0 : pg_log_warning("could not parse %s array", "reloptions");
20323 594 : }
20324 :
20325 : /*
20326 : * read_dump_filters - retrieve object identifier patterns from file
20327 : *
20328 : * Parse the specified filter file for include and exclude patterns, and add
20329 : * them to the relevant lists. If the filename is "-" then filters will be
20330 : * read from STDIN rather than a file.
20331 : */
20332 : static void
20333 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20334 : {
20335 : FilterStateData fstate;
20336 : char *objname;
20337 : FilterCommandType comtype;
20338 : FilterObjectType objtype;
20339 :
20340 52 : filter_init(&fstate, filename, exit_nicely);
20341 :
20342 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20343 : {
20344 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20345 : {
20346 34 : switch (objtype)
20347 : {
20348 0 : case FILTER_OBJECT_TYPE_NONE:
20349 0 : break;
20350 0 : case FILTER_OBJECT_TYPE_DATABASE:
20351 : case FILTER_OBJECT_TYPE_FUNCTION:
20352 : case FILTER_OBJECT_TYPE_INDEX:
20353 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20354 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20355 : case FILTER_OBJECT_TYPE_TRIGGER:
20356 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20357 : "include",
20358 : filter_object_type_name(objtype));
20359 0 : exit_nicely(1);
20360 : break; /* unreachable */
20361 :
20362 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20363 2 : simple_string_list_append(&extension_include_patterns, objname);
20364 2 : break;
20365 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20366 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20367 2 : break;
20368 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20369 2 : simple_string_list_append(&schema_include_patterns, objname);
20370 2 : dopt->include_everything = false;
20371 2 : break;
20372 26 : case FILTER_OBJECT_TYPE_TABLE:
20373 26 : simple_string_list_append(&table_include_patterns, objname);
20374 26 : dopt->include_everything = false;
20375 26 : break;
20376 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20377 2 : simple_string_list_append(&table_include_patterns_and_children,
20378 : objname);
20379 2 : dopt->include_everything = false;
20380 2 : break;
20381 : }
20382 : }
20383 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20384 : {
20385 18 : switch (objtype)
20386 : {
20387 0 : case FILTER_OBJECT_TYPE_NONE:
20388 0 : break;
20389 2 : case FILTER_OBJECT_TYPE_DATABASE:
20390 : case FILTER_OBJECT_TYPE_FUNCTION:
20391 : case FILTER_OBJECT_TYPE_INDEX:
20392 : case FILTER_OBJECT_TYPE_TRIGGER:
20393 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20394 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20395 : "exclude",
20396 : filter_object_type_name(objtype));
20397 2 : exit_nicely(1);
20398 : break;
20399 :
20400 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20401 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20402 2 : break;
20403 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20404 2 : simple_string_list_append(&tabledata_exclude_patterns,
20405 : objname);
20406 2 : break;
20407 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20408 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20409 : objname);
20410 2 : break;
20411 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20412 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20413 4 : break;
20414 4 : case FILTER_OBJECT_TYPE_TABLE:
20415 4 : simple_string_list_append(&table_exclude_patterns, objname);
20416 4 : break;
20417 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20418 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20419 : objname);
20420 2 : break;
20421 : }
20422 : }
20423 : else
20424 : {
20425 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20426 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20427 : }
20428 :
20429 64 : if (objname)
20430 50 : free(objname);
20431 : }
20432 :
20433 44 : filter_free(&fstate);
20434 44 : }
|