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 588 : main(int argc, char **argv)
422 : {
423 : int c;
424 588 : const char *filename = NULL;
425 588 : 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 588 : bool g_verbose = false;
436 588 : const char *dumpencoding = NULL;
437 588 : const char *dumpsnapshot = NULL;
438 588 : char *use_role = NULL;
439 588 : int numWorkers = 1;
440 588 : int plainText = 0;
441 588 : ArchiveFormat archiveFormat = archUnknown;
442 : ArchiveMode archiveMode;
443 588 : pg_compress_specification compression_spec = {0};
444 588 : char *compression_detail = NULL;
445 588 : char *compression_algorithm_str = "none";
446 588 : char *error_detail = NULL;
447 588 : bool user_compression_defined = false;
448 588 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
449 588 : bool data_only = false;
450 588 : bool schema_only = false;
451 588 : bool statistics_only = false;
452 588 : bool with_statistics = false;
453 588 : bool no_data = false;
454 588 : bool no_schema = false;
455 588 : bool no_statistics = false;
456 :
457 : static DumpOptions dopt;
458 :
459 : static struct option long_options[] = {
460 : {"data-only", no_argument, NULL, 'a'},
461 : {"blobs", no_argument, NULL, 'b'},
462 : {"large-objects", no_argument, NULL, 'b'},
463 : {"no-blobs", no_argument, NULL, 'B'},
464 : {"no-large-objects", no_argument, NULL, 'B'},
465 : {"clean", no_argument, NULL, 'c'},
466 : {"create", no_argument, NULL, 'C'},
467 : {"dbname", required_argument, NULL, 'd'},
468 : {"extension", required_argument, NULL, 'e'},
469 : {"file", required_argument, NULL, 'f'},
470 : {"format", required_argument, NULL, 'F'},
471 : {"host", required_argument, NULL, 'h'},
472 : {"jobs", 1, NULL, 'j'},
473 : {"no-reconnect", no_argument, NULL, 'R'},
474 : {"no-owner", no_argument, NULL, 'O'},
475 : {"port", required_argument, NULL, 'p'},
476 : {"schema", required_argument, NULL, 'n'},
477 : {"exclude-schema", required_argument, NULL, 'N'},
478 : {"schema-only", no_argument, NULL, 's'},
479 : {"superuser", required_argument, NULL, 'S'},
480 : {"table", required_argument, NULL, 't'},
481 : {"exclude-table", required_argument, NULL, 'T'},
482 : {"no-password", no_argument, NULL, 'w'},
483 : {"password", no_argument, NULL, 'W'},
484 : {"username", required_argument, NULL, 'U'},
485 : {"verbose", no_argument, NULL, 'v'},
486 : {"no-privileges", no_argument, NULL, 'x'},
487 : {"no-acl", no_argument, NULL, 'x'},
488 : {"compress", required_argument, NULL, 'Z'},
489 : {"encoding", required_argument, NULL, 'E'},
490 : {"help", no_argument, NULL, '?'},
491 : {"version", no_argument, NULL, 'V'},
492 :
493 : /*
494 : * the following options don't have an equivalent short option letter
495 : */
496 : {"attribute-inserts", no_argument, &dopt.column_inserts, 1},
497 : {"binary-upgrade", no_argument, &dopt.binary_upgrade, 1},
498 : {"column-inserts", no_argument, &dopt.column_inserts, 1},
499 : {"disable-dollar-quoting", no_argument, &dopt.disable_dollar_quoting, 1},
500 : {"disable-triggers", no_argument, &dopt.disable_triggers, 1},
501 : {"enable-row-security", no_argument, &dopt.enable_row_security, 1},
502 : {"exclude-table-data", required_argument, NULL, 4},
503 : {"extra-float-digits", required_argument, NULL, 8},
504 : {"if-exists", no_argument, &dopt.if_exists, 1},
505 : {"inserts", no_argument, NULL, 9},
506 : {"lock-wait-timeout", required_argument, NULL, 2},
507 : {"no-table-access-method", no_argument, &dopt.outputNoTableAm, 1},
508 : {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
509 : {"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
510 : {"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
511 : {"role", required_argument, NULL, 3},
512 : {"section", required_argument, NULL, 5},
513 : {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
514 : {"snapshot", required_argument, NULL, 6},
515 : {"statistics", no_argument, NULL, 22},
516 : {"statistics-only", no_argument, NULL, 18},
517 : {"strict-names", no_argument, &strict_names, 1},
518 : {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
519 : {"no-comments", no_argument, &dopt.no_comments, 1},
520 : {"no-data", no_argument, NULL, 19},
521 : {"no-policies", no_argument, &dopt.no_policies, 1},
522 : {"no-publications", no_argument, &dopt.no_publications, 1},
523 : {"no-schema", no_argument, NULL, 20},
524 : {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
525 : {"no-statistics", no_argument, NULL, 21},
526 : {"no-subscriptions", no_argument, &dopt.no_subscriptions, 1},
527 : {"no-toast-compression", no_argument, &dopt.no_toast_compression, 1},
528 : {"no-unlogged-table-data", no_argument, &dopt.no_unlogged_table_data, 1},
529 : {"no-sync", no_argument, NULL, 7},
530 : {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
531 : {"rows-per-insert", required_argument, NULL, 10},
532 : {"include-foreign-data", required_argument, NULL, 11},
533 : {"table-and-children", required_argument, NULL, 12},
534 : {"exclude-table-and-children", required_argument, NULL, 13},
535 : {"exclude-table-data-and-children", required_argument, NULL, 14},
536 : {"sync-method", required_argument, NULL, 15},
537 : {"filter", required_argument, NULL, 16},
538 : {"exclude-extension", required_argument, NULL, 17},
539 : {"sequence-data", no_argument, &dopt.sequence_data, 1},
540 : {"restrict-key", required_argument, NULL, 25},
541 :
542 : {NULL, 0, NULL, 0}
543 : };
544 :
545 588 : pg_logging_init(argv[0]);
546 588 : pg_logging_set_level(PG_LOG_WARNING);
547 588 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_dump"));
548 :
549 : /*
550 : * Initialize what we need for parallel execution, especially for thread
551 : * support on Windows.
552 : */
553 588 : init_parallel_dump_utils();
554 :
555 588 : progname = get_progname(argv[0]);
556 :
557 588 : if (argc > 1)
558 : {
559 588 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
560 : {
561 2 : help(progname);
562 2 : exit_nicely(0);
563 : }
564 586 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
565 : {
566 124 : puts("pg_dump (PostgreSQL) " PG_VERSION);
567 124 : exit_nicely(0);
568 : }
569 : }
570 :
571 462 : InitDumpOptions(&dopt);
572 :
573 2574 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
574 2574 : long_options, &optindex)) != -1)
575 : {
576 2128 : switch (c)
577 : {
578 18 : case 'a': /* Dump data only */
579 18 : data_only = true;
580 18 : break;
581 :
582 2 : case 'b': /* Dump LOs */
583 2 : dopt.outputLOs = true;
584 2 : break;
585 :
586 4 : case 'B': /* Don't dump LOs */
587 4 : dopt.dontOutputLOs = true;
588 4 : break;
589 :
590 12 : case 'c': /* clean (i.e., drop) schema prior to create */
591 12 : dopt.outputClean = 1;
592 12 : break;
593 :
594 58 : case 'C': /* Create DB */
595 58 : dopt.outputCreateDB = 1;
596 58 : break;
597 :
598 10 : case 'd': /* database name */
599 10 : dopt.cparams.dbname = pg_strdup(optarg);
600 10 : break;
601 :
602 8 : case 'e': /* include extension(s) */
603 8 : simple_string_list_append(&extension_include_patterns, optarg);
604 8 : dopt.include_everything = false;
605 8 : break;
606 :
607 4 : case 'E': /* Dump encoding */
608 4 : dumpencoding = pg_strdup(optarg);
609 4 : break;
610 :
611 368 : case 'f':
612 368 : filename = pg_strdup(optarg);
613 368 : break;
614 :
615 222 : case 'F':
616 222 : format = pg_strdup(optarg);
617 222 : break;
618 :
619 68 : case 'h': /* server host */
620 68 : dopt.cparams.pghost = pg_strdup(optarg);
621 68 : break;
622 :
623 22 : case 'j': /* number of dump jobs */
624 22 : if (!option_parse_int(optarg, "-j/--jobs", 1,
625 : PG_MAX_JOBS,
626 : &numWorkers))
627 2 : exit_nicely(1);
628 20 : break;
629 :
630 34 : case 'n': /* include schema(s) */
631 34 : simple_string_list_append(&schema_include_patterns, optarg);
632 34 : dopt.include_everything = false;
633 34 : break;
634 :
635 2 : case 'N': /* exclude schema(s) */
636 2 : simple_string_list_append(&schema_exclude_patterns, optarg);
637 2 : break;
638 :
639 4 : case 'O': /* Don't reconnect to match owner */
640 4 : dopt.outputNoOwner = 1;
641 4 : break;
642 :
643 146 : case 'p': /* server port */
644 146 : dopt.cparams.pgport = pg_strdup(optarg);
645 146 : break;
646 :
647 4 : case 'R':
648 : /* no-op, still accepted for backwards compatibility */
649 4 : break;
650 :
651 14 : case 's': /* dump schema only */
652 14 : schema_only = true;
653 14 : break;
654 :
655 2 : case 'S': /* Username for superuser in plain text output */
656 2 : dopt.outputSuperuser = pg_strdup(optarg);
657 2 : break;
658 :
659 16 : case 't': /* include table(s) */
660 16 : simple_string_list_append(&table_include_patterns, optarg);
661 16 : dopt.include_everything = false;
662 16 : break;
663 :
664 8 : case 'T': /* exclude table(s) */
665 8 : simple_string_list_append(&table_exclude_patterns, optarg);
666 8 : break;
667 :
668 72 : case 'U':
669 72 : dopt.cparams.username = pg_strdup(optarg);
670 72 : break;
671 :
672 12 : case 'v': /* verbose */
673 12 : g_verbose = true;
674 12 : pg_logging_increase_verbosity();
675 12 : break;
676 :
677 2 : case 'w':
678 2 : dopt.cparams.promptPassword = TRI_NO;
679 2 : break;
680 :
681 0 : case 'W':
682 0 : dopt.cparams.promptPassword = TRI_YES;
683 0 : break;
684 :
685 4 : case 'x': /* skip ACL dump */
686 4 : dopt.aclsSkip = true;
687 4 : break;
688 :
689 26 : case 'Z': /* Compression */
690 26 : parse_compress_options(optarg, &compression_algorithm_str,
691 : &compression_detail);
692 26 : user_compression_defined = true;
693 26 : break;
694 :
695 258 : case 0:
696 : /* This covers the long options. */
697 258 : break;
698 :
699 4 : case 2: /* lock-wait-timeout */
700 4 : dopt.lockWaitTimeout = pg_strdup(optarg);
701 4 : break;
702 :
703 6 : case 3: /* SET ROLE */
704 6 : use_role = pg_strdup(optarg);
705 6 : break;
706 :
707 2 : case 4: /* exclude table(s) data */
708 2 : simple_string_list_append(&tabledata_exclude_patterns, optarg);
709 2 : break;
710 :
711 12 : case 5: /* section */
712 12 : set_dump_section(optarg, &dopt.dumpSections);
713 12 : break;
714 :
715 0 : case 6: /* snapshot */
716 0 : dumpsnapshot = pg_strdup(optarg);
717 0 : break;
718 :
719 296 : case 7: /* no-sync */
720 296 : dosync = false;
721 296 : break;
722 :
723 2 : case 8:
724 2 : have_extra_float_digits = true;
725 2 : if (!option_parse_int(optarg, "--extra-float-digits", -15, 3,
726 : &extra_float_digits))
727 2 : exit_nicely(1);
728 0 : break;
729 :
730 4 : case 9: /* inserts */
731 :
732 : /*
733 : * dump_inserts also stores --rows-per-insert, careful not to
734 : * overwrite that.
735 : */
736 4 : if (dopt.dump_inserts == 0)
737 4 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
738 4 : break;
739 :
740 4 : case 10: /* rows per insert */
741 4 : if (!option_parse_int(optarg, "--rows-per-insert", 1, INT_MAX,
742 : &dopt.dump_inserts))
743 2 : exit_nicely(1);
744 2 : break;
745 :
746 8 : case 11: /* include foreign data */
747 8 : simple_string_list_append(&foreign_servers_include_patterns,
748 : optarg);
749 8 : break;
750 :
751 2 : case 12: /* include table(s) and their children */
752 2 : simple_string_list_append(&table_include_patterns_and_children,
753 : optarg);
754 2 : dopt.include_everything = false;
755 2 : break;
756 :
757 2 : case 13: /* exclude table(s) and their children */
758 2 : simple_string_list_append(&table_exclude_patterns_and_children,
759 : optarg);
760 2 : break;
761 :
762 2 : case 14: /* exclude data of table(s) and children */
763 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
764 : optarg);
765 2 : break;
766 :
767 0 : case 15:
768 0 : if (!parse_sync_method(optarg, &sync_method))
769 0 : exit_nicely(1);
770 0 : break;
771 :
772 52 : case 16: /* read object filters from file */
773 52 : read_dump_filters(optarg, &dopt);
774 44 : break;
775 :
776 2 : case 17: /* exclude extension(s) */
777 2 : simple_string_list_append(&extension_exclude_patterns,
778 : optarg);
779 2 : break;
780 :
781 8 : case 18:
782 8 : statistics_only = true;
783 8 : break;
784 :
785 72 : case 19:
786 72 : no_data = true;
787 72 : break;
788 :
789 4 : case 20:
790 4 : no_schema = true;
791 4 : break;
792 :
793 16 : case 21:
794 16 : no_statistics = true;
795 16 : break;
796 :
797 176 : case 22:
798 176 : with_statistics = true;
799 176 : break;
800 :
801 52 : case 25:
802 52 : dopt.restrict_key = pg_strdup(optarg);
803 52 : break;
804 :
805 2 : default:
806 : /* getopt_long already emitted a complaint */
807 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
808 2 : exit_nicely(1);
809 : }
810 : }
811 :
812 : /*
813 : * Non-option argument specifies database name as long as it wasn't
814 : * already specified with -d / --dbname
815 : */
816 446 : if (optind < argc && dopt.cparams.dbname == NULL)
817 374 : dopt.cparams.dbname = argv[optind++];
818 :
819 : /* Complain if any arguments remain */
820 446 : if (optind < argc)
821 : {
822 2 : pg_log_error("too many command-line arguments (first is \"%s\")",
823 : argv[optind]);
824 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
825 2 : exit_nicely(1);
826 : }
827 :
828 : /* --column-inserts implies --inserts */
829 444 : if (dopt.column_inserts && dopt.dump_inserts == 0)
830 2 : dopt.dump_inserts = DUMP_DEFAULT_ROWS_PER_INSERT;
831 :
832 : /* reject conflicting "-only" options */
833 444 : if (data_only && schema_only)
834 2 : pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
835 442 : if (schema_only && statistics_only)
836 2 : pg_fatal("options -s/--schema-only and --statistics-only cannot be used together");
837 440 : if (data_only && statistics_only)
838 2 : pg_fatal("options -a/--data-only and --statistics-only cannot be used together");
839 :
840 : /* reject conflicting "-only" and "no-" options */
841 438 : if (data_only && no_data)
842 0 : pg_fatal("options -a/--data-only and --no-data cannot be used together");
843 438 : if (schema_only && no_schema)
844 0 : pg_fatal("options -s/--schema-only and --no-schema cannot be used together");
845 438 : if (statistics_only && no_statistics)
846 2 : pg_fatal("options --statistics-only and --no-statistics cannot be used together");
847 :
848 : /* reject conflicting "no-" options */
849 436 : if (with_statistics && no_statistics)
850 0 : pg_fatal("options --statistics and --no-statistics cannot be used together");
851 :
852 : /* reject conflicting "-only" options */
853 436 : if (data_only && with_statistics)
854 0 : pg_fatal("options %s and %s cannot be used together",
855 : "-a/--data-only", "--statistics");
856 436 : if (schema_only && with_statistics)
857 2 : pg_fatal("options %s and %s cannot be used together",
858 : "-s/--schema-only", "--statistics");
859 :
860 434 : if (schema_only && foreign_servers_include_patterns.head != NULL)
861 2 : pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
862 :
863 432 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
864 2 : pg_fatal("option --include-foreign-data is not supported with parallel backup");
865 :
866 430 : if (data_only && dopt.outputClean)
867 2 : pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
868 :
869 428 : if (dopt.if_exists && !dopt.outputClean)
870 2 : pg_fatal("option --if-exists requires option -c/--clean");
871 :
872 : /*
873 : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
874 : * "--schema-only --no-schema", will have already caused an error in one
875 : * of the checks above.
876 : */
877 426 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
878 852 : data_only) && !no_data;
879 426 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
880 852 : schema_only) && !no_schema;
881 426 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
882 852 : (statistics_only || with_statistics)) && !no_statistics;
883 :
884 :
885 : /*
886 : * --inserts are already implied above if --column-inserts or
887 : * --rows-per-insert were specified.
888 : */
889 426 : if (dopt.do_nothing && dopt.dump_inserts == 0)
890 2 : pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
891 :
892 : /* Identify archive format to emit */
893 424 : archiveFormat = parseArchiveFormat(format, &archiveMode);
894 :
895 : /* archiveFormat specific setup */
896 422 : if (archiveFormat == archNull)
897 : {
898 308 : plainText = 1;
899 :
900 : /*
901 : * If you don't provide a restrict key, one will be appointed for you.
902 : */
903 308 : if (!dopt.restrict_key)
904 256 : dopt.restrict_key = generate_restrict_key();
905 308 : if (!dopt.restrict_key)
906 0 : pg_fatal("could not generate restrict key");
907 308 : if (!valid_restrict_key(dopt.restrict_key))
908 0 : pg_fatal("invalid restrict key");
909 : }
910 114 : else if (dopt.restrict_key)
911 0 : pg_fatal("option --restrict-key can only be used with --format=plain");
912 :
913 : /*
914 : * Custom and directory formats are compressed by default with gzip when
915 : * available, not the others. If gzip is not available, no compression is
916 : * done by default.
917 : */
918 422 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
919 108 : !user_compression_defined)
920 : {
921 : #ifdef HAVE_LIBZ
922 96 : compression_algorithm_str = "gzip";
923 : #else
924 : compression_algorithm_str = "none";
925 : #endif
926 : }
927 :
928 : /*
929 : * Compression options
930 : */
931 422 : if (!parse_compress_algorithm(compression_algorithm_str,
932 : &compression_algorithm))
933 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
934 : compression_algorithm_str);
935 :
936 420 : parse_compress_specification(compression_algorithm, compression_detail,
937 : &compression_spec);
938 420 : error_detail = validate_compress_specification(&compression_spec);
939 420 : if (error_detail != NULL)
940 6 : pg_fatal("invalid compression specification: %s",
941 : error_detail);
942 :
943 414 : error_detail = supports_compression(compression_spec);
944 414 : if (error_detail != NULL)
945 0 : pg_fatal("%s", error_detail);
946 :
947 : /*
948 : * Disable support for zstd workers for now - these are based on
949 : * threading, and it's unclear how it interacts with parallel dumps on
950 : * platforms where that relies on threads too (e.g. Windows).
951 : */
952 414 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
953 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
954 : "workers");
955 :
956 : /*
957 : * If emitting an archive format, we always want to emit a DATABASE item,
958 : * in case --create is specified at pg_restore time.
959 : */
960 414 : if (!plainText)
961 114 : dopt.outputCreateDB = 1;
962 :
963 : /* Parallel backup only in the directory archive format so far */
964 414 : if (archiveFormat != archDirectory && numWorkers > 1)
965 2 : pg_fatal("parallel backup only supported by the directory format");
966 :
967 : /* Open the output file */
968 412 : fout = CreateArchive(filename, archiveFormat, compression_spec,
969 : dosync, archiveMode, setupDumpWorker, sync_method);
970 :
971 : /* Make dump options accessible right away */
972 410 : SetArchiveOptions(fout, &dopt, NULL);
973 :
974 : /* Register the cleanup hook */
975 410 : on_exit_close_archive(fout);
976 :
977 : /* Let the archiver know how noisy to be */
978 410 : fout->verbose = g_verbose;
979 :
980 :
981 : /*
982 : * We allow the server to be back to 9.2, and up to any minor release of
983 : * our own major version. (See also version check in pg_dumpall.c.)
984 : */
985 410 : fout->minRemoteVersion = 90200;
986 410 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
987 :
988 410 : fout->numWorkers = numWorkers;
989 :
990 : /*
991 : * Open the database using the Archiver, so it knows about it. Errors mean
992 : * death.
993 : */
994 410 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
995 406 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
996 :
997 : /*
998 : * On hot standbys, never try to dump unlogged table data, since it will
999 : * just throw an error.
1000 : */
1001 406 : if (fout->isStandby)
1002 8 : dopt.no_unlogged_table_data = true;
1003 :
1004 : /*
1005 : * Find the last built-in OID, if needed (prior to 8.1)
1006 : *
1007 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1008 : */
1009 406 : g_last_builtin_oid = FirstNormalObjectId - 1;
1010 :
1011 406 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1012 :
1013 : /* Expand schema selection patterns into OID lists */
1014 406 : if (schema_include_patterns.head != NULL)
1015 : {
1016 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
1017 : &schema_include_oids,
1018 : strict_names);
1019 24 : if (schema_include_oids.head == NULL)
1020 2 : pg_fatal("no matching schemas were found");
1021 : }
1022 392 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1023 : &schema_exclude_oids,
1024 : false);
1025 : /* non-matching exclusion patterns aren't an error */
1026 :
1027 : /* Expand table selection patterns into OID lists */
1028 392 : expand_table_name_patterns(fout, &table_include_patterns,
1029 : &table_include_oids,
1030 : strict_names, false);
1031 382 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1032 : &table_include_oids,
1033 : strict_names, true);
1034 382 : if ((table_include_patterns.head != NULL ||
1035 360 : table_include_patterns_and_children.head != NULL) &&
1036 26 : table_include_oids.head == NULL)
1037 4 : pg_fatal("no matching tables were found");
1038 :
1039 378 : expand_table_name_patterns(fout, &table_exclude_patterns,
1040 : &table_exclude_oids,
1041 : false, false);
1042 378 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1043 : &table_exclude_oids,
1044 : false, true);
1045 :
1046 378 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1047 : &tabledata_exclude_oids,
1048 : false, false);
1049 378 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1050 : &tabledata_exclude_oids,
1051 : false, true);
1052 :
1053 378 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1054 : &foreign_servers_include_oids);
1055 :
1056 : /* non-matching exclusion patterns aren't an error */
1057 :
1058 : /* Expand extension selection patterns into OID lists */
1059 376 : if (extension_include_patterns.head != NULL)
1060 : {
1061 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1062 : &extension_include_oids,
1063 : strict_names);
1064 10 : if (extension_include_oids.head == NULL)
1065 2 : pg_fatal("no matching extensions were found");
1066 : }
1067 374 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1068 : &extension_exclude_oids,
1069 : false);
1070 : /* non-matching exclusion patterns aren't an error */
1071 :
1072 : /*
1073 : * Dumping LOs is the default for dumps where an inclusion switch is not
1074 : * used (an "include everything" dump). -B can be used to exclude LOs
1075 : * from those dumps. -b can be used to include LOs even when an inclusion
1076 : * switch is used.
1077 : *
1078 : * -s means "schema only" and LOs are data, not schema, so we never
1079 : * include LOs when -s is used.
1080 : */
1081 374 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1082 244 : dopt.outputLOs = true;
1083 :
1084 : /*
1085 : * Collect role names so we can map object owner OIDs to names.
1086 : */
1087 374 : collectRoleNames(fout);
1088 :
1089 : /*
1090 : * Now scan the database and create DumpableObject structs for all the
1091 : * objects we intend to dump.
1092 : */
1093 374 : tblinfo = getSchemaData(fout, &numTables);
1094 :
1095 372 : if (dopt.dumpData)
1096 : {
1097 292 : getTableData(&dopt, tblinfo, numTables, 0);
1098 292 : buildMatViewRefreshDependencies(fout);
1099 292 : if (!dopt.dumpSchema)
1100 14 : getTableDataFKConstraints();
1101 : }
1102 :
1103 372 : if (!dopt.dumpData && dopt.sequence_data)
1104 64 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1105 :
1106 : /*
1107 : * For binary upgrade mode, dump pg_largeobject_metadata and the
1108 : * associated pg_shdepend rows. This is faster to restore than the
1109 : * equivalent set of large object commands. We can only do this for
1110 : * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1111 : * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1112 : */
1113 372 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1114 : {
1115 72 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1116 72 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1117 :
1118 72 : makeTableDataInfo(&dopt, lo_metadata);
1119 72 : makeTableDataInfo(&dopt, shdepend);
1120 :
1121 : /*
1122 : * Save pg_largeobject_metadata's dump ID for use as a dependency for
1123 : * pg_shdepend and any large object comments/seclabels.
1124 : */
1125 72 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1126 72 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1127 :
1128 : /*
1129 : * Only dump large object shdepend rows for this database.
1130 : */
1131 72 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1132 : "AND dbid = (SELECT oid FROM pg_database "
1133 : " WHERE datname = current_database())";
1134 :
1135 : /*
1136 : * If upgrading from v16 or newer, only dump large objects with
1137 : * comments/seclabels. For these upgrades, pg_upgrade can copy/link
1138 : * pg_largeobject_metadata's files (which is usually faster) but we
1139 : * still need to dump LOs with comments/seclabels here so that the
1140 : * subsequent COMMENT and SECURITY LABEL commands work. pg_upgrade
1141 : * can't copy/link the files from older versions because aclitem
1142 : * (needed by pg_largeobject_metadata.lomacl) changed its storage
1143 : * format in v16.
1144 : */
1145 72 : if (fout->remoteVersion >= 160000)
1146 72 : lo_metadata->dataObj->filtercond = "WHERE oid IN "
1147 : "(SELECT objoid FROM pg_description "
1148 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
1149 : "UNION SELECT objoid FROM pg_seclabel "
1150 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) ")";
1151 : }
1152 :
1153 : /*
1154 : * In binary-upgrade mode, we do not have to worry about the actual LO
1155 : * data or the associated metadata that resides in the pg_largeobject and
1156 : * pg_largeobject_metadata tables, respectively.
1157 : *
1158 : * However, we do need to collect LO information as there may be comments
1159 : * or other information on LOs that we do need to dump out.
1160 : */
1161 372 : if (dopt.outputLOs || dopt.binary_upgrade)
1162 316 : getLOs(fout);
1163 :
1164 : /*
1165 : * Collect dependency data to assist in ordering the objects.
1166 : */
1167 372 : getDependencies(fout);
1168 :
1169 : /*
1170 : * Collect ACLs, comments, and security labels, if wanted.
1171 : */
1172 372 : if (!dopt.aclsSkip)
1173 368 : getAdditionalACLs(fout);
1174 372 : if (!dopt.no_comments)
1175 372 : collectComments(fout);
1176 372 : if (!dopt.no_security_labels)
1177 372 : collectSecLabels(fout);
1178 :
1179 : /* For binary upgrade mode, collect required pg_class information. */
1180 372 : if (dopt.binary_upgrade)
1181 72 : collectBinaryUpgradeClassOids(fout);
1182 :
1183 : /* Collect sequence information. */
1184 372 : collectSequences(fout);
1185 :
1186 : /* Lastly, create dummy objects to represent the section boundaries */
1187 372 : boundaryObjs = createBoundaryObjects();
1188 :
1189 : /* Get pointers to all the known DumpableObjects */
1190 372 : getDumpableObjects(&dobjs, &numObjs);
1191 :
1192 : /*
1193 : * Add dummy dependencies to enforce the dump section ordering.
1194 : */
1195 372 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1196 :
1197 : /*
1198 : * Sort the objects into a safe dump order (no forward references).
1199 : *
1200 : * We rely on dependency information to help us determine a safe order, so
1201 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1202 : * ensure that logically identical schemas will dump identically.
1203 : */
1204 372 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1205 :
1206 372 : sortDumpableObjects(dobjs, numObjs,
1207 372 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1208 :
1209 : /*
1210 : * Create archive TOC entries for all the objects to be dumped, in a safe
1211 : * order.
1212 : */
1213 :
1214 : /*
1215 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1216 : */
1217 372 : dumpEncoding(fout);
1218 372 : dumpStdStrings(fout);
1219 372 : dumpSearchPath(fout);
1220 :
1221 : /* The database items are always next, unless we don't want them at all */
1222 372 : if (dopt.outputCreateDB)
1223 170 : dumpDatabase(fout);
1224 :
1225 : /* Now the rearrangeable objects. */
1226 1379832 : for (i = 0; i < numObjs; i++)
1227 1379460 : dumpDumpableObject(fout, dobjs[i]);
1228 :
1229 : /*
1230 : * Set up options info to ensure we dump what we want.
1231 : */
1232 372 : ropt = NewRestoreOptions();
1233 372 : ropt->filename = filename;
1234 :
1235 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1236 372 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1237 372 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1238 372 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1239 372 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1240 372 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1241 372 : ropt->dropSchema = dopt.outputClean;
1242 372 : ropt->dumpData = dopt.dumpData;
1243 372 : ropt->dumpSchema = dopt.dumpSchema;
1244 372 : ropt->dumpStatistics = dopt.dumpStatistics;
1245 372 : ropt->if_exists = dopt.if_exists;
1246 372 : ropt->column_inserts = dopt.column_inserts;
1247 372 : ropt->dumpSections = dopt.dumpSections;
1248 372 : ropt->aclsSkip = dopt.aclsSkip;
1249 372 : ropt->superuser = dopt.outputSuperuser;
1250 372 : ropt->createDB = dopt.outputCreateDB;
1251 372 : ropt->noOwner = dopt.outputNoOwner;
1252 372 : ropt->noTableAm = dopt.outputNoTableAm;
1253 372 : ropt->noTablespace = dopt.outputNoTablespaces;
1254 372 : ropt->disable_triggers = dopt.disable_triggers;
1255 372 : ropt->use_setsessauth = dopt.use_setsessauth;
1256 372 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1257 372 : ropt->dump_inserts = dopt.dump_inserts;
1258 372 : ropt->no_comments = dopt.no_comments;
1259 372 : ropt->no_policies = dopt.no_policies;
1260 372 : ropt->no_publications = dopt.no_publications;
1261 372 : ropt->no_security_labels = dopt.no_security_labels;
1262 372 : ropt->no_subscriptions = dopt.no_subscriptions;
1263 372 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1264 372 : ropt->include_everything = dopt.include_everything;
1265 372 : ropt->enable_row_security = dopt.enable_row_security;
1266 372 : ropt->sequence_data = dopt.sequence_data;
1267 372 : ropt->binary_upgrade = dopt.binary_upgrade;
1268 372 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1269 :
1270 372 : ropt->compression_spec = compression_spec;
1271 :
1272 372 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1273 :
1274 372 : SetArchiveOptions(fout, &dopt, ropt);
1275 :
1276 : /* Mark which entries should be output */
1277 372 : ProcessArchiveRestoreOptions(fout);
1278 :
1279 : /*
1280 : * The archive's TOC entries are now marked as to which ones will actually
1281 : * be output, so we can set up their dependency lists properly. This isn't
1282 : * necessary for plain-text output, though.
1283 : */
1284 372 : if (!plainText)
1285 112 : BuildArchiveDependencies(fout);
1286 :
1287 : /*
1288 : * And finally we can do the actual output.
1289 : *
1290 : * Note: for non-plain-text output formats, the output file is written
1291 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1292 : * right now.
1293 : */
1294 372 : if (plainText)
1295 260 : RestoreArchive(fout);
1296 :
1297 370 : CloseArchive(fout);
1298 :
1299 370 : exit_nicely(0);
1300 : }
1301 :
1302 :
1303 : static void
1304 2 : help(const char *progname)
1305 : {
1306 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1307 2 : printf(_("Usage:\n"));
1308 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1309 :
1310 2 : printf(_("\nGeneral options:\n"));
1311 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1312 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1313 : " plain text (default))\n"));
1314 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1315 2 : printf(_(" -v, --verbose verbose mode\n"));
1316 2 : printf(_(" -V, --version output version information, then exit\n"));
1317 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1318 : " compress as specified\n"));
1319 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1320 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1321 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1322 2 : printf(_(" -?, --help show this help, then exit\n"));
1323 :
1324 2 : printf(_("\nOptions controlling the output content:\n"));
1325 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1326 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1327 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1328 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1329 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1330 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1331 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1332 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1333 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1334 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1335 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1336 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1337 : " plain-text format\n"));
1338 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1339 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1340 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1341 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1342 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1343 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1344 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1345 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1346 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1347 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1348 : " access to)\n"));
1349 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1350 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1351 : " do NOT dump the specified table(s), including\n"
1352 : " child and partition tables\n"));
1353 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1354 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1355 : " do NOT dump data for the specified table(s),\n"
1356 : " including child and partition tables\n"));
1357 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1358 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1359 : " based on expressions in FILENAME\n"));
1360 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1361 2 : printf(_(" --include-foreign-data=PATTERN\n"
1362 : " include data of foreign tables on foreign\n"
1363 : " servers matching PATTERN\n"));
1364 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1365 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1366 2 : printf(_(" --no-comments do not dump comment commands\n"));
1367 2 : printf(_(" --no-data do not dump data\n"));
1368 2 : printf(_(" --no-policies do not dump row security policies\n"));
1369 2 : printf(_(" --no-publications do not dump publications\n"));
1370 2 : printf(_(" --no-schema do not dump schema\n"));
1371 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1372 2 : printf(_(" --no-statistics do not dump statistics\n"));
1373 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1374 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1375 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1376 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1377 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1378 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1379 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1380 2 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1381 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1382 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1383 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1384 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1385 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1386 2 : printf(_(" --statistics dump the statistics\n"));
1387 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1388 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1389 : " match at least one entity each\n"));
1390 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1391 : " child and partition tables\n"));
1392 2 : printf(_(" --use-set-session-authorization\n"
1393 : " use SET SESSION AUTHORIZATION commands instead of\n"
1394 : " ALTER OWNER commands to set ownership\n"));
1395 :
1396 2 : printf(_("\nConnection options:\n"));
1397 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1398 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1399 2 : printf(_(" -p, --port=PORT database server port number\n"));
1400 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1401 2 : printf(_(" -w, --no-password never prompt for password\n"));
1402 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1403 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1404 :
1405 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1406 : "variable value is used.\n\n"));
1407 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1408 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1409 2 : }
1410 :
1411 : static void
1412 438 : setup_connection(Archive *AH, const char *dumpencoding,
1413 : const char *dumpsnapshot, char *use_role)
1414 : {
1415 438 : DumpOptions *dopt = AH->dopt;
1416 438 : PGconn *conn = GetConnection(AH);
1417 : const char *std_strings;
1418 :
1419 438 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1420 :
1421 : /*
1422 : * Set the client encoding if requested.
1423 : */
1424 438 : if (dumpencoding)
1425 : {
1426 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1427 0 : pg_fatal("invalid client encoding \"%s\" specified",
1428 : dumpencoding);
1429 : }
1430 :
1431 : /*
1432 : * Get the active encoding and the standard_conforming_strings setting, so
1433 : * we know how to escape strings.
1434 : */
1435 438 : AH->encoding = PQclientEncoding(conn);
1436 438 : setFmtEncoding(AH->encoding);
1437 :
1438 438 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1439 438 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1440 :
1441 : /*
1442 : * Set the role if requested. In a parallel dump worker, we'll be passed
1443 : * use_role == NULL, but AH->use_role is already set (if user specified it
1444 : * originally) and we should use that.
1445 : */
1446 438 : if (!use_role && AH->use_role)
1447 4 : use_role = AH->use_role;
1448 :
1449 : /* Set the role if requested */
1450 438 : if (use_role)
1451 : {
1452 10 : PQExpBuffer query = createPQExpBuffer();
1453 :
1454 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1455 10 : ExecuteSqlStatement(AH, query->data);
1456 10 : destroyPQExpBuffer(query);
1457 :
1458 : /* save it for possible later use by parallel workers */
1459 10 : if (!AH->use_role)
1460 6 : AH->use_role = pg_strdup(use_role);
1461 : }
1462 :
1463 : /* Set the datestyle to ISO to ensure the dump's portability */
1464 438 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1465 :
1466 : /* Likewise, avoid using sql_standard intervalstyle */
1467 438 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1468 :
1469 : /*
1470 : * Use an explicitly specified extra_float_digits if it has been provided.
1471 : * Otherwise, set extra_float_digits so that we can dump float data
1472 : * exactly (given correctly implemented float I/O code, anyway).
1473 : */
1474 438 : if (have_extra_float_digits)
1475 : {
1476 0 : PQExpBuffer q = createPQExpBuffer();
1477 :
1478 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1479 : extra_float_digits);
1480 0 : ExecuteSqlStatement(AH, q->data);
1481 0 : destroyPQExpBuffer(q);
1482 : }
1483 : else
1484 438 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1485 :
1486 : /*
1487 : * Disable synchronized scanning, to prevent unpredictable changes in row
1488 : * ordering across a dump and reload.
1489 : */
1490 438 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1491 :
1492 : /*
1493 : * Disable timeouts if supported.
1494 : */
1495 438 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1496 438 : if (AH->remoteVersion >= 90300)
1497 438 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1498 438 : if (AH->remoteVersion >= 90600)
1499 438 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1500 438 : if (AH->remoteVersion >= 170000)
1501 438 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1502 :
1503 : /*
1504 : * Quote all identifiers, if requested.
1505 : */
1506 438 : if (quote_all_identifiers)
1507 68 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1508 :
1509 : /*
1510 : * Adjust row-security mode, if supported.
1511 : */
1512 438 : if (AH->remoteVersion >= 90500)
1513 : {
1514 438 : if (dopt->enable_row_security)
1515 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1516 : else
1517 438 : ExecuteSqlStatement(AH, "SET row_security = off");
1518 : }
1519 :
1520 : /*
1521 : * For security reasons, we restrict the expansion of non-system views and
1522 : * access to foreign tables during the pg_dump process. This restriction
1523 : * is adjusted when dumping foreign table data.
1524 : */
1525 438 : set_restrict_relation_kind(AH, "view, foreign-table");
1526 :
1527 : /*
1528 : * Initialize prepared-query state to "nothing prepared". We do this here
1529 : * so that a parallel dump worker will have its own state.
1530 : */
1531 438 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1532 :
1533 : /*
1534 : * Start transaction-snapshot mode transaction to dump consistent data.
1535 : */
1536 438 : ExecuteSqlStatement(AH, "BEGIN");
1537 :
1538 : /*
1539 : * To support the combination of serializable_deferrable with the jobs
1540 : * option we use REPEATABLE READ for the worker connections that are
1541 : * passed a snapshot. As long as the snapshot is acquired in a
1542 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1543 : * REPEATABLE READ transaction provides the appropriate integrity
1544 : * guarantees. This is a kluge, but safe for back-patching.
1545 : */
1546 438 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1547 0 : ExecuteSqlStatement(AH,
1548 : "SET TRANSACTION ISOLATION LEVEL "
1549 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1550 : else
1551 438 : ExecuteSqlStatement(AH,
1552 : "SET TRANSACTION ISOLATION LEVEL "
1553 : "REPEATABLE READ, READ ONLY");
1554 :
1555 : /*
1556 : * If user specified a snapshot to use, select that. In a parallel dump
1557 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1558 : * is already set (if the server can handle it) and we should use that.
1559 : */
1560 438 : if (dumpsnapshot)
1561 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1562 :
1563 438 : if (AH->sync_snapshot_id)
1564 : {
1565 32 : PQExpBuffer query = createPQExpBuffer();
1566 :
1567 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1568 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1569 32 : ExecuteSqlStatement(AH, query->data);
1570 32 : destroyPQExpBuffer(query);
1571 : }
1572 406 : else if (AH->numWorkers > 1)
1573 : {
1574 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1575 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1576 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1577 : }
1578 438 : }
1579 :
1580 : /* Set up connection for a parallel worker process */
1581 : static void
1582 32 : setupDumpWorker(Archive *AH)
1583 : {
1584 : /*
1585 : * We want to re-select all the same values the leader connection is
1586 : * using. We'll have inherited directly-usable values in
1587 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1588 : * inherited encoding value back to a string to pass to setup_connection.
1589 : */
1590 32 : setup_connection(AH,
1591 : pg_encoding_to_char(AH->encoding),
1592 : NULL,
1593 : NULL);
1594 32 : }
1595 :
1596 : static char *
1597 16 : get_synchronized_snapshot(Archive *fout)
1598 : {
1599 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1600 : char *result;
1601 : PGresult *res;
1602 :
1603 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1604 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1605 16 : PQclear(res);
1606 :
1607 16 : return result;
1608 : }
1609 :
1610 : static ArchiveFormat
1611 424 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1612 : {
1613 : ArchiveFormat archiveFormat;
1614 :
1615 424 : *mode = archModeWrite;
1616 :
1617 424 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1618 : {
1619 : /* This is used by pg_dumpall, and is not documented */
1620 86 : archiveFormat = archNull;
1621 86 : *mode = archModeAppend;
1622 : }
1623 338 : else if (pg_strcasecmp(format, "c") == 0)
1624 0 : archiveFormat = archCustom;
1625 338 : else if (pg_strcasecmp(format, "custom") == 0)
1626 88 : archiveFormat = archCustom;
1627 250 : else if (pg_strcasecmp(format, "d") == 0)
1628 0 : archiveFormat = archDirectory;
1629 250 : else if (pg_strcasecmp(format, "directory") == 0)
1630 20 : archiveFormat = archDirectory;
1631 230 : else if (pg_strcasecmp(format, "p") == 0)
1632 216 : archiveFormat = archNull;
1633 14 : else if (pg_strcasecmp(format, "plain") == 0)
1634 6 : archiveFormat = archNull;
1635 8 : else if (pg_strcasecmp(format, "t") == 0)
1636 0 : archiveFormat = archTar;
1637 8 : else if (pg_strcasecmp(format, "tar") == 0)
1638 6 : archiveFormat = archTar;
1639 : else
1640 2 : pg_fatal("invalid output format \"%s\" specified", format);
1641 422 : return archiveFormat;
1642 : }
1643 :
1644 : /*
1645 : * Find the OIDs of all schemas matching the given list of patterns,
1646 : * and append them to the given OID list.
1647 : */
1648 : static void
1649 428 : expand_schema_name_patterns(Archive *fout,
1650 : SimpleStringList *patterns,
1651 : SimpleOidList *oids,
1652 : bool strict_names)
1653 : {
1654 : PQExpBuffer query;
1655 : PGresult *res;
1656 : SimpleStringListCell *cell;
1657 : int i;
1658 :
1659 428 : if (patterns->head == NULL)
1660 386 : return; /* nothing to do */
1661 :
1662 42 : query = createPQExpBuffer();
1663 :
1664 : /*
1665 : * The loop below runs multiple SELECTs might sometimes result in
1666 : * duplicate entries in the OID list, but we don't care.
1667 : */
1668 :
1669 72 : for (cell = patterns->head; cell; cell = cell->next)
1670 : {
1671 : PQExpBufferData dbbuf;
1672 : int dotcnt;
1673 :
1674 42 : appendPQExpBufferStr(query,
1675 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1676 42 : initPQExpBuffer(&dbbuf);
1677 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1678 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1679 : &dotcnt);
1680 42 : if (dotcnt > 1)
1681 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1682 : cell->val);
1683 38 : else if (dotcnt == 1)
1684 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1685 32 : termPQExpBuffer(&dbbuf);
1686 :
1687 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1688 32 : if (strict_names && PQntuples(res) == 0)
1689 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1690 :
1691 58 : for (i = 0; i < PQntuples(res); i++)
1692 : {
1693 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1694 : }
1695 :
1696 30 : PQclear(res);
1697 30 : resetPQExpBuffer(query);
1698 : }
1699 :
1700 30 : destroyPQExpBuffer(query);
1701 : }
1702 :
1703 : /*
1704 : * Find the OIDs of all extensions matching the given list of patterns,
1705 : * and append them to the given OID list.
1706 : */
1707 : static void
1708 384 : expand_extension_name_patterns(Archive *fout,
1709 : SimpleStringList *patterns,
1710 : SimpleOidList *oids,
1711 : bool strict_names)
1712 : {
1713 : PQExpBuffer query;
1714 : PGresult *res;
1715 : SimpleStringListCell *cell;
1716 : int i;
1717 :
1718 384 : if (patterns->head == NULL)
1719 370 : return; /* nothing to do */
1720 :
1721 14 : query = createPQExpBuffer();
1722 :
1723 : /*
1724 : * The loop below runs multiple SELECTs might sometimes result in
1725 : * duplicate entries in the OID list, but we don't care.
1726 : */
1727 28 : for (cell = patterns->head; cell; cell = cell->next)
1728 : {
1729 : int dotcnt;
1730 :
1731 14 : appendPQExpBufferStr(query,
1732 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1733 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1734 : false, NULL, "e.extname", NULL, NULL, NULL,
1735 : &dotcnt);
1736 14 : if (dotcnt > 0)
1737 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1738 : cell->val);
1739 :
1740 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1741 14 : if (strict_names && PQntuples(res) == 0)
1742 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1743 :
1744 26 : for (i = 0; i < PQntuples(res); i++)
1745 : {
1746 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1747 : }
1748 :
1749 14 : PQclear(res);
1750 14 : resetPQExpBuffer(query);
1751 : }
1752 :
1753 14 : destroyPQExpBuffer(query);
1754 : }
1755 :
1756 : /*
1757 : * Find the OIDs of all foreign servers matching the given list of patterns,
1758 : * and append them to the given OID list.
1759 : */
1760 : static void
1761 378 : expand_foreign_server_name_patterns(Archive *fout,
1762 : SimpleStringList *patterns,
1763 : SimpleOidList *oids)
1764 : {
1765 : PQExpBuffer query;
1766 : PGresult *res;
1767 : SimpleStringListCell *cell;
1768 : int i;
1769 :
1770 378 : if (patterns->head == NULL)
1771 372 : return; /* nothing to do */
1772 :
1773 6 : query = createPQExpBuffer();
1774 :
1775 : /*
1776 : * The loop below runs multiple SELECTs might sometimes result in
1777 : * duplicate entries in the OID list, but we don't care.
1778 : */
1779 :
1780 10 : for (cell = patterns->head; cell; cell = cell->next)
1781 : {
1782 : int dotcnt;
1783 :
1784 6 : appendPQExpBufferStr(query,
1785 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1786 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1787 : false, NULL, "s.srvname", NULL, NULL, NULL,
1788 : &dotcnt);
1789 6 : if (dotcnt > 0)
1790 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1791 : cell->val);
1792 :
1793 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1794 6 : if (PQntuples(res) == 0)
1795 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1796 :
1797 8 : for (i = 0; i < PQntuples(res); i++)
1798 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1799 :
1800 4 : PQclear(res);
1801 4 : resetPQExpBuffer(query);
1802 : }
1803 :
1804 4 : destroyPQExpBuffer(query);
1805 : }
1806 :
1807 : /*
1808 : * Find the OIDs of all tables matching the given list of patterns,
1809 : * and append them to the given OID list. See also expand_dbname_patterns()
1810 : * in pg_dumpall.c
1811 : */
1812 : static void
1813 2286 : expand_table_name_patterns(Archive *fout,
1814 : SimpleStringList *patterns, SimpleOidList *oids,
1815 : bool strict_names, bool with_child_tables)
1816 : {
1817 : PQExpBuffer query;
1818 : PGresult *res;
1819 : SimpleStringListCell *cell;
1820 : int i;
1821 :
1822 2286 : if (patterns->head == NULL)
1823 2228 : return; /* nothing to do */
1824 :
1825 58 : query = createPQExpBuffer();
1826 :
1827 : /*
1828 : * this might sometimes result in duplicate entries in the OID list, but
1829 : * we don't care.
1830 : */
1831 :
1832 118 : for (cell = patterns->head; cell; cell = cell->next)
1833 : {
1834 : PQExpBufferData dbbuf;
1835 : int dotcnt;
1836 :
1837 : /*
1838 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1839 : * would be unnecessary given a pg_table_is_visible() variant taking a
1840 : * search_path argument.
1841 : *
1842 : * For with_child_tables, we start with the basic query's results and
1843 : * recursively search the inheritance tree to add child tables.
1844 : */
1845 70 : if (with_child_tables)
1846 : {
1847 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1848 : }
1849 :
1850 70 : appendPQExpBuffer(query,
1851 : "SELECT c.oid"
1852 : "\nFROM pg_catalog.pg_class c"
1853 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1854 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1855 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1856 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1857 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1858 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1859 : RELKIND_PARTITIONED_TABLE);
1860 70 : initPQExpBuffer(&dbbuf);
1861 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1862 : false, "n.nspname", "c.relname", NULL,
1863 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1864 : &dotcnt);
1865 70 : if (dotcnt > 2)
1866 2 : pg_fatal("improper relation name (too many dotted names): %s",
1867 : cell->val);
1868 68 : else if (dotcnt == 2)
1869 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1870 64 : termPQExpBuffer(&dbbuf);
1871 :
1872 64 : if (with_child_tables)
1873 : {
1874 12 : appendPQExpBufferStr(query, "UNION"
1875 : "\nSELECT i.inhrelid"
1876 : "\nFROM partition_tree p"
1877 : "\n JOIN pg_catalog.pg_inherits i"
1878 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1879 : "\n)"
1880 : "\nSELECT relid FROM partition_tree");
1881 : }
1882 :
1883 64 : ExecuteSqlStatement(fout, "RESET search_path");
1884 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1885 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1886 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1887 64 : if (strict_names && PQntuples(res) == 0)
1888 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1889 :
1890 148 : for (i = 0; i < PQntuples(res); i++)
1891 : {
1892 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1893 : }
1894 :
1895 60 : PQclear(res);
1896 60 : resetPQExpBuffer(query);
1897 : }
1898 :
1899 48 : destroyPQExpBuffer(query);
1900 : }
1901 :
1902 : /*
1903 : * Verifies that the connected database name matches the given database name,
1904 : * and if not, dies with an error about the given pattern.
1905 : *
1906 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1907 : */
1908 : static void
1909 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1910 : {
1911 : const char *db;
1912 :
1913 10 : db = PQdb(conn);
1914 10 : if (db == NULL)
1915 0 : pg_fatal("You are currently not connected to a database.");
1916 :
1917 10 : if (strcmp(db, dbname) != 0)
1918 10 : pg_fatal("cross-database references are not implemented: %s",
1919 : pattern);
1920 0 : }
1921 :
1922 : /*
1923 : * checkExtensionMembership
1924 : * Determine whether object is an extension member, and if so,
1925 : * record an appropriate dependency and set the object's dump flag.
1926 : *
1927 : * It's important to call this for each object that could be an extension
1928 : * member. Generally, we integrate this with determining the object's
1929 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1930 : *
1931 : * Returns true if object is an extension member, else false.
1932 : */
1933 : static bool
1934 1173358 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1935 : {
1936 1173358 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1937 :
1938 1173358 : if (ext == NULL)
1939 1171760 : return false;
1940 :
1941 1598 : dobj->ext_member = true;
1942 :
1943 : /* Record dependency so that getDependencies needn't deal with that */
1944 1598 : addObjectDependency(dobj, ext->dobj.dumpId);
1945 :
1946 : /*
1947 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1948 : * dumped. (Any initial ACLs will be removed later, using data from
1949 : * pg_init_privs, so that we'll dump only the delta from the extension's
1950 : * initial setup.)
1951 : *
1952 : * Prior to 9.6, we do not include any extension member components.
1953 : *
1954 : * In binary upgrades, we still dump all components of the members
1955 : * individually, since the idea is to exactly reproduce the database
1956 : * contents rather than replace the extension contents with something
1957 : * different.
1958 : *
1959 : * Note: it might be interesting someday to implement storage and delta
1960 : * dumping of extension members' RLS policies and/or security labels.
1961 : * However there is a pitfall for RLS policies: trying to dump them
1962 : * requires getting a lock on their tables, and the calling user might not
1963 : * have privileges for that. We need no lock to examine a table's ACLs,
1964 : * so the current feature doesn't have a problem of that sort.
1965 : */
1966 1598 : if (fout->dopt->binary_upgrade)
1967 338 : dobj->dump = ext->dobj.dump;
1968 : else
1969 : {
1970 1260 : if (fout->remoteVersion < 90600)
1971 0 : dobj->dump = DUMP_COMPONENT_NONE;
1972 : else
1973 1260 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1974 : }
1975 :
1976 1598 : return true;
1977 : }
1978 :
1979 : /*
1980 : * selectDumpableNamespace: policy-setting subroutine
1981 : * Mark a namespace as to be dumped or not
1982 : */
1983 : static void
1984 2852 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1985 : {
1986 : /*
1987 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
1988 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
1989 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
1990 : */
1991 2852 : nsinfo->create = true;
1992 :
1993 : /*
1994 : * If specific tables are being dumped, do not dump any complete
1995 : * namespaces. If specific namespaces are being dumped, dump just those
1996 : * namespaces. Otherwise, dump all non-system namespaces.
1997 : */
1998 2852 : if (table_include_oids.head != NULL)
1999 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2000 2752 : else if (schema_include_oids.head != NULL)
2001 374 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2002 374 : simple_oid_list_member(&schema_include_oids,
2003 : nsinfo->dobj.catId.oid) ?
2004 374 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2005 2378 : else if (fout->remoteVersion >= 90600 &&
2006 2378 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2007 : {
2008 : /*
2009 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2010 : * they are interesting (and not the original ACLs which were set at
2011 : * initdb time, see pg_init_privs).
2012 : */
2013 330 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2014 : }
2015 2048 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2016 1006 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2017 : {
2018 : /* Other system schemas don't get dumped */
2019 1372 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2020 : }
2021 676 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2022 : {
2023 : /*
2024 : * The public schema is a strange beast that sits in a sort of
2025 : * no-mans-land between being a system object and a user object.
2026 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2027 : * a comment and an indication of ownership. If the owner is the
2028 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2029 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2030 : */
2031 322 : nsinfo->create = false;
2032 322 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2033 322 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2034 238 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2035 322 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2036 :
2037 : /*
2038 : * Also, make like it has a comment even if it doesn't; this is so
2039 : * that we'll emit a command to drop the comment, if appropriate.
2040 : * (Without this, we'd not call dumpCommentExtended for it.)
2041 : */
2042 322 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2043 : }
2044 : else
2045 354 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2046 :
2047 : /*
2048 : * In any case, a namespace can be excluded by an exclusion switch
2049 : */
2050 3880 : if (nsinfo->dobj.dump_contains &&
2051 1028 : simple_oid_list_member(&schema_exclude_oids,
2052 : nsinfo->dobj.catId.oid))
2053 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2054 :
2055 : /*
2056 : * If the schema belongs to an extension, allow extension membership to
2057 : * override the dump decision for the schema itself. However, this does
2058 : * not change dump_contains, so this won't change what we do with objects
2059 : * within the schema. (If they belong to the extension, they'll get
2060 : * suppressed by it, otherwise not.)
2061 : */
2062 2852 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2063 2852 : }
2064 :
2065 : /*
2066 : * selectDumpableTable: policy-setting subroutine
2067 : * Mark a table as to be dumped or not
2068 : */
2069 : static void
2070 98150 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2071 : {
2072 98150 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2073 450 : return; /* extension membership overrides all else */
2074 :
2075 : /*
2076 : * If specific tables are being dumped, dump just those tables; else, dump
2077 : * according to the parent namespace's dump flag.
2078 : */
2079 97700 : if (table_include_oids.head != NULL)
2080 10364 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2081 : tbinfo->dobj.catId.oid) ?
2082 5182 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2083 : else
2084 92518 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2085 :
2086 : /*
2087 : * In any case, a table can be excluded by an exclusion switch
2088 : */
2089 159810 : if (tbinfo->dobj.dump &&
2090 62110 : simple_oid_list_member(&table_exclude_oids,
2091 : tbinfo->dobj.catId.oid))
2092 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2093 : }
2094 :
2095 : /*
2096 : * selectDumpableType: policy-setting subroutine
2097 : * Mark a type as to be dumped or not
2098 : *
2099 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2100 : * special type code to facilitate sorting into the desired order. (We don't
2101 : * want to consider those to be ordinary types because that would bring tables
2102 : * up into the datatype part of the dump order.) We still set the object's
2103 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2104 : * need it so that casts involving such types will be dumped correctly -- see
2105 : * dumpCast. This means the flag should be set the same as for the underlying
2106 : * object (the table or base type).
2107 : */
2108 : static void
2109 269552 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2110 : {
2111 : /* skip complex types, except for standalone composite types */
2112 269552 : if (OidIsValid(tyinfo->typrelid) &&
2113 96700 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2114 : {
2115 96336 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2116 :
2117 96336 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2118 96336 : if (tytable != NULL)
2119 96336 : tyinfo->dobj.dump = tytable->dobj.dump;
2120 : else
2121 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2122 96336 : return;
2123 : }
2124 :
2125 : /* skip auto-generated array and multirange types */
2126 173216 : if (tyinfo->isArray || tyinfo->isMultirange)
2127 : {
2128 131826 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2129 :
2130 : /*
2131 : * Fall through to set the dump flag; we assume that the subsequent
2132 : * rules will do the same thing as they would for the array's base
2133 : * type or multirange's range type. (We cannot reliably look up the
2134 : * base type here, since getTypes may not have processed it yet.)
2135 : */
2136 : }
2137 :
2138 173216 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2139 300 : return; /* extension membership overrides all else */
2140 :
2141 : /* Dump based on if the contents of the namespace are being dumped */
2142 172916 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2143 : }
2144 :
2145 : /*
2146 : * selectDumpableDefaultACL: policy-setting subroutine
2147 : * Mark a default ACL as to be dumped or not
2148 : *
2149 : * For per-schema default ACLs, dump if the schema is to be dumped.
2150 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2151 : * and aclsSkip are checked separately.
2152 : */
2153 : static void
2154 388 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2155 : {
2156 : /* Default ACLs can't be extension members */
2157 :
2158 388 : if (dinfo->dobj.namespace)
2159 : /* default ACLs are considered part of the namespace */
2160 180 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2161 : else
2162 208 : dinfo->dobj.dump = dopt->include_everything ?
2163 208 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2164 388 : }
2165 :
2166 : /*
2167 : * selectDumpableCast: policy-setting subroutine
2168 : * Mark a cast as to be dumped or not
2169 : *
2170 : * Casts do not belong to any particular namespace (since they haven't got
2171 : * names), nor do they have identifiable owners. To distinguish user-defined
2172 : * casts from built-in ones, we must resort to checking whether the cast's
2173 : * OID is in the range reserved for initdb.
2174 : */
2175 : static void
2176 87966 : selectDumpableCast(CastInfo *cast, Archive *fout)
2177 : {
2178 87966 : if (checkExtensionMembership(&cast->dobj, fout))
2179 0 : return; /* extension membership overrides all else */
2180 :
2181 : /*
2182 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2183 : * support ACLs currently.
2184 : */
2185 87966 : if (cast->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2186 87792 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2187 : else
2188 174 : cast->dobj.dump = fout->dopt->include_everything ?
2189 174 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2190 : }
2191 :
2192 : /*
2193 : * selectDumpableProcLang: policy-setting subroutine
2194 : * Mark a procedural language as to be dumped or not
2195 : *
2196 : * Procedural languages do not belong to any particular namespace. To
2197 : * identify built-in languages, we must resort to checking whether the
2198 : * language's OID is in the range reserved for initdb.
2199 : */
2200 : static void
2201 462 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2202 : {
2203 462 : if (checkExtensionMembership(&plang->dobj, fout))
2204 372 : return; /* extension membership overrides all else */
2205 :
2206 : /*
2207 : * Only include procedural languages when we are dumping everything.
2208 : *
2209 : * For from-initdb procedural languages, only include ACLs, as we do for
2210 : * the pg_catalog namespace. We need this because procedural languages do
2211 : * not live in any namespace.
2212 : */
2213 90 : if (!fout->dopt->include_everything)
2214 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2215 : else
2216 : {
2217 74 : if (plang->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2218 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2219 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2220 : else
2221 74 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2222 : }
2223 : }
2224 :
2225 : /*
2226 : * selectDumpableAccessMethod: policy-setting subroutine
2227 : * Mark an access method as to be dumped or not
2228 : *
2229 : * Access methods do not belong to any particular namespace. To identify
2230 : * built-in access methods, we must resort to checking whether the
2231 : * method's OID is in the range reserved for initdb.
2232 : */
2233 : static void
2234 2848 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2235 : {
2236 : /* see getAccessMethods() comment about v9.6. */
2237 2848 : if (fout->remoteVersion < 90600)
2238 : {
2239 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2240 0 : return;
2241 : }
2242 :
2243 2848 : if (checkExtensionMembership(&method->dobj, fout))
2244 50 : return; /* extension membership overrides all else */
2245 :
2246 : /*
2247 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2248 : * they do not support ACLs currently.
2249 : */
2250 2798 : if (method->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2251 2604 : method->dobj.dump = DUMP_COMPONENT_NONE;
2252 : else
2253 194 : method->dobj.dump = fout->dopt->include_everything ?
2254 194 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2255 : }
2256 :
2257 : /*
2258 : * selectDumpableExtension: policy-setting subroutine
2259 : * Mark an extension as to be dumped or not
2260 : *
2261 : * Built-in extensions should be skipped except for checking ACLs, since we
2262 : * assume those will already be installed in the target database. We identify
2263 : * such extensions by their having OIDs in the range reserved for initdb.
2264 : * We dump all user-added extensions by default. No extensions are dumped
2265 : * if include_everything is false (i.e., a --schema or --table switch was
2266 : * given), except if --extension specifies a list of extensions to dump.
2267 : */
2268 : static void
2269 434 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2270 : {
2271 : /*
2272 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2273 : * change permissions on their member objects, if they wish to, and have
2274 : * those changes preserved.
2275 : */
2276 434 : if (extinfo->dobj.catId.oid <= (Oid) g_last_builtin_oid)
2277 374 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2278 : else
2279 : {
2280 : /* check if there is a list of extensions to dump */
2281 60 : if (extension_include_oids.head != NULL)
2282 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2283 8 : simple_oid_list_member(&extension_include_oids,
2284 : extinfo->dobj.catId.oid) ?
2285 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2286 : else
2287 52 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2288 52 : dopt->include_everything ?
2289 52 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2290 :
2291 : /* check that the extension is not explicitly excluded */
2292 112 : if (extinfo->dobj.dump &&
2293 52 : simple_oid_list_member(&extension_exclude_oids,
2294 : extinfo->dobj.catId.oid))
2295 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2296 : }
2297 434 : }
2298 :
2299 : /*
2300 : * selectDumpablePublicationObject: policy-setting subroutine
2301 : * Mark a publication object as to be dumped or not
2302 : *
2303 : * A publication can have schemas and tables which have schemas, but those are
2304 : * ignored in decision making, because publications are only dumped when we are
2305 : * dumping everything.
2306 : */
2307 : static void
2308 950 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2309 : {
2310 950 : if (checkExtensionMembership(dobj, fout))
2311 0 : return; /* extension membership overrides all else */
2312 :
2313 950 : dobj->dump = fout->dopt->include_everything ?
2314 950 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2315 : }
2316 :
2317 : /*
2318 : * selectDumpableStatisticsObject: policy-setting subroutine
2319 : * Mark an extended statistics object as to be dumped or not
2320 : *
2321 : * We dump an extended statistics object if the schema it's in and the table
2322 : * it's for are being dumped. (This'll need more thought if statistics
2323 : * objects ever support cross-table stats.)
2324 : */
2325 : static void
2326 326 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2327 : {
2328 326 : if (checkExtensionMembership(&sobj->dobj, fout))
2329 0 : return; /* extension membership overrides all else */
2330 :
2331 326 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2332 326 : if (sobj->stattable == NULL ||
2333 326 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2334 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2335 : }
2336 :
2337 : /*
2338 : * selectDumpableObject: policy-setting subroutine
2339 : * Mark a generic dumpable object as to be dumped or not
2340 : *
2341 : * Use this only for object types without a special-case routine above.
2342 : */
2343 : static void
2344 806588 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2345 : {
2346 806588 : if (checkExtensionMembership(dobj, fout))
2347 376 : return; /* extension membership overrides all else */
2348 :
2349 : /*
2350 : * Default policy is to dump if parent namespace is dumpable, or for
2351 : * non-namespace-associated items, dump if we're dumping "everything".
2352 : */
2353 806212 : if (dobj->namespace)
2354 804750 : dobj->dump = dobj->namespace->dobj.dump_contains;
2355 : else
2356 1462 : dobj->dump = fout->dopt->include_everything ?
2357 1462 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2358 : }
2359 :
2360 : /*
2361 : * Dump a table's contents for loading using the COPY command
2362 : * - this routine is called by the Archiver when it wants the table
2363 : * to be dumped.
2364 : */
2365 : static int
2366 8114 : dumpTableData_copy(Archive *fout, const void *dcontext)
2367 : {
2368 8114 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2369 8114 : TableInfo *tbinfo = tdinfo->tdtable;
2370 8114 : const char *classname = tbinfo->dobj.name;
2371 8114 : PQExpBuffer q = createPQExpBuffer();
2372 :
2373 : /*
2374 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2375 : * which uses it already.
2376 : */
2377 8114 : PQExpBuffer clistBuf = createPQExpBuffer();
2378 8114 : PGconn *conn = GetConnection(fout);
2379 : PGresult *res;
2380 : int ret;
2381 : char *copybuf;
2382 : const char *column_list;
2383 :
2384 8114 : pg_log_info("dumping contents of table \"%s.%s\"",
2385 : tbinfo->dobj.namespace->dobj.name, classname);
2386 :
2387 : /*
2388 : * Specify the column list explicitly so that we have no possibility of
2389 : * retrieving data in the wrong column order. (The default column
2390 : * ordering of COPY will not be what we want in certain corner cases
2391 : * involving ADD COLUMN and inheritance.)
2392 : */
2393 8114 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2394 :
2395 : /*
2396 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2397 : * a filter condition was specified. For other cases a simple COPY
2398 : * suffices.
2399 : */
2400 8114 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2401 : {
2402 : /* Temporary allows to access to foreign tables to dump data */
2403 146 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2404 2 : set_restrict_relation_kind(fout, "view");
2405 :
2406 146 : appendPQExpBufferStr(q, "COPY (SELECT ");
2407 : /* klugery to get rid of parens in column list */
2408 146 : if (strlen(column_list) > 2)
2409 : {
2410 146 : appendPQExpBufferStr(q, column_list + 1);
2411 146 : q->data[q->len - 1] = ' ';
2412 : }
2413 : else
2414 0 : appendPQExpBufferStr(q, "* ");
2415 :
2416 292 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2417 146 : fmtQualifiedDumpable(tbinfo),
2418 146 : tdinfo->filtercond ? tdinfo->filtercond : "");
2419 : }
2420 : else
2421 : {
2422 7968 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2423 7968 : fmtQualifiedDumpable(tbinfo),
2424 : column_list);
2425 : }
2426 8114 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2427 8112 : PQclear(res);
2428 8112 : destroyPQExpBuffer(clistBuf);
2429 :
2430 : for (;;)
2431 : {
2432 3620154 : ret = PQgetCopyData(conn, ©buf, 0);
2433 :
2434 3620154 : if (ret < 0)
2435 8112 : break; /* done or error */
2436 :
2437 3612042 : if (copybuf)
2438 : {
2439 3612042 : WriteData(fout, copybuf, ret);
2440 3612042 : PQfreemem(copybuf);
2441 : }
2442 :
2443 : /* ----------
2444 : * THROTTLE:
2445 : *
2446 : * There was considerable discussion in late July, 2000 regarding
2447 : * slowing down pg_dump when backing up large tables. Users with both
2448 : * slow & fast (multi-processor) machines experienced performance
2449 : * degradation when doing a backup.
2450 : *
2451 : * Initial attempts based on sleeping for a number of ms for each ms
2452 : * of work were deemed too complex, then a simple 'sleep in each loop'
2453 : * implementation was suggested. The latter failed because the loop
2454 : * was too tight. Finally, the following was implemented:
2455 : *
2456 : * If throttle is non-zero, then
2457 : * See how long since the last sleep.
2458 : * Work out how long to sleep (based on ratio).
2459 : * If sleep is more than 100ms, then
2460 : * sleep
2461 : * reset timer
2462 : * EndIf
2463 : * EndIf
2464 : *
2465 : * where the throttle value was the number of ms to sleep per ms of
2466 : * work. The calculation was done in each loop.
2467 : *
2468 : * Most of the hard work is done in the backend, and this solution
2469 : * still did not work particularly well: on slow machines, the ratio
2470 : * was 50:1, and on medium paced machines, 1:1, and on fast
2471 : * multi-processor machines, it had little or no effect, for reasons
2472 : * that were unclear.
2473 : *
2474 : * Further discussion ensued, and the proposal was dropped.
2475 : *
2476 : * For those people who want this feature, it can be implemented using
2477 : * gettimeofday in each loop, calculating the time since last sleep,
2478 : * multiplying that by the sleep ratio, then if the result is more
2479 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2480 : * function to sleep for a subsecond period ie.
2481 : *
2482 : * select(0, NULL, NULL, NULL, &tvi);
2483 : *
2484 : * This will return after the interval specified in the structure tvi.
2485 : * Finally, call gettimeofday again to save the 'last sleep time'.
2486 : * ----------
2487 : */
2488 : }
2489 8112 : archprintf(fout, "\\.\n\n\n");
2490 :
2491 8112 : if (ret == -2)
2492 : {
2493 : /* copy data transfer failed */
2494 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2495 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2496 0 : pg_log_error_detail("Command was: %s", q->data);
2497 0 : exit_nicely(1);
2498 : }
2499 :
2500 : /* Check command status and return to normal libpq state */
2501 8112 : res = PQgetResult(conn);
2502 8112 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2503 : {
2504 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2505 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2506 0 : pg_log_error_detail("Command was: %s", q->data);
2507 0 : exit_nicely(1);
2508 : }
2509 8112 : PQclear(res);
2510 :
2511 : /* Do this to ensure we've pumped libpq back to idle state */
2512 8112 : if (PQgetResult(conn) != NULL)
2513 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2514 : classname);
2515 :
2516 8112 : destroyPQExpBuffer(q);
2517 :
2518 : /* Revert back the setting */
2519 8112 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2520 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2521 :
2522 8112 : return 1;
2523 : }
2524 :
2525 : /*
2526 : * Dump table data using INSERT commands.
2527 : *
2528 : * Caution: when we restore from an archive file direct to database, the
2529 : * INSERT commands emitted by this function have to be parsed by
2530 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2531 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2532 : */
2533 : static int
2534 162 : dumpTableData_insert(Archive *fout, const void *dcontext)
2535 : {
2536 162 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2537 162 : TableInfo *tbinfo = tdinfo->tdtable;
2538 162 : DumpOptions *dopt = fout->dopt;
2539 162 : PQExpBuffer q = createPQExpBuffer();
2540 162 : PQExpBuffer insertStmt = NULL;
2541 : char *attgenerated;
2542 : PGresult *res;
2543 : int nfields,
2544 : i;
2545 162 : int rows_per_statement = dopt->dump_inserts;
2546 162 : int rows_this_statement = 0;
2547 :
2548 : /* Temporary allows to access to foreign tables to dump data */
2549 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2550 0 : set_restrict_relation_kind(fout, "view");
2551 :
2552 : /*
2553 : * If we're going to emit INSERTs with column names, the most efficient
2554 : * way to deal with generated columns is to exclude them entirely. For
2555 : * INSERTs without column names, we have to emit DEFAULT rather than the
2556 : * actual column value --- but we can save a few cycles by fetching nulls
2557 : * rather than the uninteresting-to-us value.
2558 : */
2559 162 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2560 162 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2561 162 : nfields = 0;
2562 502 : for (i = 0; i < tbinfo->numatts; i++)
2563 : {
2564 340 : if (tbinfo->attisdropped[i])
2565 4 : continue;
2566 336 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2567 16 : continue;
2568 320 : if (nfields > 0)
2569 172 : appendPQExpBufferStr(q, ", ");
2570 320 : if (tbinfo->attgenerated[i])
2571 16 : appendPQExpBufferStr(q, "NULL");
2572 : else
2573 304 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2574 320 : attgenerated[nfields] = tbinfo->attgenerated[i];
2575 320 : nfields++;
2576 : }
2577 : /* Servers before 9.4 will complain about zero-column SELECT */
2578 162 : if (nfields == 0)
2579 14 : appendPQExpBufferStr(q, "NULL");
2580 162 : appendPQExpBuffer(q, " FROM ONLY %s",
2581 162 : fmtQualifiedDumpable(tbinfo));
2582 162 : if (tdinfo->filtercond)
2583 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2584 :
2585 162 : ExecuteSqlStatement(fout, q->data);
2586 :
2587 : while (1)
2588 : {
2589 262 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2590 : PGRES_TUPLES_OK);
2591 :
2592 : /* cross-check field count, allowing for dummy NULL if any */
2593 262 : if (nfields != PQnfields(res) &&
2594 20 : !(nfields == 0 && PQnfields(res) == 1))
2595 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2596 : tbinfo->dobj.name);
2597 :
2598 : /*
2599 : * First time through, we build as much of the INSERT statement as
2600 : * possible in "insertStmt", which we can then just print for each
2601 : * statement. If the table happens to have zero dumpable columns then
2602 : * this will be a complete statement, otherwise it will end in
2603 : * "VALUES" and be ready to have the row's column values printed.
2604 : */
2605 262 : if (insertStmt == NULL)
2606 : {
2607 : TableInfo *targettab;
2608 :
2609 162 : insertStmt = createPQExpBuffer();
2610 :
2611 : /*
2612 : * When load-via-partition-root is set or forced, get the root
2613 : * table name for the partition table, so that we can reload data
2614 : * through the root table.
2615 : */
2616 162 : if (tbinfo->ispartition &&
2617 96 : (dopt->load_via_partition_root ||
2618 48 : forcePartitionRootLoad(tbinfo)))
2619 14 : targettab = getRootTableInfo(tbinfo);
2620 : else
2621 148 : targettab = tbinfo;
2622 :
2623 162 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2624 162 : fmtQualifiedDumpable(targettab));
2625 :
2626 : /* corner case for zero-column table */
2627 162 : if (nfields == 0)
2628 : {
2629 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2630 : }
2631 : else
2632 : {
2633 : /* append the list of column names if required */
2634 148 : if (dopt->column_inserts)
2635 : {
2636 66 : appendPQExpBufferChar(insertStmt, '(');
2637 200 : for (int field = 0; field < nfields; field++)
2638 : {
2639 134 : if (field > 0)
2640 68 : appendPQExpBufferStr(insertStmt, ", ");
2641 134 : appendPQExpBufferStr(insertStmt,
2642 134 : fmtId(PQfname(res, field)));
2643 : }
2644 66 : appendPQExpBufferStr(insertStmt, ") ");
2645 : }
2646 :
2647 148 : if (tbinfo->needs_override)
2648 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2649 :
2650 148 : appendPQExpBufferStr(insertStmt, "VALUES");
2651 : }
2652 : }
2653 :
2654 6800 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2655 : {
2656 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2657 6538 : if (rows_this_statement == 0)
2658 6526 : archputs(insertStmt->data, fout);
2659 :
2660 : /*
2661 : * If it is zero-column table then we've already written the
2662 : * complete statement, which will mean we've disobeyed
2663 : * --rows-per-insert when it's set greater than 1. We do support
2664 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2665 : * UNION ALL ... but that's non-standard so we should avoid it
2666 : * given that using INSERTs is mostly only ever needed for
2667 : * cross-database exports.
2668 : */
2669 6538 : if (nfields == 0)
2670 12 : continue;
2671 :
2672 : /* Emit a row heading */
2673 6526 : if (rows_per_statement == 1)
2674 6508 : archputs(" (", fout);
2675 18 : else if (rows_this_statement > 0)
2676 12 : archputs(",\n\t(", fout);
2677 : else
2678 6 : archputs("\n\t(", fout);
2679 :
2680 19690 : for (int field = 0; field < nfields; field++)
2681 : {
2682 13164 : if (field > 0)
2683 6638 : archputs(", ", fout);
2684 13164 : if (attgenerated[field])
2685 : {
2686 4 : archputs("DEFAULT", fout);
2687 4 : continue;
2688 : }
2689 13160 : if (PQgetisnull(res, tuple, field))
2690 : {
2691 166 : archputs("NULL", fout);
2692 166 : continue;
2693 : }
2694 :
2695 : /* XXX This code is partially duplicated in ruleutils.c */
2696 12994 : switch (PQftype(res, field))
2697 : {
2698 8938 : case INT2OID:
2699 : case INT4OID:
2700 : case INT8OID:
2701 : case OIDOID:
2702 : case FLOAT4OID:
2703 : case FLOAT8OID:
2704 : case NUMERICOID:
2705 : {
2706 : /*
2707 : * These types are printed without quotes unless
2708 : * they contain values that aren't accepted by the
2709 : * scanner unquoted (e.g., 'NaN'). Note that
2710 : * strtod() and friends might accept NaN, so we
2711 : * can't use that to test.
2712 : *
2713 : * In reality we only need to defend against
2714 : * infinity and NaN, so we need not get too crazy
2715 : * about pattern matching here.
2716 : */
2717 8938 : const char *s = PQgetvalue(res, tuple, field);
2718 :
2719 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2720 8934 : archputs(s, fout);
2721 : else
2722 4 : archprintf(fout, "'%s'", s);
2723 : }
2724 8938 : break;
2725 :
2726 4 : case BITOID:
2727 : case VARBITOID:
2728 4 : archprintf(fout, "B'%s'",
2729 : PQgetvalue(res, tuple, field));
2730 4 : break;
2731 :
2732 8 : case BOOLOID:
2733 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2734 4 : archputs("true", fout);
2735 : else
2736 4 : archputs("false", fout);
2737 8 : break;
2738 :
2739 4044 : default:
2740 : /* All other types are printed as string literals. */
2741 4044 : resetPQExpBuffer(q);
2742 4044 : appendStringLiteralAH(q,
2743 : PQgetvalue(res, tuple, field),
2744 : fout);
2745 4044 : archputs(q->data, fout);
2746 4044 : break;
2747 : }
2748 : }
2749 :
2750 : /* Terminate the row ... */
2751 6526 : archputs(")", fout);
2752 :
2753 : /* ... and the statement, if the target no. of rows is reached */
2754 6526 : if (++rows_this_statement >= rows_per_statement)
2755 : {
2756 6512 : if (dopt->do_nothing)
2757 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2758 : else
2759 6512 : archputs(";\n", fout);
2760 : /* Reset the row counter */
2761 6512 : rows_this_statement = 0;
2762 : }
2763 : }
2764 :
2765 262 : if (PQntuples(res) <= 0)
2766 : {
2767 162 : PQclear(res);
2768 162 : break;
2769 : }
2770 100 : PQclear(res);
2771 : }
2772 :
2773 : /* Terminate any statements that didn't make the row count. */
2774 162 : if (rows_this_statement > 0)
2775 : {
2776 2 : if (dopt->do_nothing)
2777 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2778 : else
2779 2 : archputs(";\n", fout);
2780 : }
2781 :
2782 162 : archputs("\n\n", fout);
2783 :
2784 162 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2785 :
2786 162 : destroyPQExpBuffer(q);
2787 162 : if (insertStmt != NULL)
2788 162 : destroyPQExpBuffer(insertStmt);
2789 162 : free(attgenerated);
2790 :
2791 : /* Revert back the setting */
2792 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2793 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2794 :
2795 162 : return 1;
2796 : }
2797 :
2798 : /*
2799 : * getRootTableInfo:
2800 : * get the root TableInfo for the given partition table.
2801 : */
2802 : static TableInfo *
2803 158 : getRootTableInfo(const TableInfo *tbinfo)
2804 : {
2805 : TableInfo *parentTbinfo;
2806 :
2807 : Assert(tbinfo->ispartition);
2808 : Assert(tbinfo->numParents == 1);
2809 :
2810 158 : parentTbinfo = tbinfo->parents[0];
2811 158 : while (parentTbinfo->ispartition)
2812 : {
2813 : Assert(parentTbinfo->numParents == 1);
2814 0 : parentTbinfo = parentTbinfo->parents[0];
2815 : }
2816 :
2817 158 : return parentTbinfo;
2818 : }
2819 :
2820 : /*
2821 : * forcePartitionRootLoad
2822 : * Check if we must force load_via_partition_root for this partition.
2823 : *
2824 : * This is required if any level of ancestral partitioned table has an
2825 : * unsafe partitioning scheme.
2826 : */
2827 : static bool
2828 2096 : forcePartitionRootLoad(const TableInfo *tbinfo)
2829 : {
2830 : TableInfo *parentTbinfo;
2831 :
2832 : Assert(tbinfo->ispartition);
2833 : Assert(tbinfo->numParents == 1);
2834 :
2835 2096 : parentTbinfo = tbinfo->parents[0];
2836 2096 : if (parentTbinfo->unsafe_partitions)
2837 158 : return true;
2838 2370 : while (parentTbinfo->ispartition)
2839 : {
2840 : Assert(parentTbinfo->numParents == 1);
2841 432 : parentTbinfo = parentTbinfo->parents[0];
2842 432 : if (parentTbinfo->unsafe_partitions)
2843 0 : return true;
2844 : }
2845 :
2846 1938 : return false;
2847 : }
2848 :
2849 : /*
2850 : * dumpTableData -
2851 : * dump the contents of a single table
2852 : *
2853 : * Actually, this just makes an ArchiveEntry for the table contents.
2854 : */
2855 : static void
2856 8436 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2857 : {
2858 8436 : DumpOptions *dopt = fout->dopt;
2859 8436 : TableInfo *tbinfo = tdinfo->tdtable;
2860 8436 : PQExpBuffer copyBuf = createPQExpBuffer();
2861 8436 : PQExpBuffer clistBuf = createPQExpBuffer();
2862 : DataDumperPtr dumpFn;
2863 8436 : char *tdDefn = NULL;
2864 : char *copyStmt;
2865 : const char *copyFrom;
2866 :
2867 : /* We had better have loaded per-column details about this table */
2868 : Assert(tbinfo->interesting);
2869 :
2870 : /*
2871 : * When load-via-partition-root is set or forced, get the root table name
2872 : * for the partition table, so that we can reload data through the root
2873 : * table. Then construct a comment to be inserted into the TOC entry's
2874 : * defn field, so that such cases can be identified reliably.
2875 : */
2876 8436 : if (tbinfo->ispartition &&
2877 4096 : (dopt->load_via_partition_root ||
2878 2048 : forcePartitionRootLoad(tbinfo)))
2879 144 : {
2880 : TableInfo *parentTbinfo;
2881 : char *sanitized;
2882 :
2883 144 : parentTbinfo = getRootTableInfo(tbinfo);
2884 144 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2885 144 : sanitized = sanitize_line(copyFrom, true);
2886 144 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2887 : sanitized);
2888 144 : free(sanitized);
2889 144 : tdDefn = pg_strdup(copyBuf->data);
2890 : }
2891 : else
2892 8292 : copyFrom = fmtQualifiedDumpable(tbinfo);
2893 :
2894 8436 : if (dopt->dump_inserts == 0)
2895 : {
2896 : /* Dump/restore using COPY */
2897 8274 : dumpFn = dumpTableData_copy;
2898 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2899 8274 : printfPQExpBuffer(copyBuf, "COPY %s ",
2900 : copyFrom);
2901 8274 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2902 : fmtCopyColumnList(tbinfo, clistBuf));
2903 8274 : copyStmt = copyBuf->data;
2904 : }
2905 : else
2906 : {
2907 : /* Restore using INSERT */
2908 162 : dumpFn = dumpTableData_insert;
2909 162 : copyStmt = NULL;
2910 : }
2911 :
2912 : /*
2913 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2914 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2915 : * See comments for BuildArchiveDependencies.
2916 : */
2917 8436 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2918 : {
2919 : TocEntry *te;
2920 :
2921 8436 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2922 8436 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2923 : .namespace = tbinfo->dobj.namespace->dobj.name,
2924 : .owner = tbinfo->rolname,
2925 : .description = "TABLE DATA",
2926 : .section = SECTION_DATA,
2927 : .createStmt = tdDefn,
2928 : .copyStmt = copyStmt,
2929 : .deps = &(tbinfo->dobj.dumpId),
2930 : .nDeps = 1,
2931 : .dumpFn = dumpFn,
2932 : .dumpArg = tdinfo));
2933 :
2934 : /*
2935 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2936 : * and want to order dump jobs by table size. We choose to measure
2937 : * dataLength in table pages (including TOAST pages) during dump, so
2938 : * no scaling is needed.
2939 : *
2940 : * However, relpages is declared as "integer" in pg_class, and hence
2941 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2942 : * Cast so that we get the right interpretation of table sizes
2943 : * exceeding INT_MAX pages.
2944 : */
2945 8436 : te->dataLength = (BlockNumber) tbinfo->relpages;
2946 8436 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2947 :
2948 : /*
2949 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2950 : * and instead we'd better worry about integer overflow. Clamp to
2951 : * INT_MAX if the correct result exceeds that.
2952 : */
2953 : if (sizeof(te->dataLength) == 4 &&
2954 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2955 : te->dataLength < 0))
2956 : te->dataLength = INT_MAX;
2957 : }
2958 :
2959 8436 : destroyPQExpBuffer(copyBuf);
2960 8436 : destroyPQExpBuffer(clistBuf);
2961 8436 : }
2962 :
2963 : /*
2964 : * refreshMatViewData -
2965 : * load or refresh the contents of a single materialized view
2966 : *
2967 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2968 : * statement.
2969 : */
2970 : static void
2971 690 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2972 : {
2973 690 : TableInfo *tbinfo = tdinfo->tdtable;
2974 : PQExpBuffer q;
2975 :
2976 : /* If the materialized view is not flagged as populated, skip this. */
2977 690 : if (!tbinfo->relispopulated)
2978 136 : return;
2979 :
2980 554 : q = createPQExpBuffer();
2981 :
2982 554 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2983 554 : fmtQualifiedDumpable(tbinfo));
2984 :
2985 554 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2986 554 : ArchiveEntry(fout,
2987 : tdinfo->dobj.catId, /* catalog ID */
2988 554 : tdinfo->dobj.dumpId, /* dump ID */
2989 554 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2990 : .namespace = tbinfo->dobj.namespace->dobj.name,
2991 : .owner = tbinfo->rolname,
2992 : .description = "MATERIALIZED VIEW DATA",
2993 : .section = SECTION_POST_DATA,
2994 : .createStmt = q->data,
2995 : .deps = tdinfo->dobj.dependencies,
2996 : .nDeps = tdinfo->dobj.nDeps));
2997 :
2998 554 : destroyPQExpBuffer(q);
2999 : }
3000 :
3001 : /*
3002 : * getTableData -
3003 : * set up dumpable objects representing the contents of tables
3004 : */
3005 : static void
3006 356 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3007 : {
3008 : int i;
3009 :
3010 94528 : for (i = 0; i < numTables; i++)
3011 : {
3012 94172 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3013 1854 : (!relkind || tblinfo[i].relkind == relkind))
3014 11760 : makeTableDataInfo(dopt, &(tblinfo[i]));
3015 : }
3016 356 : }
3017 :
3018 : /*
3019 : * Make a dumpable object for the data of this specific table
3020 : *
3021 : * Note: we make a TableDataInfo if and only if we are going to dump the
3022 : * table data; the "dump" field in such objects isn't very interesting.
3023 : */
3024 : static void
3025 11982 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3026 : {
3027 : TableDataInfo *tdinfo;
3028 :
3029 : /*
3030 : * Nothing to do if we already decided to dump the table. This will
3031 : * happen for "config" tables.
3032 : */
3033 11982 : if (tbinfo->dataObj != NULL)
3034 2 : return;
3035 :
3036 : /* Skip VIEWs (no data to dump) */
3037 11980 : if (tbinfo->relkind == RELKIND_VIEW)
3038 968 : return;
3039 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3040 11012 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3041 76 : (foreign_servers_include_oids.head == NULL ||
3042 8 : !simple_oid_list_member(&foreign_servers_include_oids,
3043 : tbinfo->foreign_server)))
3044 74 : return;
3045 : /* Skip partitioned tables (data in partitions) */
3046 10938 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3047 974 : return;
3048 :
3049 : /* Don't dump data in unlogged tables, if so requested */
3050 9964 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3051 82 : dopt->no_unlogged_table_data)
3052 36 : return;
3053 :
3054 : /* Check that the data is not explicitly excluded */
3055 9928 : if (simple_oid_list_member(&tabledata_exclude_oids,
3056 : tbinfo->dobj.catId.oid))
3057 16 : return;
3058 :
3059 : /* OK, let's dump it */
3060 9912 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3061 :
3062 9912 : if (tbinfo->relkind == RELKIND_MATVIEW)
3063 690 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3064 9222 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3065 786 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3066 : else
3067 8436 : tdinfo->dobj.objType = DO_TABLE_DATA;
3068 :
3069 : /*
3070 : * Note: use tableoid 0 so that this object won't be mistaken for
3071 : * something that pg_depend entries apply to.
3072 : */
3073 9912 : tdinfo->dobj.catId.tableoid = 0;
3074 9912 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3075 9912 : AssignDumpId(&tdinfo->dobj);
3076 9912 : tdinfo->dobj.name = tbinfo->dobj.name;
3077 9912 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3078 9912 : tdinfo->tdtable = tbinfo;
3079 9912 : tdinfo->filtercond = NULL; /* might get set later */
3080 9912 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3081 :
3082 : /* A TableDataInfo contains data, of course */
3083 9912 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3084 :
3085 9912 : tbinfo->dataObj = tdinfo;
3086 :
3087 : /*
3088 : * Materialized view statistics must be restored after the data, because
3089 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3090 : *
3091 : * The dependency is added here because the statistics objects are created
3092 : * first.
3093 : */
3094 9912 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3095 : {
3096 536 : tbinfo->stats->section = SECTION_POST_DATA;
3097 536 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3098 : }
3099 :
3100 : /* Make sure that we'll collect per-column info for this table. */
3101 9912 : tbinfo->interesting = true;
3102 : }
3103 :
3104 : /*
3105 : * The refresh for a materialized view must be dependent on the refresh for
3106 : * any materialized view that this one is dependent on.
3107 : *
3108 : * This must be called after all the objects are created, but before they are
3109 : * sorted.
3110 : */
3111 : static void
3112 292 : buildMatViewRefreshDependencies(Archive *fout)
3113 : {
3114 : PQExpBuffer query;
3115 : PGresult *res;
3116 : int ntups,
3117 : i;
3118 : int i_classid,
3119 : i_objid,
3120 : i_refobjid;
3121 :
3122 : /* No Mat Views before 9.3. */
3123 292 : if (fout->remoteVersion < 90300)
3124 0 : return;
3125 :
3126 292 : query = createPQExpBuffer();
3127 :
3128 292 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3129 : "( "
3130 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3131 : "FROM pg_depend d1 "
3132 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3133 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3134 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3135 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3136 : "AND d2.objid = r1.oid "
3137 : "AND d2.refobjid <> d1.objid "
3138 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3139 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3140 : CppAsString2(RELKIND_VIEW) ") "
3141 : "WHERE d1.classid = 'pg_class'::regclass "
3142 : "UNION "
3143 : "SELECT w.objid, d3.refobjid, c3.relkind "
3144 : "FROM w "
3145 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3146 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3147 : "AND d3.objid = r3.oid "
3148 : "AND d3.refobjid <> w.refobjid "
3149 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3150 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3151 : CppAsString2(RELKIND_VIEW) ") "
3152 : ") "
3153 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3154 : "FROM w "
3155 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3156 :
3157 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3158 :
3159 292 : ntups = PQntuples(res);
3160 :
3161 292 : i_classid = PQfnumber(res, "classid");
3162 292 : i_objid = PQfnumber(res, "objid");
3163 292 : i_refobjid = PQfnumber(res, "refobjid");
3164 :
3165 820 : for (i = 0; i < ntups; i++)
3166 : {
3167 : CatalogId objId;
3168 : CatalogId refobjId;
3169 : DumpableObject *dobj;
3170 : DumpableObject *refdobj;
3171 : TableInfo *tbinfo;
3172 : TableInfo *reftbinfo;
3173 :
3174 528 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3175 528 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3176 528 : refobjId.tableoid = objId.tableoid;
3177 528 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3178 :
3179 528 : dobj = findObjectByCatalogId(objId);
3180 528 : if (dobj == NULL)
3181 96 : continue;
3182 :
3183 : Assert(dobj->objType == DO_TABLE);
3184 528 : tbinfo = (TableInfo *) dobj;
3185 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3186 528 : dobj = (DumpableObject *) tbinfo->dataObj;
3187 528 : if (dobj == NULL)
3188 96 : continue;
3189 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3190 :
3191 432 : refdobj = findObjectByCatalogId(refobjId);
3192 432 : if (refdobj == NULL)
3193 0 : continue;
3194 :
3195 : Assert(refdobj->objType == DO_TABLE);
3196 432 : reftbinfo = (TableInfo *) refdobj;
3197 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3198 432 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3199 432 : if (refdobj == NULL)
3200 0 : continue;
3201 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3202 :
3203 432 : addObjectDependency(dobj, refdobj->dumpId);
3204 :
3205 432 : if (!reftbinfo->relispopulated)
3206 68 : tbinfo->relispopulated = false;
3207 : }
3208 :
3209 292 : PQclear(res);
3210 :
3211 292 : destroyPQExpBuffer(query);
3212 : }
3213 :
3214 : /*
3215 : * getTableDataFKConstraints -
3216 : * add dump-order dependencies reflecting foreign key constraints
3217 : *
3218 : * This code is executed only in a data-only dump --- in schema+data dumps
3219 : * we handle foreign key issues by not creating the FK constraints until
3220 : * after the data is loaded. In a data-only dump, however, we want to
3221 : * order the table data objects in such a way that a table's referenced
3222 : * tables are restored first. (In the presence of circular references or
3223 : * self-references this may be impossible; we'll detect and complain about
3224 : * that during the dependency sorting step.)
3225 : */
3226 : static void
3227 14 : getTableDataFKConstraints(void)
3228 : {
3229 : DumpableObject **dobjs;
3230 : int numObjs;
3231 : int i;
3232 :
3233 : /* Search through all the dumpable objects for FK constraints */
3234 14 : getDumpableObjects(&dobjs, &numObjs);
3235 51536 : for (i = 0; i < numObjs; i++)
3236 : {
3237 51522 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3238 : {
3239 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3240 : TableInfo *ftable;
3241 :
3242 : /* Not interesting unless both tables are to be dumped */
3243 16 : if (cinfo->contable == NULL ||
3244 16 : cinfo->contable->dataObj == NULL)
3245 8 : continue;
3246 8 : ftable = findTableByOid(cinfo->confrelid);
3247 8 : if (ftable == NULL ||
3248 8 : ftable->dataObj == NULL)
3249 0 : continue;
3250 :
3251 : /*
3252 : * Okay, make referencing table's TABLE_DATA object depend on the
3253 : * referenced table's TABLE_DATA object.
3254 : */
3255 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3256 8 : ftable->dataObj->dobj.dumpId);
3257 : }
3258 : }
3259 14 : free(dobjs);
3260 14 : }
3261 :
3262 :
3263 : /*
3264 : * dumpDatabase:
3265 : * dump the database definition
3266 : */
3267 : static void
3268 170 : dumpDatabase(Archive *fout)
3269 : {
3270 170 : DumpOptions *dopt = fout->dopt;
3271 170 : PQExpBuffer dbQry = createPQExpBuffer();
3272 170 : PQExpBuffer delQry = createPQExpBuffer();
3273 170 : PQExpBuffer creaQry = createPQExpBuffer();
3274 170 : PQExpBuffer labelq = createPQExpBuffer();
3275 170 : PGconn *conn = GetConnection(fout);
3276 : PGresult *res;
3277 : int i_tableoid,
3278 : i_oid,
3279 : i_datname,
3280 : i_datdba,
3281 : i_encoding,
3282 : i_datlocprovider,
3283 : i_collate,
3284 : i_ctype,
3285 : i_datlocale,
3286 : i_daticurules,
3287 : i_frozenxid,
3288 : i_minmxid,
3289 : i_datacl,
3290 : i_acldefault,
3291 : i_datistemplate,
3292 : i_datconnlimit,
3293 : i_datcollversion,
3294 : i_tablespace;
3295 : CatalogId dbCatId;
3296 : DumpId dbDumpId;
3297 : DumpableAcl dbdacl;
3298 : const char *datname,
3299 : *dba,
3300 : *encoding,
3301 : *datlocprovider,
3302 : *collate,
3303 : *ctype,
3304 : *locale,
3305 : *icurules,
3306 : *datistemplate,
3307 : *datconnlimit,
3308 : *tablespace;
3309 : uint32 frozenxid,
3310 : minmxid;
3311 : char *qdatname;
3312 :
3313 170 : pg_log_info("saving database definition");
3314 :
3315 : /*
3316 : * Fetch the database-level properties for this database.
3317 : */
3318 170 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3319 : "datdba, "
3320 : "pg_encoding_to_char(encoding) AS encoding, "
3321 : "datcollate, datctype, datfrozenxid, "
3322 : "datacl, acldefault('d', datdba) AS acldefault, "
3323 : "datistemplate, datconnlimit, ");
3324 170 : if (fout->remoteVersion >= 90300)
3325 170 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3326 : else
3327 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3328 170 : if (fout->remoteVersion >= 170000)
3329 170 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3330 0 : else if (fout->remoteVersion >= 150000)
3331 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3332 : else
3333 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3334 170 : if (fout->remoteVersion >= 160000)
3335 170 : appendPQExpBufferStr(dbQry, "daticurules, ");
3336 : else
3337 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3338 170 : appendPQExpBufferStr(dbQry,
3339 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3340 : "shobj_description(oid, 'pg_database') AS description "
3341 : "FROM pg_database "
3342 : "WHERE datname = current_database()");
3343 :
3344 170 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3345 :
3346 170 : i_tableoid = PQfnumber(res, "tableoid");
3347 170 : i_oid = PQfnumber(res, "oid");
3348 170 : i_datname = PQfnumber(res, "datname");
3349 170 : i_datdba = PQfnumber(res, "datdba");
3350 170 : i_encoding = PQfnumber(res, "encoding");
3351 170 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3352 170 : i_collate = PQfnumber(res, "datcollate");
3353 170 : i_ctype = PQfnumber(res, "datctype");
3354 170 : i_datlocale = PQfnumber(res, "datlocale");
3355 170 : i_daticurules = PQfnumber(res, "daticurules");
3356 170 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3357 170 : i_minmxid = PQfnumber(res, "datminmxid");
3358 170 : i_datacl = PQfnumber(res, "datacl");
3359 170 : i_acldefault = PQfnumber(res, "acldefault");
3360 170 : i_datistemplate = PQfnumber(res, "datistemplate");
3361 170 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3362 170 : i_datcollversion = PQfnumber(res, "datcollversion");
3363 170 : i_tablespace = PQfnumber(res, "tablespace");
3364 :
3365 170 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3366 170 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3367 170 : datname = PQgetvalue(res, 0, i_datname);
3368 170 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3369 170 : encoding = PQgetvalue(res, 0, i_encoding);
3370 170 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3371 170 : collate = PQgetvalue(res, 0, i_collate);
3372 170 : ctype = PQgetvalue(res, 0, i_ctype);
3373 170 : if (!PQgetisnull(res, 0, i_datlocale))
3374 28 : locale = PQgetvalue(res, 0, i_datlocale);
3375 : else
3376 142 : locale = NULL;
3377 170 : if (!PQgetisnull(res, 0, i_daticurules))
3378 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3379 : else
3380 170 : icurules = NULL;
3381 170 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3382 170 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3383 170 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3384 170 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3385 170 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3386 170 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3387 170 : tablespace = PQgetvalue(res, 0, i_tablespace);
3388 :
3389 170 : qdatname = pg_strdup(fmtId(datname));
3390 :
3391 : /*
3392 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3393 : * to preserve that), as well as the encoding, locale, and tablespace
3394 : * since those can't be altered later. Other DB properties are left to
3395 : * the DATABASE PROPERTIES entry, so that they can be applied after
3396 : * reconnecting to the target DB.
3397 : *
3398 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3399 : * shown it to be faster. When the server is in binary upgrade mode, it
3400 : * will also skip the checkpoints this strategy ordinarily performs.
3401 : */
3402 170 : if (dopt->binary_upgrade)
3403 : {
3404 70 : appendPQExpBuffer(creaQry,
3405 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3406 : "OID = %u STRATEGY = FILE_COPY",
3407 : qdatname, dbCatId.oid);
3408 : }
3409 : else
3410 : {
3411 100 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3412 : qdatname);
3413 : }
3414 170 : if (strlen(encoding) > 0)
3415 : {
3416 170 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3417 170 : appendStringLiteralAH(creaQry, encoding, fout);
3418 : }
3419 :
3420 170 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3421 170 : if (datlocprovider[0] == 'b')
3422 28 : appendPQExpBufferStr(creaQry, "builtin");
3423 142 : else if (datlocprovider[0] == 'c')
3424 142 : appendPQExpBufferStr(creaQry, "libc");
3425 0 : else if (datlocprovider[0] == 'i')
3426 0 : appendPQExpBufferStr(creaQry, "icu");
3427 : else
3428 0 : pg_fatal("unrecognized locale provider: %s",
3429 : datlocprovider);
3430 :
3431 170 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3432 : {
3433 170 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3434 170 : appendStringLiteralAH(creaQry, collate, fout);
3435 : }
3436 : else
3437 : {
3438 0 : if (strlen(collate) > 0)
3439 : {
3440 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3441 0 : appendStringLiteralAH(creaQry, collate, fout);
3442 : }
3443 0 : if (strlen(ctype) > 0)
3444 : {
3445 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3446 0 : appendStringLiteralAH(creaQry, ctype, fout);
3447 : }
3448 : }
3449 170 : if (locale)
3450 : {
3451 28 : if (datlocprovider[0] == 'b')
3452 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3453 : else
3454 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3455 :
3456 28 : appendStringLiteralAH(creaQry, locale, fout);
3457 : }
3458 :
3459 170 : if (icurules)
3460 : {
3461 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3462 0 : appendStringLiteralAH(creaQry, icurules, fout);
3463 : }
3464 :
3465 : /*
3466 : * For binary upgrade, carry over the collation version. For normal
3467 : * dump/restore, omit the version, so that it is computed upon restore.
3468 : */
3469 170 : if (dopt->binary_upgrade)
3470 : {
3471 70 : if (!PQgetisnull(res, 0, i_datcollversion))
3472 : {
3473 70 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3474 70 : appendStringLiteralAH(creaQry,
3475 : PQgetvalue(res, 0, i_datcollversion),
3476 : fout);
3477 : }
3478 : }
3479 :
3480 : /*
3481 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3482 : * thing; the decision whether to specify a tablespace should be left till
3483 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3484 : * label the DATABASE entry with the tablespace and let the normal
3485 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3486 : * attention to default_tablespace, so that won't work.
3487 : */
3488 170 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3489 10 : !dopt->outputNoTablespaces)
3490 10 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3491 : fmtId(tablespace));
3492 170 : appendPQExpBufferStr(creaQry, ";\n");
3493 :
3494 170 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3495 : qdatname);
3496 :
3497 170 : dbDumpId = createDumpId();
3498 :
3499 170 : ArchiveEntry(fout,
3500 : dbCatId, /* catalog ID */
3501 : dbDumpId, /* dump ID */
3502 170 : ARCHIVE_OPTS(.tag = datname,
3503 : .owner = dba,
3504 : .description = "DATABASE",
3505 : .section = SECTION_PRE_DATA,
3506 : .createStmt = creaQry->data,
3507 : .dropStmt = delQry->data));
3508 :
3509 : /* Compute correct tag for archive entry */
3510 170 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3511 :
3512 : /* Dump DB comment if any */
3513 : {
3514 : /*
3515 : * 8.2 and up keep comments on shared objects in a shared table, so we
3516 : * cannot use the dumpComment() code used for other database objects.
3517 : * Be careful that the ArchiveEntry parameters match that function.
3518 : */
3519 170 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3520 :
3521 170 : if (comment && *comment && !dopt->no_comments)
3522 : {
3523 80 : resetPQExpBuffer(dbQry);
3524 :
3525 : /*
3526 : * Generates warning when loaded into a differently-named
3527 : * database.
3528 : */
3529 80 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3530 80 : appendStringLiteralAH(dbQry, comment, fout);
3531 80 : appendPQExpBufferStr(dbQry, ";\n");
3532 :
3533 80 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3534 80 : ARCHIVE_OPTS(.tag = labelq->data,
3535 : .owner = dba,
3536 : .description = "COMMENT",
3537 : .section = SECTION_NONE,
3538 : .createStmt = dbQry->data,
3539 : .deps = &dbDumpId,
3540 : .nDeps = 1));
3541 : }
3542 : }
3543 :
3544 : /* Dump DB security label, if enabled */
3545 170 : if (!dopt->no_security_labels)
3546 : {
3547 : PGresult *shres;
3548 : PQExpBuffer seclabelQry;
3549 :
3550 170 : seclabelQry = createPQExpBuffer();
3551 :
3552 170 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3553 170 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3554 170 : resetPQExpBuffer(seclabelQry);
3555 170 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3556 170 : if (seclabelQry->len > 0)
3557 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3558 0 : ARCHIVE_OPTS(.tag = labelq->data,
3559 : .owner = dba,
3560 : .description = "SECURITY LABEL",
3561 : .section = SECTION_NONE,
3562 : .createStmt = seclabelQry->data,
3563 : .deps = &dbDumpId,
3564 : .nDeps = 1));
3565 170 : destroyPQExpBuffer(seclabelQry);
3566 170 : PQclear(shres);
3567 : }
3568 :
3569 : /*
3570 : * Dump ACL if any. Note that we do not support initial privileges
3571 : * (pg_init_privs) on databases.
3572 : */
3573 170 : dbdacl.privtype = 0;
3574 170 : dbdacl.initprivs = NULL;
3575 :
3576 170 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3577 : qdatname, NULL, NULL,
3578 : NULL, dba, &dbdacl);
3579 :
3580 : /*
3581 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3582 : * non-default database-level properties. (The reason this must be
3583 : * separate is that we cannot put any additional commands into the TOC
3584 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3585 : * in an implicit transaction block, and the backend won't allow CREATE
3586 : * DATABASE in that context.)
3587 : */
3588 170 : resetPQExpBuffer(creaQry);
3589 170 : resetPQExpBuffer(delQry);
3590 :
3591 170 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3592 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3593 : qdatname, datconnlimit);
3594 :
3595 170 : if (strcmp(datistemplate, "t") == 0)
3596 : {
3597 20 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3598 : qdatname);
3599 :
3600 : /*
3601 : * The backend won't accept DROP DATABASE on a template database. We
3602 : * can deal with that by removing the template marking before the DROP
3603 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3604 : * since no such command is currently supported, fake it with a direct
3605 : * UPDATE on pg_database.
3606 : */
3607 20 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3608 : "SET datistemplate = false WHERE datname = ");
3609 20 : appendStringLiteralAH(delQry, datname, fout);
3610 20 : appendPQExpBufferStr(delQry, ";\n");
3611 : }
3612 :
3613 : /*
3614 : * We do not restore pg_database.dathasloginevt because it is set
3615 : * automatically on login event trigger creation.
3616 : */
3617 :
3618 : /* Add database-specific SET options */
3619 170 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3620 :
3621 : /*
3622 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3623 : * entry, too, for lack of a better place.
3624 : */
3625 170 : if (dopt->binary_upgrade)
3626 : {
3627 70 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3628 70 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3629 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3630 : "WHERE datname = ",
3631 : frozenxid, minmxid);
3632 70 : appendStringLiteralAH(creaQry, datname, fout);
3633 70 : appendPQExpBufferStr(creaQry, ";\n");
3634 : }
3635 :
3636 170 : if (creaQry->len > 0)
3637 78 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3638 78 : ARCHIVE_OPTS(.tag = datname,
3639 : .owner = dba,
3640 : .description = "DATABASE PROPERTIES",
3641 : .section = SECTION_PRE_DATA,
3642 : .createStmt = creaQry->data,
3643 : .dropStmt = delQry->data,
3644 : .deps = &dbDumpId));
3645 :
3646 : /*
3647 : * pg_largeobject comes from the old system intact, so set its
3648 : * relfrozenxids, relminmxids and relfilenode.
3649 : *
3650 : * pg_largeobject_metadata also comes from the old system intact for
3651 : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3652 : * relfilenode, too. pg_upgrade can't copy/link the files from older
3653 : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3654 : * changed its storage format in v16.
3655 : */
3656 170 : if (dopt->binary_upgrade)
3657 : {
3658 : PGresult *lo_res;
3659 70 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3660 70 : PQExpBuffer loOutQry = createPQExpBuffer();
3661 70 : PQExpBuffer lomOutQry = createPQExpBuffer();
3662 70 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3663 70 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3664 : int ii_relfrozenxid,
3665 : ii_relfilenode,
3666 : ii_oid,
3667 : ii_relminmxid;
3668 :
3669 70 : if (fout->remoteVersion >= 90300)
3670 70 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3671 : "FROM pg_catalog.pg_class\n"
3672 : "WHERE oid IN (%u, %u, %u, %u);\n",
3673 : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3674 : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3675 : else
3676 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3677 : "FROM pg_catalog.pg_class\n"
3678 : "WHERE oid IN (%u, %u);\n",
3679 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3680 :
3681 70 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3682 :
3683 70 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3684 70 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3685 70 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3686 70 : ii_oid = PQfnumber(lo_res, "oid");
3687 :
3688 70 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3689 70 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3690 70 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3691 70 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3692 350 : for (int i = 0; i < PQntuples(lo_res); ++i)
3693 : {
3694 : Oid oid;
3695 : RelFileNumber relfilenumber;
3696 : PQExpBuffer horizonQry;
3697 : PQExpBuffer outQry;
3698 :
3699 280 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3700 280 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3701 :
3702 280 : if (oid == LargeObjectRelationId ||
3703 : oid == LargeObjectLOidPNIndexId)
3704 : {
3705 140 : horizonQry = loHorizonQry;
3706 140 : outQry = loOutQry;
3707 : }
3708 : else
3709 : {
3710 140 : horizonQry = lomHorizonQry;
3711 140 : outQry = lomOutQry;
3712 : }
3713 :
3714 280 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3715 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3716 : "WHERE oid = %u;\n",
3717 280 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3718 280 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3719 280 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3720 :
3721 280 : if (oid == LargeObjectRelationId ||
3722 : oid == LargeObjectMetadataRelationId)
3723 140 : appendPQExpBuffer(outQry,
3724 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3725 : relfilenumber);
3726 140 : else if (oid == LargeObjectLOidPNIndexId ||
3727 : oid == LargeObjectMetadataOidIndexId)
3728 140 : appendPQExpBuffer(outQry,
3729 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3730 : relfilenumber);
3731 : }
3732 :
3733 70 : appendPQExpBufferStr(loOutQry,
3734 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3735 70 : appendPQExpBufferStr(lomOutQry,
3736 : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3737 :
3738 70 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3739 70 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3740 :
3741 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3742 70 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3743 : .description = "pg_largeobject",
3744 : .section = SECTION_PRE_DATA,
3745 : .createStmt = loOutQry->data));
3746 :
3747 70 : if (fout->remoteVersion >= 160000)
3748 70 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3749 70 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3750 : .description = "pg_largeobject_metadata",
3751 : .section = SECTION_PRE_DATA,
3752 : .createStmt = lomOutQry->data));
3753 :
3754 70 : PQclear(lo_res);
3755 :
3756 70 : destroyPQExpBuffer(loFrozenQry);
3757 70 : destroyPQExpBuffer(loHorizonQry);
3758 70 : destroyPQExpBuffer(lomHorizonQry);
3759 70 : destroyPQExpBuffer(loOutQry);
3760 70 : destroyPQExpBuffer(lomOutQry);
3761 : }
3762 :
3763 170 : PQclear(res);
3764 :
3765 170 : free(qdatname);
3766 170 : destroyPQExpBuffer(dbQry);
3767 170 : destroyPQExpBuffer(delQry);
3768 170 : destroyPQExpBuffer(creaQry);
3769 170 : destroyPQExpBuffer(labelq);
3770 170 : }
3771 :
3772 : /*
3773 : * Collect any database-specific or role-and-database-specific SET options
3774 : * for this database, and append them to outbuf.
3775 : */
3776 : static void
3777 170 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3778 : const char *dbname, Oid dboid)
3779 : {
3780 170 : PGconn *conn = GetConnection(AH);
3781 170 : PQExpBuffer buf = createPQExpBuffer();
3782 : PGresult *res;
3783 :
3784 : /* First collect database-specific options */
3785 170 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3786 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3787 : dboid);
3788 :
3789 170 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3790 :
3791 230 : for (int i = 0; i < PQntuples(res); i++)
3792 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3793 : "DATABASE", dbname, NULL, NULL,
3794 : outbuf);
3795 :
3796 170 : PQclear(res);
3797 :
3798 : /* Now look for role-and-database-specific options */
3799 170 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3800 : "FROM pg_db_role_setting s, pg_roles r "
3801 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3802 : dboid);
3803 :
3804 170 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3805 :
3806 170 : for (int i = 0; i < PQntuples(res); i++)
3807 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3808 0 : "ROLE", PQgetvalue(res, i, 0),
3809 : "DATABASE", dbname,
3810 : outbuf);
3811 :
3812 170 : PQclear(res);
3813 :
3814 170 : destroyPQExpBuffer(buf);
3815 170 : }
3816 :
3817 : /*
3818 : * dumpEncoding: put the correct encoding into the archive
3819 : */
3820 : static void
3821 372 : dumpEncoding(Archive *AH)
3822 : {
3823 372 : const char *encname = pg_encoding_to_char(AH->encoding);
3824 372 : PQExpBuffer qry = createPQExpBuffer();
3825 :
3826 372 : pg_log_info("saving encoding = %s", encname);
3827 :
3828 372 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3829 372 : appendStringLiteralAH(qry, encname, AH);
3830 372 : appendPQExpBufferStr(qry, ";\n");
3831 :
3832 372 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3833 372 : ARCHIVE_OPTS(.tag = "ENCODING",
3834 : .description = "ENCODING",
3835 : .section = SECTION_PRE_DATA,
3836 : .createStmt = qry->data));
3837 :
3838 372 : destroyPQExpBuffer(qry);
3839 372 : }
3840 :
3841 :
3842 : /*
3843 : * dumpStdStrings: put the correct escape string behavior into the archive
3844 : */
3845 : static void
3846 372 : dumpStdStrings(Archive *AH)
3847 : {
3848 372 : const char *stdstrings = AH->std_strings ? "on" : "off";
3849 372 : PQExpBuffer qry = createPQExpBuffer();
3850 :
3851 372 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3852 : stdstrings);
3853 :
3854 372 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3855 : stdstrings);
3856 :
3857 372 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3858 372 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3859 : .description = "STDSTRINGS",
3860 : .section = SECTION_PRE_DATA,
3861 : .createStmt = qry->data));
3862 :
3863 372 : destroyPQExpBuffer(qry);
3864 372 : }
3865 :
3866 : /*
3867 : * dumpSearchPath: record the active search_path in the archive
3868 : */
3869 : static void
3870 372 : dumpSearchPath(Archive *AH)
3871 : {
3872 372 : PQExpBuffer qry = createPQExpBuffer();
3873 372 : PQExpBuffer path = createPQExpBuffer();
3874 : PGresult *res;
3875 372 : char **schemanames = NULL;
3876 372 : int nschemanames = 0;
3877 : int i;
3878 :
3879 : /*
3880 : * We use the result of current_schemas(), not the search_path GUC,
3881 : * because that might contain wildcards such as "$user", which won't
3882 : * necessarily have the same value during restore. Also, this way avoids
3883 : * listing schemas that may appear in search_path but not actually exist,
3884 : * which seems like a prudent exclusion.
3885 : */
3886 372 : res = ExecuteSqlQueryForSingleRow(AH,
3887 : "SELECT pg_catalog.current_schemas(false)");
3888 :
3889 372 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3890 0 : pg_fatal("could not parse result of current_schemas()");
3891 :
3892 : /*
3893 : * We use set_config(), not a simple "SET search_path" command, because
3894 : * the latter has less-clean behavior if the search path is empty. While
3895 : * that's likely to get fixed at some point, it seems like a good idea to
3896 : * be as backwards-compatible as possible in what we put into archives.
3897 : */
3898 372 : for (i = 0; i < nschemanames; i++)
3899 : {
3900 0 : if (i > 0)
3901 0 : appendPQExpBufferStr(path, ", ");
3902 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3903 : }
3904 :
3905 372 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3906 372 : appendStringLiteralAH(qry, path->data, AH);
3907 372 : appendPQExpBufferStr(qry, ", false);\n");
3908 :
3909 372 : pg_log_info("saving \"search_path = %s\"", path->data);
3910 :
3911 372 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3912 372 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3913 : .description = "SEARCHPATH",
3914 : .section = SECTION_PRE_DATA,
3915 : .createStmt = qry->data));
3916 :
3917 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3918 372 : AH->searchpath = pg_strdup(qry->data);
3919 :
3920 372 : free(schemanames);
3921 372 : PQclear(res);
3922 372 : destroyPQExpBuffer(qry);
3923 372 : destroyPQExpBuffer(path);
3924 372 : }
3925 :
3926 :
3927 : /*
3928 : * getLOs:
3929 : * Collect schema-level data about large objects
3930 : */
3931 : static void
3932 316 : getLOs(Archive *fout)
3933 : {
3934 316 : DumpOptions *dopt = fout->dopt;
3935 316 : PQExpBuffer loQry = createPQExpBuffer();
3936 : PGresult *res;
3937 : int ntups;
3938 : int i;
3939 : int n;
3940 : int i_oid;
3941 : int i_lomowner;
3942 : int i_lomacl;
3943 : int i_acldefault;
3944 :
3945 316 : pg_log_info("reading large objects");
3946 :
3947 : /*
3948 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3949 : * with the same owner/ACL appear together.
3950 : */
3951 316 : appendPQExpBufferStr(loQry,
3952 : "SELECT oid, lomowner, lomacl, "
3953 : "acldefault('L', lomowner) AS acldefault "
3954 : "FROM pg_largeobject_metadata "
3955 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3956 :
3957 316 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3958 :
3959 316 : i_oid = PQfnumber(res, "oid");
3960 316 : i_lomowner = PQfnumber(res, "lomowner");
3961 316 : i_lomacl = PQfnumber(res, "lomacl");
3962 316 : i_acldefault = PQfnumber(res, "acldefault");
3963 :
3964 316 : ntups = PQntuples(res);
3965 :
3966 : /*
3967 : * Group the blobs into suitably-sized groups that have the same owner and
3968 : * ACL setting, and build a metadata and a data DumpableObject for each
3969 : * group. (If we supported initprivs for blobs, we'd have to insist that
3970 : * groups also share initprivs settings, since the DumpableObject only has
3971 : * room for one.) i is the index of the first tuple in the current group,
3972 : * and n is the number of tuples we include in the group.
3973 : */
3974 496 : for (i = 0; i < ntups; i += n)
3975 : {
3976 180 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3977 180 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3978 180 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3979 : LoInfo *loinfo;
3980 : DumpableObject *lodata;
3981 : char namebuf[64];
3982 :
3983 : /* Scan to find first tuple not to be included in group */
3984 180 : n = 1;
3985 210 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
3986 : {
3987 108 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
3988 98 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
3989 : break;
3990 30 : n++;
3991 : }
3992 :
3993 : /* Build the metadata DumpableObject */
3994 180 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
3995 :
3996 180 : loinfo->dobj.objType = DO_LARGE_OBJECT;
3997 180 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
3998 180 : loinfo->dobj.catId.oid = thisoid;
3999 180 : AssignDumpId(&loinfo->dobj);
4000 :
4001 180 : if (n > 1)
4002 20 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4003 20 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
4004 : else
4005 160 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4006 180 : loinfo->dobj.name = pg_strdup(namebuf);
4007 180 : loinfo->dacl.acl = pg_strdup(thisacl);
4008 180 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4009 180 : loinfo->dacl.privtype = 0;
4010 180 : loinfo->dacl.initprivs = NULL;
4011 180 : loinfo->rolname = getRoleName(thisowner);
4012 180 : loinfo->numlos = n;
4013 180 : loinfo->looids[0] = thisoid;
4014 : /* Collect OIDs of the remaining blobs in this group */
4015 210 : for (int k = 1; k < n; k++)
4016 : {
4017 : CatalogId extraID;
4018 :
4019 30 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4020 :
4021 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4022 30 : extraID.tableoid = LargeObjectRelationId;
4023 30 : extraID.oid = loinfo->looids[k];
4024 30 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4025 : }
4026 :
4027 : /* LOs have data */
4028 180 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4029 :
4030 : /* Mark whether LO group has a non-empty ACL */
4031 180 : if (!PQgetisnull(res, i, i_lomacl))
4032 78 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4033 :
4034 : /*
4035 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4036 : * as it will be copied by pg_upgrade, which simply copies the
4037 : * pg_largeobject table. We *do* however dump out anything but the
4038 : * data, as pg_upgrade copies just pg_largeobject, but not
4039 : * pg_largeobject_metadata, after the dump is restored. In versions
4040 : * before v12, this is done via proper large object commands. In
4041 : * newer versions, we dump the content of pg_largeobject_metadata and
4042 : * any associated pg_shdepend rows, which is faster to restore. (On
4043 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4044 : * column is hidden and won't be dumped.)
4045 : */
4046 180 : if (dopt->binary_upgrade)
4047 : {
4048 26 : if (fout->remoteVersion >= 120000)
4049 : {
4050 : /*
4051 : * We should've saved pg_largeobject_metadata's dump ID before
4052 : * this point.
4053 : */
4054 : Assert(lo_metadata_dumpId);
4055 :
4056 26 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4057 :
4058 : /*
4059 : * Mark the large object as dependent on
4060 : * pg_largeobject_metadata so that any large object
4061 : * comments/seclables are dumped after it.
4062 : */
4063 26 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4064 26 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
4065 26 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4066 : }
4067 : else
4068 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4069 : }
4070 :
4071 : /*
4072 : * Create a "BLOBS" data item for the group, too. This is just a
4073 : * placeholder for sorting; it carries no data now.
4074 : */
4075 180 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4076 180 : lodata->objType = DO_LARGE_OBJECT_DATA;
4077 180 : lodata->catId = nilCatalogId;
4078 180 : AssignDumpId(lodata);
4079 180 : lodata->name = pg_strdup(namebuf);
4080 180 : lodata->components |= DUMP_COMPONENT_DATA;
4081 : /* Set up explicit dependency from data to metadata */
4082 180 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4083 180 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4084 180 : lodata->nDeps = lodata->allocDeps = 1;
4085 : }
4086 :
4087 316 : PQclear(res);
4088 316 : destroyPQExpBuffer(loQry);
4089 316 : }
4090 :
4091 : /*
4092 : * dumpLO
4093 : *
4094 : * dump the definition (metadata) of the given large object group
4095 : */
4096 : static void
4097 168 : dumpLO(Archive *fout, const LoInfo *loinfo)
4098 : {
4099 168 : PQExpBuffer cquery = createPQExpBuffer();
4100 :
4101 : /*
4102 : * The "definition" is just a newline-separated list of OIDs. We need to
4103 : * put something into the dropStmt too, but it can just be a comment.
4104 : */
4105 366 : for (int i = 0; i < loinfo->numlos; i++)
4106 198 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4107 :
4108 168 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4109 154 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4110 154 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4111 : .owner = loinfo->rolname,
4112 : .description = "BLOB METADATA",
4113 : .section = SECTION_DATA,
4114 : .createStmt = cquery->data,
4115 : .dropStmt = "-- dummy"));
4116 :
4117 : /*
4118 : * Dump per-blob comments and seclabels if any. We assume these are rare
4119 : * enough that it's okay to generate retail TOC entries for them.
4120 : */
4121 168 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4122 : DUMP_COMPONENT_SECLABEL))
4123 : {
4124 206 : for (int i = 0; i < loinfo->numlos; i++)
4125 : {
4126 : CatalogId catId;
4127 : char namebuf[32];
4128 :
4129 : /* Build identifying info for this blob */
4130 118 : catId.tableoid = loinfo->dobj.catId.tableoid;
4131 118 : catId.oid = loinfo->looids[i];
4132 118 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4133 :
4134 118 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4135 118 : dumpComment(fout, "LARGE OBJECT", namebuf,
4136 118 : NULL, loinfo->rolname,
4137 118 : catId, 0, loinfo->dobj.dumpId);
4138 :
4139 118 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4140 20 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4141 20 : NULL, loinfo->rolname,
4142 20 : catId, 0, loinfo->dobj.dumpId);
4143 : }
4144 : }
4145 :
4146 : /*
4147 : * Dump the ACLs if any (remember that all blobs in the group will have
4148 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4149 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4150 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4151 : * string to emit a mutated version for each blob.
4152 : */
4153 168 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4154 : {
4155 : char namebuf[32];
4156 :
4157 : /* Build identifying info for the first blob */
4158 66 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4159 :
4160 66 : if (loinfo->numlos > 1)
4161 : {
4162 : char tagbuf[64];
4163 :
4164 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4165 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4166 :
4167 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4168 : "LARGE OBJECT", namebuf, NULL, NULL,
4169 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4170 : }
4171 : else
4172 : {
4173 66 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4174 : "LARGE OBJECT", namebuf, NULL, NULL,
4175 66 : NULL, loinfo->rolname, &loinfo->dacl);
4176 : }
4177 : }
4178 :
4179 168 : destroyPQExpBuffer(cquery);
4180 168 : }
4181 :
4182 : /*
4183 : * dumpLOs:
4184 : * dump the data contents of the large objects in the given group
4185 : */
4186 : static int
4187 146 : dumpLOs(Archive *fout, const void *arg)
4188 : {
4189 146 : const LoInfo *loinfo = (const LoInfo *) arg;
4190 146 : PGconn *conn = GetConnection(fout);
4191 : char buf[LOBBUFSIZE];
4192 :
4193 146 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4194 :
4195 308 : for (int i = 0; i < loinfo->numlos; i++)
4196 : {
4197 162 : Oid loOid = loinfo->looids[i];
4198 : int loFd;
4199 : int cnt;
4200 :
4201 : /* Open the LO */
4202 162 : loFd = lo_open(conn, loOid, INV_READ);
4203 162 : if (loFd == -1)
4204 0 : pg_fatal("could not open large object %u: %s",
4205 : loOid, PQerrorMessage(conn));
4206 :
4207 162 : StartLO(fout, loOid);
4208 :
4209 : /* Now read it in chunks, sending data to archive */
4210 : do
4211 : {
4212 254 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4213 254 : if (cnt < 0)
4214 0 : pg_fatal("error reading large object %u: %s",
4215 : loOid, PQerrorMessage(conn));
4216 :
4217 254 : WriteData(fout, buf, cnt);
4218 254 : } while (cnt > 0);
4219 :
4220 162 : lo_close(conn, loFd);
4221 :
4222 162 : EndLO(fout, loOid);
4223 : }
4224 :
4225 146 : return 1;
4226 : }
4227 :
4228 : /*
4229 : * getPolicies
4230 : * get information about all RLS policies on dumpable tables.
4231 : */
4232 : void
4233 372 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4234 : {
4235 372 : DumpOptions *dopt = fout->dopt;
4236 : PQExpBuffer query;
4237 : PQExpBuffer tbloids;
4238 : PGresult *res;
4239 : PolicyInfo *polinfo;
4240 : int i_oid;
4241 : int i_tableoid;
4242 : int i_polrelid;
4243 : int i_polname;
4244 : int i_polcmd;
4245 : int i_polpermissive;
4246 : int i_polroles;
4247 : int i_polqual;
4248 : int i_polwithcheck;
4249 : int i,
4250 : j,
4251 : ntups;
4252 :
4253 : /* No policies before 9.5 */
4254 372 : if (fout->remoteVersion < 90500)
4255 0 : return;
4256 :
4257 : /* Skip if --no-policies was specified */
4258 372 : if (dopt->no_policies)
4259 2 : return;
4260 :
4261 370 : query = createPQExpBuffer();
4262 370 : tbloids = createPQExpBuffer();
4263 :
4264 : /*
4265 : * Identify tables of interest, and check which ones have RLS enabled.
4266 : */
4267 370 : appendPQExpBufferChar(tbloids, '{');
4268 97806 : for (i = 0; i < numTables; i++)
4269 : {
4270 97436 : TableInfo *tbinfo = &tblinfo[i];
4271 :
4272 : /* Ignore row security on tables not to be dumped */
4273 97436 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4274 83722 : continue;
4275 :
4276 : /* It can't have RLS or policies if it's not a table */
4277 13714 : if (tbinfo->relkind != RELKIND_RELATION &&
4278 3872 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4279 2700 : continue;
4280 :
4281 : /* Add it to the list of table OIDs to be probed below */
4282 11014 : if (tbloids->len > 1) /* do we have more than the '{'? */
4283 10772 : appendPQExpBufferChar(tbloids, ',');
4284 11014 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4285 :
4286 : /* Is RLS enabled? (That's separate from whether it has policies) */
4287 11014 : if (tbinfo->rowsec)
4288 : {
4289 106 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4290 :
4291 : /*
4292 : * We represent RLS being enabled on a table by creating a
4293 : * PolicyInfo object with null polname.
4294 : *
4295 : * Note: use tableoid 0 so that this object won't be mistaken for
4296 : * something that pg_depend entries apply to.
4297 : */
4298 106 : polinfo = pg_malloc(sizeof(PolicyInfo));
4299 106 : polinfo->dobj.objType = DO_POLICY;
4300 106 : polinfo->dobj.catId.tableoid = 0;
4301 106 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4302 106 : AssignDumpId(&polinfo->dobj);
4303 106 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4304 106 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4305 106 : polinfo->poltable = tbinfo;
4306 106 : polinfo->polname = NULL;
4307 106 : polinfo->polcmd = '\0';
4308 106 : polinfo->polpermissive = 0;
4309 106 : polinfo->polroles = NULL;
4310 106 : polinfo->polqual = NULL;
4311 106 : polinfo->polwithcheck = NULL;
4312 : }
4313 : }
4314 370 : appendPQExpBufferChar(tbloids, '}');
4315 :
4316 : /*
4317 : * Now, read all RLS policies belonging to the tables of interest, and
4318 : * create PolicyInfo objects for them. (Note that we must filter the
4319 : * results server-side not locally, because we dare not apply pg_get_expr
4320 : * to tables we don't have lock on.)
4321 : */
4322 370 : pg_log_info("reading row-level security policies");
4323 :
4324 370 : printfPQExpBuffer(query,
4325 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4326 370 : if (fout->remoteVersion >= 100000)
4327 370 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4328 : else
4329 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4330 370 : appendPQExpBuffer(query,
4331 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4332 : " 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, "
4333 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4334 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4335 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4336 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4337 : tbloids->data);
4338 :
4339 370 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4340 :
4341 370 : ntups = PQntuples(res);
4342 370 : if (ntups > 0)
4343 : {
4344 86 : i_oid = PQfnumber(res, "oid");
4345 86 : i_tableoid = PQfnumber(res, "tableoid");
4346 86 : i_polrelid = PQfnumber(res, "polrelid");
4347 86 : i_polname = PQfnumber(res, "polname");
4348 86 : i_polcmd = PQfnumber(res, "polcmd");
4349 86 : i_polpermissive = PQfnumber(res, "polpermissive");
4350 86 : i_polroles = PQfnumber(res, "polroles");
4351 86 : i_polqual = PQfnumber(res, "polqual");
4352 86 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4353 :
4354 86 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4355 :
4356 632 : for (j = 0; j < ntups; j++)
4357 : {
4358 546 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4359 546 : TableInfo *tbinfo = findTableByOid(polrelid);
4360 :
4361 546 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4362 :
4363 546 : polinfo[j].dobj.objType = DO_POLICY;
4364 546 : polinfo[j].dobj.catId.tableoid =
4365 546 : atooid(PQgetvalue(res, j, i_tableoid));
4366 546 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4367 546 : AssignDumpId(&polinfo[j].dobj);
4368 546 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4369 546 : polinfo[j].poltable = tbinfo;
4370 546 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4371 546 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4372 :
4373 546 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4374 546 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4375 :
4376 546 : if (PQgetisnull(res, j, i_polroles))
4377 242 : polinfo[j].polroles = NULL;
4378 : else
4379 304 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4380 :
4381 546 : if (PQgetisnull(res, j, i_polqual))
4382 76 : polinfo[j].polqual = NULL;
4383 : else
4384 470 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4385 :
4386 546 : if (PQgetisnull(res, j, i_polwithcheck))
4387 288 : polinfo[j].polwithcheck = NULL;
4388 : else
4389 258 : polinfo[j].polwithcheck
4390 258 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4391 : }
4392 : }
4393 :
4394 370 : PQclear(res);
4395 :
4396 370 : destroyPQExpBuffer(query);
4397 370 : destroyPQExpBuffer(tbloids);
4398 : }
4399 :
4400 : /*
4401 : * dumpPolicy
4402 : * dump the definition of the given policy
4403 : */
4404 : static void
4405 652 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4406 : {
4407 652 : DumpOptions *dopt = fout->dopt;
4408 652 : TableInfo *tbinfo = polinfo->poltable;
4409 : PQExpBuffer query;
4410 : PQExpBuffer delqry;
4411 : PQExpBuffer polprefix;
4412 : char *qtabname;
4413 : const char *cmd;
4414 : char *tag;
4415 :
4416 : /* Do nothing if not dumping schema */
4417 652 : if (!dopt->dumpSchema)
4418 98 : return;
4419 :
4420 : /*
4421 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4422 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4423 : * ROW LEVEL SECURITY.
4424 : */
4425 554 : if (polinfo->polname == NULL)
4426 : {
4427 92 : query = createPQExpBuffer();
4428 :
4429 92 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4430 92 : fmtQualifiedDumpable(tbinfo));
4431 :
4432 : /*
4433 : * We must emit the ROW SECURITY object's dependency on its table
4434 : * explicitly, because it will not match anything in pg_depend (unlike
4435 : * the case for other PolicyInfo objects).
4436 : */
4437 92 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4438 92 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4439 92 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4440 : .namespace = polinfo->dobj.namespace->dobj.name,
4441 : .owner = tbinfo->rolname,
4442 : .description = "ROW SECURITY",
4443 : .section = SECTION_POST_DATA,
4444 : .createStmt = query->data,
4445 : .deps = &(tbinfo->dobj.dumpId),
4446 : .nDeps = 1));
4447 :
4448 92 : destroyPQExpBuffer(query);
4449 92 : return;
4450 : }
4451 :
4452 462 : if (polinfo->polcmd == '*')
4453 154 : cmd = "";
4454 308 : else if (polinfo->polcmd == 'r')
4455 82 : cmd = " FOR SELECT";
4456 226 : else if (polinfo->polcmd == 'a')
4457 62 : cmd = " FOR INSERT";
4458 164 : else if (polinfo->polcmd == 'w')
4459 82 : cmd = " FOR UPDATE";
4460 82 : else if (polinfo->polcmd == 'd')
4461 82 : cmd = " FOR DELETE";
4462 : else
4463 0 : pg_fatal("unexpected policy command type: %c",
4464 : polinfo->polcmd);
4465 :
4466 462 : query = createPQExpBuffer();
4467 462 : delqry = createPQExpBuffer();
4468 462 : polprefix = createPQExpBuffer();
4469 :
4470 462 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4471 :
4472 462 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4473 :
4474 462 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4475 462 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4476 :
4477 462 : if (polinfo->polroles != NULL)
4478 248 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4479 :
4480 462 : if (polinfo->polqual != NULL)
4481 400 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4482 :
4483 462 : if (polinfo->polwithcheck != NULL)
4484 216 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4485 :
4486 462 : appendPQExpBufferStr(query, ";\n");
4487 :
4488 462 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4489 462 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4490 :
4491 462 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4492 462 : fmtId(polinfo->polname));
4493 :
4494 462 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4495 :
4496 462 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4497 462 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4498 462 : ARCHIVE_OPTS(.tag = tag,
4499 : .namespace = polinfo->dobj.namespace->dobj.name,
4500 : .owner = tbinfo->rolname,
4501 : .description = "POLICY",
4502 : .section = SECTION_POST_DATA,
4503 : .createStmt = query->data,
4504 : .dropStmt = delqry->data));
4505 :
4506 462 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4507 62 : dumpComment(fout, polprefix->data, qtabname,
4508 62 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4509 62 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4510 :
4511 462 : free(tag);
4512 462 : destroyPQExpBuffer(query);
4513 462 : destroyPQExpBuffer(delqry);
4514 462 : destroyPQExpBuffer(polprefix);
4515 462 : free(qtabname);
4516 : }
4517 :
4518 : /*
4519 : * getPublications
4520 : * get information about publications
4521 : */
4522 : void
4523 372 : getPublications(Archive *fout)
4524 : {
4525 372 : DumpOptions *dopt = fout->dopt;
4526 : PQExpBuffer query;
4527 : PGresult *res;
4528 : PublicationInfo *pubinfo;
4529 : int i_tableoid;
4530 : int i_oid;
4531 : int i_pubname;
4532 : int i_pubowner;
4533 : int i_puballtables;
4534 : int i_puballsequences;
4535 : int i_pubinsert;
4536 : int i_pubupdate;
4537 : int i_pubdelete;
4538 : int i_pubtruncate;
4539 : int i_pubviaroot;
4540 : int i_pubgencols;
4541 : int i,
4542 : ntups;
4543 :
4544 372 : if (dopt->no_publications || fout->remoteVersion < 100000)
4545 0 : return;
4546 :
4547 372 : query = createPQExpBuffer();
4548 :
4549 : /* Get the publications. */
4550 372 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4551 : "p.pubowner, p.puballtables, p.pubinsert, "
4552 : "p.pubupdate, p.pubdelete, ");
4553 :
4554 372 : if (fout->remoteVersion >= 110000)
4555 372 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4556 : else
4557 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4558 :
4559 372 : if (fout->remoteVersion >= 130000)
4560 372 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4561 : else
4562 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4563 :
4564 372 : if (fout->remoteVersion >= 180000)
4565 372 : appendPQExpBufferStr(query, "p.pubgencols, ");
4566 : else
4567 0 : appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4568 :
4569 372 : if (fout->remoteVersion >= 190000)
4570 372 : appendPQExpBufferStr(query, "p.puballsequences ");
4571 : else
4572 0 : appendPQExpBufferStr(query, "false AS puballsequences ");
4573 :
4574 372 : appendPQExpBufferStr(query, "FROM pg_publication p");
4575 :
4576 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4577 :
4578 372 : ntups = PQntuples(res);
4579 :
4580 372 : if (ntups == 0)
4581 266 : goto cleanup;
4582 :
4583 106 : i_tableoid = PQfnumber(res, "tableoid");
4584 106 : i_oid = PQfnumber(res, "oid");
4585 106 : i_pubname = PQfnumber(res, "pubname");
4586 106 : i_pubowner = PQfnumber(res, "pubowner");
4587 106 : i_puballtables = PQfnumber(res, "puballtables");
4588 106 : i_puballsequences = PQfnumber(res, "puballsequences");
4589 106 : i_pubinsert = PQfnumber(res, "pubinsert");
4590 106 : i_pubupdate = PQfnumber(res, "pubupdate");
4591 106 : i_pubdelete = PQfnumber(res, "pubdelete");
4592 106 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4593 106 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4594 106 : i_pubgencols = PQfnumber(res, "pubgencols");
4595 :
4596 106 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4597 :
4598 808 : for (i = 0; i < ntups; i++)
4599 : {
4600 702 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4601 702 : pubinfo[i].dobj.catId.tableoid =
4602 702 : atooid(PQgetvalue(res, i, i_tableoid));
4603 702 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4604 702 : AssignDumpId(&pubinfo[i].dobj);
4605 702 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4606 702 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4607 702 : pubinfo[i].puballtables =
4608 702 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4609 702 : pubinfo[i].puballsequences =
4610 702 : (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4611 702 : pubinfo[i].pubinsert =
4612 702 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4613 702 : pubinfo[i].pubupdate =
4614 702 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4615 702 : pubinfo[i].pubdelete =
4616 702 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4617 702 : pubinfo[i].pubtruncate =
4618 702 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4619 702 : pubinfo[i].pubviaroot =
4620 702 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4621 702 : pubinfo[i].pubgencols_type =
4622 702 : *(PQgetvalue(res, i, i_pubgencols));
4623 :
4624 : /* Decide whether we want to dump it */
4625 702 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4626 : }
4627 :
4628 106 : cleanup:
4629 372 : PQclear(res);
4630 :
4631 372 : destroyPQExpBuffer(query);
4632 : }
4633 :
4634 : /*
4635 : * dumpPublication
4636 : * dump the definition of the given publication
4637 : */
4638 : static void
4639 570 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4640 : {
4641 570 : DumpOptions *dopt = fout->dopt;
4642 : PQExpBuffer delq;
4643 : PQExpBuffer query;
4644 : char *qpubname;
4645 570 : bool first = true;
4646 :
4647 : /* Do nothing if not dumping schema */
4648 570 : if (!dopt->dumpSchema)
4649 84 : return;
4650 :
4651 486 : delq = createPQExpBuffer();
4652 486 : query = createPQExpBuffer();
4653 :
4654 486 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4655 :
4656 486 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4657 : qpubname);
4658 :
4659 486 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4660 : qpubname);
4661 :
4662 486 : if (pubinfo->puballtables && pubinfo->puballsequences)
4663 62 : appendPQExpBufferStr(query, " FOR ALL TABLES, ALL SEQUENCES");
4664 424 : else if (pubinfo->puballtables)
4665 64 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4666 360 : else if (pubinfo->puballsequences)
4667 62 : appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4668 :
4669 486 : appendPQExpBufferStr(query, " WITH (publish = '");
4670 486 : if (pubinfo->pubinsert)
4671 : {
4672 362 : appendPQExpBufferStr(query, "insert");
4673 362 : first = false;
4674 : }
4675 :
4676 486 : if (pubinfo->pubupdate)
4677 : {
4678 362 : if (!first)
4679 362 : appendPQExpBufferStr(query, ", ");
4680 :
4681 362 : appendPQExpBufferStr(query, "update");
4682 362 : first = false;
4683 : }
4684 :
4685 486 : if (pubinfo->pubdelete)
4686 : {
4687 362 : if (!first)
4688 362 : appendPQExpBufferStr(query, ", ");
4689 :
4690 362 : appendPQExpBufferStr(query, "delete");
4691 362 : first = false;
4692 : }
4693 :
4694 486 : if (pubinfo->pubtruncate)
4695 : {
4696 362 : if (!first)
4697 362 : appendPQExpBufferStr(query, ", ");
4698 :
4699 362 : appendPQExpBufferStr(query, "truncate");
4700 362 : first = false;
4701 : }
4702 :
4703 486 : appendPQExpBufferChar(query, '\'');
4704 :
4705 486 : if (pubinfo->pubviaroot)
4706 10 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4707 :
4708 486 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4709 62 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4710 :
4711 486 : appendPQExpBufferStr(query, ");\n");
4712 :
4713 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4714 486 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4715 486 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4716 : .owner = pubinfo->rolname,
4717 : .description = "PUBLICATION",
4718 : .section = SECTION_POST_DATA,
4719 : .createStmt = query->data,
4720 : .dropStmt = delq->data));
4721 :
4722 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4723 62 : dumpComment(fout, "PUBLICATION", qpubname,
4724 62 : NULL, pubinfo->rolname,
4725 62 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4726 :
4727 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4728 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4729 0 : NULL, pubinfo->rolname,
4730 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4731 :
4732 486 : destroyPQExpBuffer(delq);
4733 486 : destroyPQExpBuffer(query);
4734 486 : free(qpubname);
4735 : }
4736 :
4737 : /*
4738 : * getPublicationNamespaces
4739 : * get information about publication membership for dumpable schemas.
4740 : */
4741 : void
4742 372 : getPublicationNamespaces(Archive *fout)
4743 : {
4744 : PQExpBuffer query;
4745 : PGresult *res;
4746 : PublicationSchemaInfo *pubsinfo;
4747 372 : DumpOptions *dopt = fout->dopt;
4748 : int i_tableoid;
4749 : int i_oid;
4750 : int i_pnpubid;
4751 : int i_pnnspid;
4752 : int i,
4753 : j,
4754 : ntups;
4755 :
4756 372 : if (dopt->no_publications || fout->remoteVersion < 150000)
4757 0 : return;
4758 :
4759 372 : query = createPQExpBuffer();
4760 :
4761 : /* Collect all publication membership info. */
4762 372 : appendPQExpBufferStr(query,
4763 : "SELECT tableoid, oid, pnpubid, pnnspid "
4764 : "FROM pg_catalog.pg_publication_namespace");
4765 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4766 :
4767 372 : ntups = PQntuples(res);
4768 :
4769 372 : i_tableoid = PQfnumber(res, "tableoid");
4770 372 : i_oid = PQfnumber(res, "oid");
4771 372 : i_pnpubid = PQfnumber(res, "pnpubid");
4772 372 : i_pnnspid = PQfnumber(res, "pnnspid");
4773 :
4774 : /* this allocation may be more than we need */
4775 372 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4776 372 : j = 0;
4777 :
4778 622 : for (i = 0; i < ntups; i++)
4779 : {
4780 250 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4781 250 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4782 : PublicationInfo *pubinfo;
4783 : NamespaceInfo *nspinfo;
4784 :
4785 : /*
4786 : * Ignore any entries for which we aren't interested in either the
4787 : * publication or the rel.
4788 : */
4789 250 : pubinfo = findPublicationByOid(pnpubid);
4790 250 : if (pubinfo == NULL)
4791 0 : continue;
4792 250 : nspinfo = findNamespaceByOid(pnnspid);
4793 250 : if (nspinfo == NULL)
4794 0 : continue;
4795 :
4796 : /* OK, make a DumpableObject for this relationship */
4797 250 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4798 250 : pubsinfo[j].dobj.catId.tableoid =
4799 250 : atooid(PQgetvalue(res, i, i_tableoid));
4800 250 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4801 250 : AssignDumpId(&pubsinfo[j].dobj);
4802 250 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4803 250 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4804 250 : pubsinfo[j].publication = pubinfo;
4805 250 : pubsinfo[j].pubschema = nspinfo;
4806 :
4807 : /* Decide whether we want to dump it */
4808 250 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4809 :
4810 250 : j++;
4811 : }
4812 :
4813 372 : PQclear(res);
4814 372 : destroyPQExpBuffer(query);
4815 : }
4816 :
4817 : /*
4818 : * getPublicationTables
4819 : * get information about publication membership for dumpable tables.
4820 : */
4821 : void
4822 372 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4823 : {
4824 : PQExpBuffer query;
4825 : PGresult *res;
4826 : PublicationRelInfo *pubrinfo;
4827 372 : DumpOptions *dopt = fout->dopt;
4828 : int i_tableoid;
4829 : int i_oid;
4830 : int i_prpubid;
4831 : int i_prrelid;
4832 : int i_prrelqual;
4833 : int i_prattrs;
4834 : int i,
4835 : j,
4836 : ntups;
4837 :
4838 372 : if (dopt->no_publications || fout->remoteVersion < 100000)
4839 0 : return;
4840 :
4841 372 : query = createPQExpBuffer();
4842 :
4843 : /* Collect all publication membership info. */
4844 372 : if (fout->remoteVersion >= 150000)
4845 372 : appendPQExpBufferStr(query,
4846 : "SELECT tableoid, oid, prpubid, prrelid, "
4847 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4848 : "(CASE\n"
4849 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4850 : " (SELECT array_agg(attname)\n"
4851 : " FROM\n"
4852 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4853 : " pg_catalog.pg_attribute\n"
4854 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4855 : " ELSE NULL END) prattrs "
4856 : "FROM pg_catalog.pg_publication_rel pr");
4857 : else
4858 0 : appendPQExpBufferStr(query,
4859 : "SELECT tableoid, oid, prpubid, prrelid, "
4860 : "NULL AS prrelqual, NULL AS prattrs "
4861 : "FROM pg_catalog.pg_publication_rel");
4862 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4863 :
4864 372 : ntups = PQntuples(res);
4865 :
4866 372 : i_tableoid = PQfnumber(res, "tableoid");
4867 372 : i_oid = PQfnumber(res, "oid");
4868 372 : i_prpubid = PQfnumber(res, "prpubid");
4869 372 : i_prrelid = PQfnumber(res, "prrelid");
4870 372 : i_prrelqual = PQfnumber(res, "prrelqual");
4871 372 : i_prattrs = PQfnumber(res, "prattrs");
4872 :
4873 : /* this allocation may be more than we need */
4874 372 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4875 372 : j = 0;
4876 :
4877 1072 : for (i = 0; i < ntups; i++)
4878 : {
4879 700 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4880 700 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4881 : PublicationInfo *pubinfo;
4882 : TableInfo *tbinfo;
4883 :
4884 : /*
4885 : * Ignore any entries for which we aren't interested in either the
4886 : * publication or the rel.
4887 : */
4888 700 : pubinfo = findPublicationByOid(prpubid);
4889 700 : if (pubinfo == NULL)
4890 0 : continue;
4891 700 : tbinfo = findTableByOid(prrelid);
4892 700 : if (tbinfo == NULL)
4893 0 : continue;
4894 :
4895 : /* OK, make a DumpableObject for this relationship */
4896 700 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4897 700 : pubrinfo[j].dobj.catId.tableoid =
4898 700 : atooid(PQgetvalue(res, i, i_tableoid));
4899 700 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4900 700 : AssignDumpId(&pubrinfo[j].dobj);
4901 700 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4902 700 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4903 700 : pubrinfo[j].publication = pubinfo;
4904 700 : pubrinfo[j].pubtable = tbinfo;
4905 700 : if (PQgetisnull(res, i, i_prrelqual))
4906 388 : pubrinfo[j].pubrelqual = NULL;
4907 : else
4908 312 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4909 :
4910 700 : if (!PQgetisnull(res, i, i_prattrs))
4911 : {
4912 : char **attnames;
4913 : int nattnames;
4914 : PQExpBuffer attribs;
4915 :
4916 222 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4917 : &attnames, &nattnames))
4918 0 : pg_fatal("could not parse %s array", "prattrs");
4919 222 : attribs = createPQExpBuffer();
4920 638 : for (int k = 0; k < nattnames; k++)
4921 : {
4922 416 : if (k > 0)
4923 194 : appendPQExpBufferStr(attribs, ", ");
4924 :
4925 416 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4926 : }
4927 222 : pubrinfo[j].pubrattrs = attribs->data;
4928 222 : free(attribs); /* but not attribs->data */
4929 222 : free(attnames);
4930 : }
4931 : else
4932 478 : pubrinfo[j].pubrattrs = NULL;
4933 :
4934 : /* Decide whether we want to dump it */
4935 700 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4936 :
4937 700 : j++;
4938 : }
4939 :
4940 372 : PQclear(res);
4941 372 : destroyPQExpBuffer(query);
4942 : }
4943 :
4944 : /*
4945 : * dumpPublicationNamespace
4946 : * dump the definition of the given publication schema mapping.
4947 : */
4948 : static void
4949 198 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4950 : {
4951 198 : DumpOptions *dopt = fout->dopt;
4952 198 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4953 198 : PublicationInfo *pubinfo = pubsinfo->publication;
4954 : PQExpBuffer query;
4955 : char *tag;
4956 :
4957 : /* Do nothing if not dumping schema */
4958 198 : if (!dopt->dumpSchema)
4959 24 : return;
4960 :
4961 174 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4962 :
4963 174 : query = createPQExpBuffer();
4964 :
4965 174 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4966 174 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4967 :
4968 : /*
4969 : * There is no point in creating drop query as the drop is done by schema
4970 : * drop.
4971 : */
4972 174 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4973 174 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4974 174 : ARCHIVE_OPTS(.tag = tag,
4975 : .namespace = schemainfo->dobj.name,
4976 : .owner = pubinfo->rolname,
4977 : .description = "PUBLICATION TABLES IN SCHEMA",
4978 : .section = SECTION_POST_DATA,
4979 : .createStmt = query->data));
4980 :
4981 : /* These objects can't currently have comments or seclabels */
4982 :
4983 174 : free(tag);
4984 174 : destroyPQExpBuffer(query);
4985 : }
4986 :
4987 : /*
4988 : * dumpPublicationTable
4989 : * dump the definition of the given publication table mapping
4990 : */
4991 : static void
4992 568 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
4993 : {
4994 568 : DumpOptions *dopt = fout->dopt;
4995 568 : PublicationInfo *pubinfo = pubrinfo->publication;
4996 568 : TableInfo *tbinfo = pubrinfo->pubtable;
4997 : PQExpBuffer query;
4998 : char *tag;
4999 :
5000 : /* Do nothing if not dumping schema */
5001 568 : if (!dopt->dumpSchema)
5002 84 : return;
5003 :
5004 484 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5005 :
5006 484 : query = createPQExpBuffer();
5007 :
5008 484 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5009 484 : fmtId(pubinfo->dobj.name));
5010 484 : appendPQExpBuffer(query, " %s",
5011 484 : fmtQualifiedDumpable(tbinfo));
5012 :
5013 484 : if (pubrinfo->pubrattrs)
5014 154 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5015 :
5016 484 : if (pubrinfo->pubrelqual)
5017 : {
5018 : /*
5019 : * It's necessary to add parentheses around the expression because
5020 : * pg_get_expr won't supply the parentheses for things like WHERE
5021 : * TRUE.
5022 : */
5023 216 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5024 : }
5025 484 : appendPQExpBufferStr(query, ";\n");
5026 :
5027 : /*
5028 : * There is no point in creating a drop query as the drop is done by table
5029 : * drop. (If you think to change this, see also _printTocEntry().)
5030 : * Although this object doesn't really have ownership as such, set the
5031 : * owner field anyway to ensure that the command is run by the correct
5032 : * role at restore time.
5033 : */
5034 484 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5035 484 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5036 484 : ARCHIVE_OPTS(.tag = tag,
5037 : .namespace = tbinfo->dobj.namespace->dobj.name,
5038 : .owner = pubinfo->rolname,
5039 : .description = "PUBLICATION TABLE",
5040 : .section = SECTION_POST_DATA,
5041 : .createStmt = query->data));
5042 :
5043 : /* These objects can't currently have comments or seclabels */
5044 :
5045 484 : free(tag);
5046 484 : destroyPQExpBuffer(query);
5047 : }
5048 :
5049 : /*
5050 : * Is the currently connected user a superuser?
5051 : */
5052 : static bool
5053 370 : is_superuser(Archive *fout)
5054 : {
5055 370 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5056 : const char *val;
5057 :
5058 370 : val = PQparameterStatus(AH->connection, "is_superuser");
5059 :
5060 370 : if (val && strcmp(val, "on") == 0)
5061 364 : return true;
5062 :
5063 6 : return false;
5064 : }
5065 :
5066 : /*
5067 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5068 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5069 : * the setting query is effective only where available.
5070 : */
5071 : static void
5072 440 : set_restrict_relation_kind(Archive *AH, const char *value)
5073 : {
5074 440 : PQExpBuffer query = createPQExpBuffer();
5075 : PGresult *res;
5076 :
5077 440 : appendPQExpBuffer(query,
5078 : "SELECT set_config(name, '%s', false) "
5079 : "FROM pg_settings "
5080 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5081 : value);
5082 440 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5083 :
5084 440 : PQclear(res);
5085 440 : destroyPQExpBuffer(query);
5086 440 : }
5087 :
5088 : /*
5089 : * getSubscriptions
5090 : * get information about subscriptions
5091 : */
5092 : void
5093 372 : getSubscriptions(Archive *fout)
5094 : {
5095 372 : DumpOptions *dopt = fout->dopt;
5096 : PQExpBuffer query;
5097 : PGresult *res;
5098 : SubscriptionInfo *subinfo;
5099 : int i_tableoid;
5100 : int i_oid;
5101 : int i_subname;
5102 : int i_subowner;
5103 : int i_subbinary;
5104 : int i_substream;
5105 : int i_subtwophasestate;
5106 : int i_subdisableonerr;
5107 : int i_subpasswordrequired;
5108 : int i_subrunasowner;
5109 : int i_subconninfo;
5110 : int i_subslotname;
5111 : int i_subsynccommit;
5112 : int i_subpublications;
5113 : int i_suborigin;
5114 : int i_suboriginremotelsn;
5115 : int i_subenabled;
5116 : int i_subfailover;
5117 : int i_subretaindeadtuples;
5118 : int i_submaxretention;
5119 : int i,
5120 : ntups;
5121 :
5122 372 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5123 2 : return;
5124 :
5125 370 : if (!is_superuser(fout))
5126 : {
5127 : int n;
5128 :
5129 6 : res = ExecuteSqlQuery(fout,
5130 : "SELECT count(*) FROM pg_subscription "
5131 : "WHERE subdbid = (SELECT oid FROM pg_database"
5132 : " WHERE datname = current_database())",
5133 : PGRES_TUPLES_OK);
5134 6 : n = atoi(PQgetvalue(res, 0, 0));
5135 6 : if (n > 0)
5136 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5137 6 : PQclear(res);
5138 6 : return;
5139 : }
5140 :
5141 364 : query = createPQExpBuffer();
5142 :
5143 : /* Get the subscriptions in current database. */
5144 364 : appendPQExpBufferStr(query,
5145 : "SELECT s.tableoid, s.oid, s.subname,\n"
5146 : " s.subowner,\n"
5147 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5148 : " s.subpublications,\n");
5149 :
5150 364 : if (fout->remoteVersion >= 140000)
5151 364 : appendPQExpBufferStr(query, " s.subbinary,\n");
5152 : else
5153 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5154 :
5155 364 : if (fout->remoteVersion >= 140000)
5156 364 : appendPQExpBufferStr(query, " s.substream,\n");
5157 : else
5158 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5159 :
5160 364 : if (fout->remoteVersion >= 150000)
5161 364 : appendPQExpBufferStr(query,
5162 : " s.subtwophasestate,\n"
5163 : " s.subdisableonerr,\n");
5164 : else
5165 0 : appendPQExpBuffer(query,
5166 : " '%c' AS subtwophasestate,\n"
5167 : " false AS subdisableonerr,\n",
5168 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5169 :
5170 364 : if (fout->remoteVersion >= 160000)
5171 364 : appendPQExpBufferStr(query,
5172 : " s.subpasswordrequired,\n"
5173 : " s.subrunasowner,\n"
5174 : " s.suborigin,\n");
5175 : else
5176 0 : appendPQExpBuffer(query,
5177 : " 't' AS subpasswordrequired,\n"
5178 : " 't' AS subrunasowner,\n"
5179 : " '%s' AS suborigin,\n",
5180 : LOGICALREP_ORIGIN_ANY);
5181 :
5182 364 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5183 72 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5184 : " s.subenabled,\n");
5185 : else
5186 292 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5187 : " false AS subenabled,\n");
5188 :
5189 364 : if (fout->remoteVersion >= 170000)
5190 364 : appendPQExpBufferStr(query,
5191 : " s.subfailover,\n");
5192 : else
5193 0 : appendPQExpBufferStr(query,
5194 : " false AS subfailover,\n");
5195 :
5196 364 : if (fout->remoteVersion >= 190000)
5197 364 : appendPQExpBufferStr(query,
5198 : " s.subretaindeadtuples,\n");
5199 : else
5200 0 : appendPQExpBufferStr(query,
5201 : " false AS subretaindeadtuples,\n");
5202 :
5203 364 : if (fout->remoteVersion >= 190000)
5204 364 : appendPQExpBufferStr(query,
5205 : " s.submaxretention\n");
5206 : else
5207 0 : appendPQExpBuffer(query,
5208 : " 0 AS submaxretention\n");
5209 :
5210 364 : appendPQExpBufferStr(query,
5211 : "FROM pg_subscription s\n");
5212 :
5213 364 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5214 72 : appendPQExpBufferStr(query,
5215 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5216 : " ON o.external_id = 'pg_' || s.oid::text \n");
5217 :
5218 364 : appendPQExpBufferStr(query,
5219 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5220 : " WHERE datname = current_database())");
5221 :
5222 364 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5223 :
5224 364 : ntups = PQntuples(res);
5225 :
5226 : /*
5227 : * Get subscription fields. We don't include subskiplsn in the dump as
5228 : * after restoring the dump this value may no longer be relevant.
5229 : */
5230 364 : i_tableoid = PQfnumber(res, "tableoid");
5231 364 : i_oid = PQfnumber(res, "oid");
5232 364 : i_subname = PQfnumber(res, "subname");
5233 364 : i_subowner = PQfnumber(res, "subowner");
5234 364 : i_subenabled = PQfnumber(res, "subenabled");
5235 364 : i_subbinary = PQfnumber(res, "subbinary");
5236 364 : i_substream = PQfnumber(res, "substream");
5237 364 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5238 364 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5239 364 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5240 364 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5241 364 : i_subfailover = PQfnumber(res, "subfailover");
5242 364 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5243 364 : i_submaxretention = PQfnumber(res, "submaxretention");
5244 364 : i_subconninfo = PQfnumber(res, "subconninfo");
5245 364 : i_subslotname = PQfnumber(res, "subslotname");
5246 364 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5247 364 : i_subpublications = PQfnumber(res, "subpublications");
5248 364 : i_suborigin = PQfnumber(res, "suborigin");
5249 364 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5250 :
5251 364 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5252 :
5253 620 : for (i = 0; i < ntups; i++)
5254 : {
5255 256 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5256 256 : subinfo[i].dobj.catId.tableoid =
5257 256 : atooid(PQgetvalue(res, i, i_tableoid));
5258 256 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5259 256 : AssignDumpId(&subinfo[i].dobj);
5260 256 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5261 256 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5262 :
5263 256 : subinfo[i].subenabled =
5264 256 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5265 256 : subinfo[i].subbinary =
5266 256 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5267 256 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5268 256 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5269 256 : subinfo[i].subdisableonerr =
5270 256 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5271 256 : subinfo[i].subpasswordrequired =
5272 256 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5273 256 : subinfo[i].subrunasowner =
5274 256 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5275 256 : subinfo[i].subfailover =
5276 256 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5277 256 : subinfo[i].subretaindeadtuples =
5278 256 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5279 256 : subinfo[i].submaxretention =
5280 256 : atoi(PQgetvalue(res, i, i_submaxretention));
5281 512 : subinfo[i].subconninfo =
5282 256 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5283 256 : if (PQgetisnull(res, i, i_subslotname))
5284 0 : subinfo[i].subslotname = NULL;
5285 : else
5286 256 : subinfo[i].subslotname =
5287 256 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5288 512 : subinfo[i].subsynccommit =
5289 256 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5290 512 : subinfo[i].subpublications =
5291 256 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5292 256 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5293 256 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5294 254 : subinfo[i].suboriginremotelsn = NULL;
5295 : else
5296 2 : subinfo[i].suboriginremotelsn =
5297 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5298 :
5299 : /* Decide whether we want to dump it */
5300 256 : selectDumpableObject(&(subinfo[i].dobj), fout);
5301 : }
5302 364 : PQclear(res);
5303 :
5304 364 : destroyPQExpBuffer(query);
5305 : }
5306 :
5307 : /*
5308 : * getSubscriptionRelations
5309 : * Get information about subscription membership for dumpable relations. This
5310 : * will be used only in binary-upgrade mode for PG17 or later versions.
5311 : */
5312 : void
5313 372 : getSubscriptionRelations(Archive *fout)
5314 : {
5315 372 : DumpOptions *dopt = fout->dopt;
5316 372 : SubscriptionInfo *subinfo = NULL;
5317 : SubRelInfo *subrinfo;
5318 : PGresult *res;
5319 : int i_srsubid;
5320 : int i_srrelid;
5321 : int i_srsubstate;
5322 : int i_srsublsn;
5323 : int ntups;
5324 372 : Oid last_srsubid = InvalidOid;
5325 :
5326 372 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5327 72 : fout->remoteVersion < 170000)
5328 300 : return;
5329 :
5330 72 : res = ExecuteSqlQuery(fout,
5331 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5332 : "FROM pg_catalog.pg_subscription_rel "
5333 : "ORDER BY srsubid",
5334 : PGRES_TUPLES_OK);
5335 72 : ntups = PQntuples(res);
5336 72 : if (ntups == 0)
5337 70 : goto cleanup;
5338 :
5339 : /* Get pg_subscription_rel attributes */
5340 2 : i_srsubid = PQfnumber(res, "srsubid");
5341 2 : i_srrelid = PQfnumber(res, "srrelid");
5342 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5343 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5344 :
5345 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5346 6 : for (int i = 0; i < ntups; i++)
5347 : {
5348 4 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5349 4 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5350 : TableInfo *tblinfo;
5351 :
5352 : /*
5353 : * If we switched to a new subscription, check if the subscription
5354 : * exists.
5355 : */
5356 4 : if (cur_srsubid != last_srsubid)
5357 : {
5358 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5359 4 : if (subinfo == NULL)
5360 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5361 :
5362 4 : last_srsubid = cur_srsubid;
5363 : }
5364 :
5365 4 : tblinfo = findTableByOid(relid);
5366 4 : if (tblinfo == NULL)
5367 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5368 : relid);
5369 :
5370 : /* OK, make a DumpableObject for this relationship */
5371 4 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5372 4 : subrinfo[i].dobj.catId.tableoid = relid;
5373 4 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5374 4 : AssignDumpId(&subrinfo[i].dobj);
5375 4 : subrinfo[i].dobj.name = pg_strdup(subinfo->dobj.name);
5376 4 : subrinfo[i].tblinfo = tblinfo;
5377 4 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5378 4 : if (PQgetisnull(res, i, i_srsublsn))
5379 2 : subrinfo[i].srsublsn = NULL;
5380 : else
5381 2 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5382 :
5383 4 : subrinfo[i].subinfo = subinfo;
5384 :
5385 : /* Decide whether we want to dump it */
5386 4 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5387 : }
5388 :
5389 2 : cleanup:
5390 72 : PQclear(res);
5391 : }
5392 :
5393 : /*
5394 : * dumpSubscriptionTable
5395 : * Dump the definition of the given subscription table mapping. This will be
5396 : * used only in binary-upgrade mode for PG17 or later versions.
5397 : */
5398 : static void
5399 4 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5400 : {
5401 4 : DumpOptions *dopt = fout->dopt;
5402 4 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5403 : PQExpBuffer query;
5404 : char *tag;
5405 :
5406 : /* Do nothing if not dumping schema */
5407 4 : if (!dopt->dumpSchema)
5408 0 : return;
5409 :
5410 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5411 :
5412 4 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->dobj.name);
5413 :
5414 4 : query = createPQExpBuffer();
5415 :
5416 4 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5417 : {
5418 : /*
5419 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5420 : * to pg_subscription_rel table. This will be used only in
5421 : * binary-upgrade mode.
5422 : */
5423 4 : appendPQExpBufferStr(query,
5424 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5425 4 : appendPQExpBufferStr(query,
5426 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5427 4 : appendStringLiteralAH(query, subrinfo->dobj.name, fout);
5428 4 : appendPQExpBuffer(query,
5429 : ", %u, '%c'",
5430 4 : subrinfo->tblinfo->dobj.catId.oid,
5431 4 : subrinfo->srsubstate);
5432 :
5433 4 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5434 2 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5435 : else
5436 2 : appendPQExpBufferStr(query, ", NULL");
5437 :
5438 4 : appendPQExpBufferStr(query, ");\n");
5439 : }
5440 :
5441 : /*
5442 : * There is no point in creating a drop query as the drop is done by table
5443 : * drop. (If you think to change this, see also _printTocEntry().)
5444 : * Although this object doesn't really have ownership as such, set the
5445 : * owner field anyway to ensure that the command is run by the correct
5446 : * role at restore time.
5447 : */
5448 4 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5449 4 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5450 4 : ARCHIVE_OPTS(.tag = tag,
5451 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5452 : .owner = subinfo->rolname,
5453 : .description = "SUBSCRIPTION TABLE",
5454 : .section = SECTION_POST_DATA,
5455 : .createStmt = query->data));
5456 :
5457 : /* These objects can't currently have comments or seclabels */
5458 :
5459 4 : free(tag);
5460 4 : destroyPQExpBuffer(query);
5461 : }
5462 :
5463 : /*
5464 : * dumpSubscription
5465 : * dump the definition of the given subscription
5466 : */
5467 : static void
5468 220 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5469 : {
5470 220 : DumpOptions *dopt = fout->dopt;
5471 : PQExpBuffer delq;
5472 : PQExpBuffer query;
5473 : PQExpBuffer publications;
5474 : char *qsubname;
5475 220 : char **pubnames = NULL;
5476 220 : int npubnames = 0;
5477 : int i;
5478 :
5479 : /* Do nothing if not dumping schema */
5480 220 : if (!dopt->dumpSchema)
5481 36 : return;
5482 :
5483 184 : delq = createPQExpBuffer();
5484 184 : query = createPQExpBuffer();
5485 :
5486 184 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5487 :
5488 184 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5489 : qsubname);
5490 :
5491 184 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5492 : qsubname);
5493 184 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5494 :
5495 : /* Build list of quoted publications and append them to query. */
5496 184 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5497 0 : pg_fatal("could not parse %s array", "subpublications");
5498 :
5499 184 : publications = createPQExpBuffer();
5500 368 : for (i = 0; i < npubnames; i++)
5501 : {
5502 184 : if (i > 0)
5503 0 : appendPQExpBufferStr(publications, ", ");
5504 :
5505 184 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5506 : }
5507 :
5508 184 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5509 184 : if (subinfo->subslotname)
5510 184 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5511 : else
5512 0 : appendPQExpBufferStr(query, "NONE");
5513 :
5514 184 : if (subinfo->subbinary)
5515 0 : appendPQExpBufferStr(query, ", binary = true");
5516 :
5517 184 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5518 60 : appendPQExpBufferStr(query, ", streaming = on");
5519 124 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5520 64 : appendPQExpBufferStr(query, ", streaming = parallel");
5521 : else
5522 60 : appendPQExpBufferStr(query, ", streaming = off");
5523 :
5524 184 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5525 0 : appendPQExpBufferStr(query, ", two_phase = on");
5526 :
5527 184 : if (subinfo->subdisableonerr)
5528 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5529 :
5530 184 : if (!subinfo->subpasswordrequired)
5531 0 : appendPQExpBufferStr(query, ", password_required = false");
5532 :
5533 184 : if (subinfo->subrunasowner)
5534 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5535 :
5536 184 : if (subinfo->subfailover)
5537 2 : appendPQExpBufferStr(query, ", failover = true");
5538 :
5539 184 : if (subinfo->subretaindeadtuples)
5540 2 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5541 :
5542 184 : if (subinfo->submaxretention)
5543 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5544 :
5545 184 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5546 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5547 :
5548 184 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5549 60 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5550 :
5551 184 : appendPQExpBufferStr(query, ");\n");
5552 :
5553 : /*
5554 : * In binary-upgrade mode, we allow the replication to continue after the
5555 : * upgrade.
5556 : */
5557 184 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5558 : {
5559 10 : if (subinfo->suboriginremotelsn)
5560 : {
5561 : /*
5562 : * Preserve the remote_lsn for the subscriber's replication
5563 : * origin. This value is required to start the replication from
5564 : * the position before the upgrade. This value will be stale if
5565 : * the publisher gets upgraded before the subscriber node.
5566 : * However, this shouldn't be a problem as the upgrade of the
5567 : * publisher ensures that all the transactions were replicated
5568 : * before upgrading it.
5569 : */
5570 2 : appendPQExpBufferStr(query,
5571 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5572 2 : appendPQExpBufferStr(query,
5573 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5574 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5575 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5576 : }
5577 :
5578 10 : if (subinfo->subenabled)
5579 : {
5580 : /*
5581 : * Enable the subscription to allow the replication to continue
5582 : * after the upgrade.
5583 : */
5584 2 : appendPQExpBufferStr(query,
5585 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5586 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5587 : }
5588 : }
5589 :
5590 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5591 184 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5592 184 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5593 : .owner = subinfo->rolname,
5594 : .description = "SUBSCRIPTION",
5595 : .section = SECTION_POST_DATA,
5596 : .createStmt = query->data,
5597 : .dropStmt = delq->data));
5598 :
5599 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5600 60 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5601 60 : NULL, subinfo->rolname,
5602 60 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5603 :
5604 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5605 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5606 0 : NULL, subinfo->rolname,
5607 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5608 :
5609 184 : destroyPQExpBuffer(publications);
5610 184 : free(pubnames);
5611 :
5612 184 : destroyPQExpBuffer(delq);
5613 184 : destroyPQExpBuffer(query);
5614 184 : free(qsubname);
5615 : }
5616 :
5617 : /*
5618 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5619 : * the object needs.
5620 : */
5621 : static void
5622 10080 : append_depends_on_extension(Archive *fout,
5623 : PQExpBuffer create,
5624 : const DumpableObject *dobj,
5625 : const char *catalog,
5626 : const char *keyword,
5627 : const char *objname)
5628 : {
5629 10080 : if (dobj->depends_on_ext)
5630 : {
5631 : char *nm;
5632 : PGresult *res;
5633 : PQExpBuffer query;
5634 : int ntups;
5635 : int i_extname;
5636 : int i;
5637 :
5638 : /* dodge fmtId() non-reentrancy */
5639 84 : nm = pg_strdup(objname);
5640 :
5641 84 : query = createPQExpBuffer();
5642 84 : appendPQExpBuffer(query,
5643 : "SELECT e.extname "
5644 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5645 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5646 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5647 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5648 : catalog,
5649 84 : dobj->catId.oid);
5650 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5651 84 : ntups = PQntuples(res);
5652 84 : i_extname = PQfnumber(res, "extname");
5653 168 : for (i = 0; i < ntups; i++)
5654 : {
5655 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5656 : keyword, nm,
5657 84 : fmtId(PQgetvalue(res, i, i_extname)));
5658 : }
5659 :
5660 84 : PQclear(res);
5661 84 : destroyPQExpBuffer(query);
5662 84 : pg_free(nm);
5663 : }
5664 10080 : }
5665 :
5666 : static Oid
5667 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5668 : {
5669 : /*
5670 : * If the old version didn't assign an array type, but the new version
5671 : * does, we must select an unused type OID to assign. This currently only
5672 : * happens for domains, when upgrading pre-v11 to v11 and up.
5673 : *
5674 : * Note: local state here is kind of ugly, but we must have some, since we
5675 : * mustn't choose the same unused OID more than once.
5676 : */
5677 : static Oid next_possible_free_oid = FirstNormalObjectId;
5678 : PGresult *res;
5679 : bool is_dup;
5680 :
5681 : do
5682 : {
5683 0 : ++next_possible_free_oid;
5684 0 : printfPQExpBuffer(upgrade_query,
5685 : "SELECT EXISTS(SELECT 1 "
5686 : "FROM pg_catalog.pg_type "
5687 : "WHERE oid = '%u'::pg_catalog.oid);",
5688 : next_possible_free_oid);
5689 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5690 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5691 0 : PQclear(res);
5692 0 : } while (is_dup);
5693 :
5694 0 : return next_possible_free_oid;
5695 : }
5696 :
5697 : static void
5698 1882 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5699 : PQExpBuffer upgrade_buffer,
5700 : Oid pg_type_oid,
5701 : bool force_array_type,
5702 : bool include_multirange_type)
5703 : {
5704 1882 : PQExpBuffer upgrade_query = createPQExpBuffer();
5705 : PGresult *res;
5706 : Oid pg_type_array_oid;
5707 : Oid pg_type_multirange_oid;
5708 : Oid pg_type_multirange_array_oid;
5709 : TypeInfo *tinfo;
5710 :
5711 1882 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5712 1882 : appendPQExpBuffer(upgrade_buffer,
5713 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5714 : pg_type_oid);
5715 :
5716 1882 : tinfo = findTypeByOid(pg_type_oid);
5717 1882 : if (tinfo)
5718 1882 : pg_type_array_oid = tinfo->typarray;
5719 : else
5720 0 : pg_type_array_oid = InvalidOid;
5721 :
5722 1882 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5723 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5724 :
5725 1882 : if (OidIsValid(pg_type_array_oid))
5726 : {
5727 1878 : appendPQExpBufferStr(upgrade_buffer,
5728 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5729 1878 : appendPQExpBuffer(upgrade_buffer,
5730 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5731 : pg_type_array_oid);
5732 : }
5733 :
5734 : /*
5735 : * Pre-set the multirange type oid and its own array type oid.
5736 : */
5737 1882 : if (include_multirange_type)
5738 : {
5739 16 : if (fout->remoteVersion >= 140000)
5740 : {
5741 16 : printfPQExpBuffer(upgrade_query,
5742 : "SELECT t.oid, t.typarray "
5743 : "FROM pg_catalog.pg_type t "
5744 : "JOIN pg_catalog.pg_range r "
5745 : "ON t.oid = r.rngmultitypid "
5746 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5747 : pg_type_oid);
5748 :
5749 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5750 :
5751 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5752 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5753 :
5754 16 : PQclear(res);
5755 : }
5756 : else
5757 : {
5758 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5759 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5760 : }
5761 :
5762 16 : appendPQExpBufferStr(upgrade_buffer,
5763 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5764 16 : appendPQExpBuffer(upgrade_buffer,
5765 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5766 : pg_type_multirange_oid);
5767 16 : appendPQExpBufferStr(upgrade_buffer,
5768 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5769 16 : appendPQExpBuffer(upgrade_buffer,
5770 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5771 : pg_type_multirange_array_oid);
5772 : }
5773 :
5774 1882 : destroyPQExpBuffer(upgrade_query);
5775 1882 : }
5776 :
5777 : static void
5778 1732 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5779 : PQExpBuffer upgrade_buffer,
5780 : const TableInfo *tbinfo)
5781 : {
5782 1732 : Oid pg_type_oid = tbinfo->reltype;
5783 :
5784 1732 : if (OidIsValid(pg_type_oid))
5785 1732 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5786 : pg_type_oid, false, false);
5787 1732 : }
5788 :
5789 : /*
5790 : * bsearch() comparator for BinaryUpgradeClassOidItem
5791 : */
5792 : static int
5793 24696 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5794 : {
5795 24696 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5796 24696 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5797 :
5798 24696 : return pg_cmp_u32(v1.oid, v2.oid);
5799 : }
5800 :
5801 : /*
5802 : * collectBinaryUpgradeClassOids
5803 : *
5804 : * Construct a table of pg_class information required for
5805 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5806 : * lookup.
5807 : */
5808 : static void
5809 72 : collectBinaryUpgradeClassOids(Archive *fout)
5810 : {
5811 : PGresult *res;
5812 : const char *query;
5813 :
5814 72 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5815 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5816 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5817 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5818 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5819 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5820 : "ORDER BY c.oid;";
5821 :
5822 72 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5823 :
5824 72 : nbinaryUpgradeClassOids = PQntuples(res);
5825 72 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5826 72 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5827 :
5828 33708 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5829 : {
5830 33636 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5831 33636 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5832 33636 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5833 33636 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5834 33636 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5835 33636 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5836 33636 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5837 : }
5838 :
5839 72 : PQclear(res);
5840 72 : }
5841 :
5842 : static void
5843 2504 : binary_upgrade_set_pg_class_oids(Archive *fout,
5844 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5845 : {
5846 2504 : BinaryUpgradeClassOidItem key = {0};
5847 : BinaryUpgradeClassOidItem *entry;
5848 :
5849 : Assert(binaryUpgradeClassOids);
5850 :
5851 : /*
5852 : * Preserve the OID and relfilenumber of the table, table's index, table's
5853 : * toast table and toast table's index if any.
5854 : *
5855 : * One complexity is that the current table definition might not require
5856 : * the creation of a TOAST table, but the old database might have a TOAST
5857 : * table that was created earlier, before some wide columns were dropped.
5858 : * By setting the TOAST oid we force creation of the TOAST heap and index
5859 : * by the new backend, so we can copy the files during binary upgrade
5860 : * without worrying about this case.
5861 : */
5862 2504 : key.oid = pg_class_oid;
5863 2504 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5864 : sizeof(BinaryUpgradeClassOidItem),
5865 : BinaryUpgradeClassOidItemCmp);
5866 :
5867 2504 : appendPQExpBufferStr(upgrade_buffer,
5868 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5869 :
5870 2504 : if (entry->relkind != RELKIND_INDEX &&
5871 1950 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5872 : {
5873 1900 : appendPQExpBuffer(upgrade_buffer,
5874 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5875 : pg_class_oid);
5876 :
5877 : /*
5878 : * Not every relation has storage. Also, in a pre-v12 database,
5879 : * partitioned tables have a relfilenumber, which should not be
5880 : * preserved when upgrading.
5881 : */
5882 1900 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5883 1576 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5884 1576 : appendPQExpBuffer(upgrade_buffer,
5885 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5886 : entry->relfilenumber);
5887 :
5888 : /*
5889 : * In a pre-v12 database, partitioned tables might be marked as having
5890 : * toast tables, but we should ignore them if so.
5891 : */
5892 1900 : if (OidIsValid(entry->toast_oid) &&
5893 554 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5894 : {
5895 554 : appendPQExpBuffer(upgrade_buffer,
5896 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5897 : entry->toast_oid);
5898 554 : appendPQExpBuffer(upgrade_buffer,
5899 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5900 : entry->toast_relfilenumber);
5901 :
5902 : /* every toast table has an index */
5903 554 : appendPQExpBuffer(upgrade_buffer,
5904 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5905 : entry->toast_index_oid);
5906 554 : appendPQExpBuffer(upgrade_buffer,
5907 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5908 : entry->toast_index_relfilenumber);
5909 : }
5910 : }
5911 : else
5912 : {
5913 : /* Preserve the OID and relfilenumber of the index */
5914 604 : appendPQExpBuffer(upgrade_buffer,
5915 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5916 : pg_class_oid);
5917 604 : appendPQExpBuffer(upgrade_buffer,
5918 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5919 : entry->relfilenumber);
5920 : }
5921 :
5922 2504 : appendPQExpBufferChar(upgrade_buffer, '\n');
5923 2504 : }
5924 :
5925 : /*
5926 : * If the DumpableObject is a member of an extension, add a suitable
5927 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5928 : *
5929 : * For somewhat historical reasons, objname should already be quoted,
5930 : * but not objnamespace (if any).
5931 : */
5932 : static void
5933 3004 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5934 : const DumpableObject *dobj,
5935 : const char *objtype,
5936 : const char *objname,
5937 : const char *objnamespace)
5938 : {
5939 3004 : DumpableObject *extobj = NULL;
5940 : int i;
5941 :
5942 3004 : if (!dobj->ext_member)
5943 2962 : return;
5944 :
5945 : /*
5946 : * Find the parent extension. We could avoid this search if we wanted to
5947 : * add a link field to DumpableObject, but the space costs of that would
5948 : * be considerable. We assume that member objects could only have a
5949 : * direct dependency on their own extension, not any others.
5950 : */
5951 42 : for (i = 0; i < dobj->nDeps; i++)
5952 : {
5953 42 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5954 42 : if (extobj && extobj->objType == DO_EXTENSION)
5955 42 : break;
5956 0 : extobj = NULL;
5957 : }
5958 42 : if (extobj == NULL)
5959 0 : pg_fatal("could not find parent extension for %s %s",
5960 : objtype, objname);
5961 :
5962 42 : appendPQExpBufferStr(upgrade_buffer,
5963 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5964 42 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5965 42 : fmtId(extobj->name),
5966 : objtype);
5967 42 : if (objnamespace && *objnamespace)
5968 36 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5969 42 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5970 : }
5971 :
5972 : /*
5973 : * getNamespaces:
5974 : * get information about all namespaces in the system catalogs
5975 : */
5976 : void
5977 374 : getNamespaces(Archive *fout)
5978 : {
5979 : PGresult *res;
5980 : int ntups;
5981 : int i;
5982 : PQExpBuffer query;
5983 : NamespaceInfo *nsinfo;
5984 : int i_tableoid;
5985 : int i_oid;
5986 : int i_nspname;
5987 : int i_nspowner;
5988 : int i_nspacl;
5989 : int i_acldefault;
5990 :
5991 374 : query = createPQExpBuffer();
5992 :
5993 : /*
5994 : * we fetch all namespaces including system ones, so that every object we
5995 : * read in can be linked to a containing namespace.
5996 : */
5997 374 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
5998 : "n.nspowner, "
5999 : "n.nspacl, "
6000 : "acldefault('n', n.nspowner) AS acldefault "
6001 : "FROM pg_namespace n");
6002 :
6003 374 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6004 :
6005 374 : ntups = PQntuples(res);
6006 :
6007 374 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
6008 :
6009 374 : i_tableoid = PQfnumber(res, "tableoid");
6010 374 : i_oid = PQfnumber(res, "oid");
6011 374 : i_nspname = PQfnumber(res, "nspname");
6012 374 : i_nspowner = PQfnumber(res, "nspowner");
6013 374 : i_nspacl = PQfnumber(res, "nspacl");
6014 374 : i_acldefault = PQfnumber(res, "acldefault");
6015 :
6016 3226 : for (i = 0; i < ntups; i++)
6017 : {
6018 : const char *nspowner;
6019 :
6020 2852 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6021 2852 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6022 2852 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6023 2852 : AssignDumpId(&nsinfo[i].dobj);
6024 2852 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6025 2852 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6026 2852 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6027 2852 : nsinfo[i].dacl.privtype = 0;
6028 2852 : nsinfo[i].dacl.initprivs = NULL;
6029 2852 : nspowner = PQgetvalue(res, i, i_nspowner);
6030 2852 : nsinfo[i].nspowner = atooid(nspowner);
6031 2852 : nsinfo[i].rolname = getRoleName(nspowner);
6032 :
6033 : /* Decide whether to dump this namespace */
6034 2852 : selectDumpableNamespace(&nsinfo[i], fout);
6035 :
6036 : /* Mark whether namespace has an ACL */
6037 2852 : if (!PQgetisnull(res, i, i_nspacl))
6038 1246 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6039 :
6040 : /*
6041 : * We ignore any pg_init_privs.initprivs entry for the public schema
6042 : * and assume a predetermined default, for several reasons. First,
6043 : * dropping and recreating the schema removes its pg_init_privs entry,
6044 : * but an empty destination database starts with this ACL nonetheless.
6045 : * Second, we support dump/reload of public schema ownership changes.
6046 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6047 : * initprivs continues to reflect the initial owner. Hence,
6048 : * synthesize the value that nspacl will have after the restore's
6049 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6050 : * match the source's ACL, even if the latter was an initdb-default
6051 : * ACL, which changed in v15. An upgrade pulls in changes to most
6052 : * system object ACLs that the DBA had not customized. We've made the
6053 : * public schema depart from that, because changing its ACL so easily
6054 : * breaks applications.
6055 : */
6056 2852 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6057 : {
6058 366 : PQExpBuffer aclarray = createPQExpBuffer();
6059 366 : PQExpBuffer aclitem = createPQExpBuffer();
6060 :
6061 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6062 366 : appendPQExpBufferChar(aclarray, '{');
6063 366 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6064 366 : appendPQExpBufferStr(aclitem, "=UC/");
6065 366 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6066 366 : appendPGArray(aclarray, aclitem->data);
6067 366 : resetPQExpBuffer(aclitem);
6068 366 : appendPQExpBufferStr(aclitem, "=U/");
6069 366 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6070 366 : appendPGArray(aclarray, aclitem->data);
6071 366 : appendPQExpBufferChar(aclarray, '}');
6072 :
6073 366 : nsinfo[i].dacl.privtype = 'i';
6074 366 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6075 366 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6076 :
6077 366 : destroyPQExpBuffer(aclarray);
6078 366 : destroyPQExpBuffer(aclitem);
6079 : }
6080 : }
6081 :
6082 374 : PQclear(res);
6083 374 : destroyPQExpBuffer(query);
6084 374 : }
6085 :
6086 : /*
6087 : * findNamespace:
6088 : * given a namespace OID, look up the info read by getNamespaces
6089 : */
6090 : static NamespaceInfo *
6091 1173700 : findNamespace(Oid nsoid)
6092 : {
6093 : NamespaceInfo *nsinfo;
6094 :
6095 1173700 : nsinfo = findNamespaceByOid(nsoid);
6096 1173700 : if (nsinfo == NULL)
6097 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6098 1173700 : return nsinfo;
6099 : }
6100 :
6101 : /*
6102 : * getExtensions:
6103 : * read all extensions in the system catalogs and return them in the
6104 : * ExtensionInfo* structure
6105 : *
6106 : * numExtensions is set to the number of extensions read in
6107 : */
6108 : ExtensionInfo *
6109 374 : getExtensions(Archive *fout, int *numExtensions)
6110 : {
6111 374 : DumpOptions *dopt = fout->dopt;
6112 : PGresult *res;
6113 : int ntups;
6114 : int i;
6115 : PQExpBuffer query;
6116 374 : ExtensionInfo *extinfo = NULL;
6117 : int i_tableoid;
6118 : int i_oid;
6119 : int i_extname;
6120 : int i_nspname;
6121 : int i_extrelocatable;
6122 : int i_extversion;
6123 : int i_extconfig;
6124 : int i_extcondition;
6125 :
6126 374 : query = createPQExpBuffer();
6127 :
6128 374 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6129 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6130 : "FROM pg_extension x "
6131 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6132 :
6133 374 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6134 :
6135 374 : ntups = PQntuples(res);
6136 374 : if (ntups == 0)
6137 0 : goto cleanup;
6138 :
6139 374 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6140 :
6141 374 : i_tableoid = PQfnumber(res, "tableoid");
6142 374 : i_oid = PQfnumber(res, "oid");
6143 374 : i_extname = PQfnumber(res, "extname");
6144 374 : i_nspname = PQfnumber(res, "nspname");
6145 374 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6146 374 : i_extversion = PQfnumber(res, "extversion");
6147 374 : i_extconfig = PQfnumber(res, "extconfig");
6148 374 : i_extcondition = PQfnumber(res, "extcondition");
6149 :
6150 808 : for (i = 0; i < ntups; i++)
6151 : {
6152 434 : extinfo[i].dobj.objType = DO_EXTENSION;
6153 434 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6154 434 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6155 434 : AssignDumpId(&extinfo[i].dobj);
6156 434 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6157 434 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6158 434 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6159 434 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6160 434 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6161 434 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6162 :
6163 : /* Decide whether we want to dump it */
6164 434 : selectDumpableExtension(&(extinfo[i]), dopt);
6165 : }
6166 :
6167 374 : cleanup:
6168 374 : PQclear(res);
6169 374 : destroyPQExpBuffer(query);
6170 :
6171 374 : *numExtensions = ntups;
6172 :
6173 374 : return extinfo;
6174 : }
6175 :
6176 : /*
6177 : * getTypes:
6178 : * get information about all types in the system catalogs
6179 : *
6180 : * NB: this must run after getFuncs() because we assume we can do
6181 : * findFuncByOid().
6182 : */
6183 : void
6184 372 : getTypes(Archive *fout)
6185 : {
6186 : PGresult *res;
6187 : int ntups;
6188 : int i;
6189 372 : PQExpBuffer query = createPQExpBuffer();
6190 : TypeInfo *tyinfo;
6191 : ShellTypeInfo *stinfo;
6192 : int i_tableoid;
6193 : int i_oid;
6194 : int i_typname;
6195 : int i_typnamespace;
6196 : int i_typacl;
6197 : int i_acldefault;
6198 : int i_typowner;
6199 : int i_typelem;
6200 : int i_typrelid;
6201 : int i_typrelkind;
6202 : int i_typtype;
6203 : int i_typisdefined;
6204 : int i_isarray;
6205 : int i_typarray;
6206 :
6207 : /*
6208 : * we include even the built-in types because those may be used as array
6209 : * elements by user-defined types
6210 : *
6211 : * we filter out the built-in types when we dump out the types
6212 : *
6213 : * same approach for undefined (shell) types and array types
6214 : *
6215 : * Note: as of 8.3 we can reliably detect whether a type is an
6216 : * auto-generated array type by checking the element type's typarray.
6217 : * (Before that the test is capable of generating false positives.) We
6218 : * still check for name beginning with '_', though, so as to avoid the
6219 : * cost of the subselect probe for all standard types. This would have to
6220 : * be revisited if the backend ever allows renaming of array types.
6221 : */
6222 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6223 : "typnamespace, typacl, "
6224 : "acldefault('T', typowner) AS acldefault, "
6225 : "typowner, "
6226 : "typelem, typrelid, typarray, "
6227 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6228 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6229 : "typtype, typisdefined, "
6230 : "typname[0] = '_' AND typelem != 0 AND "
6231 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6232 : "FROM pg_type");
6233 :
6234 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6235 :
6236 372 : ntups = PQntuples(res);
6237 :
6238 372 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6239 :
6240 372 : i_tableoid = PQfnumber(res, "tableoid");
6241 372 : i_oid = PQfnumber(res, "oid");
6242 372 : i_typname = PQfnumber(res, "typname");
6243 372 : i_typnamespace = PQfnumber(res, "typnamespace");
6244 372 : i_typacl = PQfnumber(res, "typacl");
6245 372 : i_acldefault = PQfnumber(res, "acldefault");
6246 372 : i_typowner = PQfnumber(res, "typowner");
6247 372 : i_typelem = PQfnumber(res, "typelem");
6248 372 : i_typrelid = PQfnumber(res, "typrelid");
6249 372 : i_typrelkind = PQfnumber(res, "typrelkind");
6250 372 : i_typtype = PQfnumber(res, "typtype");
6251 372 : i_typisdefined = PQfnumber(res, "typisdefined");
6252 372 : i_isarray = PQfnumber(res, "isarray");
6253 372 : i_typarray = PQfnumber(res, "typarray");
6254 :
6255 269924 : for (i = 0; i < ntups; i++)
6256 : {
6257 269552 : tyinfo[i].dobj.objType = DO_TYPE;
6258 269552 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6259 269552 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6260 269552 : AssignDumpId(&tyinfo[i].dobj);
6261 269552 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6262 539104 : tyinfo[i].dobj.namespace =
6263 269552 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6264 269552 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6265 269552 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6266 269552 : tyinfo[i].dacl.privtype = 0;
6267 269552 : tyinfo[i].dacl.initprivs = NULL;
6268 269552 : tyinfo[i].ftypname = NULL; /* may get filled later */
6269 269552 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6270 269552 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6271 269552 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6272 269552 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6273 269552 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6274 269552 : tyinfo[i].shellType = NULL;
6275 :
6276 269552 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6277 269448 : tyinfo[i].isDefined = true;
6278 : else
6279 104 : tyinfo[i].isDefined = false;
6280 :
6281 269552 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6282 129330 : tyinfo[i].isArray = true;
6283 : else
6284 140222 : tyinfo[i].isArray = false;
6285 :
6286 269552 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6287 :
6288 269552 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6289 2496 : tyinfo[i].isMultirange = true;
6290 : else
6291 267056 : tyinfo[i].isMultirange = false;
6292 :
6293 : /* Decide whether we want to dump it */
6294 269552 : selectDumpableType(&tyinfo[i], fout);
6295 :
6296 : /* Mark whether type has an ACL */
6297 269552 : if (!PQgetisnull(res, i, i_typacl))
6298 410 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6299 :
6300 : /*
6301 : * If it's a domain, fetch info about its constraints, if any
6302 : */
6303 269552 : tyinfo[i].nDomChecks = 0;
6304 269552 : tyinfo[i].domChecks = NULL;
6305 269552 : tyinfo[i].notnull = NULL;
6306 269552 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6307 29894 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6308 316 : getDomainConstraints(fout, &(tyinfo[i]));
6309 :
6310 : /*
6311 : * If it's a base type, make a DumpableObject representing a shell
6312 : * definition of the type. We will need to dump that ahead of the I/O
6313 : * functions for the type. Similarly, range types need a shell
6314 : * definition in case they have a canonicalize function.
6315 : *
6316 : * Note: the shell type doesn't have a catId. You might think it
6317 : * should copy the base type's catId, but then it might capture the
6318 : * pg_depend entries for the type, which we don't want.
6319 : */
6320 269552 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6321 29894 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6322 14520 : tyinfo[i].typtype == TYPTYPE_RANGE))
6323 : {
6324 15622 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6325 15622 : stinfo->dobj.objType = DO_SHELL_TYPE;
6326 15622 : stinfo->dobj.catId = nilCatalogId;
6327 15622 : AssignDumpId(&stinfo->dobj);
6328 15622 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6329 15622 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6330 15622 : stinfo->baseType = &(tyinfo[i]);
6331 15622 : tyinfo[i].shellType = stinfo;
6332 :
6333 : /*
6334 : * Initially mark the shell type as not to be dumped. We'll only
6335 : * dump it if the I/O or canonicalize functions need to be dumped;
6336 : * this is taken care of while sorting dependencies.
6337 : */
6338 15622 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6339 : }
6340 : }
6341 :
6342 372 : PQclear(res);
6343 :
6344 372 : destroyPQExpBuffer(query);
6345 372 : }
6346 :
6347 : /*
6348 : * getOperators:
6349 : * get information about all operators in the system catalogs
6350 : */
6351 : void
6352 372 : getOperators(Archive *fout)
6353 : {
6354 : PGresult *res;
6355 : int ntups;
6356 : int i;
6357 372 : PQExpBuffer query = createPQExpBuffer();
6358 : OprInfo *oprinfo;
6359 : int i_tableoid;
6360 : int i_oid;
6361 : int i_oprname;
6362 : int i_oprnamespace;
6363 : int i_oprowner;
6364 : int i_oprkind;
6365 : int i_oprleft;
6366 : int i_oprright;
6367 : int i_oprcode;
6368 :
6369 : /*
6370 : * find all operators, including builtin operators; we filter out
6371 : * system-defined operators at dump-out time.
6372 : */
6373 :
6374 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6375 : "oprnamespace, "
6376 : "oprowner, "
6377 : "oprkind, "
6378 : "oprleft, "
6379 : "oprright, "
6380 : "oprcode::oid AS oprcode "
6381 : "FROM pg_operator");
6382 :
6383 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6384 :
6385 372 : ntups = PQntuples(res);
6386 :
6387 372 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6388 :
6389 372 : i_tableoid = PQfnumber(res, "tableoid");
6390 372 : i_oid = PQfnumber(res, "oid");
6391 372 : i_oprname = PQfnumber(res, "oprname");
6392 372 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6393 372 : i_oprowner = PQfnumber(res, "oprowner");
6394 372 : i_oprkind = PQfnumber(res, "oprkind");
6395 372 : i_oprleft = PQfnumber(res, "oprleft");
6396 372 : i_oprright = PQfnumber(res, "oprright");
6397 372 : i_oprcode = PQfnumber(res, "oprcode");
6398 :
6399 297884 : for (i = 0; i < ntups; i++)
6400 : {
6401 297512 : oprinfo[i].dobj.objType = DO_OPERATOR;
6402 297512 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6403 297512 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6404 297512 : AssignDumpId(&oprinfo[i].dobj);
6405 297512 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6406 595024 : oprinfo[i].dobj.namespace =
6407 297512 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6408 297512 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6409 297512 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6410 297512 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6411 297512 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6412 297512 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6413 :
6414 : /* Decide whether we want to dump it */
6415 297512 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6416 : }
6417 :
6418 372 : PQclear(res);
6419 :
6420 372 : destroyPQExpBuffer(query);
6421 372 : }
6422 :
6423 : /*
6424 : * getCollations:
6425 : * get information about all collations in the system catalogs
6426 : */
6427 : void
6428 372 : getCollations(Archive *fout)
6429 : {
6430 : PGresult *res;
6431 : int ntups;
6432 : int i;
6433 : PQExpBuffer query;
6434 : CollInfo *collinfo;
6435 : int i_tableoid;
6436 : int i_oid;
6437 : int i_collname;
6438 : int i_collnamespace;
6439 : int i_collowner;
6440 : int i_collencoding;
6441 :
6442 372 : query = createPQExpBuffer();
6443 :
6444 : /*
6445 : * find all collations, including builtin collations; we filter out
6446 : * system-defined collations at dump-out time.
6447 : */
6448 :
6449 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6450 : "collnamespace, "
6451 : "collowner, "
6452 : "collencoding "
6453 : "FROM pg_collation");
6454 :
6455 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6456 :
6457 372 : ntups = PQntuples(res);
6458 :
6459 372 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6460 :
6461 372 : i_tableoid = PQfnumber(res, "tableoid");
6462 372 : i_oid = PQfnumber(res, "oid");
6463 372 : i_collname = PQfnumber(res, "collname");
6464 372 : i_collnamespace = PQfnumber(res, "collnamespace");
6465 372 : i_collowner = PQfnumber(res, "collowner");
6466 372 : i_collencoding = PQfnumber(res, "collencoding");
6467 :
6468 304146 : for (i = 0; i < ntups; i++)
6469 : {
6470 303774 : collinfo[i].dobj.objType = DO_COLLATION;
6471 303774 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6472 303774 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6473 303774 : AssignDumpId(&collinfo[i].dobj);
6474 303774 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6475 607548 : collinfo[i].dobj.namespace =
6476 303774 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6477 303774 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6478 303774 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6479 :
6480 : /* Decide whether we want to dump it */
6481 303774 : selectDumpableObject(&(collinfo[i].dobj), fout);
6482 : }
6483 :
6484 372 : PQclear(res);
6485 :
6486 372 : destroyPQExpBuffer(query);
6487 372 : }
6488 :
6489 : /*
6490 : * getConversions:
6491 : * get information about all conversions in the system catalogs
6492 : */
6493 : void
6494 372 : getConversions(Archive *fout)
6495 : {
6496 : PGresult *res;
6497 : int ntups;
6498 : int i;
6499 : PQExpBuffer query;
6500 : ConvInfo *convinfo;
6501 : int i_tableoid;
6502 : int i_oid;
6503 : int i_conname;
6504 : int i_connamespace;
6505 : int i_conowner;
6506 :
6507 372 : query = createPQExpBuffer();
6508 :
6509 : /*
6510 : * find all conversions, including builtin conversions; we filter out
6511 : * system-defined conversions at dump-out time.
6512 : */
6513 :
6514 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6515 : "connamespace, "
6516 : "conowner "
6517 : "FROM pg_conversion");
6518 :
6519 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6520 :
6521 372 : ntups = PQntuples(res);
6522 :
6523 372 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6524 :
6525 372 : i_tableoid = PQfnumber(res, "tableoid");
6526 372 : i_oid = PQfnumber(res, "oid");
6527 372 : i_conname = PQfnumber(res, "conname");
6528 372 : i_connamespace = PQfnumber(res, "connamespace");
6529 372 : i_conowner = PQfnumber(res, "conowner");
6530 :
6531 48078 : for (i = 0; i < ntups; i++)
6532 : {
6533 47706 : convinfo[i].dobj.objType = DO_CONVERSION;
6534 47706 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6535 47706 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6536 47706 : AssignDumpId(&convinfo[i].dobj);
6537 47706 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6538 95412 : convinfo[i].dobj.namespace =
6539 47706 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6540 47706 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6541 :
6542 : /* Decide whether we want to dump it */
6543 47706 : selectDumpableObject(&(convinfo[i].dobj), fout);
6544 : }
6545 :
6546 372 : PQclear(res);
6547 :
6548 372 : destroyPQExpBuffer(query);
6549 372 : }
6550 :
6551 : /*
6552 : * getAccessMethods:
6553 : * get information about all user-defined access methods
6554 : */
6555 : void
6556 372 : getAccessMethods(Archive *fout)
6557 : {
6558 : PGresult *res;
6559 : int ntups;
6560 : int i;
6561 : PQExpBuffer query;
6562 : AccessMethodInfo *aminfo;
6563 : int i_tableoid;
6564 : int i_oid;
6565 : int i_amname;
6566 : int i_amhandler;
6567 : int i_amtype;
6568 :
6569 372 : query = createPQExpBuffer();
6570 :
6571 : /*
6572 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6573 : * ACCESS METHOD, so earlier versions usually have only built-in access
6574 : * methods. v9.6 also changed the access method API, replacing dozens of
6575 : * pg_am columns with amhandler. Even if a user created an access method
6576 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6577 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6578 : * pg_am just to facilitate findAccessMethodByOid() providing the
6579 : * OID-to-name mapping.
6580 : */
6581 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6582 372 : if (fout->remoteVersion >= 90600)
6583 372 : appendPQExpBufferStr(query,
6584 : "amtype, "
6585 : "amhandler::pg_catalog.regproc AS amhandler ");
6586 : else
6587 0 : appendPQExpBufferStr(query,
6588 : "'i'::pg_catalog.\"char\" AS amtype, "
6589 : "'-'::pg_catalog.regproc AS amhandler ");
6590 372 : appendPQExpBufferStr(query, "FROM pg_am");
6591 :
6592 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6593 :
6594 372 : ntups = PQntuples(res);
6595 :
6596 372 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6597 :
6598 372 : i_tableoid = PQfnumber(res, "tableoid");
6599 372 : i_oid = PQfnumber(res, "oid");
6600 372 : i_amname = PQfnumber(res, "amname");
6601 372 : i_amhandler = PQfnumber(res, "amhandler");
6602 372 : i_amtype = PQfnumber(res, "amtype");
6603 :
6604 3220 : for (i = 0; i < ntups; i++)
6605 : {
6606 2848 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6607 2848 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6608 2848 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6609 2848 : AssignDumpId(&aminfo[i].dobj);
6610 2848 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6611 2848 : aminfo[i].dobj.namespace = NULL;
6612 2848 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6613 2848 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6614 :
6615 : /* Decide whether we want to dump it */
6616 2848 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6617 : }
6618 :
6619 372 : PQclear(res);
6620 :
6621 372 : destroyPQExpBuffer(query);
6622 372 : }
6623 :
6624 :
6625 : /*
6626 : * getOpclasses:
6627 : * get information about all opclasses in the system catalogs
6628 : */
6629 : void
6630 372 : getOpclasses(Archive *fout)
6631 : {
6632 : PGresult *res;
6633 : int ntups;
6634 : int i;
6635 372 : PQExpBuffer query = createPQExpBuffer();
6636 : OpclassInfo *opcinfo;
6637 : int i_tableoid;
6638 : int i_oid;
6639 : int i_opcmethod;
6640 : int i_opcname;
6641 : int i_opcnamespace;
6642 : int i_opcowner;
6643 :
6644 : /*
6645 : * find all opclasses, including builtin opclasses; we filter out
6646 : * system-defined opclasses at dump-out time.
6647 : */
6648 :
6649 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6650 : "opcnamespace, "
6651 : "opcowner "
6652 : "FROM pg_opclass");
6653 :
6654 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6655 :
6656 372 : ntups = PQntuples(res);
6657 :
6658 372 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6659 :
6660 372 : i_tableoid = PQfnumber(res, "tableoid");
6661 372 : i_oid = PQfnumber(res, "oid");
6662 372 : i_opcmethod = PQfnumber(res, "opcmethod");
6663 372 : i_opcname = PQfnumber(res, "opcname");
6664 372 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6665 372 : i_opcowner = PQfnumber(res, "opcowner");
6666 :
6667 66528 : for (i = 0; i < ntups; i++)
6668 : {
6669 66156 : opcinfo[i].dobj.objType = DO_OPCLASS;
6670 66156 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6671 66156 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6672 66156 : AssignDumpId(&opcinfo[i].dobj);
6673 66156 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6674 132312 : opcinfo[i].dobj.namespace =
6675 66156 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6676 66156 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6677 66156 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6678 :
6679 : /* Decide whether we want to dump it */
6680 66156 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6681 : }
6682 :
6683 372 : PQclear(res);
6684 :
6685 372 : destroyPQExpBuffer(query);
6686 372 : }
6687 :
6688 : /*
6689 : * getOpfamilies:
6690 : * get information about all opfamilies in the system catalogs
6691 : */
6692 : void
6693 372 : getOpfamilies(Archive *fout)
6694 : {
6695 : PGresult *res;
6696 : int ntups;
6697 : int i;
6698 : PQExpBuffer query;
6699 : OpfamilyInfo *opfinfo;
6700 : int i_tableoid;
6701 : int i_oid;
6702 : int i_opfmethod;
6703 : int i_opfname;
6704 : int i_opfnamespace;
6705 : int i_opfowner;
6706 :
6707 372 : query = createPQExpBuffer();
6708 :
6709 : /*
6710 : * find all opfamilies, including builtin opfamilies; we filter out
6711 : * system-defined opfamilies at dump-out time.
6712 : */
6713 :
6714 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6715 : "opfnamespace, "
6716 : "opfowner "
6717 : "FROM pg_opfamily");
6718 :
6719 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6720 :
6721 372 : ntups = PQntuples(res);
6722 :
6723 372 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6724 :
6725 372 : i_tableoid = PQfnumber(res, "tableoid");
6726 372 : i_oid = PQfnumber(res, "oid");
6727 372 : i_opfname = PQfnumber(res, "opfname");
6728 372 : i_opfmethod = PQfnumber(res, "opfmethod");
6729 372 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6730 372 : i_opfowner = PQfnumber(res, "opfowner");
6731 :
6732 54962 : for (i = 0; i < ntups; i++)
6733 : {
6734 54590 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6735 54590 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6736 54590 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6737 54590 : AssignDumpId(&opfinfo[i].dobj);
6738 54590 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6739 109180 : opfinfo[i].dobj.namespace =
6740 54590 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6741 54590 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6742 54590 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6743 :
6744 : /* Decide whether we want to dump it */
6745 54590 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6746 : }
6747 :
6748 372 : PQclear(res);
6749 :
6750 372 : destroyPQExpBuffer(query);
6751 372 : }
6752 :
6753 : /*
6754 : * getAggregates:
6755 : * get information about all user-defined aggregates in the system catalogs
6756 : */
6757 : void
6758 372 : getAggregates(Archive *fout)
6759 : {
6760 372 : DumpOptions *dopt = fout->dopt;
6761 : PGresult *res;
6762 : int ntups;
6763 : int i;
6764 372 : PQExpBuffer query = createPQExpBuffer();
6765 : AggInfo *agginfo;
6766 : int i_tableoid;
6767 : int i_oid;
6768 : int i_aggname;
6769 : int i_aggnamespace;
6770 : int i_pronargs;
6771 : int i_proargtypes;
6772 : int i_proowner;
6773 : int i_aggacl;
6774 : int i_acldefault;
6775 :
6776 : /*
6777 : * Find all interesting aggregates. See comment in getFuncs() for the
6778 : * rationale behind the filtering logic.
6779 : */
6780 372 : if (fout->remoteVersion >= 90600)
6781 : {
6782 : const char *agg_check;
6783 :
6784 744 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6785 372 : : "p.proisagg");
6786 :
6787 372 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6788 : "p.proname AS aggname, "
6789 : "p.pronamespace AS aggnamespace, "
6790 : "p.pronargs, p.proargtypes, "
6791 : "p.proowner, "
6792 : "p.proacl AS aggacl, "
6793 : "acldefault('f', p.proowner) AS acldefault "
6794 : "FROM pg_proc p "
6795 : "LEFT JOIN pg_init_privs pip ON "
6796 : "(p.oid = pip.objoid "
6797 : "AND pip.classoid = 'pg_proc'::regclass "
6798 : "AND pip.objsubid = 0) "
6799 : "WHERE %s AND ("
6800 : "p.pronamespace != "
6801 : "(SELECT oid FROM pg_namespace "
6802 : "WHERE nspname = 'pg_catalog') OR "
6803 : "p.proacl IS DISTINCT FROM pip.initprivs",
6804 : agg_check);
6805 372 : if (dopt->binary_upgrade)
6806 72 : appendPQExpBufferStr(query,
6807 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6808 : "classid = 'pg_proc'::regclass AND "
6809 : "objid = p.oid AND "
6810 : "refclassid = 'pg_extension'::regclass AND "
6811 : "deptype = 'e')");
6812 372 : appendPQExpBufferChar(query, ')');
6813 : }
6814 : else
6815 : {
6816 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6817 : "pronamespace AS aggnamespace, "
6818 : "pronargs, proargtypes, "
6819 : "proowner, "
6820 : "proacl AS aggacl, "
6821 : "acldefault('f', proowner) AS acldefault "
6822 : "FROM pg_proc p "
6823 : "WHERE proisagg AND ("
6824 : "pronamespace != "
6825 : "(SELECT oid FROM pg_namespace "
6826 : "WHERE nspname = 'pg_catalog')");
6827 0 : if (dopt->binary_upgrade)
6828 0 : appendPQExpBufferStr(query,
6829 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6830 : "classid = 'pg_proc'::regclass AND "
6831 : "objid = p.oid AND "
6832 : "refclassid = 'pg_extension'::regclass AND "
6833 : "deptype = 'e')");
6834 0 : appendPQExpBufferChar(query, ')');
6835 : }
6836 :
6837 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6838 :
6839 372 : ntups = PQntuples(res);
6840 :
6841 372 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6842 :
6843 372 : i_tableoid = PQfnumber(res, "tableoid");
6844 372 : i_oid = PQfnumber(res, "oid");
6845 372 : i_aggname = PQfnumber(res, "aggname");
6846 372 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6847 372 : i_pronargs = PQfnumber(res, "pronargs");
6848 372 : i_proargtypes = PQfnumber(res, "proargtypes");
6849 372 : i_proowner = PQfnumber(res, "proowner");
6850 372 : i_aggacl = PQfnumber(res, "aggacl");
6851 372 : i_acldefault = PQfnumber(res, "acldefault");
6852 :
6853 1170 : for (i = 0; i < ntups; i++)
6854 : {
6855 798 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6856 798 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6857 798 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6858 798 : AssignDumpId(&agginfo[i].aggfn.dobj);
6859 798 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6860 1596 : agginfo[i].aggfn.dobj.namespace =
6861 798 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6862 798 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6863 798 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6864 798 : agginfo[i].aggfn.dacl.privtype = 0;
6865 798 : agginfo[i].aggfn.dacl.initprivs = NULL;
6866 798 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6867 798 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6868 798 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6869 798 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6870 798 : if (agginfo[i].aggfn.nargs == 0)
6871 112 : agginfo[i].aggfn.argtypes = NULL;
6872 : else
6873 : {
6874 686 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6875 686 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6876 686 : agginfo[i].aggfn.argtypes,
6877 686 : agginfo[i].aggfn.nargs);
6878 : }
6879 798 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6880 :
6881 : /* Decide whether we want to dump it */
6882 798 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6883 :
6884 : /* Mark whether aggregate has an ACL */
6885 798 : if (!PQgetisnull(res, i, i_aggacl))
6886 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6887 : }
6888 :
6889 372 : PQclear(res);
6890 :
6891 372 : destroyPQExpBuffer(query);
6892 372 : }
6893 :
6894 : /*
6895 : * getFuncs:
6896 : * get information about all user-defined functions in the system catalogs
6897 : */
6898 : void
6899 372 : getFuncs(Archive *fout)
6900 : {
6901 372 : DumpOptions *dopt = fout->dopt;
6902 : PGresult *res;
6903 : int ntups;
6904 : int i;
6905 372 : PQExpBuffer query = createPQExpBuffer();
6906 : FuncInfo *finfo;
6907 : int i_tableoid;
6908 : int i_oid;
6909 : int i_proname;
6910 : int i_pronamespace;
6911 : int i_proowner;
6912 : int i_prolang;
6913 : int i_pronargs;
6914 : int i_proargtypes;
6915 : int i_prorettype;
6916 : int i_proacl;
6917 : int i_acldefault;
6918 :
6919 : /*
6920 : * Find all interesting functions. This is a bit complicated:
6921 : *
6922 : * 1. Always exclude aggregates; those are handled elsewhere.
6923 : *
6924 : * 2. Always exclude functions that are internally dependent on something
6925 : * else, since presumably those will be created as a result of creating
6926 : * the something else. This currently acts only to suppress constructor
6927 : * functions for range types. Note this is OK only because the
6928 : * constructors don't have any dependencies the range type doesn't have;
6929 : * otherwise we might not get creation ordering correct.
6930 : *
6931 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6932 : * they're members of extensions and we are in binary-upgrade mode then
6933 : * include them, since we want to dump extension members individually in
6934 : * that mode. Also, if they are used by casts or transforms then we need
6935 : * to gather the information about them, though they won't be dumped if
6936 : * they are built-in. Also, in 9.6 and up, include functions in
6937 : * pg_catalog if they have an ACL different from what's shown in
6938 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6939 : */
6940 372 : if (fout->remoteVersion >= 90600)
6941 : {
6942 : const char *not_agg_check;
6943 :
6944 744 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6945 372 : : "NOT p.proisagg");
6946 :
6947 372 : appendPQExpBuffer(query,
6948 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6949 : "p.pronargs, p.proargtypes, p.prorettype, "
6950 : "p.proacl, "
6951 : "acldefault('f', p.proowner) AS acldefault, "
6952 : "p.pronamespace, "
6953 : "p.proowner "
6954 : "FROM pg_proc p "
6955 : "LEFT JOIN pg_init_privs pip ON "
6956 : "(p.oid = pip.objoid "
6957 : "AND pip.classoid = 'pg_proc'::regclass "
6958 : "AND pip.objsubid = 0) "
6959 : "WHERE %s"
6960 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6961 : "WHERE classid = 'pg_proc'::regclass AND "
6962 : "objid = p.oid AND deptype = 'i')"
6963 : "\n AND ("
6964 : "\n pronamespace != "
6965 : "(SELECT oid FROM pg_namespace "
6966 : "WHERE nspname = 'pg_catalog')"
6967 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6968 : "\n WHERE pg_cast.oid > %u "
6969 : "\n AND p.oid = pg_cast.castfunc)"
6970 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6971 : "\n WHERE pg_transform.oid > %u AND "
6972 : "\n (p.oid = pg_transform.trffromsql"
6973 : "\n OR p.oid = pg_transform.trftosql))",
6974 : not_agg_check,
6975 : g_last_builtin_oid,
6976 : g_last_builtin_oid);
6977 372 : if (dopt->binary_upgrade)
6978 72 : appendPQExpBufferStr(query,
6979 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6980 : "classid = 'pg_proc'::regclass AND "
6981 : "objid = p.oid AND "
6982 : "refclassid = 'pg_extension'::regclass AND "
6983 : "deptype = 'e')");
6984 372 : appendPQExpBufferStr(query,
6985 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
6986 372 : appendPQExpBufferChar(query, ')');
6987 : }
6988 : else
6989 : {
6990 0 : appendPQExpBuffer(query,
6991 : "SELECT tableoid, oid, proname, prolang, "
6992 : "pronargs, proargtypes, prorettype, proacl, "
6993 : "acldefault('f', proowner) AS acldefault, "
6994 : "pronamespace, "
6995 : "proowner "
6996 : "FROM pg_proc p "
6997 : "WHERE NOT proisagg"
6998 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6999 : "WHERE classid = 'pg_proc'::regclass AND "
7000 : "objid = p.oid AND deptype = 'i')"
7001 : "\n AND ("
7002 : "\n pronamespace != "
7003 : "(SELECT oid FROM pg_namespace "
7004 : "WHERE nspname = 'pg_catalog')"
7005 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7006 : "\n WHERE pg_cast.oid > '%u'::oid"
7007 : "\n AND p.oid = pg_cast.castfunc)",
7008 : g_last_builtin_oid);
7009 :
7010 0 : if (fout->remoteVersion >= 90500)
7011 0 : appendPQExpBuffer(query,
7012 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7013 : "\n WHERE pg_transform.oid > '%u'::oid"
7014 : "\n AND (p.oid = pg_transform.trffromsql"
7015 : "\n OR p.oid = pg_transform.trftosql))",
7016 : g_last_builtin_oid);
7017 :
7018 0 : if (dopt->binary_upgrade)
7019 0 : appendPQExpBufferStr(query,
7020 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7021 : "classid = 'pg_proc'::regclass AND "
7022 : "objid = p.oid AND "
7023 : "refclassid = 'pg_extension'::regclass AND "
7024 : "deptype = 'e')");
7025 0 : appendPQExpBufferChar(query, ')');
7026 : }
7027 :
7028 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7029 :
7030 372 : ntups = PQntuples(res);
7031 :
7032 372 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7033 :
7034 372 : i_tableoid = PQfnumber(res, "tableoid");
7035 372 : i_oid = PQfnumber(res, "oid");
7036 372 : i_proname = PQfnumber(res, "proname");
7037 372 : i_pronamespace = PQfnumber(res, "pronamespace");
7038 372 : i_proowner = PQfnumber(res, "proowner");
7039 372 : i_prolang = PQfnumber(res, "prolang");
7040 372 : i_pronargs = PQfnumber(res, "pronargs");
7041 372 : i_proargtypes = PQfnumber(res, "proargtypes");
7042 372 : i_prorettype = PQfnumber(res, "prorettype");
7043 372 : i_proacl = PQfnumber(res, "proacl");
7044 372 : i_acldefault = PQfnumber(res, "acldefault");
7045 :
7046 9868 : for (i = 0; i < ntups; i++)
7047 : {
7048 9496 : finfo[i].dobj.objType = DO_FUNC;
7049 9496 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7050 9496 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7051 9496 : AssignDumpId(&finfo[i].dobj);
7052 9496 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7053 18992 : finfo[i].dobj.namespace =
7054 9496 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7055 9496 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7056 9496 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7057 9496 : finfo[i].dacl.privtype = 0;
7058 9496 : finfo[i].dacl.initprivs = NULL;
7059 9496 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7060 9496 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7061 9496 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7062 9496 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7063 9496 : if (finfo[i].nargs == 0)
7064 2126 : finfo[i].argtypes = NULL;
7065 : else
7066 : {
7067 7370 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7068 7370 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7069 7370 : finfo[i].argtypes, finfo[i].nargs);
7070 : }
7071 9496 : finfo[i].postponed_def = false; /* might get set during sort */
7072 :
7073 : /* Decide whether we want to dump it */
7074 9496 : selectDumpableObject(&(finfo[i].dobj), fout);
7075 :
7076 : /* Mark whether function has an ACL */
7077 9496 : if (!PQgetisnull(res, i, i_proacl))
7078 280 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7079 : }
7080 :
7081 372 : PQclear(res);
7082 :
7083 372 : destroyPQExpBuffer(query);
7084 372 : }
7085 :
7086 : /*
7087 : * getRelationStatistics
7088 : * register the statistics object as a dependent of the relation.
7089 : *
7090 : * reltuples is passed as a string to avoid complexities in converting from/to
7091 : * floating point.
7092 : */
7093 : static RelStatsInfo *
7094 19090 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7095 : char *reltuples, int32 relallvisible,
7096 : int32 relallfrozen, char relkind,
7097 : char **indAttNames, int nindAttNames)
7098 : {
7099 19090 : if (!fout->dopt->dumpStatistics)
7100 12044 : return NULL;
7101 :
7102 7046 : if ((relkind == RELKIND_RELATION) ||
7103 2956 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7104 1768 : (relkind == RELKIND_INDEX) ||
7105 1144 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7106 538 : (relkind == RELKIND_MATVIEW ||
7107 : relkind == RELKIND_FOREIGN_TABLE))
7108 : {
7109 6572 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7110 6572 : DumpableObject *dobj = &info->dobj;
7111 :
7112 6572 : dobj->objType = DO_REL_STATS;
7113 6572 : dobj->catId.tableoid = 0;
7114 6572 : dobj->catId.oid = 0;
7115 6572 : AssignDumpId(dobj);
7116 6572 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7117 6572 : dobj->dependencies[0] = rel->dumpId;
7118 6572 : dobj->nDeps = 1;
7119 6572 : dobj->allocDeps = 1;
7120 6572 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7121 6572 : dobj->name = pg_strdup(rel->name);
7122 6572 : dobj->namespace = rel->namespace;
7123 6572 : info->relpages = relpages;
7124 6572 : info->reltuples = pstrdup(reltuples);
7125 6572 : info->relallvisible = relallvisible;
7126 6572 : info->relallfrozen = relallfrozen;
7127 6572 : info->relkind = relkind;
7128 6572 : info->indAttNames = indAttNames;
7129 6572 : info->nindAttNames = nindAttNames;
7130 :
7131 : /*
7132 : * Ordinarily, stats go in SECTION_DATA for tables and
7133 : * SECTION_POST_DATA for indexes.
7134 : *
7135 : * However, the section may be updated later for materialized view
7136 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7137 : * the stats, so the stats must be restored after the data. Also, the
7138 : * materialized view definition may be postponed to SECTION_POST_DATA
7139 : * (see repairMatViewBoundaryMultiLoop()).
7140 : */
7141 6572 : switch (info->relkind)
7142 : {
7143 4760 : case RELKIND_RELATION:
7144 : case RELKIND_PARTITIONED_TABLE:
7145 : case RELKIND_MATVIEW:
7146 : case RELKIND_FOREIGN_TABLE:
7147 4760 : info->section = SECTION_DATA;
7148 4760 : break;
7149 1812 : case RELKIND_INDEX:
7150 : case RELKIND_PARTITIONED_INDEX:
7151 1812 : info->section = SECTION_POST_DATA;
7152 1812 : break;
7153 0 : default:
7154 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7155 : info->relkind);
7156 : }
7157 :
7158 6572 : return info;
7159 : }
7160 474 : return NULL;
7161 : }
7162 :
7163 : /*
7164 : * getTables
7165 : * read all the tables (no indexes) in the system catalogs,
7166 : * and return them as an array of TableInfo structures
7167 : *
7168 : * *numTables is set to the number of tables read in
7169 : */
7170 : TableInfo *
7171 374 : getTables(Archive *fout, int *numTables)
7172 : {
7173 374 : DumpOptions *dopt = fout->dopt;
7174 : PGresult *res;
7175 : int ntups;
7176 : int i;
7177 374 : PQExpBuffer query = createPQExpBuffer();
7178 : TableInfo *tblinfo;
7179 : int i_reltableoid;
7180 : int i_reloid;
7181 : int i_relname;
7182 : int i_relnamespace;
7183 : int i_relkind;
7184 : int i_reltype;
7185 : int i_relowner;
7186 : int i_relchecks;
7187 : int i_relhasindex;
7188 : int i_relhasrules;
7189 : int i_relpages;
7190 : int i_reltuples;
7191 : int i_relallvisible;
7192 : int i_relallfrozen;
7193 : int i_toastpages;
7194 : int i_owning_tab;
7195 : int i_owning_col;
7196 : int i_reltablespace;
7197 : int i_relhasoids;
7198 : int i_relhastriggers;
7199 : int i_relpersistence;
7200 : int i_relispopulated;
7201 : int i_relreplident;
7202 : int i_relrowsec;
7203 : int i_relforcerowsec;
7204 : int i_relfrozenxid;
7205 : int i_toastfrozenxid;
7206 : int i_toastoid;
7207 : int i_relminmxid;
7208 : int i_toastminmxid;
7209 : int i_reloptions;
7210 : int i_checkoption;
7211 : int i_toastreloptions;
7212 : int i_reloftype;
7213 : int i_foreignserver;
7214 : int i_amname;
7215 : int i_is_identity_sequence;
7216 : int i_relacl;
7217 : int i_acldefault;
7218 : int i_ispartition;
7219 :
7220 : /*
7221 : * Find all the tables and table-like objects.
7222 : *
7223 : * We must fetch all tables in this phase because otherwise we cannot
7224 : * correctly identify inherited columns, owned sequences, etc.
7225 : *
7226 : * We include system catalogs, so that we can work if a user table is
7227 : * defined to inherit from a system catalog (pretty weird, but...)
7228 : *
7229 : * Note: in this phase we should collect only a minimal amount of
7230 : * information about each table, basically just enough to decide if it is
7231 : * interesting. In particular, since we do not yet have lock on any user
7232 : * table, we MUST NOT invoke any server-side data collection functions
7233 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7234 : * wrong answers if any concurrent DDL is happening.
7235 : */
7236 :
7237 374 : appendPQExpBufferStr(query,
7238 : "SELECT c.tableoid, c.oid, c.relname, "
7239 : "c.relnamespace, c.relkind, c.reltype, "
7240 : "c.relowner, "
7241 : "c.relchecks, "
7242 : "c.relhasindex, c.relhasrules, c.relpages, "
7243 : "c.reltuples, c.relallvisible, ");
7244 :
7245 374 : if (fout->remoteVersion >= 180000)
7246 374 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7247 : else
7248 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7249 :
7250 374 : appendPQExpBufferStr(query,
7251 : "c.relhastriggers, c.relpersistence, "
7252 : "c.reloftype, "
7253 : "c.relacl, "
7254 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7255 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7256 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7257 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7258 : "ELSE 0 END AS foreignserver, "
7259 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7260 : "tc.oid AS toid, "
7261 : "tc.relpages AS toastpages, "
7262 : "tc.reloptions AS toast_reloptions, "
7263 : "d.refobjid AS owning_tab, "
7264 : "d.refobjsubid AS owning_col, "
7265 : "tsp.spcname AS reltablespace, ");
7266 :
7267 374 : if (fout->remoteVersion >= 120000)
7268 374 : appendPQExpBufferStr(query,
7269 : "false AS relhasoids, ");
7270 : else
7271 0 : appendPQExpBufferStr(query,
7272 : "c.relhasoids, ");
7273 :
7274 374 : if (fout->remoteVersion >= 90300)
7275 374 : appendPQExpBufferStr(query,
7276 : "c.relispopulated, ");
7277 : else
7278 0 : appendPQExpBufferStr(query,
7279 : "'t' as relispopulated, ");
7280 :
7281 374 : if (fout->remoteVersion >= 90400)
7282 374 : appendPQExpBufferStr(query,
7283 : "c.relreplident, ");
7284 : else
7285 0 : appendPQExpBufferStr(query,
7286 : "'d' AS relreplident, ");
7287 :
7288 374 : if (fout->remoteVersion >= 90500)
7289 374 : appendPQExpBufferStr(query,
7290 : "c.relrowsecurity, c.relforcerowsecurity, ");
7291 : else
7292 0 : appendPQExpBufferStr(query,
7293 : "false AS relrowsecurity, "
7294 : "false AS relforcerowsecurity, ");
7295 :
7296 374 : if (fout->remoteVersion >= 90300)
7297 374 : appendPQExpBufferStr(query,
7298 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7299 : else
7300 0 : appendPQExpBufferStr(query,
7301 : "0 AS relminmxid, 0 AS tminmxid, ");
7302 :
7303 374 : if (fout->remoteVersion >= 90300)
7304 374 : appendPQExpBufferStr(query,
7305 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7306 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7307 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7308 : else
7309 0 : appendPQExpBufferStr(query,
7310 : "c.reloptions, NULL AS checkoption, ");
7311 :
7312 374 : if (fout->remoteVersion >= 90600)
7313 374 : appendPQExpBufferStr(query,
7314 : "am.amname, ");
7315 : else
7316 0 : appendPQExpBufferStr(query,
7317 : "NULL AS amname, ");
7318 :
7319 374 : if (fout->remoteVersion >= 90600)
7320 374 : appendPQExpBufferStr(query,
7321 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7322 : else
7323 0 : appendPQExpBufferStr(query,
7324 : "false AS is_identity_sequence, ");
7325 :
7326 374 : if (fout->remoteVersion >= 100000)
7327 374 : appendPQExpBufferStr(query,
7328 : "c.relispartition AS ispartition ");
7329 : else
7330 0 : appendPQExpBufferStr(query,
7331 : "false AS ispartition ");
7332 :
7333 : /*
7334 : * Left join to pg_depend to pick up dependency info linking sequences to
7335 : * their owning column, if any (note this dependency is AUTO except for
7336 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7337 : * collect the spcname.
7338 : */
7339 374 : appendPQExpBufferStr(query,
7340 : "\nFROM pg_class c\n"
7341 : "LEFT JOIN pg_depend d ON "
7342 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7343 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7344 : "d.objsubid = 0 AND "
7345 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7346 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7347 :
7348 : /*
7349 : * In 9.6 and up, left join to pg_am to pick up the amname.
7350 : */
7351 374 : if (fout->remoteVersion >= 90600)
7352 374 : appendPQExpBufferStr(query,
7353 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7354 :
7355 : /*
7356 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7357 : * that versions 10 and 11 have them, but later versions do not, so
7358 : * emitting them causes the upgrade to fail.
7359 : */
7360 374 : appendPQExpBufferStr(query,
7361 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7362 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7363 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7364 :
7365 : /*
7366 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7367 : * relkinds are possible in older servers, but it's not worth the trouble
7368 : * to emit a version-dependent list.
7369 : *
7370 : * Composite-type table entries won't be dumped as such, but we have to
7371 : * make a DumpableObject for them so that we can track dependencies of the
7372 : * composite type (pg_depend entries for columns of the composite type
7373 : * link to the pg_class entry not the pg_type entry).
7374 : */
7375 374 : appendPQExpBufferStr(query,
7376 : "WHERE c.relkind IN ("
7377 : CppAsString2(RELKIND_RELATION) ", "
7378 : CppAsString2(RELKIND_SEQUENCE) ", "
7379 : CppAsString2(RELKIND_VIEW) ", "
7380 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7381 : CppAsString2(RELKIND_MATVIEW) ", "
7382 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7383 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7384 : "ORDER BY c.oid");
7385 :
7386 374 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7387 :
7388 374 : ntups = PQntuples(res);
7389 :
7390 374 : *numTables = ntups;
7391 :
7392 : /*
7393 : * Extract data from result and lock dumpable tables. We do the locking
7394 : * before anything else, to minimize the window wherein a table could
7395 : * disappear under us.
7396 : *
7397 : * Note that we have to save info about all tables here, even when dumping
7398 : * only one, because we don't yet know which tables might be inheritance
7399 : * ancestors of the target table.
7400 : */
7401 374 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7402 :
7403 374 : i_reltableoid = PQfnumber(res, "tableoid");
7404 374 : i_reloid = PQfnumber(res, "oid");
7405 374 : i_relname = PQfnumber(res, "relname");
7406 374 : i_relnamespace = PQfnumber(res, "relnamespace");
7407 374 : i_relkind = PQfnumber(res, "relkind");
7408 374 : i_reltype = PQfnumber(res, "reltype");
7409 374 : i_relowner = PQfnumber(res, "relowner");
7410 374 : i_relchecks = PQfnumber(res, "relchecks");
7411 374 : i_relhasindex = PQfnumber(res, "relhasindex");
7412 374 : i_relhasrules = PQfnumber(res, "relhasrules");
7413 374 : i_relpages = PQfnumber(res, "relpages");
7414 374 : i_reltuples = PQfnumber(res, "reltuples");
7415 374 : i_relallvisible = PQfnumber(res, "relallvisible");
7416 374 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7417 374 : i_toastpages = PQfnumber(res, "toastpages");
7418 374 : i_owning_tab = PQfnumber(res, "owning_tab");
7419 374 : i_owning_col = PQfnumber(res, "owning_col");
7420 374 : i_reltablespace = PQfnumber(res, "reltablespace");
7421 374 : i_relhasoids = PQfnumber(res, "relhasoids");
7422 374 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7423 374 : i_relpersistence = PQfnumber(res, "relpersistence");
7424 374 : i_relispopulated = PQfnumber(res, "relispopulated");
7425 374 : i_relreplident = PQfnumber(res, "relreplident");
7426 374 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7427 374 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7428 374 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7429 374 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7430 374 : i_toastoid = PQfnumber(res, "toid");
7431 374 : i_relminmxid = PQfnumber(res, "relminmxid");
7432 374 : i_toastminmxid = PQfnumber(res, "tminmxid");
7433 374 : i_reloptions = PQfnumber(res, "reloptions");
7434 374 : i_checkoption = PQfnumber(res, "checkoption");
7435 374 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7436 374 : i_reloftype = PQfnumber(res, "reloftype");
7437 374 : i_foreignserver = PQfnumber(res, "foreignserver");
7438 374 : i_amname = PQfnumber(res, "amname");
7439 374 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7440 374 : i_relacl = PQfnumber(res, "relacl");
7441 374 : i_acldefault = PQfnumber(res, "acldefault");
7442 374 : i_ispartition = PQfnumber(res, "ispartition");
7443 :
7444 374 : if (dopt->lockWaitTimeout)
7445 : {
7446 : /*
7447 : * Arrange to fail instead of waiting forever for a table lock.
7448 : *
7449 : * NB: this coding assumes that the only queries issued within the
7450 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7451 : * applied to other things too.
7452 : */
7453 4 : resetPQExpBuffer(query);
7454 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7455 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7456 4 : ExecuteSqlStatement(fout, query->data);
7457 : }
7458 :
7459 374 : resetPQExpBuffer(query);
7460 :
7461 98890 : for (i = 0; i < ntups; i++)
7462 : {
7463 98516 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7464 98516 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7465 :
7466 98516 : tblinfo[i].dobj.objType = DO_TABLE;
7467 98516 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7468 98516 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7469 98516 : AssignDumpId(&tblinfo[i].dobj);
7470 98516 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7471 197032 : tblinfo[i].dobj.namespace =
7472 98516 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7473 98516 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7474 98516 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7475 98516 : tblinfo[i].dacl.privtype = 0;
7476 98516 : tblinfo[i].dacl.initprivs = NULL;
7477 98516 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7478 98516 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7479 98516 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7480 98516 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7481 98516 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7482 98516 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7483 98516 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7484 98516 : if (PQgetisnull(res, i, i_toastpages))
7485 79190 : tblinfo[i].toastpages = 0;
7486 : else
7487 19326 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7488 98516 : if (PQgetisnull(res, i, i_owning_tab))
7489 : {
7490 97686 : tblinfo[i].owning_tab = InvalidOid;
7491 97686 : tblinfo[i].owning_col = 0;
7492 : }
7493 : else
7494 : {
7495 830 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7496 830 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7497 : }
7498 98516 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7499 98516 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7500 98516 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7501 98516 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7502 98516 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7503 98516 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7504 98516 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7505 98516 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7506 98516 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7507 98516 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7508 98516 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7509 98516 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7510 98516 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7511 98516 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7512 98516 : if (PQgetisnull(res, i, i_checkoption))
7513 98424 : tblinfo[i].checkoption = NULL;
7514 : else
7515 92 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7516 98516 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7517 98516 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7518 98516 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7519 98516 : if (PQgetisnull(res, i, i_amname))
7520 59034 : tblinfo[i].amname = NULL;
7521 : else
7522 39482 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7523 98516 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7524 98516 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7525 :
7526 : /* other fields were zeroed above */
7527 :
7528 : /*
7529 : * Decide whether we want to dump this table.
7530 : */
7531 98516 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7532 366 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7533 : else
7534 98150 : selectDumpableTable(&tblinfo[i], fout);
7535 :
7536 : /*
7537 : * Now, consider the table "interesting" if we need to dump its
7538 : * definition, data or its statistics. Later on, we'll skip a lot of
7539 : * data collection for uninteresting tables.
7540 : *
7541 : * Note: the "interesting" flag will also be set by flagInhTables for
7542 : * parents of interesting tables, so that we collect necessary
7543 : * inheritance info even when the parents are not themselves being
7544 : * dumped. This is the main reason why we need an "interesting" flag
7545 : * that's separate from the components-to-dump bitmask.
7546 : */
7547 98516 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7548 : (DUMP_COMPONENT_DEFINITION |
7549 : DUMP_COMPONENT_DATA |
7550 98516 : DUMP_COMPONENT_STATISTICS)) != 0;
7551 :
7552 98516 : tblinfo[i].dummy_view = false; /* might get set during sort */
7553 98516 : tblinfo[i].postponed_def = false; /* might get set during sort */
7554 :
7555 : /* Tables have data */
7556 98516 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7557 :
7558 : /* Mark whether table has an ACL */
7559 98516 : if (!PQgetisnull(res, i, i_relacl))
7560 78928 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7561 98516 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7562 :
7563 : /* Add statistics */
7564 98516 : if (tblinfo[i].interesting)
7565 : {
7566 : RelStatsInfo *stats;
7567 :
7568 27848 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7569 13924 : tblinfo[i].relpages,
7570 : PQgetvalue(res, i, i_reltuples),
7571 : relallvisible, relallfrozen,
7572 13924 : tblinfo[i].relkind, NULL, 0);
7573 13924 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7574 796 : tblinfo[i].stats = stats;
7575 : }
7576 :
7577 : /*
7578 : * Read-lock target tables to make sure they aren't DROPPED or altered
7579 : * in schema before we get around to dumping them.
7580 : *
7581 : * Note that we don't explicitly lock parents of the target tables; we
7582 : * assume our lock on the child is enough to prevent schema
7583 : * alterations to parent tables.
7584 : *
7585 : * NOTE: it'd be kinda nice to lock other relations too, not only
7586 : * plain or partitioned tables, but the backend doesn't presently
7587 : * allow that.
7588 : *
7589 : * We only need to lock the table for certain components; see
7590 : * pg_dump.h
7591 : */
7592 98516 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7593 13924 : (tblinfo[i].relkind == RELKIND_RELATION ||
7594 3938 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7595 : {
7596 : /*
7597 : * Tables are locked in batches. When dumping from a remote
7598 : * server this can save a significant amount of time by reducing
7599 : * the number of round trips.
7600 : */
7601 11174 : if (query->len == 0)
7602 246 : appendPQExpBuffer(query, "LOCK TABLE %s",
7603 246 : fmtQualifiedDumpable(&tblinfo[i]));
7604 : else
7605 : {
7606 10928 : appendPQExpBuffer(query, ", %s",
7607 10928 : fmtQualifiedDumpable(&tblinfo[i]));
7608 :
7609 : /* Arbitrarily end a batch when query length reaches 100K. */
7610 10928 : if (query->len >= 100000)
7611 : {
7612 : /* Lock another batch of tables. */
7613 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7614 0 : ExecuteSqlStatement(fout, query->data);
7615 0 : resetPQExpBuffer(query);
7616 : }
7617 : }
7618 : }
7619 : }
7620 :
7621 374 : if (query->len != 0)
7622 : {
7623 : /* Lock the tables in the last batch. */
7624 246 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7625 246 : ExecuteSqlStatement(fout, query->data);
7626 : }
7627 :
7628 372 : if (dopt->lockWaitTimeout)
7629 : {
7630 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7631 : }
7632 :
7633 372 : PQclear(res);
7634 :
7635 372 : destroyPQExpBuffer(query);
7636 :
7637 372 : return tblinfo;
7638 : }
7639 :
7640 : /*
7641 : * getOwnedSeqs
7642 : * identify owned sequences and mark them as dumpable if owning table is
7643 : *
7644 : * We used to do this in getTables(), but it's better to do it after the
7645 : * index used by findTableByOid() has been set up.
7646 : */
7647 : void
7648 372 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7649 : {
7650 : int i;
7651 :
7652 : /*
7653 : * Force sequences that are "owned" by table columns to be dumped whenever
7654 : * their owning table is being dumped.
7655 : */
7656 98348 : for (i = 0; i < numTables; i++)
7657 : {
7658 97976 : TableInfo *seqinfo = &tblinfo[i];
7659 : TableInfo *owning_tab;
7660 :
7661 97976 : if (!OidIsValid(seqinfo->owning_tab))
7662 97152 : continue; /* not an owned sequence */
7663 :
7664 824 : owning_tab = findTableByOid(seqinfo->owning_tab);
7665 824 : if (owning_tab == NULL)
7666 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7667 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7668 :
7669 : /*
7670 : * For an identity sequence, dump exactly the same components for the
7671 : * sequence as for the owning table. This is important because we
7672 : * treat the identity sequence as an integral part of the table. For
7673 : * example, there is not any DDL command that allows creation of such
7674 : * a sequence independently of the table.
7675 : *
7676 : * For other owned sequences such as serial sequences, we need to dump
7677 : * the components that are being dumped for the table and any
7678 : * components that the sequence is explicitly marked with.
7679 : *
7680 : * We can't simply use the set of components which are being dumped
7681 : * for the table as the table might be in an extension (and only the
7682 : * non-extension components, eg: ACLs if changed, security labels, and
7683 : * policies, are being dumped) while the sequence is not (and
7684 : * therefore the definition and other components should also be
7685 : * dumped).
7686 : *
7687 : * If the sequence is part of the extension then it should be properly
7688 : * marked by checkExtensionMembership() and this will be a no-op as
7689 : * the table will be equivalently marked.
7690 : */
7691 824 : if (seqinfo->is_identity_sequence)
7692 398 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7693 : else
7694 426 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7695 :
7696 : /* Make sure that necessary data is available if we're dumping it */
7697 824 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7698 : {
7699 632 : seqinfo->interesting = true;
7700 632 : owning_tab->interesting = true;
7701 : }
7702 : }
7703 372 : }
7704 :
7705 : /*
7706 : * getInherits
7707 : * read all the inheritance information
7708 : * from the system catalogs return them in the InhInfo* structure
7709 : *
7710 : * numInherits is set to the number of pairs read in
7711 : */
7712 : InhInfo *
7713 372 : getInherits(Archive *fout, int *numInherits)
7714 : {
7715 : PGresult *res;
7716 : int ntups;
7717 : int i;
7718 372 : PQExpBuffer query = createPQExpBuffer();
7719 : InhInfo *inhinfo;
7720 :
7721 : int i_inhrelid;
7722 : int i_inhparent;
7723 :
7724 : /* find all the inheritance information */
7725 372 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7726 :
7727 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7728 :
7729 372 : ntups = PQntuples(res);
7730 :
7731 372 : *numInherits = ntups;
7732 :
7733 372 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7734 :
7735 372 : i_inhrelid = PQfnumber(res, "inhrelid");
7736 372 : i_inhparent = PQfnumber(res, "inhparent");
7737 :
7738 7108 : for (i = 0; i < ntups; i++)
7739 : {
7740 6736 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7741 6736 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7742 : }
7743 :
7744 372 : PQclear(res);
7745 :
7746 372 : destroyPQExpBuffer(query);
7747 :
7748 372 : return inhinfo;
7749 : }
7750 :
7751 : /*
7752 : * getPartitioningInfo
7753 : * get information about partitioning
7754 : *
7755 : * For the most part, we only collect partitioning info about tables we
7756 : * intend to dump. However, this function has to consider all partitioned
7757 : * tables in the database, because we need to know about parents of partitions
7758 : * we are going to dump even if the parents themselves won't be dumped.
7759 : *
7760 : * Specifically, what we need to know is whether each partitioned table
7761 : * has an "unsafe" partitioning scheme that requires us to force
7762 : * load-via-partition-root mode for its children. Currently the only case
7763 : * for which we force that is hash partitioning on enum columns, since the
7764 : * hash codes depend on enum value OIDs which won't be replicated across
7765 : * dump-and-reload. There are other cases in which load-via-partition-root
7766 : * might be necessary, but we expect users to cope with them.
7767 : */
7768 : void
7769 372 : getPartitioningInfo(Archive *fout)
7770 : {
7771 : PQExpBuffer query;
7772 : PGresult *res;
7773 : int ntups;
7774 :
7775 : /* hash partitioning didn't exist before v11 */
7776 372 : if (fout->remoteVersion < 110000)
7777 0 : return;
7778 : /* needn't bother if not dumping data */
7779 372 : if (!fout->dopt->dumpData)
7780 80 : return;
7781 :
7782 292 : query = createPQExpBuffer();
7783 :
7784 : /*
7785 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7786 : * appears among the partition opclasses. We needn't check partstrat.
7787 : *
7788 : * Note that this query may well retrieve info about tables we aren't
7789 : * going to dump and hence have no lock on. That's okay since we need not
7790 : * invoke any unsafe server-side functions.
7791 : */
7792 292 : appendPQExpBufferStr(query,
7793 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7794 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7795 : "ON c.opcmethod = a.oid\n"
7796 : "WHERE opcname = 'enum_ops' "
7797 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7798 : "AND amname = 'hash') = ANY(partclass)");
7799 :
7800 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7801 :
7802 292 : ntups = PQntuples(res);
7803 :
7804 378 : for (int i = 0; i < ntups; i++)
7805 : {
7806 86 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7807 : TableInfo *tbinfo;
7808 :
7809 86 : tbinfo = findTableByOid(tabrelid);
7810 86 : if (tbinfo == NULL)
7811 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7812 : tabrelid);
7813 86 : tbinfo->unsafe_partitions = true;
7814 : }
7815 :
7816 292 : PQclear(res);
7817 :
7818 292 : destroyPQExpBuffer(query);
7819 : }
7820 :
7821 : /*
7822 : * getIndexes
7823 : * get information about every index on a dumpable table
7824 : *
7825 : * Note: index data is not returned directly to the caller, but it
7826 : * does get entered into the DumpableObject tables.
7827 : */
7828 : void
7829 372 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7830 : {
7831 372 : PQExpBuffer query = createPQExpBuffer();
7832 372 : PQExpBuffer tbloids = createPQExpBuffer();
7833 : PGresult *res;
7834 : int ntups;
7835 : int curtblindx;
7836 : IndxInfo *indxinfo;
7837 : int i_tableoid,
7838 : i_oid,
7839 : i_indrelid,
7840 : i_indexname,
7841 : i_relpages,
7842 : i_reltuples,
7843 : i_relallvisible,
7844 : i_relallfrozen,
7845 : i_parentidx,
7846 : i_indexdef,
7847 : i_indnkeyatts,
7848 : i_indnatts,
7849 : i_indkey,
7850 : i_indisclustered,
7851 : i_indisreplident,
7852 : i_indnullsnotdistinct,
7853 : i_contype,
7854 : i_conname,
7855 : i_condeferrable,
7856 : i_condeferred,
7857 : i_conperiod,
7858 : i_contableoid,
7859 : i_conoid,
7860 : i_condef,
7861 : i_indattnames,
7862 : i_tablespace,
7863 : i_indreloptions,
7864 : i_indstatcols,
7865 : i_indstatvals;
7866 :
7867 : /*
7868 : * We want to perform just one query against pg_index. However, we
7869 : * mustn't try to select every row of the catalog and then sort it out on
7870 : * the client side, because some of the server-side functions we need
7871 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7872 : * build an array of the OIDs of tables we care about (and now have lock
7873 : * on!), and use a WHERE clause to constrain which rows are selected.
7874 : */
7875 372 : appendPQExpBufferChar(tbloids, '{');
7876 98348 : for (int i = 0; i < numTables; i++)
7877 : {
7878 97976 : TableInfo *tbinfo = &tblinfo[i];
7879 :
7880 97976 : if (!tbinfo->hasindex)
7881 69420 : continue;
7882 :
7883 : /*
7884 : * We can ignore indexes of uninteresting tables.
7885 : */
7886 28556 : if (!tbinfo->interesting)
7887 24582 : continue;
7888 :
7889 : /* OK, we need info for this table */
7890 3974 : if (tbloids->len > 1) /* do we have more than the '{'? */
7891 3818 : appendPQExpBufferChar(tbloids, ',');
7892 3974 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7893 : }
7894 372 : appendPQExpBufferChar(tbloids, '}');
7895 :
7896 372 : appendPQExpBufferStr(query,
7897 : "SELECT t.tableoid, t.oid, i.indrelid, "
7898 : "t.relname AS indexname, "
7899 : "t.relpages, t.reltuples, t.relallvisible, ");
7900 :
7901 372 : if (fout->remoteVersion >= 180000)
7902 372 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7903 : else
7904 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7905 :
7906 372 : appendPQExpBufferStr(query,
7907 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7908 : "i.indkey, i.indisclustered, "
7909 : "c.contype, c.conname, "
7910 : "c.condeferrable, c.condeferred, "
7911 : "c.tableoid AS contableoid, "
7912 : "c.oid AS conoid, "
7913 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7914 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7915 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7916 : " FROM pg_catalog.pg_attribute "
7917 : " WHERE attrelid = i.indexrelid) "
7918 : "ELSE NULL END AS indattnames, "
7919 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7920 : "t.reloptions AS indreloptions, ");
7921 :
7922 :
7923 372 : if (fout->remoteVersion >= 90400)
7924 372 : appendPQExpBufferStr(query,
7925 : "i.indisreplident, ");
7926 : else
7927 0 : appendPQExpBufferStr(query,
7928 : "false AS indisreplident, ");
7929 :
7930 372 : if (fout->remoteVersion >= 110000)
7931 372 : appendPQExpBufferStr(query,
7932 : "inh.inhparent AS parentidx, "
7933 : "i.indnkeyatts AS indnkeyatts, "
7934 : "i.indnatts AS indnatts, "
7935 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7936 : " FROM pg_catalog.pg_attribute "
7937 : " WHERE attrelid = i.indexrelid AND "
7938 : " attstattarget >= 0) AS indstatcols, "
7939 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7940 : " FROM pg_catalog.pg_attribute "
7941 : " WHERE attrelid = i.indexrelid AND "
7942 : " attstattarget >= 0) AS indstatvals, ");
7943 : else
7944 0 : appendPQExpBufferStr(query,
7945 : "0 AS parentidx, "
7946 : "i.indnatts AS indnkeyatts, "
7947 : "i.indnatts AS indnatts, "
7948 : "'' AS indstatcols, "
7949 : "'' AS indstatvals, ");
7950 :
7951 372 : if (fout->remoteVersion >= 150000)
7952 372 : appendPQExpBufferStr(query,
7953 : "i.indnullsnotdistinct, ");
7954 : else
7955 0 : appendPQExpBufferStr(query,
7956 : "false AS indnullsnotdistinct, ");
7957 :
7958 372 : if (fout->remoteVersion >= 180000)
7959 372 : appendPQExpBufferStr(query,
7960 : "c.conperiod ");
7961 : else
7962 0 : appendPQExpBufferStr(query,
7963 : "NULL AS conperiod ");
7964 :
7965 : /*
7966 : * The point of the messy-looking outer join is to find a constraint that
7967 : * is related by an internal dependency link to the index. If we find one,
7968 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7969 : * index won't have more than one internal dependency.
7970 : *
7971 : * Note: the check on conrelid is redundant, but useful because that
7972 : * column is indexed while conindid is not.
7973 : */
7974 372 : if (fout->remoteVersion >= 110000)
7975 : {
7976 372 : appendPQExpBuffer(query,
7977 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7978 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7979 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7980 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7981 : "LEFT JOIN pg_catalog.pg_constraint c "
7982 : "ON (i.indrelid = c.conrelid AND "
7983 : "i.indexrelid = c.conindid AND "
7984 : "c.contype IN ('p','u','x')) "
7985 : "LEFT JOIN pg_catalog.pg_inherits inh "
7986 : "ON (inh.inhrelid = indexrelid) "
7987 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
7988 : "AND i.indisready "
7989 : "ORDER BY i.indrelid, indexname",
7990 : tbloids->data);
7991 : }
7992 : else
7993 : {
7994 : /*
7995 : * the test on indisready is necessary in 9.2, and harmless in
7996 : * earlier/later versions
7997 : */
7998 0 : appendPQExpBuffer(query,
7999 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8000 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8001 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8002 : "LEFT JOIN pg_catalog.pg_constraint c "
8003 : "ON (i.indrelid = c.conrelid AND "
8004 : "i.indexrelid = c.conindid AND "
8005 : "c.contype IN ('p','u','x')) "
8006 : "WHERE i.indisvalid AND i.indisready "
8007 : "ORDER BY i.indrelid, indexname",
8008 : tbloids->data);
8009 : }
8010 :
8011 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8012 :
8013 372 : ntups = PQntuples(res);
8014 :
8015 372 : i_tableoid = PQfnumber(res, "tableoid");
8016 372 : i_oid = PQfnumber(res, "oid");
8017 372 : i_indrelid = PQfnumber(res, "indrelid");
8018 372 : i_indexname = PQfnumber(res, "indexname");
8019 372 : i_relpages = PQfnumber(res, "relpages");
8020 372 : i_reltuples = PQfnumber(res, "reltuples");
8021 372 : i_relallvisible = PQfnumber(res, "relallvisible");
8022 372 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8023 372 : i_parentidx = PQfnumber(res, "parentidx");
8024 372 : i_indexdef = PQfnumber(res, "indexdef");
8025 372 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8026 372 : i_indnatts = PQfnumber(res, "indnatts");
8027 372 : i_indkey = PQfnumber(res, "indkey");
8028 372 : i_indisclustered = PQfnumber(res, "indisclustered");
8029 372 : i_indisreplident = PQfnumber(res, "indisreplident");
8030 372 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8031 372 : i_contype = PQfnumber(res, "contype");
8032 372 : i_conname = PQfnumber(res, "conname");
8033 372 : i_condeferrable = PQfnumber(res, "condeferrable");
8034 372 : i_condeferred = PQfnumber(res, "condeferred");
8035 372 : i_conperiod = PQfnumber(res, "conperiod");
8036 372 : i_contableoid = PQfnumber(res, "contableoid");
8037 372 : i_conoid = PQfnumber(res, "conoid");
8038 372 : i_condef = PQfnumber(res, "condef");
8039 372 : i_indattnames = PQfnumber(res, "indattnames");
8040 372 : i_tablespace = PQfnumber(res, "tablespace");
8041 372 : i_indreloptions = PQfnumber(res, "indreloptions");
8042 372 : i_indstatcols = PQfnumber(res, "indstatcols");
8043 372 : i_indstatvals = PQfnumber(res, "indstatvals");
8044 :
8045 372 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8046 :
8047 : /*
8048 : * Outer loop iterates once per table, not once per row. Incrementing of
8049 : * j is handled by the inner loop.
8050 : */
8051 372 : curtblindx = -1;
8052 4314 : for (int j = 0; j < ntups;)
8053 : {
8054 3942 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8055 3942 : TableInfo *tbinfo = NULL;
8056 3942 : char **indAttNames = NULL;
8057 3942 : int nindAttNames = 0;
8058 : int numinds;
8059 :
8060 : /* Count rows for this table */
8061 5166 : for (numinds = 1; numinds < ntups - j; numinds++)
8062 5010 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8063 3786 : break;
8064 :
8065 : /*
8066 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8067 : * order.
8068 : */
8069 45980 : while (++curtblindx < numTables)
8070 : {
8071 45980 : tbinfo = &tblinfo[curtblindx];
8072 45980 : if (tbinfo->dobj.catId.oid == indrelid)
8073 3942 : break;
8074 : }
8075 3942 : if (curtblindx >= numTables)
8076 0 : pg_fatal("unrecognized table OID %u", indrelid);
8077 : /* cross-check that we only got requested tables */
8078 3942 : if (!tbinfo->hasindex ||
8079 3942 : !tbinfo->interesting)
8080 0 : pg_fatal("unexpected index data for table \"%s\"",
8081 : tbinfo->dobj.name);
8082 :
8083 : /* Save data for this table */
8084 3942 : tbinfo->indexes = indxinfo + j;
8085 3942 : tbinfo->numIndexes = numinds;
8086 :
8087 9108 : for (int c = 0; c < numinds; c++, j++)
8088 : {
8089 : char contype;
8090 : char indexkind;
8091 : RelStatsInfo *relstats;
8092 5166 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8093 5166 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8094 5166 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8095 :
8096 5166 : indxinfo[j].dobj.objType = DO_INDEX;
8097 5166 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8098 5166 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8099 5166 : AssignDumpId(&indxinfo[j].dobj);
8100 5166 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8101 5166 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8102 5166 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8103 5166 : indxinfo[j].indextable = tbinfo;
8104 5166 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8105 5166 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8106 5166 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8107 5166 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8108 5166 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8109 5166 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8110 5166 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8111 5166 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8112 5166 : parseOidArray(PQgetvalue(res, j, i_indkey),
8113 5166 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8114 5166 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8115 5166 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8116 5166 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8117 5166 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8118 5166 : indxinfo[j].partattaches = (SimplePtrList)
8119 : {
8120 : NULL, NULL
8121 : };
8122 :
8123 5166 : if (indxinfo[j].parentidx == 0)
8124 4038 : indexkind = RELKIND_INDEX;
8125 : else
8126 1128 : indexkind = RELKIND_PARTITIONED_INDEX;
8127 :
8128 5166 : if (!PQgetisnull(res, j, i_indattnames))
8129 : {
8130 292 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8131 : &indAttNames, &nindAttNames))
8132 0 : pg_fatal("could not parse %s array", "indattnames");
8133 : }
8134 :
8135 5166 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8136 : PQgetvalue(res, j, i_reltuples),
8137 : relallvisible, relallfrozen, indexkind,
8138 : indAttNames, nindAttNames);
8139 :
8140 5166 : contype = *(PQgetvalue(res, j, i_contype));
8141 5166 : if (contype == 'p' || contype == 'u' || contype == 'x')
8142 3002 : {
8143 : /*
8144 : * If we found a constraint matching the index, create an
8145 : * entry for it.
8146 : */
8147 : ConstraintInfo *constrinfo;
8148 :
8149 3002 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8150 3002 : constrinfo->dobj.objType = DO_CONSTRAINT;
8151 3002 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8152 3002 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8153 3002 : AssignDumpId(&constrinfo->dobj);
8154 3002 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8155 3002 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8156 3002 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8157 3002 : constrinfo->contable = tbinfo;
8158 3002 : constrinfo->condomain = NULL;
8159 3002 : constrinfo->contype = contype;
8160 3002 : if (contype == 'x')
8161 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8162 : else
8163 2982 : constrinfo->condef = NULL;
8164 3002 : constrinfo->confrelid = InvalidOid;
8165 3002 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8166 3002 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8167 3002 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8168 3002 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8169 3002 : constrinfo->conislocal = true;
8170 3002 : constrinfo->separate = true;
8171 :
8172 3002 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8173 3002 : if (relstats != NULL)
8174 1052 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8175 : }
8176 : else
8177 : {
8178 : /* Plain secondary index */
8179 2164 : indxinfo[j].indexconstraint = 0;
8180 : }
8181 : }
8182 : }
8183 :
8184 372 : PQclear(res);
8185 :
8186 372 : destroyPQExpBuffer(query);
8187 372 : destroyPQExpBuffer(tbloids);
8188 372 : }
8189 :
8190 : /*
8191 : * getExtendedStatistics
8192 : * get information about extended-statistics objects.
8193 : *
8194 : * Note: extended statistics data is not returned directly to the caller, but
8195 : * it does get entered into the DumpableObject tables.
8196 : */
8197 : void
8198 372 : getExtendedStatistics(Archive *fout)
8199 : {
8200 : PQExpBuffer query;
8201 : PGresult *res;
8202 : StatsExtInfo *statsextinfo;
8203 : int ntups;
8204 : int i_tableoid;
8205 : int i_oid;
8206 : int i_stxname;
8207 : int i_stxnamespace;
8208 : int i_stxowner;
8209 : int i_stxrelid;
8210 : int i_stattarget;
8211 : int i;
8212 :
8213 : /* Extended statistics were new in v10 */
8214 372 : if (fout->remoteVersion < 100000)
8215 0 : return;
8216 :
8217 372 : query = createPQExpBuffer();
8218 :
8219 372 : if (fout->remoteVersion < 130000)
8220 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8221 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8222 : "FROM pg_catalog.pg_statistic_ext");
8223 : else
8224 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8225 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8226 : "FROM pg_catalog.pg_statistic_ext");
8227 :
8228 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8229 :
8230 372 : ntups = PQntuples(res);
8231 :
8232 372 : i_tableoid = PQfnumber(res, "tableoid");
8233 372 : i_oid = PQfnumber(res, "oid");
8234 372 : i_stxname = PQfnumber(res, "stxname");
8235 372 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8236 372 : i_stxowner = PQfnumber(res, "stxowner");
8237 372 : i_stxrelid = PQfnumber(res, "stxrelid");
8238 372 : i_stattarget = PQfnumber(res, "stxstattarget");
8239 :
8240 372 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8241 :
8242 698 : for (i = 0; i < ntups; i++)
8243 : {
8244 326 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8245 326 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8246 326 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8247 326 : AssignDumpId(&statsextinfo[i].dobj);
8248 326 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8249 652 : statsextinfo[i].dobj.namespace =
8250 326 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8251 326 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8252 652 : statsextinfo[i].stattable =
8253 326 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8254 326 : if (PQgetisnull(res, i, i_stattarget))
8255 236 : statsextinfo[i].stattarget = -1;
8256 : else
8257 90 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8258 :
8259 : /* Decide whether we want to dump it */
8260 326 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8261 : }
8262 :
8263 372 : PQclear(res);
8264 372 : destroyPQExpBuffer(query);
8265 : }
8266 :
8267 : /*
8268 : * getConstraints
8269 : *
8270 : * Get info about constraints on dumpable tables.
8271 : *
8272 : * Currently handles foreign keys only.
8273 : * Unique and primary key constraints are handled with indexes,
8274 : * while check constraints are processed in getTableAttrs().
8275 : */
8276 : void
8277 372 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8278 : {
8279 372 : PQExpBuffer query = createPQExpBuffer();
8280 372 : PQExpBuffer tbloids = createPQExpBuffer();
8281 : PGresult *res;
8282 : int ntups;
8283 : int curtblindx;
8284 372 : TableInfo *tbinfo = NULL;
8285 : ConstraintInfo *constrinfo;
8286 : int i_contableoid,
8287 : i_conoid,
8288 : i_conrelid,
8289 : i_conname,
8290 : i_confrelid,
8291 : i_conindid,
8292 : i_condef;
8293 :
8294 : /*
8295 : * We want to perform just one query against pg_constraint. However, we
8296 : * mustn't try to select every row of the catalog and then sort it out on
8297 : * the client side, because some of the server-side functions we need
8298 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8299 : * build an array of the OIDs of tables we care about (and now have lock
8300 : * on!), and use a WHERE clause to constrain which rows are selected.
8301 : */
8302 372 : appendPQExpBufferChar(tbloids, '{');
8303 98348 : for (int i = 0; i < numTables; i++)
8304 : {
8305 97976 : TableInfo *tinfo = &tblinfo[i];
8306 :
8307 97976 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8308 84154 : continue;
8309 :
8310 : /* OK, we need info for this table */
8311 13822 : if (tbloids->len > 1) /* do we have more than the '{'? */
8312 13574 : appendPQExpBufferChar(tbloids, ',');
8313 13822 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8314 : }
8315 372 : appendPQExpBufferChar(tbloids, '}');
8316 :
8317 372 : appendPQExpBufferStr(query,
8318 : "SELECT c.tableoid, c.oid, "
8319 : "conrelid, conname, confrelid, ");
8320 372 : if (fout->remoteVersion >= 110000)
8321 372 : appendPQExpBufferStr(query, "conindid, ");
8322 : else
8323 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8324 372 : appendPQExpBuffer(query,
8325 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8326 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8327 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8328 : "WHERE contype = 'f' ",
8329 : tbloids->data);
8330 372 : if (fout->remoteVersion >= 110000)
8331 372 : appendPQExpBufferStr(query,
8332 : "AND conparentid = 0 ");
8333 372 : appendPQExpBufferStr(query,
8334 : "ORDER BY conrelid, conname");
8335 :
8336 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8337 :
8338 372 : ntups = PQntuples(res);
8339 :
8340 372 : i_contableoid = PQfnumber(res, "tableoid");
8341 372 : i_conoid = PQfnumber(res, "oid");
8342 372 : i_conrelid = PQfnumber(res, "conrelid");
8343 372 : i_conname = PQfnumber(res, "conname");
8344 372 : i_confrelid = PQfnumber(res, "confrelid");
8345 372 : i_conindid = PQfnumber(res, "conindid");
8346 372 : i_condef = PQfnumber(res, "condef");
8347 :
8348 372 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8349 :
8350 372 : curtblindx = -1;
8351 714 : for (int j = 0; j < ntups; j++)
8352 : {
8353 342 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8354 : TableInfo *reftable;
8355 :
8356 : /*
8357 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8358 : * order.
8359 : */
8360 342 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8361 : {
8362 26990 : while (++curtblindx < numTables)
8363 : {
8364 26990 : tbinfo = &tblinfo[curtblindx];
8365 26990 : if (tbinfo->dobj.catId.oid == conrelid)
8366 322 : break;
8367 : }
8368 322 : if (curtblindx >= numTables)
8369 0 : pg_fatal("unrecognized table OID %u", conrelid);
8370 : }
8371 :
8372 342 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8373 342 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8374 342 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8375 342 : AssignDumpId(&constrinfo[j].dobj);
8376 342 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8377 342 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8378 342 : constrinfo[j].contable = tbinfo;
8379 342 : constrinfo[j].condomain = NULL;
8380 342 : constrinfo[j].contype = 'f';
8381 342 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8382 342 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8383 342 : constrinfo[j].conindex = 0;
8384 342 : constrinfo[j].condeferrable = false;
8385 342 : constrinfo[j].condeferred = false;
8386 342 : constrinfo[j].conislocal = true;
8387 342 : constrinfo[j].separate = true;
8388 :
8389 : /*
8390 : * Restoring an FK that points to a partitioned table requires that
8391 : * all partition indexes have been attached beforehand. Ensure that
8392 : * happens by making the constraint depend on each index partition
8393 : * attach object.
8394 : */
8395 342 : reftable = findTableByOid(constrinfo[j].confrelid);
8396 342 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8397 : {
8398 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8399 :
8400 40 : if (indexOid != InvalidOid)
8401 : {
8402 40 : for (int k = 0; k < reftable->numIndexes; k++)
8403 : {
8404 : IndxInfo *refidx;
8405 :
8406 : /* not our index? */
8407 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8408 0 : continue;
8409 :
8410 40 : refidx = &reftable->indexes[k];
8411 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8412 40 : break;
8413 : }
8414 : }
8415 : }
8416 : }
8417 :
8418 372 : PQclear(res);
8419 :
8420 372 : destroyPQExpBuffer(query);
8421 372 : destroyPQExpBuffer(tbloids);
8422 372 : }
8423 :
8424 : /*
8425 : * addConstrChildIdxDeps
8426 : *
8427 : * Recursive subroutine for getConstraints
8428 : *
8429 : * Given an object representing a foreign key constraint and an index on the
8430 : * partitioned table it references, mark the constraint object as dependent
8431 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8432 : * drilling down to their partitions if any. This ensures that the FK is not
8433 : * restored until the index is fully marked valid.
8434 : */
8435 : static void
8436 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8437 : {
8438 : SimplePtrListCell *cell;
8439 :
8440 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8441 :
8442 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8443 : {
8444 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8445 :
8446 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8447 :
8448 220 : if (attach->partitionIdx->partattaches.head != NULL)
8449 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8450 : }
8451 90 : }
8452 :
8453 : /*
8454 : * getDomainConstraints
8455 : *
8456 : * Get info about constraints on a domain.
8457 : */
8458 : static void
8459 316 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8460 : {
8461 : ConstraintInfo *constrinfo;
8462 316 : PQExpBuffer query = createPQExpBuffer();
8463 : PGresult *res;
8464 : int i_tableoid,
8465 : i_oid,
8466 : i_conname,
8467 : i_consrc,
8468 : i_convalidated,
8469 : i_contype;
8470 : int ntups;
8471 :
8472 316 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8473 : {
8474 : /*
8475 : * Set up query for constraint-specific details. For servers 17 and
8476 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8477 : * just the latter.
8478 : */
8479 86 : appendPQExpBuffer(query,
8480 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8481 : "SELECT tableoid, oid, conname, "
8482 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8483 : "convalidated, contype "
8484 : "FROM pg_catalog.pg_constraint "
8485 : "WHERE contypid = $1 AND contype IN (%s) "
8486 : "ORDER BY conname",
8487 86 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8488 :
8489 86 : ExecuteSqlStatement(fout, query->data);
8490 :
8491 86 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8492 : }
8493 :
8494 316 : printfPQExpBuffer(query,
8495 : "EXECUTE getDomainConstraints('%u')",
8496 : tyinfo->dobj.catId.oid);
8497 :
8498 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8499 :
8500 316 : ntups = PQntuples(res);
8501 :
8502 316 : i_tableoid = PQfnumber(res, "tableoid");
8503 316 : i_oid = PQfnumber(res, "oid");
8504 316 : i_conname = PQfnumber(res, "conname");
8505 316 : i_consrc = PQfnumber(res, "consrc");
8506 316 : i_convalidated = PQfnumber(res, "convalidated");
8507 316 : i_contype = PQfnumber(res, "contype");
8508 :
8509 316 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8510 316 : tyinfo->domChecks = constrinfo;
8511 :
8512 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8513 648 : for (int i = 0, j = 0; i < ntups; i++)
8514 : {
8515 332 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8516 332 : char contype = (PQgetvalue(res, i, i_contype))[0];
8517 : ConstraintInfo *constraint;
8518 :
8519 332 : if (contype == CONSTRAINT_CHECK)
8520 : {
8521 226 : constraint = &constrinfo[j++];
8522 226 : tyinfo->nDomChecks++;
8523 : }
8524 : else
8525 : {
8526 : Assert(contype == CONSTRAINT_NOTNULL);
8527 : Assert(tyinfo->notnull == NULL);
8528 : /* use last item in array for the not-null constraint */
8529 106 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8530 106 : constraint = tyinfo->notnull;
8531 : }
8532 :
8533 332 : constraint->dobj.objType = DO_CONSTRAINT;
8534 332 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8535 332 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8536 332 : AssignDumpId(&(constraint->dobj));
8537 332 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8538 332 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8539 332 : constraint->contable = NULL;
8540 332 : constraint->condomain = tyinfo;
8541 332 : constraint->contype = contype;
8542 332 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8543 332 : constraint->confrelid = InvalidOid;
8544 332 : constraint->conindex = 0;
8545 332 : constraint->condeferrable = false;
8546 332 : constraint->condeferred = false;
8547 332 : constraint->conislocal = true;
8548 :
8549 332 : constraint->separate = !validated;
8550 :
8551 : /*
8552 : * Make the domain depend on the constraint, ensuring it won't be
8553 : * output till any constraint dependencies are OK. If the constraint
8554 : * has not been validated, it's going to be dumped after the domain
8555 : * anyway, so this doesn't matter.
8556 : */
8557 332 : if (validated)
8558 322 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8559 : }
8560 :
8561 316 : PQclear(res);
8562 :
8563 316 : destroyPQExpBuffer(query);
8564 316 : }
8565 :
8566 : /*
8567 : * getRules
8568 : * get basic information about every rule in the system
8569 : */
8570 : void
8571 372 : getRules(Archive *fout)
8572 : {
8573 : PGresult *res;
8574 : int ntups;
8575 : int i;
8576 372 : PQExpBuffer query = createPQExpBuffer();
8577 : RuleInfo *ruleinfo;
8578 : int i_tableoid;
8579 : int i_oid;
8580 : int i_rulename;
8581 : int i_ruletable;
8582 : int i_ev_type;
8583 : int i_is_instead;
8584 : int i_ev_enabled;
8585 :
8586 372 : appendPQExpBufferStr(query, "SELECT "
8587 : "tableoid, oid, rulename, "
8588 : "ev_class AS ruletable, ev_type, is_instead, "
8589 : "ev_enabled "
8590 : "FROM pg_rewrite "
8591 : "ORDER BY oid");
8592 :
8593 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8594 :
8595 372 : ntups = PQntuples(res);
8596 :
8597 372 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8598 :
8599 372 : i_tableoid = PQfnumber(res, "tableoid");
8600 372 : i_oid = PQfnumber(res, "oid");
8601 372 : i_rulename = PQfnumber(res, "rulename");
8602 372 : i_ruletable = PQfnumber(res, "ruletable");
8603 372 : i_ev_type = PQfnumber(res, "ev_type");
8604 372 : i_is_instead = PQfnumber(res, "is_instead");
8605 372 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8606 :
8607 58086 : for (i = 0; i < ntups; i++)
8608 : {
8609 : Oid ruletableoid;
8610 :
8611 57714 : ruleinfo[i].dobj.objType = DO_RULE;
8612 57714 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8613 57714 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8614 57714 : AssignDumpId(&ruleinfo[i].dobj);
8615 57714 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8616 57714 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8617 57714 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8618 57714 : if (ruleinfo[i].ruletable == NULL)
8619 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8620 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8621 57714 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8622 57714 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8623 57714 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8624 57714 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8625 57714 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8626 57714 : if (ruleinfo[i].ruletable)
8627 : {
8628 : /*
8629 : * If the table is a view or materialized view, force its ON
8630 : * SELECT rule to be sorted before the view itself --- this
8631 : * ensures that any dependencies for the rule affect the table's
8632 : * positioning. Other rules are forced to appear after their
8633 : * table.
8634 : */
8635 57714 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8636 1398 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8637 57252 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8638 : {
8639 56424 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8640 56424 : ruleinfo[i].dobj.dumpId);
8641 : /* We'll merge the rule into CREATE VIEW, if possible */
8642 56424 : ruleinfo[i].separate = false;
8643 : }
8644 : else
8645 : {
8646 1290 : addObjectDependency(&ruleinfo[i].dobj,
8647 1290 : ruleinfo[i].ruletable->dobj.dumpId);
8648 1290 : ruleinfo[i].separate = true;
8649 : }
8650 : }
8651 : else
8652 0 : ruleinfo[i].separate = true;
8653 : }
8654 :
8655 372 : PQclear(res);
8656 :
8657 372 : destroyPQExpBuffer(query);
8658 372 : }
8659 :
8660 : /*
8661 : * getTriggers
8662 : * get information about every trigger on a dumpable table
8663 : *
8664 : * Note: trigger data is not returned directly to the caller, but it
8665 : * does get entered into the DumpableObject tables.
8666 : */
8667 : void
8668 372 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8669 : {
8670 372 : PQExpBuffer query = createPQExpBuffer();
8671 372 : PQExpBuffer tbloids = createPQExpBuffer();
8672 : PGresult *res;
8673 : int ntups;
8674 : int curtblindx;
8675 : TriggerInfo *tginfo;
8676 : int i_tableoid,
8677 : i_oid,
8678 : i_tgrelid,
8679 : i_tgname,
8680 : i_tgenabled,
8681 : i_tgispartition,
8682 : i_tgdef;
8683 :
8684 : /*
8685 : * We want to perform just one query against pg_trigger. However, we
8686 : * mustn't try to select every row of the catalog and then sort it out on
8687 : * the client side, because some of the server-side functions we need
8688 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8689 : * build an array of the OIDs of tables we care about (and now have lock
8690 : * on!), and use a WHERE clause to constrain which rows are selected.
8691 : */
8692 372 : appendPQExpBufferChar(tbloids, '{');
8693 98348 : for (int i = 0; i < numTables; i++)
8694 : {
8695 97976 : TableInfo *tbinfo = &tblinfo[i];
8696 :
8697 97976 : if (!tbinfo->hastriggers ||
8698 2228 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8699 96272 : continue;
8700 :
8701 : /* OK, we need info for this table */
8702 1704 : if (tbloids->len > 1) /* do we have more than the '{'? */
8703 1602 : appendPQExpBufferChar(tbloids, ',');
8704 1704 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8705 : }
8706 372 : appendPQExpBufferChar(tbloids, '}');
8707 :
8708 372 : if (fout->remoteVersion >= 150000)
8709 : {
8710 : /*
8711 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8712 : * result in non-forward-compatible dumps of WHEN clauses due to
8713 : * under-parenthesization.
8714 : *
8715 : * NB: We need to see partition triggers in case the tgenabled flag
8716 : * has been changed from the parent.
8717 : */
8718 372 : appendPQExpBuffer(query,
8719 : "SELECT t.tgrelid, t.tgname, "
8720 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8721 : "t.tgenabled, t.tableoid, t.oid, "
8722 : "t.tgparentid <> 0 AS tgispartition\n"
8723 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8724 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8725 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8726 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8727 : "OR t.tgenabled != u.tgenabled) "
8728 : "ORDER BY t.tgrelid, t.tgname",
8729 : tbloids->data);
8730 : }
8731 0 : else if (fout->remoteVersion >= 130000)
8732 : {
8733 : /*
8734 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8735 : * result in non-forward-compatible dumps of WHEN clauses due to
8736 : * under-parenthesization.
8737 : *
8738 : * NB: We need to see tgisinternal triggers in partitions, in case the
8739 : * tgenabled flag has been changed from the parent.
8740 : */
8741 0 : appendPQExpBuffer(query,
8742 : "SELECT t.tgrelid, t.tgname, "
8743 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8744 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8745 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8746 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8747 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8748 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8749 : "ORDER BY t.tgrelid, t.tgname",
8750 : tbloids->data);
8751 : }
8752 0 : else if (fout->remoteVersion >= 110000)
8753 : {
8754 : /*
8755 : * NB: We need to see tgisinternal triggers in partitions, in case the
8756 : * tgenabled flag has been changed from the parent. No tgparentid in
8757 : * version 11-12, so we have to match them via pg_depend.
8758 : *
8759 : * See above about pretty=true in pg_get_triggerdef.
8760 : */
8761 0 : appendPQExpBuffer(query,
8762 : "SELECT t.tgrelid, t.tgname, "
8763 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8764 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8765 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8766 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8767 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8768 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8769 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8770 : " d.objid = t.oid "
8771 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8772 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8773 : "ORDER BY t.tgrelid, t.tgname",
8774 : tbloids->data);
8775 : }
8776 : else
8777 : {
8778 : /* See above about pretty=true in pg_get_triggerdef */
8779 0 : appendPQExpBuffer(query,
8780 : "SELECT t.tgrelid, t.tgname, "
8781 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8782 : "t.tgenabled, false as tgispartition, "
8783 : "t.tableoid, t.oid "
8784 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8785 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8786 : "WHERE NOT tgisinternal "
8787 : "ORDER BY t.tgrelid, t.tgname",
8788 : tbloids->data);
8789 : }
8790 :
8791 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8792 :
8793 372 : ntups = PQntuples(res);
8794 :
8795 372 : i_tableoid = PQfnumber(res, "tableoid");
8796 372 : i_oid = PQfnumber(res, "oid");
8797 372 : i_tgrelid = PQfnumber(res, "tgrelid");
8798 372 : i_tgname = PQfnumber(res, "tgname");
8799 372 : i_tgenabled = PQfnumber(res, "tgenabled");
8800 372 : i_tgispartition = PQfnumber(res, "tgispartition");
8801 372 : i_tgdef = PQfnumber(res, "tgdef");
8802 :
8803 372 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8804 :
8805 : /*
8806 : * Outer loop iterates once per table, not once per row. Incrementing of
8807 : * j is handled by the inner loop.
8808 : */
8809 372 : curtblindx = -1;
8810 984 : for (int j = 0; j < ntups;)
8811 : {
8812 612 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8813 612 : TableInfo *tbinfo = NULL;
8814 : int numtrigs;
8815 :
8816 : /* Count rows for this table */
8817 1046 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8818 944 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8819 510 : break;
8820 :
8821 : /*
8822 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8823 : * order.
8824 : */
8825 32090 : while (++curtblindx < numTables)
8826 : {
8827 32090 : tbinfo = &tblinfo[curtblindx];
8828 32090 : if (tbinfo->dobj.catId.oid == tgrelid)
8829 612 : break;
8830 : }
8831 612 : if (curtblindx >= numTables)
8832 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8833 :
8834 : /* Save data for this table */
8835 612 : tbinfo->triggers = tginfo + j;
8836 612 : tbinfo->numTriggers = numtrigs;
8837 :
8838 1658 : for (int c = 0; c < numtrigs; c++, j++)
8839 : {
8840 1046 : tginfo[j].dobj.objType = DO_TRIGGER;
8841 1046 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8842 1046 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8843 1046 : AssignDumpId(&tginfo[j].dobj);
8844 1046 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8845 1046 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8846 1046 : tginfo[j].tgtable = tbinfo;
8847 1046 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8848 1046 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8849 1046 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8850 : }
8851 : }
8852 :
8853 372 : PQclear(res);
8854 :
8855 372 : destroyPQExpBuffer(query);
8856 372 : destroyPQExpBuffer(tbloids);
8857 372 : }
8858 :
8859 : /*
8860 : * getEventTriggers
8861 : * get information about event triggers
8862 : */
8863 : void
8864 372 : getEventTriggers(Archive *fout)
8865 : {
8866 : int i;
8867 : PQExpBuffer query;
8868 : PGresult *res;
8869 : EventTriggerInfo *evtinfo;
8870 : int i_tableoid,
8871 : i_oid,
8872 : i_evtname,
8873 : i_evtevent,
8874 : i_evtowner,
8875 : i_evttags,
8876 : i_evtfname,
8877 : i_evtenabled;
8878 : int ntups;
8879 :
8880 : /* Before 9.3, there are no event triggers */
8881 372 : if (fout->remoteVersion < 90300)
8882 0 : return;
8883 :
8884 372 : query = createPQExpBuffer();
8885 :
8886 372 : appendPQExpBufferStr(query,
8887 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8888 : "evtevent, evtowner, "
8889 : "array_to_string(array("
8890 : "select quote_literal(x) "
8891 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8892 : "e.evtfoid::regproc as evtfname "
8893 : "FROM pg_event_trigger e "
8894 : "ORDER BY e.oid");
8895 :
8896 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8897 :
8898 372 : ntups = PQntuples(res);
8899 :
8900 372 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8901 :
8902 372 : i_tableoid = PQfnumber(res, "tableoid");
8903 372 : i_oid = PQfnumber(res, "oid");
8904 372 : i_evtname = PQfnumber(res, "evtname");
8905 372 : i_evtevent = PQfnumber(res, "evtevent");
8906 372 : i_evtowner = PQfnumber(res, "evtowner");
8907 372 : i_evttags = PQfnumber(res, "evttags");
8908 372 : i_evtfname = PQfnumber(res, "evtfname");
8909 372 : i_evtenabled = PQfnumber(res, "evtenabled");
8910 :
8911 476 : for (i = 0; i < ntups; i++)
8912 : {
8913 104 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8914 104 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8915 104 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8916 104 : AssignDumpId(&evtinfo[i].dobj);
8917 104 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8918 104 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8919 104 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8920 104 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8921 104 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8922 104 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8923 104 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8924 :
8925 : /* Decide whether we want to dump it */
8926 104 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8927 : }
8928 :
8929 372 : PQclear(res);
8930 :
8931 372 : destroyPQExpBuffer(query);
8932 : }
8933 :
8934 : /*
8935 : * getProcLangs
8936 : * get basic information about every procedural language in the system
8937 : *
8938 : * NB: this must run after getFuncs() because we assume we can do
8939 : * findFuncByOid().
8940 : */
8941 : void
8942 372 : getProcLangs(Archive *fout)
8943 : {
8944 : PGresult *res;
8945 : int ntups;
8946 : int i;
8947 372 : PQExpBuffer query = createPQExpBuffer();
8948 : ProcLangInfo *planginfo;
8949 : int i_tableoid;
8950 : int i_oid;
8951 : int i_lanname;
8952 : int i_lanpltrusted;
8953 : int i_lanplcallfoid;
8954 : int i_laninline;
8955 : int i_lanvalidator;
8956 : int i_lanacl;
8957 : int i_acldefault;
8958 : int i_lanowner;
8959 :
8960 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8961 : "lanname, lanpltrusted, lanplcallfoid, "
8962 : "laninline, lanvalidator, "
8963 : "lanacl, "
8964 : "acldefault('l', lanowner) AS acldefault, "
8965 : "lanowner "
8966 : "FROM pg_language "
8967 : "WHERE lanispl "
8968 : "ORDER BY oid");
8969 :
8970 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8971 :
8972 372 : ntups = PQntuples(res);
8973 :
8974 372 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8975 :
8976 372 : i_tableoid = PQfnumber(res, "tableoid");
8977 372 : i_oid = PQfnumber(res, "oid");
8978 372 : i_lanname = PQfnumber(res, "lanname");
8979 372 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8980 372 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8981 372 : i_laninline = PQfnumber(res, "laninline");
8982 372 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8983 372 : i_lanacl = PQfnumber(res, "lanacl");
8984 372 : i_acldefault = PQfnumber(res, "acldefault");
8985 372 : i_lanowner = PQfnumber(res, "lanowner");
8986 :
8987 834 : for (i = 0; i < ntups; i++)
8988 : {
8989 462 : planginfo[i].dobj.objType = DO_PROCLANG;
8990 462 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8991 462 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8992 462 : AssignDumpId(&planginfo[i].dobj);
8993 :
8994 462 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
8995 462 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
8996 462 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
8997 462 : planginfo[i].dacl.privtype = 0;
8998 462 : planginfo[i].dacl.initprivs = NULL;
8999 462 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9000 462 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9001 462 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9002 462 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9003 462 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9004 :
9005 : /* Decide whether we want to dump it */
9006 462 : selectDumpableProcLang(&(planginfo[i]), fout);
9007 :
9008 : /* Mark whether language has an ACL */
9009 462 : if (!PQgetisnull(res, i, i_lanacl))
9010 90 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9011 : }
9012 :
9013 372 : PQclear(res);
9014 :
9015 372 : destroyPQExpBuffer(query);
9016 372 : }
9017 :
9018 : /*
9019 : * getCasts
9020 : * get basic information about most casts in the system
9021 : *
9022 : * Skip casts from a range to its multirange, since we'll create those
9023 : * automatically.
9024 : */
9025 : void
9026 372 : getCasts(Archive *fout)
9027 : {
9028 : PGresult *res;
9029 : int ntups;
9030 : int i;
9031 372 : PQExpBuffer query = createPQExpBuffer();
9032 : CastInfo *castinfo;
9033 : int i_tableoid;
9034 : int i_oid;
9035 : int i_castsource;
9036 : int i_casttarget;
9037 : int i_castfunc;
9038 : int i_castcontext;
9039 : int i_castmethod;
9040 :
9041 372 : if (fout->remoteVersion >= 140000)
9042 : {
9043 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9044 : "castsource, casttarget, castfunc, castcontext, "
9045 : "castmethod "
9046 : "FROM pg_cast c "
9047 : "WHERE NOT EXISTS ( "
9048 : "SELECT 1 FROM pg_range r "
9049 : "WHERE c.castsource = r.rngtypid "
9050 : "AND c.casttarget = r.rngmultitypid "
9051 : ") "
9052 : "ORDER BY 3,4");
9053 : }
9054 : else
9055 : {
9056 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9057 : "castsource, casttarget, castfunc, castcontext, "
9058 : "castmethod "
9059 : "FROM pg_cast ORDER BY 3,4");
9060 : }
9061 :
9062 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9063 :
9064 372 : ntups = PQntuples(res);
9065 :
9066 372 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9067 :
9068 372 : i_tableoid = PQfnumber(res, "tableoid");
9069 372 : i_oid = PQfnumber(res, "oid");
9070 372 : i_castsource = PQfnumber(res, "castsource");
9071 372 : i_casttarget = PQfnumber(res, "casttarget");
9072 372 : i_castfunc = PQfnumber(res, "castfunc");
9073 372 : i_castcontext = PQfnumber(res, "castcontext");
9074 372 : i_castmethod = PQfnumber(res, "castmethod");
9075 :
9076 88338 : for (i = 0; i < ntups; i++)
9077 : {
9078 : PQExpBufferData namebuf;
9079 : TypeInfo *sTypeInfo;
9080 : TypeInfo *tTypeInfo;
9081 :
9082 87966 : castinfo[i].dobj.objType = DO_CAST;
9083 87966 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9084 87966 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9085 87966 : AssignDumpId(&castinfo[i].dobj);
9086 87966 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9087 87966 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9088 87966 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9089 87966 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9090 87966 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9091 :
9092 : /*
9093 : * Try to name cast as concatenation of typnames. This is only used
9094 : * for purposes of sorting. If we fail to find either type, the name
9095 : * will be an empty string.
9096 : */
9097 87966 : initPQExpBuffer(&namebuf);
9098 87966 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9099 87966 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9100 87966 : if (sTypeInfo && tTypeInfo)
9101 87966 : appendPQExpBuffer(&namebuf, "%s %s",
9102 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9103 87966 : castinfo[i].dobj.name = namebuf.data;
9104 :
9105 : /* Decide whether we want to dump it */
9106 87966 : selectDumpableCast(&(castinfo[i]), fout);
9107 : }
9108 :
9109 372 : PQclear(res);
9110 :
9111 372 : destroyPQExpBuffer(query);
9112 372 : }
9113 :
9114 : static char *
9115 176 : get_language_name(Archive *fout, Oid langid)
9116 : {
9117 : PQExpBuffer query;
9118 : PGresult *res;
9119 : char *lanname;
9120 :
9121 176 : query = createPQExpBuffer();
9122 176 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9123 176 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9124 176 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9125 176 : destroyPQExpBuffer(query);
9126 176 : PQclear(res);
9127 :
9128 176 : return lanname;
9129 : }
9130 :
9131 : /*
9132 : * getTransforms
9133 : * get basic information about every transform in the system
9134 : */
9135 : void
9136 372 : getTransforms(Archive *fout)
9137 : {
9138 : PGresult *res;
9139 : int ntups;
9140 : int i;
9141 : PQExpBuffer query;
9142 : TransformInfo *transforminfo;
9143 : int i_tableoid;
9144 : int i_oid;
9145 : int i_trftype;
9146 : int i_trflang;
9147 : int i_trffromsql;
9148 : int i_trftosql;
9149 :
9150 : /* Transforms didn't exist pre-9.5 */
9151 372 : if (fout->remoteVersion < 90500)
9152 0 : return;
9153 :
9154 372 : query = createPQExpBuffer();
9155 :
9156 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9157 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9158 : "FROM pg_transform "
9159 : "ORDER BY 3,4");
9160 :
9161 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9162 :
9163 372 : ntups = PQntuples(res);
9164 :
9165 372 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9166 :
9167 372 : i_tableoid = PQfnumber(res, "tableoid");
9168 372 : i_oid = PQfnumber(res, "oid");
9169 372 : i_trftype = PQfnumber(res, "trftype");
9170 372 : i_trflang = PQfnumber(res, "trflang");
9171 372 : i_trffromsql = PQfnumber(res, "trffromsql");
9172 372 : i_trftosql = PQfnumber(res, "trftosql");
9173 :
9174 476 : for (i = 0; i < ntups; i++)
9175 : {
9176 : PQExpBufferData namebuf;
9177 : TypeInfo *typeInfo;
9178 : char *lanname;
9179 :
9180 104 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9181 104 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9182 104 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9183 104 : AssignDumpId(&transforminfo[i].dobj);
9184 104 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9185 104 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9186 104 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9187 104 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9188 :
9189 : /*
9190 : * Try to name transform as concatenation of type and language name.
9191 : * This is only used for purposes of sorting. If we fail to find
9192 : * either, the name will be an empty string.
9193 : */
9194 104 : initPQExpBuffer(&namebuf);
9195 104 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9196 104 : lanname = get_language_name(fout, transforminfo[i].trflang);
9197 104 : if (typeInfo && lanname)
9198 104 : appendPQExpBuffer(&namebuf, "%s %s",
9199 : typeInfo->dobj.name, lanname);
9200 104 : transforminfo[i].dobj.name = namebuf.data;
9201 104 : free(lanname);
9202 :
9203 : /* Decide whether we want to dump it */
9204 104 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9205 : }
9206 :
9207 372 : PQclear(res);
9208 :
9209 372 : destroyPQExpBuffer(query);
9210 : }
9211 :
9212 : /*
9213 : * getTableAttrs -
9214 : * for each interesting table, read info about its attributes
9215 : * (names, types, default values, CHECK constraints, etc)
9216 : *
9217 : * modifies tblinfo
9218 : */
9219 : void
9220 372 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9221 : {
9222 372 : DumpOptions *dopt = fout->dopt;
9223 372 : PQExpBuffer q = createPQExpBuffer();
9224 372 : PQExpBuffer tbloids = createPQExpBuffer();
9225 372 : PQExpBuffer checkoids = createPQExpBuffer();
9226 372 : PQExpBuffer invalidnotnulloids = NULL;
9227 : PGresult *res;
9228 : int ntups;
9229 : int curtblindx;
9230 : int i_attrelid;
9231 : int i_attnum;
9232 : int i_attname;
9233 : int i_atttypname;
9234 : int i_attstattarget;
9235 : int i_attstorage;
9236 : int i_typstorage;
9237 : int i_attidentity;
9238 : int i_attgenerated;
9239 : int i_attisdropped;
9240 : int i_attlen;
9241 : int i_attalign;
9242 : int i_attislocal;
9243 : int i_notnull_name;
9244 : int i_notnull_comment;
9245 : int i_notnull_noinherit;
9246 : int i_notnull_islocal;
9247 : int i_notnull_invalidoid;
9248 : int i_attoptions;
9249 : int i_attcollation;
9250 : int i_attcompression;
9251 : int i_attfdwoptions;
9252 : int i_attmissingval;
9253 : int i_atthasdef;
9254 :
9255 : /*
9256 : * We want to perform just one query against pg_attribute, and then just
9257 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9258 : * (for CHECK constraints and for NOT NULL constraints). However, we
9259 : * mustn't try to select every row of those catalogs and then sort it out
9260 : * on the client side, because some of the server-side functions we need
9261 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9262 : * build an array of the OIDs of tables we care about (and now have lock
9263 : * on!), and use a WHERE clause to constrain which rows are selected.
9264 : */
9265 372 : appendPQExpBufferChar(tbloids, '{');
9266 372 : appendPQExpBufferChar(checkoids, '{');
9267 98348 : for (int i = 0; i < numTables; i++)
9268 : {
9269 97976 : TableInfo *tbinfo = &tblinfo[i];
9270 :
9271 : /* Don't bother to collect info for sequences */
9272 97976 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9273 1276 : continue;
9274 :
9275 : /*
9276 : * Don't bother with uninteresting tables, either. For binary
9277 : * upgrades, this is bypassed for pg_largeobject_metadata and
9278 : * pg_shdepend so that the columns names are collected for the
9279 : * corresponding COPY commands. Restoring the data for those catalogs
9280 : * is faster than restoring the equivalent set of large object
9281 : * commands. We can only do this for upgrades from v12 and newer; in
9282 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9283 : * the OID column is hidden and won't be dumped.
9284 : */
9285 96700 : if (!tbinfo->interesting &&
9286 83584 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9287 15516 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9288 15444 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9289 83440 : continue;
9290 :
9291 : /* OK, we need info for this table */
9292 13260 : if (tbloids->len > 1) /* do we have more than the '{'? */
9293 12976 : appendPQExpBufferChar(tbloids, ',');
9294 13260 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9295 :
9296 13260 : if (tbinfo->ncheck > 0)
9297 : {
9298 : /* Also make a list of the ones with check constraints */
9299 1056 : if (checkoids->len > 1) /* do we have more than the '{'? */
9300 918 : appendPQExpBufferChar(checkoids, ',');
9301 1056 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9302 : }
9303 : }
9304 372 : appendPQExpBufferChar(tbloids, '}');
9305 372 : appendPQExpBufferChar(checkoids, '}');
9306 :
9307 : /*
9308 : * Find all the user attributes and their types.
9309 : *
9310 : * Since we only want to dump COLLATE clauses for attributes whose
9311 : * collation is different from their type's default, we use a CASE here to
9312 : * suppress uninteresting attcollations cheaply.
9313 : */
9314 372 : appendPQExpBufferStr(q,
9315 : "SELECT\n"
9316 : "a.attrelid,\n"
9317 : "a.attnum,\n"
9318 : "a.attname,\n"
9319 : "a.attstattarget,\n"
9320 : "a.attstorage,\n"
9321 : "t.typstorage,\n"
9322 : "a.atthasdef,\n"
9323 : "a.attisdropped,\n"
9324 : "a.attlen,\n"
9325 : "a.attalign,\n"
9326 : "a.attislocal,\n"
9327 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9328 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9329 : "CASE WHEN a.attcollation <> t.typcollation "
9330 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9331 : "pg_catalog.array_to_string(ARRAY("
9332 : "SELECT pg_catalog.quote_ident(option_name) || "
9333 : "' ' || pg_catalog.quote_literal(option_value) "
9334 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9335 : "ORDER BY option_name"
9336 : "), E',\n ') AS attfdwoptions,\n");
9337 :
9338 : /*
9339 : * Find out any NOT NULL markings for each column. In 18 and up we read
9340 : * pg_constraint to obtain the constraint name, and for valid constraints
9341 : * also pg_description to obtain its comment. notnull_noinherit is set
9342 : * according to the NO INHERIT property. For versions prior to 18, we
9343 : * store an empty string as the name when a constraint is marked as
9344 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9345 : * without a name); also, such cases are never NO INHERIT.
9346 : *
9347 : * For invalid constraints, we need to store their OIDs for processing
9348 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9349 : * is invalid, and NULL otherwise. Their comments are handled not here
9350 : * but by collectComments, because they're their own dumpable object.
9351 : *
9352 : * We track in notnull_islocal whether the constraint was defined directly
9353 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9354 : * might modify this later.
9355 : */
9356 372 : if (fout->remoteVersion >= 180000)
9357 372 : appendPQExpBufferStr(q,
9358 : "co.conname AS notnull_name,\n"
9359 : "CASE WHEN co.convalidated THEN pt.description"
9360 : " ELSE NULL END AS notnull_comment,\n"
9361 : "CASE WHEN NOT co.convalidated THEN co.oid "
9362 : "ELSE NULL END AS notnull_invalidoid,\n"
9363 : "co.connoinherit AS notnull_noinherit,\n"
9364 : "co.conislocal AS notnull_islocal,\n");
9365 : else
9366 0 : appendPQExpBufferStr(q,
9367 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9368 : "NULL AS notnull_comment,\n"
9369 : "NULL AS notnull_invalidoid,\n"
9370 : "false AS notnull_noinherit,\n"
9371 : "CASE WHEN a.attislocal THEN true\n"
9372 : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9373 : " ELSE false\n"
9374 : "END AS notnull_islocal,\n");
9375 :
9376 372 : if (fout->remoteVersion >= 140000)
9377 372 : appendPQExpBufferStr(q,
9378 : "a.attcompression AS attcompression,\n");
9379 : else
9380 0 : appendPQExpBufferStr(q,
9381 : "'' AS attcompression,\n");
9382 :
9383 372 : if (fout->remoteVersion >= 100000)
9384 372 : appendPQExpBufferStr(q,
9385 : "a.attidentity,\n");
9386 : else
9387 0 : appendPQExpBufferStr(q,
9388 : "'' AS attidentity,\n");
9389 :
9390 372 : if (fout->remoteVersion >= 110000)
9391 372 : appendPQExpBufferStr(q,
9392 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9393 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9394 : else
9395 0 : appendPQExpBufferStr(q,
9396 : "NULL AS attmissingval,\n");
9397 :
9398 372 : if (fout->remoteVersion >= 120000)
9399 372 : appendPQExpBufferStr(q,
9400 : "a.attgenerated\n");
9401 : else
9402 0 : appendPQExpBufferStr(q,
9403 : "'' AS attgenerated\n");
9404 :
9405 : /* need left join to pg_type to not fail on dropped columns ... */
9406 372 : appendPQExpBuffer(q,
9407 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9408 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9409 : "LEFT JOIN pg_catalog.pg_type t "
9410 : "ON (a.atttypid = t.oid)\n",
9411 : tbloids->data);
9412 :
9413 : /*
9414 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9415 : * entries and pg_description to get their comments.
9416 : */
9417 372 : if (fout->remoteVersion >= 180000)
9418 372 : appendPQExpBufferStr(q,
9419 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9420 : "(a.attrelid = co.conrelid\n"
9421 : " AND co.contype = 'n' AND "
9422 : "co.conkey = array[a.attnum])\n"
9423 : " LEFT JOIN pg_catalog.pg_description pt ON "
9424 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9425 :
9426 372 : appendPQExpBufferStr(q,
9427 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9428 : "ORDER BY a.attrelid, a.attnum");
9429 :
9430 372 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9431 :
9432 372 : ntups = PQntuples(res);
9433 :
9434 372 : i_attrelid = PQfnumber(res, "attrelid");
9435 372 : i_attnum = PQfnumber(res, "attnum");
9436 372 : i_attname = PQfnumber(res, "attname");
9437 372 : i_atttypname = PQfnumber(res, "atttypname");
9438 372 : i_attstattarget = PQfnumber(res, "attstattarget");
9439 372 : i_attstorage = PQfnumber(res, "attstorage");
9440 372 : i_typstorage = PQfnumber(res, "typstorage");
9441 372 : i_attidentity = PQfnumber(res, "attidentity");
9442 372 : i_attgenerated = PQfnumber(res, "attgenerated");
9443 372 : i_attisdropped = PQfnumber(res, "attisdropped");
9444 372 : i_attlen = PQfnumber(res, "attlen");
9445 372 : i_attalign = PQfnumber(res, "attalign");
9446 372 : i_attislocal = PQfnumber(res, "attislocal");
9447 372 : i_notnull_name = PQfnumber(res, "notnull_name");
9448 372 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9449 372 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9450 372 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9451 372 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9452 372 : i_attoptions = PQfnumber(res, "attoptions");
9453 372 : i_attcollation = PQfnumber(res, "attcollation");
9454 372 : i_attcompression = PQfnumber(res, "attcompression");
9455 372 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9456 372 : i_attmissingval = PQfnumber(res, "attmissingval");
9457 372 : i_atthasdef = PQfnumber(res, "atthasdef");
9458 :
9459 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9460 372 : resetPQExpBuffer(tbloids);
9461 372 : appendPQExpBufferChar(tbloids, '{');
9462 :
9463 : /*
9464 : * Outer loop iterates once per table, not once per row. Incrementing of
9465 : * r is handled by the inner loop.
9466 : */
9467 372 : curtblindx = -1;
9468 13356 : for (int r = 0; r < ntups;)
9469 : {
9470 12984 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9471 12984 : TableInfo *tbinfo = NULL;
9472 : int numatts;
9473 : bool hasdefaults;
9474 :
9475 : /* Count rows for this table */
9476 49238 : for (numatts = 1; numatts < ntups - r; numatts++)
9477 48960 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9478 12706 : break;
9479 :
9480 : /*
9481 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9482 : * order.
9483 : */
9484 67718 : while (++curtblindx < numTables)
9485 : {
9486 67718 : tbinfo = &tblinfo[curtblindx];
9487 67718 : if (tbinfo->dobj.catId.oid == attrelid)
9488 12984 : break;
9489 : }
9490 12984 : if (curtblindx >= numTables)
9491 0 : pg_fatal("unrecognized table OID %u", attrelid);
9492 : /* cross-check that we only got requested tables */
9493 12984 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9494 12984 : (!tbinfo->interesting &&
9495 144 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9496 144 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9497 72 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9498 0 : pg_fatal("unexpected column data for table \"%s\"",
9499 : tbinfo->dobj.name);
9500 :
9501 : /* Save data for this table */
9502 12984 : tbinfo->numatts = numatts;
9503 12984 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9504 12984 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9505 12984 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9506 12984 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9507 12984 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9508 12984 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9509 12984 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9510 12984 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9511 12984 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9512 12984 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9513 12984 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9514 12984 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9515 12984 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9516 12984 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9517 12984 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9518 12984 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9519 12984 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9520 12984 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9521 12984 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9522 12984 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9523 12984 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9524 12984 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9525 12984 : hasdefaults = false;
9526 :
9527 62222 : for (int j = 0; j < numatts; j++, r++)
9528 : {
9529 49238 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9530 0 : pg_fatal("invalid column numbering in table \"%s\"",
9531 : tbinfo->dobj.name);
9532 49238 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9533 49238 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9534 49238 : if (PQgetisnull(res, r, i_attstattarget))
9535 49158 : tbinfo->attstattarget[j] = -1;
9536 : else
9537 80 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9538 49238 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9539 49238 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9540 49238 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9541 49238 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9542 49238 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9543 49238 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9544 49238 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9545 49238 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9546 49238 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9547 :
9548 : /* Handle not-null constraint name and flags */
9549 49238 : determineNotNullFlags(fout, res, r,
9550 : tbinfo, j,
9551 : i_notnull_name,
9552 : i_notnull_comment,
9553 : i_notnull_invalidoid,
9554 : i_notnull_noinherit,
9555 : i_notnull_islocal,
9556 : &invalidnotnulloids);
9557 :
9558 49238 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9559 49238 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9560 49238 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9561 49238 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9562 49238 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9563 49238 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9564 49238 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9565 49238 : tbinfo->attrdefs[j] = NULL; /* fix below */
9566 49238 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9567 2536 : hasdefaults = true;
9568 : }
9569 :
9570 12984 : if (hasdefaults)
9571 : {
9572 : /* Collect OIDs of interesting tables that have defaults */
9573 1904 : if (tbloids->len > 1) /* do we have more than the '{'? */
9574 1768 : appendPQExpBufferChar(tbloids, ',');
9575 1904 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9576 : }
9577 : }
9578 :
9579 : /* If invalidnotnulloids has any data, finalize it */
9580 372 : if (invalidnotnulloids != NULL)
9581 86 : appendPQExpBufferChar(invalidnotnulloids, '}');
9582 :
9583 372 : PQclear(res);
9584 :
9585 : /*
9586 : * Now get info about column defaults. This is skipped for a data-only
9587 : * dump, as it is only needed for table schemas.
9588 : */
9589 372 : if (dopt->dumpSchema && tbloids->len > 1)
9590 : {
9591 : AttrDefInfo *attrdefs;
9592 : int numDefaults;
9593 120 : TableInfo *tbinfo = NULL;
9594 :
9595 120 : pg_log_info("finding table default expressions");
9596 :
9597 120 : appendPQExpBufferChar(tbloids, '}');
9598 :
9599 120 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9600 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9601 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9602 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9603 : "ORDER BY a.adrelid, a.adnum",
9604 : tbloids->data);
9605 :
9606 120 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9607 :
9608 120 : numDefaults = PQntuples(res);
9609 120 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9610 :
9611 120 : curtblindx = -1;
9612 2460 : for (int j = 0; j < numDefaults; j++)
9613 : {
9614 2340 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9615 2340 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9616 2340 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9617 2340 : int adnum = atoi(PQgetvalue(res, j, 3));
9618 2340 : char *adsrc = PQgetvalue(res, j, 4);
9619 :
9620 : /*
9621 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9622 : * OID order.
9623 : */
9624 2340 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9625 : {
9626 37426 : while (++curtblindx < numTables)
9627 : {
9628 37426 : tbinfo = &tblinfo[curtblindx];
9629 37426 : if (tbinfo->dobj.catId.oid == adrelid)
9630 1768 : break;
9631 : }
9632 1768 : if (curtblindx >= numTables)
9633 0 : pg_fatal("unrecognized table OID %u", adrelid);
9634 : }
9635 :
9636 2340 : if (adnum <= 0 || adnum > tbinfo->numatts)
9637 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9638 : adnum, tbinfo->dobj.name);
9639 :
9640 : /*
9641 : * dropped columns shouldn't have defaults, but just in case,
9642 : * ignore 'em
9643 : */
9644 2340 : if (tbinfo->attisdropped[adnum - 1])
9645 0 : continue;
9646 :
9647 2340 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9648 2340 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9649 2340 : attrdefs[j].dobj.catId.oid = adoid;
9650 2340 : AssignDumpId(&attrdefs[j].dobj);
9651 2340 : attrdefs[j].adtable = tbinfo;
9652 2340 : attrdefs[j].adnum = adnum;
9653 2340 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9654 :
9655 2340 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9656 2340 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9657 :
9658 2340 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9659 :
9660 : /*
9661 : * Figure out whether the default/generation expression should be
9662 : * dumped as part of the main CREATE TABLE (or similar) command or
9663 : * as a separate ALTER TABLE (or similar) command. The preference
9664 : * is to put it into the CREATE command, but in some cases that's
9665 : * not possible.
9666 : */
9667 2340 : if (tbinfo->attgenerated[adnum - 1])
9668 : {
9669 : /*
9670 : * Column generation expressions cannot be dumped separately,
9671 : * because there is no syntax for it. By setting separate to
9672 : * false here we prevent the "default" from being processed as
9673 : * its own dumpable object. Later, flagInhAttrs() will mark
9674 : * it as not to be dumped at all, if possible (that is, if it
9675 : * can be inherited from a parent).
9676 : */
9677 1312 : attrdefs[j].separate = false;
9678 : }
9679 1028 : else if (tbinfo->relkind == RELKIND_VIEW)
9680 : {
9681 : /*
9682 : * Defaults on a VIEW must always be dumped as separate ALTER
9683 : * TABLE commands.
9684 : */
9685 64 : attrdefs[j].separate = true;
9686 : }
9687 964 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9688 : {
9689 : /* column will be suppressed, print default separately */
9690 8 : attrdefs[j].separate = true;
9691 : }
9692 : else
9693 : {
9694 956 : attrdefs[j].separate = false;
9695 : }
9696 :
9697 2340 : if (!attrdefs[j].separate)
9698 : {
9699 : /*
9700 : * Mark the default as needing to appear before the table, so
9701 : * that any dependencies it has must be emitted before the
9702 : * CREATE TABLE. If this is not possible, we'll change to
9703 : * "separate" mode while sorting dependencies.
9704 : */
9705 2268 : addObjectDependency(&tbinfo->dobj,
9706 2268 : attrdefs[j].dobj.dumpId);
9707 : }
9708 :
9709 2340 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9710 : }
9711 :
9712 120 : PQclear(res);
9713 : }
9714 :
9715 : /*
9716 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9717 : * data-only dump, as it is only needed for table schemas.
9718 : */
9719 372 : if (dopt->dumpSchema && invalidnotnulloids)
9720 : {
9721 : ConstraintInfo *constrs;
9722 : int numConstrs;
9723 : int i_tableoid;
9724 : int i_oid;
9725 : int i_conrelid;
9726 : int i_conname;
9727 : int i_consrc;
9728 : int i_conislocal;
9729 :
9730 74 : pg_log_info("finding invalid not-null constraints");
9731 :
9732 74 : resetPQExpBuffer(q);
9733 74 : appendPQExpBuffer(q,
9734 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9735 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9736 : "conislocal, convalidated "
9737 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9738 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9739 : "ORDER BY c.conrelid, c.conname",
9740 74 : invalidnotnulloids->data);
9741 :
9742 74 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9743 :
9744 74 : numConstrs = PQntuples(res);
9745 74 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9746 :
9747 74 : i_tableoid = PQfnumber(res, "tableoid");
9748 74 : i_oid = PQfnumber(res, "oid");
9749 74 : i_conrelid = PQfnumber(res, "conrelid");
9750 74 : i_conname = PQfnumber(res, "conname");
9751 74 : i_consrc = PQfnumber(res, "consrc");
9752 74 : i_conislocal = PQfnumber(res, "conislocal");
9753 :
9754 : /* As above, this loop iterates once per table, not once per row */
9755 74 : curtblindx = -1;
9756 208 : for (int j = 0; j < numConstrs;)
9757 : {
9758 134 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9759 134 : TableInfo *tbinfo = NULL;
9760 : int numcons;
9761 :
9762 : /* Count rows for this table */
9763 134 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9764 60 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9765 60 : break;
9766 :
9767 : /*
9768 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9769 : * OID order.
9770 : */
9771 25062 : while (++curtblindx < numTables)
9772 : {
9773 25062 : tbinfo = &tblinfo[curtblindx];
9774 25062 : if (tbinfo->dobj.catId.oid == conrelid)
9775 134 : break;
9776 : }
9777 134 : if (curtblindx >= numTables)
9778 0 : pg_fatal("unrecognized table OID %u", conrelid);
9779 :
9780 268 : for (int c = 0; c < numcons; c++, j++)
9781 : {
9782 134 : constrs[j].dobj.objType = DO_CONSTRAINT;
9783 134 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9784 134 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9785 134 : AssignDumpId(&constrs[j].dobj);
9786 134 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9787 134 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9788 134 : constrs[j].contable = tbinfo;
9789 134 : constrs[j].condomain = NULL;
9790 134 : constrs[j].contype = 'n';
9791 134 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9792 134 : constrs[j].confrelid = InvalidOid;
9793 134 : constrs[j].conindex = 0;
9794 134 : constrs[j].condeferrable = false;
9795 134 : constrs[j].condeferred = false;
9796 134 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9797 :
9798 : /*
9799 : * All invalid not-null constraints must be dumped separately,
9800 : * because CREATE TABLE would not create them as invalid, and
9801 : * also because they must be created after potentially
9802 : * violating data has been loaded.
9803 : */
9804 134 : constrs[j].separate = true;
9805 :
9806 134 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9807 : }
9808 : }
9809 74 : PQclear(res);
9810 : }
9811 :
9812 : /*
9813 : * Get info about table CHECK constraints. This is skipped for a
9814 : * data-only dump, as it is only needed for table schemas.
9815 : */
9816 372 : if (dopt->dumpSchema && checkoids->len > 2)
9817 : {
9818 : ConstraintInfo *constrs;
9819 : int numConstrs;
9820 : int i_tableoid;
9821 : int i_oid;
9822 : int i_conrelid;
9823 : int i_conname;
9824 : int i_consrc;
9825 : int i_conislocal;
9826 : int i_convalidated;
9827 :
9828 122 : pg_log_info("finding table check constraints");
9829 :
9830 122 : resetPQExpBuffer(q);
9831 122 : appendPQExpBuffer(q,
9832 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9833 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9834 : "conislocal, convalidated "
9835 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9836 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9837 : "WHERE contype = 'c' "
9838 : "ORDER BY c.conrelid, c.conname",
9839 : checkoids->data);
9840 :
9841 122 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9842 :
9843 122 : numConstrs = PQntuples(res);
9844 122 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9845 :
9846 122 : i_tableoid = PQfnumber(res, "tableoid");
9847 122 : i_oid = PQfnumber(res, "oid");
9848 122 : i_conrelid = PQfnumber(res, "conrelid");
9849 122 : i_conname = PQfnumber(res, "conname");
9850 122 : i_consrc = PQfnumber(res, "consrc");
9851 122 : i_conislocal = PQfnumber(res, "conislocal");
9852 122 : i_convalidated = PQfnumber(res, "convalidated");
9853 :
9854 : /* As above, this loop iterates once per table, not once per row */
9855 122 : curtblindx = -1;
9856 1076 : for (int j = 0; j < numConstrs;)
9857 : {
9858 954 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9859 954 : TableInfo *tbinfo = NULL;
9860 : int numcons;
9861 :
9862 : /* Count rows for this table */
9863 1224 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9864 1102 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9865 832 : break;
9866 :
9867 : /*
9868 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9869 : * OID order.
9870 : */
9871 36074 : while (++curtblindx < numTables)
9872 : {
9873 36074 : tbinfo = &tblinfo[curtblindx];
9874 36074 : if (tbinfo->dobj.catId.oid == conrelid)
9875 954 : break;
9876 : }
9877 954 : if (curtblindx >= numTables)
9878 0 : pg_fatal("unrecognized table OID %u", conrelid);
9879 :
9880 954 : if (numcons != tbinfo->ncheck)
9881 : {
9882 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9883 : "expected %d check constraints on table \"%s\" but found %d",
9884 : tbinfo->ncheck),
9885 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9886 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9887 0 : exit_nicely(1);
9888 : }
9889 :
9890 954 : tbinfo->checkexprs = constrs + j;
9891 :
9892 2178 : for (int c = 0; c < numcons; c++, j++)
9893 : {
9894 1224 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9895 :
9896 1224 : constrs[j].dobj.objType = DO_CONSTRAINT;
9897 1224 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9898 1224 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9899 1224 : AssignDumpId(&constrs[j].dobj);
9900 1224 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9901 1224 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9902 1224 : constrs[j].contable = tbinfo;
9903 1224 : constrs[j].condomain = NULL;
9904 1224 : constrs[j].contype = 'c';
9905 1224 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9906 1224 : constrs[j].confrelid = InvalidOid;
9907 1224 : constrs[j].conindex = 0;
9908 1224 : constrs[j].condeferrable = false;
9909 1224 : constrs[j].condeferred = false;
9910 1224 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9911 :
9912 : /*
9913 : * An unvalidated constraint needs to be dumped separately, so
9914 : * that potentially-violating existing data is loaded before
9915 : * the constraint.
9916 : */
9917 1224 : constrs[j].separate = !validated;
9918 :
9919 1224 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9920 :
9921 : /*
9922 : * Mark the constraint as needing to appear before the table
9923 : * --- this is so that any other dependencies of the
9924 : * constraint will be emitted before we try to create the
9925 : * table. If the constraint is to be dumped separately, it
9926 : * will be dumped after data is loaded anyway, so don't do it.
9927 : * (There's an automatic dependency in the opposite direction
9928 : * anyway, so don't need to add one manually here.)
9929 : */
9930 1224 : if (!constrs[j].separate)
9931 1094 : addObjectDependency(&tbinfo->dobj,
9932 1094 : constrs[j].dobj.dumpId);
9933 :
9934 : /*
9935 : * We will detect later whether the constraint must be split
9936 : * out from the table definition.
9937 : */
9938 : }
9939 : }
9940 :
9941 122 : PQclear(res);
9942 : }
9943 :
9944 372 : destroyPQExpBuffer(q);
9945 372 : destroyPQExpBuffer(tbloids);
9946 372 : destroyPQExpBuffer(checkoids);
9947 372 : }
9948 :
9949 : /*
9950 : * Based on the getTableAttrs query's row corresponding to one column, set
9951 : * the name and flags to handle a not-null constraint for that column in
9952 : * the tbinfo struct.
9953 : *
9954 : * Result row 'r' is for tbinfo's attribute 'j'.
9955 : *
9956 : * There are four possibilities:
9957 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9958 : * (the constraint name) remains NULL.
9959 : * 2) The column has a constraint with no name (this is the case when
9960 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9961 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9962 : * 3) The column has an invalid not-null constraint. This must be treated
9963 : * as a separate object (because it must be created after the table data
9964 : * is loaded). So we add its OID to invalidnotnulloids for processing
9965 : * elsewhere and do nothing further with it here. We distinguish this
9966 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9967 : * value, which is the constraint OID. Valid constraints have a null OID.
9968 : * 4) The column has a constraint with a known name; in that case
9969 : * notnull_constrs carries that name and dumpTableSchema will print
9970 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9971 : * (table_column_not_null) and there's no comment on the constraint,
9972 : * there's no need to print that name in the dump, so notnull_constrs
9973 : * is set to the empty string and it behaves as case 2.
9974 : *
9975 : * In a child table that inherits from a parent already containing NOT NULL
9976 : * constraints and the columns in the child don't have their own NOT NULL
9977 : * declarations, we suppress printing constraints in the child: the
9978 : * constraints are acquired at the point where the child is attached to the
9979 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9980 : * set not here but in flagInhAttrs. That flag is also used when the
9981 : * constraint was validated in a child but all its parent have it as NOT
9982 : * VALID.
9983 : *
9984 : * Any of these constraints might have the NO INHERIT bit. If so we set
9985 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
9986 : *
9987 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
9988 : * to do the right thing in all but the trivial case. However, the downside
9989 : * of getting it wrong is simply that the name is printed rather than
9990 : * suppressed, so it's not a big deal.
9991 : *
9992 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
9993 : * constraints are found, it is initialized and filled with the array of
9994 : * OIDs of such constraints, for later processing.
9995 : */
9996 : static void
9997 49238 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
9998 : TableInfo *tbinfo, int j,
9999 : int i_notnull_name,
10000 : int i_notnull_comment,
10001 : int i_notnull_invalidoid,
10002 : int i_notnull_noinherit,
10003 : int i_notnull_islocal,
10004 : PQExpBuffer *invalidnotnulloids)
10005 : {
10006 49238 : DumpOptions *dopt = fout->dopt;
10007 :
10008 : /*
10009 : * If this not-null constraint is not valid, list its OID in
10010 : * invalidnotnulloids and do nothing further. It'll be processed
10011 : * elsewhere later.
10012 : *
10013 : * Because invalid not-null constraints are rare, we don't want to malloc
10014 : * invalidnotnulloids until we're sure we're going it need it, which
10015 : * happens here.
10016 : */
10017 49238 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10018 : {
10019 146 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10020 :
10021 146 : if (*invalidnotnulloids == NULL)
10022 : {
10023 86 : *invalidnotnulloids = createPQExpBuffer();
10024 86 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10025 86 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10026 : }
10027 : else
10028 60 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10029 :
10030 : /*
10031 : * Track when a parent constraint is invalid for the cases where a
10032 : * child constraint has been validated independenly.
10033 : */
10034 146 : tbinfo->notnull_invalid[j] = true;
10035 :
10036 : /* nothing else to do */
10037 146 : tbinfo->notnull_constrs[j] = NULL;
10038 146 : return;
10039 : }
10040 :
10041 : /*
10042 : * notnull_noinh is straight from the query result. notnull_islocal also,
10043 : * though flagInhAttrs may change that one later.
10044 : */
10045 49092 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10046 49092 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10047 49092 : tbinfo->notnull_invalid[j] = false;
10048 :
10049 : /*
10050 : * Determine a constraint name to use. If the column is not marked not-
10051 : * null, we set NULL which cues ... to do nothing. An empty string says
10052 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10053 : * use.
10054 : */
10055 49092 : if (fout->remoteVersion < 180000)
10056 : {
10057 : /*
10058 : * < 18 doesn't have not-null names, so an unnamed constraint is
10059 : * sufficient.
10060 : */
10061 0 : if (PQgetisnull(res, r, i_notnull_name))
10062 0 : tbinfo->notnull_constrs[j] = NULL;
10063 : else
10064 0 : tbinfo->notnull_constrs[j] = "";
10065 : }
10066 : else
10067 : {
10068 49092 : if (PQgetisnull(res, r, i_notnull_name))
10069 43878 : tbinfo->notnull_constrs[j] = NULL;
10070 : else
10071 : {
10072 : /*
10073 : * In binary upgrade of inheritance child tables, must have a
10074 : * constraint name that we can UPDATE later; same if there's a
10075 : * comment on the constraint.
10076 : */
10077 5214 : if ((dopt->binary_upgrade &&
10078 654 : !tbinfo->ispartition &&
10079 5712 : !tbinfo->notnull_islocal) ||
10080 5214 : !PQgetisnull(res, r, i_notnull_comment))
10081 : {
10082 96 : tbinfo->notnull_constrs[j] =
10083 96 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10084 : }
10085 : else
10086 : {
10087 : char *default_name;
10088 :
10089 : /* XXX should match ChooseConstraintName better */
10090 5118 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10091 5118 : tbinfo->attnames[j]);
10092 5118 : if (strcmp(default_name,
10093 5118 : PQgetvalue(res, r, i_notnull_name)) == 0)
10094 3390 : tbinfo->notnull_constrs[j] = "";
10095 : else
10096 : {
10097 1728 : tbinfo->notnull_constrs[j] =
10098 1728 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10099 : }
10100 5118 : free(default_name);
10101 : }
10102 : }
10103 : }
10104 : }
10105 :
10106 : /*
10107 : * Test whether a column should be printed as part of table's CREATE TABLE.
10108 : * Column number is zero-based.
10109 : *
10110 : * Normally this is always true, but it's false for dropped columns, as well
10111 : * as those that were inherited without any local definition. (If we print
10112 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10113 : * For partitions, it's always true, because we want the partitions to be
10114 : * created independently and ATTACH PARTITION used afterwards.
10115 : *
10116 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10117 : * attisdropped state later, so as to keep control of the physical column
10118 : * order.
10119 : *
10120 : * This function exists because there are scattered nonobvious places that
10121 : * must be kept in sync with this decision.
10122 : */
10123 : bool
10124 79972 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10125 : {
10126 79972 : if (dopt->binary_upgrade)
10127 12428 : return true;
10128 67544 : if (tbinfo->attisdropped[colno])
10129 1452 : return false;
10130 66092 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10131 : }
10132 :
10133 :
10134 : /*
10135 : * getTSParsers:
10136 : * get information about all text search parsers in the system catalogs
10137 : */
10138 : void
10139 372 : getTSParsers(Archive *fout)
10140 : {
10141 : PGresult *res;
10142 : int ntups;
10143 : int i;
10144 : PQExpBuffer query;
10145 : TSParserInfo *prsinfo;
10146 : int i_tableoid;
10147 : int i_oid;
10148 : int i_prsname;
10149 : int i_prsnamespace;
10150 : int i_prsstart;
10151 : int i_prstoken;
10152 : int i_prsend;
10153 : int i_prsheadline;
10154 : int i_prslextype;
10155 :
10156 372 : query = createPQExpBuffer();
10157 :
10158 : /*
10159 : * find all text search objects, including builtin ones; we filter out
10160 : * system-defined objects at dump-out time.
10161 : */
10162 :
10163 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10164 : "prsstart::oid, prstoken::oid, "
10165 : "prsend::oid, prsheadline::oid, prslextype::oid "
10166 : "FROM pg_ts_parser");
10167 :
10168 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10169 :
10170 372 : ntups = PQntuples(res);
10171 :
10172 372 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10173 :
10174 372 : i_tableoid = PQfnumber(res, "tableoid");
10175 372 : i_oid = PQfnumber(res, "oid");
10176 372 : i_prsname = PQfnumber(res, "prsname");
10177 372 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10178 372 : i_prsstart = PQfnumber(res, "prsstart");
10179 372 : i_prstoken = PQfnumber(res, "prstoken");
10180 372 : i_prsend = PQfnumber(res, "prsend");
10181 372 : i_prsheadline = PQfnumber(res, "prsheadline");
10182 372 : i_prslextype = PQfnumber(res, "prslextype");
10183 :
10184 834 : for (i = 0; i < ntups; i++)
10185 : {
10186 462 : prsinfo[i].dobj.objType = DO_TSPARSER;
10187 462 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10188 462 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10189 462 : AssignDumpId(&prsinfo[i].dobj);
10190 462 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10191 924 : prsinfo[i].dobj.namespace =
10192 462 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10193 462 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10194 462 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10195 462 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10196 462 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10197 462 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10198 :
10199 : /* Decide whether we want to dump it */
10200 462 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10201 : }
10202 :
10203 372 : PQclear(res);
10204 :
10205 372 : destroyPQExpBuffer(query);
10206 372 : }
10207 :
10208 : /*
10209 : * getTSDictionaries:
10210 : * get information about all text search dictionaries in the system catalogs
10211 : */
10212 : void
10213 372 : getTSDictionaries(Archive *fout)
10214 : {
10215 : PGresult *res;
10216 : int ntups;
10217 : int i;
10218 : PQExpBuffer query;
10219 : TSDictInfo *dictinfo;
10220 : int i_tableoid;
10221 : int i_oid;
10222 : int i_dictname;
10223 : int i_dictnamespace;
10224 : int i_dictowner;
10225 : int i_dicttemplate;
10226 : int i_dictinitoption;
10227 :
10228 372 : query = createPQExpBuffer();
10229 :
10230 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10231 : "dictnamespace, dictowner, "
10232 : "dicttemplate, dictinitoption "
10233 : "FROM pg_ts_dict");
10234 :
10235 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10236 :
10237 372 : ntups = PQntuples(res);
10238 :
10239 372 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10240 :
10241 372 : i_tableoid = PQfnumber(res, "tableoid");
10242 372 : i_oid = PQfnumber(res, "oid");
10243 372 : i_dictname = PQfnumber(res, "dictname");
10244 372 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10245 372 : i_dictowner = PQfnumber(res, "dictowner");
10246 372 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10247 372 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10248 :
10249 11748 : for (i = 0; i < ntups; i++)
10250 : {
10251 11376 : dictinfo[i].dobj.objType = DO_TSDICT;
10252 11376 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10253 11376 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10254 11376 : AssignDumpId(&dictinfo[i].dobj);
10255 11376 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10256 22752 : dictinfo[i].dobj.namespace =
10257 11376 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10258 11376 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10259 11376 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10260 11376 : if (PQgetisnull(res, i, i_dictinitoption))
10261 462 : dictinfo[i].dictinitoption = NULL;
10262 : else
10263 10914 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10264 :
10265 : /* Decide whether we want to dump it */
10266 11376 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10267 : }
10268 :
10269 372 : PQclear(res);
10270 :
10271 372 : destroyPQExpBuffer(query);
10272 372 : }
10273 :
10274 : /*
10275 : * getTSTemplates:
10276 : * get information about all text search templates in the system catalogs
10277 : */
10278 : void
10279 372 : getTSTemplates(Archive *fout)
10280 : {
10281 : PGresult *res;
10282 : int ntups;
10283 : int i;
10284 : PQExpBuffer query;
10285 : TSTemplateInfo *tmplinfo;
10286 : int i_tableoid;
10287 : int i_oid;
10288 : int i_tmplname;
10289 : int i_tmplnamespace;
10290 : int i_tmplinit;
10291 : int i_tmpllexize;
10292 :
10293 372 : query = createPQExpBuffer();
10294 :
10295 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10296 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10297 : "FROM pg_ts_template");
10298 :
10299 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10300 :
10301 372 : ntups = PQntuples(res);
10302 :
10303 372 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10304 :
10305 372 : i_tableoid = PQfnumber(res, "tableoid");
10306 372 : i_oid = PQfnumber(res, "oid");
10307 372 : i_tmplname = PQfnumber(res, "tmplname");
10308 372 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10309 372 : i_tmplinit = PQfnumber(res, "tmplinit");
10310 372 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10311 :
10312 2322 : for (i = 0; i < ntups; i++)
10313 : {
10314 1950 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10315 1950 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10316 1950 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10317 1950 : AssignDumpId(&tmplinfo[i].dobj);
10318 1950 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10319 3900 : tmplinfo[i].dobj.namespace =
10320 1950 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10321 1950 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10322 1950 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10323 :
10324 : /* Decide whether we want to dump it */
10325 1950 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10326 : }
10327 :
10328 372 : PQclear(res);
10329 :
10330 372 : destroyPQExpBuffer(query);
10331 372 : }
10332 :
10333 : /*
10334 : * getTSConfigurations:
10335 : * get information about all text search configurations
10336 : */
10337 : void
10338 372 : getTSConfigurations(Archive *fout)
10339 : {
10340 : PGresult *res;
10341 : int ntups;
10342 : int i;
10343 : PQExpBuffer query;
10344 : TSConfigInfo *cfginfo;
10345 : int i_tableoid;
10346 : int i_oid;
10347 : int i_cfgname;
10348 : int i_cfgnamespace;
10349 : int i_cfgowner;
10350 : int i_cfgparser;
10351 :
10352 372 : query = createPQExpBuffer();
10353 :
10354 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10355 : "cfgnamespace, cfgowner, cfgparser "
10356 : "FROM pg_ts_config");
10357 :
10358 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10359 :
10360 372 : ntups = PQntuples(res);
10361 :
10362 372 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10363 :
10364 372 : i_tableoid = PQfnumber(res, "tableoid");
10365 372 : i_oid = PQfnumber(res, "oid");
10366 372 : i_cfgname = PQfnumber(res, "cfgname");
10367 372 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10368 372 : i_cfgowner = PQfnumber(res, "cfgowner");
10369 372 : i_cfgparser = PQfnumber(res, "cfgparser");
10370 :
10371 11678 : for (i = 0; i < ntups; i++)
10372 : {
10373 11306 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10374 11306 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10375 11306 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10376 11306 : AssignDumpId(&cfginfo[i].dobj);
10377 11306 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10378 22612 : cfginfo[i].dobj.namespace =
10379 11306 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10380 11306 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10381 11306 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10382 :
10383 : /* Decide whether we want to dump it */
10384 11306 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10385 : }
10386 :
10387 372 : PQclear(res);
10388 :
10389 372 : destroyPQExpBuffer(query);
10390 372 : }
10391 :
10392 : /*
10393 : * getForeignDataWrappers:
10394 : * get information about all foreign-data wrappers in the system catalogs
10395 : */
10396 : void
10397 372 : getForeignDataWrappers(Archive *fout)
10398 : {
10399 : PGresult *res;
10400 : int ntups;
10401 : int i;
10402 : PQExpBuffer query;
10403 : FdwInfo *fdwinfo;
10404 : int i_tableoid;
10405 : int i_oid;
10406 : int i_fdwname;
10407 : int i_fdwowner;
10408 : int i_fdwhandler;
10409 : int i_fdwvalidator;
10410 : int i_fdwacl;
10411 : int i_acldefault;
10412 : int i_fdwoptions;
10413 :
10414 372 : query = createPQExpBuffer();
10415 :
10416 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10417 : "fdwowner, "
10418 : "fdwhandler::pg_catalog.regproc, "
10419 : "fdwvalidator::pg_catalog.regproc, "
10420 : "fdwacl, "
10421 : "acldefault('F', fdwowner) AS acldefault, "
10422 : "array_to_string(ARRAY("
10423 : "SELECT quote_ident(option_name) || ' ' || "
10424 : "quote_literal(option_value) "
10425 : "FROM pg_options_to_table(fdwoptions) "
10426 : "ORDER BY option_name"
10427 : "), E',\n ') AS fdwoptions "
10428 : "FROM pg_foreign_data_wrapper");
10429 :
10430 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10431 :
10432 372 : ntups = PQntuples(res);
10433 :
10434 372 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10435 :
10436 372 : i_tableoid = PQfnumber(res, "tableoid");
10437 372 : i_oid = PQfnumber(res, "oid");
10438 372 : i_fdwname = PQfnumber(res, "fdwname");
10439 372 : i_fdwowner = PQfnumber(res, "fdwowner");
10440 372 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10441 372 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10442 372 : i_fdwacl = PQfnumber(res, "fdwacl");
10443 372 : i_acldefault = PQfnumber(res, "acldefault");
10444 372 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10445 :
10446 514 : for (i = 0; i < ntups; i++)
10447 : {
10448 142 : fdwinfo[i].dobj.objType = DO_FDW;
10449 142 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10450 142 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10451 142 : AssignDumpId(&fdwinfo[i].dobj);
10452 142 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10453 142 : fdwinfo[i].dobj.namespace = NULL;
10454 142 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10455 142 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10456 142 : fdwinfo[i].dacl.privtype = 0;
10457 142 : fdwinfo[i].dacl.initprivs = NULL;
10458 142 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10459 142 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10460 142 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10461 142 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10462 :
10463 : /* Decide whether we want to dump it */
10464 142 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10465 :
10466 : /* Mark whether FDW has an ACL */
10467 142 : if (!PQgetisnull(res, i, i_fdwacl))
10468 90 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10469 : }
10470 :
10471 372 : PQclear(res);
10472 :
10473 372 : destroyPQExpBuffer(query);
10474 372 : }
10475 :
10476 : /*
10477 : * getForeignServers:
10478 : * get information about all foreign servers in the system catalogs
10479 : */
10480 : void
10481 372 : getForeignServers(Archive *fout)
10482 : {
10483 : PGresult *res;
10484 : int ntups;
10485 : int i;
10486 : PQExpBuffer query;
10487 : ForeignServerInfo *srvinfo;
10488 : int i_tableoid;
10489 : int i_oid;
10490 : int i_srvname;
10491 : int i_srvowner;
10492 : int i_srvfdw;
10493 : int i_srvtype;
10494 : int i_srvversion;
10495 : int i_srvacl;
10496 : int i_acldefault;
10497 : int i_srvoptions;
10498 :
10499 372 : query = createPQExpBuffer();
10500 :
10501 372 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10502 : "srvowner, "
10503 : "srvfdw, srvtype, srvversion, srvacl, "
10504 : "acldefault('S', srvowner) AS acldefault, "
10505 : "array_to_string(ARRAY("
10506 : "SELECT quote_ident(option_name) || ' ' || "
10507 : "quote_literal(option_value) "
10508 : "FROM pg_options_to_table(srvoptions) "
10509 : "ORDER BY option_name"
10510 : "), E',\n ') AS srvoptions "
10511 : "FROM pg_foreign_server");
10512 :
10513 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10514 :
10515 372 : ntups = PQntuples(res);
10516 :
10517 372 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10518 :
10519 372 : i_tableoid = PQfnumber(res, "tableoid");
10520 372 : i_oid = PQfnumber(res, "oid");
10521 372 : i_srvname = PQfnumber(res, "srvname");
10522 372 : i_srvowner = PQfnumber(res, "srvowner");
10523 372 : i_srvfdw = PQfnumber(res, "srvfdw");
10524 372 : i_srvtype = PQfnumber(res, "srvtype");
10525 372 : i_srvversion = PQfnumber(res, "srvversion");
10526 372 : i_srvacl = PQfnumber(res, "srvacl");
10527 372 : i_acldefault = PQfnumber(res, "acldefault");
10528 372 : i_srvoptions = PQfnumber(res, "srvoptions");
10529 :
10530 522 : for (i = 0; i < ntups; i++)
10531 : {
10532 150 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10533 150 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10534 150 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10535 150 : AssignDumpId(&srvinfo[i].dobj);
10536 150 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10537 150 : srvinfo[i].dobj.namespace = NULL;
10538 150 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10539 150 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10540 150 : srvinfo[i].dacl.privtype = 0;
10541 150 : srvinfo[i].dacl.initprivs = NULL;
10542 150 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10543 150 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10544 150 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10545 150 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10546 150 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10547 :
10548 : /* Decide whether we want to dump it */
10549 150 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10550 :
10551 : /* Servers have user mappings */
10552 150 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10553 :
10554 : /* Mark whether server has an ACL */
10555 150 : if (!PQgetisnull(res, i, i_srvacl))
10556 90 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10557 : }
10558 :
10559 372 : PQclear(res);
10560 :
10561 372 : destroyPQExpBuffer(query);
10562 372 : }
10563 :
10564 : /*
10565 : * getDefaultACLs:
10566 : * get information about all default ACL information in the system catalogs
10567 : */
10568 : void
10569 372 : getDefaultACLs(Archive *fout)
10570 : {
10571 372 : DumpOptions *dopt = fout->dopt;
10572 : DefaultACLInfo *daclinfo;
10573 : PQExpBuffer query;
10574 : PGresult *res;
10575 : int i_oid;
10576 : int i_tableoid;
10577 : int i_defaclrole;
10578 : int i_defaclnamespace;
10579 : int i_defaclobjtype;
10580 : int i_defaclacl;
10581 : int i_acldefault;
10582 : int i,
10583 : ntups;
10584 :
10585 372 : query = createPQExpBuffer();
10586 :
10587 : /*
10588 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10589 : * ACL for their object type. We should dump them as deltas from the
10590 : * default ACL, since that will be used as a starting point for
10591 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10592 : * non-global entries can only add privileges not revoke them. We must
10593 : * dump those as-is (i.e., as deltas from an empty ACL).
10594 : *
10595 : * We can use defaclobjtype as the object type for acldefault(), except
10596 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10597 : * 's'.
10598 : */
10599 372 : appendPQExpBufferStr(query,
10600 : "SELECT oid, tableoid, "
10601 : "defaclrole, "
10602 : "defaclnamespace, "
10603 : "defaclobjtype, "
10604 : "defaclacl, "
10605 : "CASE WHEN defaclnamespace = 0 THEN "
10606 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10607 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10608 : "defaclrole) ELSE '{}' END AS acldefault "
10609 : "FROM pg_default_acl");
10610 :
10611 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10612 :
10613 372 : ntups = PQntuples(res);
10614 :
10615 372 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10616 :
10617 372 : i_oid = PQfnumber(res, "oid");
10618 372 : i_tableoid = PQfnumber(res, "tableoid");
10619 372 : i_defaclrole = PQfnumber(res, "defaclrole");
10620 372 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10621 372 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10622 372 : i_defaclacl = PQfnumber(res, "defaclacl");
10623 372 : i_acldefault = PQfnumber(res, "acldefault");
10624 :
10625 760 : for (i = 0; i < ntups; i++)
10626 : {
10627 388 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10628 :
10629 388 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10630 388 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10631 388 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10632 388 : AssignDumpId(&daclinfo[i].dobj);
10633 : /* cheesy ... is it worth coming up with a better object name? */
10634 388 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10635 :
10636 388 : if (nspid != InvalidOid)
10637 180 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10638 : else
10639 208 : daclinfo[i].dobj.namespace = NULL;
10640 :
10641 388 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10642 388 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10643 388 : daclinfo[i].dacl.privtype = 0;
10644 388 : daclinfo[i].dacl.initprivs = NULL;
10645 388 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10646 388 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10647 :
10648 : /* Default ACLs are ACLs, of course */
10649 388 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10650 :
10651 : /* Decide whether we want to dump it */
10652 388 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10653 : }
10654 :
10655 372 : PQclear(res);
10656 :
10657 372 : destroyPQExpBuffer(query);
10658 372 : }
10659 :
10660 : /*
10661 : * getRoleName -- look up the name of a role, given its OID
10662 : *
10663 : * In current usage, we don't expect failures, so error out for a bad OID.
10664 : */
10665 : static const char *
10666 1176514 : getRoleName(const char *roleoid_str)
10667 : {
10668 1176514 : Oid roleoid = atooid(roleoid_str);
10669 :
10670 : /*
10671 : * Do binary search to find the appropriate item.
10672 : */
10673 1176514 : if (nrolenames > 0)
10674 : {
10675 1176514 : RoleNameItem *low = &rolenames[0];
10676 1176514 : RoleNameItem *high = &rolenames[nrolenames - 1];
10677 :
10678 4705488 : while (low <= high)
10679 : {
10680 4705488 : RoleNameItem *middle = low + (high - low) / 2;
10681 :
10682 4705488 : if (roleoid < middle->roleoid)
10683 3527220 : high = middle - 1;
10684 1178268 : else if (roleoid > middle->roleoid)
10685 1754 : low = middle + 1;
10686 : else
10687 1176514 : return middle->rolename; /* found a match */
10688 : }
10689 : }
10690 :
10691 0 : pg_fatal("role with OID %u does not exist", roleoid);
10692 : return NULL; /* keep compiler quiet */
10693 : }
10694 :
10695 : /*
10696 : * collectRoleNames --
10697 : *
10698 : * Construct a table of all known roles.
10699 : * The table is sorted by OID for speed in lookup.
10700 : */
10701 : static void
10702 374 : collectRoleNames(Archive *fout)
10703 : {
10704 : PGresult *res;
10705 : const char *query;
10706 : int i;
10707 :
10708 374 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10709 :
10710 374 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10711 :
10712 374 : nrolenames = PQntuples(res);
10713 :
10714 374 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10715 :
10716 7342 : for (i = 0; i < nrolenames; i++)
10717 : {
10718 6968 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10719 6968 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10720 : }
10721 :
10722 374 : PQclear(res);
10723 374 : }
10724 :
10725 : /*
10726 : * getAdditionalACLs
10727 : *
10728 : * We have now created all the DumpableObjects, and collected the ACL data
10729 : * that appears in the directly-associated catalog entries. However, there's
10730 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10731 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10732 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10733 : * Also, in versions having the pg_init_privs catalog, read that and load the
10734 : * information into the relevant DumpableObjects.
10735 : */
10736 : static void
10737 368 : getAdditionalACLs(Archive *fout)
10738 : {
10739 368 : PQExpBuffer query = createPQExpBuffer();
10740 : PGresult *res;
10741 : int ntups,
10742 : i;
10743 :
10744 : /* Check for per-column ACLs */
10745 368 : appendPQExpBufferStr(query,
10746 : "SELECT DISTINCT attrelid FROM pg_attribute "
10747 : "WHERE attacl IS NOT NULL");
10748 :
10749 368 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10750 :
10751 368 : ntups = PQntuples(res);
10752 1076 : for (i = 0; i < ntups; i++)
10753 : {
10754 708 : Oid relid = atooid(PQgetvalue(res, i, 0));
10755 : TableInfo *tblinfo;
10756 :
10757 708 : tblinfo = findTableByOid(relid);
10758 : /* OK to ignore tables we haven't got a DumpableObject for */
10759 708 : if (tblinfo)
10760 : {
10761 708 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10762 708 : tblinfo->hascolumnACLs = true;
10763 : }
10764 : }
10765 368 : PQclear(res);
10766 :
10767 : /* Fetch initial-privileges data */
10768 368 : if (fout->remoteVersion >= 90600)
10769 : {
10770 368 : printfPQExpBuffer(query,
10771 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10772 : "FROM pg_init_privs");
10773 :
10774 368 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10775 :
10776 368 : ntups = PQntuples(res);
10777 87464 : for (i = 0; i < ntups; i++)
10778 : {
10779 87096 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10780 87096 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10781 87096 : int objsubid = atoi(PQgetvalue(res, i, 2));
10782 87096 : char privtype = *(PQgetvalue(res, i, 3));
10783 87096 : char *initprivs = PQgetvalue(res, i, 4);
10784 : CatalogId objId;
10785 : DumpableObject *dobj;
10786 :
10787 87096 : objId.tableoid = classoid;
10788 87096 : objId.oid = objoid;
10789 87096 : dobj = findObjectByCatalogId(objId);
10790 : /* OK to ignore entries we haven't got a DumpableObject for */
10791 87096 : if (dobj)
10792 : {
10793 : /* Cope with sub-object initprivs */
10794 62528 : if (objsubid != 0)
10795 : {
10796 7408 : if (dobj->objType == DO_TABLE)
10797 : {
10798 : /* For a column initprivs, set the table's ACL flags */
10799 7408 : dobj->components |= DUMP_COMPONENT_ACL;
10800 7408 : ((TableInfo *) dobj)->hascolumnACLs = true;
10801 : }
10802 : else
10803 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10804 : classoid, objoid, objsubid);
10805 7768 : continue;
10806 : }
10807 :
10808 : /*
10809 : * We ignore any pg_init_privs.initprivs entry for the public
10810 : * schema, as explained in getNamespaces().
10811 : */
10812 55120 : if (dobj->objType == DO_NAMESPACE &&
10813 728 : strcmp(dobj->name, "public") == 0)
10814 360 : continue;
10815 :
10816 : /* Else it had better be of a type we think has ACLs */
10817 54760 : if (dobj->objType == DO_NAMESPACE ||
10818 54392 : dobj->objType == DO_TYPE ||
10819 54344 : dobj->objType == DO_FUNC ||
10820 54160 : dobj->objType == DO_AGG ||
10821 54112 : dobj->objType == DO_TABLE ||
10822 0 : dobj->objType == DO_PROCLANG ||
10823 0 : dobj->objType == DO_FDW ||
10824 0 : dobj->objType == DO_FOREIGN_SERVER)
10825 54760 : {
10826 54760 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10827 :
10828 54760 : daobj->dacl.privtype = privtype;
10829 54760 : daobj->dacl.initprivs = pstrdup(initprivs);
10830 : }
10831 : else
10832 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10833 : classoid, objoid, objsubid);
10834 : }
10835 : }
10836 368 : PQclear(res);
10837 : }
10838 :
10839 368 : destroyPQExpBuffer(query);
10840 368 : }
10841 :
10842 : /*
10843 : * dumpCommentExtended --
10844 : *
10845 : * This routine is used to dump any comments associated with the
10846 : * object handed to this routine. The routine takes the object type
10847 : * and object name (ready to print, except for schema decoration), plus
10848 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10849 : * plus catalog ID and subid which are the lookup key for pg_description,
10850 : * plus the dump ID for the object (for setting a dependency).
10851 : * If a matching pg_description entry is found, it is dumped.
10852 : *
10853 : * Note: in some cases, such as comments for triggers and rules, the "type"
10854 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10855 : * but it doesn't seem worth complicating the API for all callers to make
10856 : * it cleaner.
10857 : *
10858 : * Note: although this routine takes a dumpId for dependency purposes,
10859 : * that purpose is just to mark the dependency in the emitted dump file
10860 : * for possible future use by pg_restore. We do NOT use it for determining
10861 : * ordering of the comment in the dump file, because this routine is called
10862 : * after dependency sorting occurs. This routine should be called just after
10863 : * calling ArchiveEntry() for the specified object.
10864 : */
10865 : static void
10866 12956 : dumpCommentExtended(Archive *fout, const char *type,
10867 : const char *name, const char *namespace,
10868 : const char *owner, CatalogId catalogId,
10869 : int subid, DumpId dumpId,
10870 : const char *initdb_comment)
10871 : {
10872 12956 : DumpOptions *dopt = fout->dopt;
10873 : CommentItem *comments;
10874 : int ncomments;
10875 :
10876 : /* do nothing, if --no-comments is supplied */
10877 12956 : if (dopt->no_comments)
10878 0 : return;
10879 :
10880 : /* Comments are schema not data ... except LO comments are data */
10881 12956 : if (strcmp(type, "LARGE OBJECT") != 0)
10882 : {
10883 12838 : if (!dopt->dumpSchema)
10884 0 : return;
10885 : }
10886 : else
10887 : {
10888 : /* We do dump LO comments in binary-upgrade mode */
10889 118 : if (!dopt->dumpData && !dopt->binary_upgrade)
10890 0 : return;
10891 : }
10892 :
10893 : /* Search for comments associated with catalogId, using table */
10894 12956 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10895 : &comments);
10896 :
10897 : /* Is there one matching the subid? */
10898 12956 : while (ncomments > 0)
10899 : {
10900 12864 : if (comments->objsubid == subid)
10901 12864 : break;
10902 0 : comments++;
10903 0 : ncomments--;
10904 : }
10905 :
10906 12956 : if (initdb_comment != NULL)
10907 : {
10908 : static CommentItem empty_comment = {.descr = ""};
10909 :
10910 : /*
10911 : * initdb creates this object with a comment. Skip dumping the
10912 : * initdb-provided comment, which would complicate matters for
10913 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10914 : * comment, replicate that.
10915 : */
10916 234 : if (ncomments == 0)
10917 : {
10918 8 : comments = &empty_comment;
10919 8 : ncomments = 1;
10920 : }
10921 226 : else if (strcmp(comments->descr, initdb_comment) == 0)
10922 226 : ncomments = 0;
10923 : }
10924 :
10925 : /* If a comment exists, build COMMENT ON statement */
10926 12956 : if (ncomments > 0)
10927 : {
10928 12646 : PQExpBuffer query = createPQExpBuffer();
10929 12646 : PQExpBuffer tag = createPQExpBuffer();
10930 :
10931 12646 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10932 12646 : if (namespace && *namespace)
10933 12294 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10934 12646 : appendPQExpBuffer(query, "%s IS ", name);
10935 12646 : appendStringLiteralAH(query, comments->descr, fout);
10936 12646 : appendPQExpBufferStr(query, ";\n");
10937 :
10938 12646 : appendPQExpBuffer(tag, "%s %s", type, name);
10939 :
10940 : /*
10941 : * We mark comments as SECTION_NONE because they really belong in the
10942 : * same section as their parent, whether that is pre-data or
10943 : * post-data.
10944 : */
10945 12646 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10946 12646 : ARCHIVE_OPTS(.tag = tag->data,
10947 : .namespace = namespace,
10948 : .owner = owner,
10949 : .description = "COMMENT",
10950 : .section = SECTION_NONE,
10951 : .createStmt = query->data,
10952 : .deps = &dumpId,
10953 : .nDeps = 1));
10954 :
10955 12646 : destroyPQExpBuffer(query);
10956 12646 : destroyPQExpBuffer(tag);
10957 : }
10958 : }
10959 :
10960 : /*
10961 : * dumpComment --
10962 : *
10963 : * Typical simplification of the above function.
10964 : */
10965 : static inline void
10966 12644 : dumpComment(Archive *fout, const char *type,
10967 : const char *name, const char *namespace,
10968 : const char *owner, CatalogId catalogId,
10969 : int subid, DumpId dumpId)
10970 : {
10971 12644 : dumpCommentExtended(fout, type, name, namespace, owner,
10972 : catalogId, subid, dumpId, NULL);
10973 12644 : }
10974 :
10975 : /*
10976 : * appendNamedArgument --
10977 : *
10978 : * Convenience routine for constructing parameters of the form:
10979 : * 'paraname', 'value'::type
10980 : */
10981 : static void
10982 9878 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10983 : const char *argtype, const char *argval)
10984 : {
10985 9878 : appendPQExpBufferStr(out, ",\n\t");
10986 :
10987 9878 : appendStringLiteralAH(out, argname, fout);
10988 9878 : appendPQExpBufferStr(out, ", ");
10989 :
10990 9878 : appendStringLiteralAH(out, argval, fout);
10991 9878 : appendPQExpBuffer(out, "::%s", argtype);
10992 9878 : }
10993 :
10994 : /*
10995 : * fetchAttributeStats --
10996 : *
10997 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
10998 : */
10999 : static PGresult *
11000 2056 : fetchAttributeStats(Archive *fout)
11001 : {
11002 2056 : ArchiveHandle *AH = (ArchiveHandle *) fout;
11003 2056 : PQExpBuffer nspnames = createPQExpBuffer();
11004 2056 : PQExpBuffer relnames = createPQExpBuffer();
11005 2056 : int count = 0;
11006 2056 : PGresult *res = NULL;
11007 : static TocEntry *te;
11008 : static bool restarted;
11009 2056 : int max_rels = MAX_ATTR_STATS_RELS;
11010 :
11011 : /*
11012 : * Our query for retrieving statistics for multiple relations uses WITH
11013 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11014 : * in v9.4. For older versions, we resort to gathering statistics for a
11015 : * single relation at a time.
11016 : */
11017 2056 : if (fout->remoteVersion < 90400)
11018 0 : max_rels = 1;
11019 :
11020 : /* If we're just starting, set our TOC pointer. */
11021 2056 : if (!te)
11022 122 : te = AH->toc->next;
11023 :
11024 : /*
11025 : * We can't easily avoid a second TOC scan for the tar format because it
11026 : * writes restore.sql separately, which means we must execute the queries
11027 : * twice. This feels risky, but there is no known reason it should
11028 : * generate different output than the first pass. Even if it does, the
11029 : * worst-case scenario is that restore.sql might have different statistics
11030 : * data than the archive.
11031 : */
11032 2056 : if (!restarted && te == AH->toc && AH->format == archTar)
11033 : {
11034 2 : te = AH->toc->next;
11035 2 : restarted = true;
11036 : }
11037 :
11038 2056 : appendPQExpBufferChar(nspnames, '{');
11039 2056 : appendPQExpBufferChar(relnames, '{');
11040 :
11041 : /*
11042 : * Scan the TOC for the next set of relevant stats entries. We assume
11043 : * that statistics are dumped in the order they are listed in the TOC.
11044 : * This is perhaps not the sturdiest assumption, so we verify it matches
11045 : * reality in dumpRelationStats_dumper().
11046 : */
11047 31226 : for (; te != AH->toc && count < max_rels; te = te->next)
11048 : {
11049 29170 : if ((te->reqs & REQ_STATS) != 0 &&
11050 6434 : strcmp(te->desc, "STATISTICS DATA") == 0)
11051 : {
11052 6434 : appendPGArray(nspnames, te->namespace);
11053 6434 : appendPGArray(relnames, te->tag);
11054 6434 : count++;
11055 : }
11056 : }
11057 :
11058 2056 : appendPQExpBufferChar(nspnames, '}');
11059 2056 : appendPQExpBufferChar(relnames, '}');
11060 :
11061 : /* Execute the query for the next batch of relations. */
11062 2056 : if (count > 0)
11063 : {
11064 208 : PQExpBuffer query = createPQExpBuffer();
11065 :
11066 208 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11067 208 : appendStringLiteralAH(query, nspnames->data, fout);
11068 208 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11069 208 : appendStringLiteralAH(query, relnames->data, fout);
11070 208 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11071 208 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11072 208 : destroyPQExpBuffer(query);
11073 : }
11074 :
11075 2056 : destroyPQExpBuffer(nspnames);
11076 2056 : destroyPQExpBuffer(relnames);
11077 2056 : return res;
11078 : }
11079 :
11080 : /*
11081 : * dumpRelationStats_dumper --
11082 : *
11083 : * Generate command to import stats into the relation on the new database.
11084 : * This routine is called by the Archiver when it wants the statistics to be
11085 : * dumped.
11086 : */
11087 : static char *
11088 6434 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11089 : {
11090 6434 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11091 : static PGresult *res;
11092 : static int rownum;
11093 : PQExpBuffer query;
11094 : PQExpBufferData out_data;
11095 6434 : PQExpBuffer out = &out_data;
11096 : int i_schemaname;
11097 : int i_tablename;
11098 : int i_attname;
11099 : int i_inherited;
11100 : int i_null_frac;
11101 : int i_avg_width;
11102 : int i_n_distinct;
11103 : int i_most_common_vals;
11104 : int i_most_common_freqs;
11105 : int i_histogram_bounds;
11106 : int i_correlation;
11107 : int i_most_common_elems;
11108 : int i_most_common_elem_freqs;
11109 : int i_elem_count_histogram;
11110 : int i_range_length_histogram;
11111 : int i_range_empty_frac;
11112 : int i_range_bounds_histogram;
11113 : static TocEntry *expected_te;
11114 :
11115 : /*
11116 : * fetchAttributeStats() assumes that the statistics are dumped in the
11117 : * order they are listed in the TOC. We verify that here for safety.
11118 : */
11119 6434 : if (!expected_te)
11120 122 : expected_te = ((ArchiveHandle *) fout)->toc;
11121 :
11122 6434 : expected_te = expected_te->next;
11123 25634 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11124 6434 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11125 19200 : expected_te = expected_te->next;
11126 :
11127 6434 : if (te != expected_te)
11128 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11129 : te->dumpId, te->desc, te->tag,
11130 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11131 :
11132 6434 : query = createPQExpBuffer();
11133 6434 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11134 : {
11135 122 : appendPQExpBufferStr(query,
11136 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11137 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11138 : "s.null_frac, s.avg_width, s.n_distinct, "
11139 : "s.most_common_vals, s.most_common_freqs, "
11140 : "s.histogram_bounds, s.correlation, "
11141 : "s.most_common_elems, s.most_common_elem_freqs, "
11142 : "s.elem_count_histogram, ");
11143 :
11144 122 : if (fout->remoteVersion >= 170000)
11145 122 : appendPQExpBufferStr(query,
11146 : "s.range_length_histogram, "
11147 : "s.range_empty_frac, "
11148 : "s.range_bounds_histogram ");
11149 : else
11150 0 : appendPQExpBufferStr(query,
11151 : "NULL AS range_length_histogram,"
11152 : "NULL AS range_empty_frac,"
11153 : "NULL AS range_bounds_histogram ");
11154 :
11155 : /*
11156 : * The results must be in the order of the relations supplied in the
11157 : * parameters to ensure we remain in sync as we walk through the TOC.
11158 : * The redundant filter clause on s.tablename = ANY(...) seems
11159 : * sufficient to convince the planner to use
11160 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11161 : * This may not work for all versions.
11162 : *
11163 : * Our query for retrieving statistics for multiple relations uses
11164 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11165 : * introduced in v9.4. For older versions, we resort to gathering
11166 : * statistics for a single relation at a time.
11167 : */
11168 122 : if (fout->remoteVersion >= 90400)
11169 122 : appendPQExpBufferStr(query,
11170 : "FROM pg_catalog.pg_stats s "
11171 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11172 : "ON s.schemaname = u.schemaname "
11173 : "AND s.tablename = u.tablename "
11174 : "WHERE s.tablename = ANY($2) "
11175 : "ORDER BY u.ord, s.attname, s.inherited");
11176 : else
11177 0 : appendPQExpBufferStr(query,
11178 : "FROM pg_catalog.pg_stats s "
11179 : "WHERE s.schemaname = $1[1] "
11180 : "AND s.tablename = $2[1] "
11181 : "ORDER BY s.attname, s.inherited");
11182 :
11183 122 : ExecuteSqlStatement(fout, query->data);
11184 :
11185 122 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11186 122 : resetPQExpBuffer(query);
11187 : }
11188 :
11189 6434 : initPQExpBuffer(out);
11190 :
11191 : /* restore relation stats */
11192 6434 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11193 6434 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11194 : fout->remoteVersion);
11195 6434 : appendPQExpBufferStr(out, "\t'schemaname', ");
11196 6434 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11197 6434 : appendPQExpBufferStr(out, ",\n");
11198 6434 : appendPQExpBufferStr(out, "\t'relname', ");
11199 6434 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11200 6434 : appendPQExpBufferStr(out, ",\n");
11201 6434 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11202 :
11203 : /*
11204 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11205 : * the relation is empty, or it could mean that it hadn't yet been
11206 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11207 : * This ambiguity allegedly can cause the planner to choose inefficient
11208 : * plans after restoring to v18 or newer. To deal with this, let's just
11209 : * set reltuples to -1 in that case.
11210 : */
11211 6434 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11212 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11213 : else
11214 6434 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11215 :
11216 6434 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11217 6434 : rsinfo->relallvisible);
11218 :
11219 6434 : if (fout->remoteVersion >= 180000)
11220 6434 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11221 :
11222 6434 : appendPQExpBufferStr(out, "\n);\n");
11223 :
11224 : /* Fetch the next batch of attribute statistics if needed. */
11225 6434 : if (rownum >= PQntuples(res))
11226 : {
11227 2056 : PQclear(res);
11228 2056 : res = fetchAttributeStats(fout);
11229 2056 : rownum = 0;
11230 : }
11231 :
11232 6434 : i_schemaname = PQfnumber(res, "schemaname");
11233 6434 : i_tablename = PQfnumber(res, "tablename");
11234 6434 : i_attname = PQfnumber(res, "attname");
11235 6434 : i_inherited = PQfnumber(res, "inherited");
11236 6434 : i_null_frac = PQfnumber(res, "null_frac");
11237 6434 : i_avg_width = PQfnumber(res, "avg_width");
11238 6434 : i_n_distinct = PQfnumber(res, "n_distinct");
11239 6434 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11240 6434 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11241 6434 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11242 6434 : i_correlation = PQfnumber(res, "correlation");
11243 6434 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11244 6434 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11245 6434 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11246 6434 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11247 6434 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11248 6434 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11249 :
11250 : /* restore attribute stats */
11251 7930 : for (; rownum < PQntuples(res); rownum++)
11252 : {
11253 : const char *attname;
11254 :
11255 : /* Stop if the next stat row in our cache isn't for this relation. */
11256 5874 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11257 1496 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11258 : break;
11259 :
11260 1496 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11261 1496 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11262 : fout->remoteVersion);
11263 1496 : appendPQExpBufferStr(out, "\t'schemaname', ");
11264 1496 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11265 1496 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11266 1496 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11267 :
11268 1496 : if (PQgetisnull(res, rownum, i_attname))
11269 0 : pg_fatal("unexpected null attname");
11270 1496 : attname = PQgetvalue(res, rownum, i_attname);
11271 :
11272 : /*
11273 : * Indexes look up attname in indAttNames to derive attnum, all others
11274 : * use attname directly. We must specify attnum for indexes, since
11275 : * their attnames are not necessarily stable across dump/reload.
11276 : */
11277 1496 : if (rsinfo->nindAttNames == 0)
11278 : {
11279 1426 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11280 1426 : appendStringLiteralAH(out, attname, fout);
11281 : }
11282 : else
11283 : {
11284 70 : bool found = false;
11285 :
11286 132 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11287 : {
11288 132 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11289 : {
11290 70 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11291 : i + 1);
11292 70 : found = true;
11293 70 : break;
11294 : }
11295 : }
11296 :
11297 70 : if (!found)
11298 0 : pg_fatal("could not find index attname \"%s\"", attname);
11299 : }
11300 :
11301 1496 : if (!PQgetisnull(res, rownum, i_inherited))
11302 1496 : appendNamedArgument(out, fout, "inherited", "boolean",
11303 1496 : PQgetvalue(res, rownum, i_inherited));
11304 1496 : if (!PQgetisnull(res, rownum, i_null_frac))
11305 1496 : appendNamedArgument(out, fout, "null_frac", "real",
11306 1496 : PQgetvalue(res, rownum, i_null_frac));
11307 1496 : if (!PQgetisnull(res, rownum, i_avg_width))
11308 1496 : appendNamedArgument(out, fout, "avg_width", "integer",
11309 1496 : PQgetvalue(res, rownum, i_avg_width));
11310 1496 : if (!PQgetisnull(res, rownum, i_n_distinct))
11311 1496 : appendNamedArgument(out, fout, "n_distinct", "real",
11312 1496 : PQgetvalue(res, rownum, i_n_distinct));
11313 1496 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11314 752 : appendNamedArgument(out, fout, "most_common_vals", "text",
11315 752 : PQgetvalue(res, rownum, i_most_common_vals));
11316 1496 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11317 752 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11318 752 : PQgetvalue(res, rownum, i_most_common_freqs));
11319 1496 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11320 892 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11321 892 : PQgetvalue(res, rownum, i_histogram_bounds));
11322 1496 : if (!PQgetisnull(res, rownum, i_correlation))
11323 1434 : appendNamedArgument(out, fout, "correlation", "real",
11324 1434 : PQgetvalue(res, rownum, i_correlation));
11325 1496 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11326 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11327 16 : PQgetvalue(res, rownum, i_most_common_elems));
11328 1496 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11329 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11330 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11331 1496 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11332 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11333 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11334 1496 : if (fout->remoteVersion >= 170000)
11335 : {
11336 1496 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11337 6 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11338 6 : PQgetvalue(res, rownum, i_range_length_histogram));
11339 1496 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11340 6 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11341 6 : PQgetvalue(res, rownum, i_range_empty_frac));
11342 1496 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11343 6 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11344 6 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11345 : }
11346 1496 : appendPQExpBufferStr(out, "\n);\n");
11347 : }
11348 :
11349 6434 : destroyPQExpBuffer(query);
11350 6434 : return out->data;
11351 : }
11352 :
11353 : /*
11354 : * dumpRelationStats --
11355 : *
11356 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11357 : * care of gathering the statistics and generating the restore commands when
11358 : * they are needed.
11359 : */
11360 : static void
11361 6572 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11362 : {
11363 6572 : const DumpableObject *dobj = &rsinfo->dobj;
11364 :
11365 : /* nothing to do if we are not dumping statistics */
11366 6572 : if (!fout->dopt->dumpStatistics)
11367 0 : return;
11368 :
11369 6572 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11370 6572 : ARCHIVE_OPTS(.tag = dobj->name,
11371 : .namespace = dobj->namespace->dobj.name,
11372 : .description = "STATISTICS DATA",
11373 : .section = rsinfo->section,
11374 : .defnFn = dumpRelationStats_dumper,
11375 : .defnArg = rsinfo,
11376 : .deps = dobj->dependencies,
11377 : .nDeps = dobj->nDeps));
11378 : }
11379 :
11380 : /*
11381 : * dumpTableComment --
11382 : *
11383 : * As above, but dump comments for both the specified table (or view)
11384 : * and its columns.
11385 : */
11386 : static void
11387 148 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11388 : const char *reltypename)
11389 : {
11390 148 : DumpOptions *dopt = fout->dopt;
11391 : CommentItem *comments;
11392 : int ncomments;
11393 : PQExpBuffer query;
11394 : PQExpBuffer tag;
11395 :
11396 : /* do nothing, if --no-comments is supplied */
11397 148 : if (dopt->no_comments)
11398 0 : return;
11399 :
11400 : /* Comments are SCHEMA not data */
11401 148 : if (!dopt->dumpSchema)
11402 0 : return;
11403 :
11404 : /* Search for comments associated with relation, using table */
11405 148 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11406 148 : tbinfo->dobj.catId.oid,
11407 : &comments);
11408 :
11409 : /* If comments exist, build COMMENT ON statements */
11410 148 : if (ncomments <= 0)
11411 0 : return;
11412 :
11413 148 : query = createPQExpBuffer();
11414 148 : tag = createPQExpBuffer();
11415 :
11416 424 : while (ncomments > 0)
11417 : {
11418 276 : const char *descr = comments->descr;
11419 276 : int objsubid = comments->objsubid;
11420 :
11421 276 : if (objsubid == 0)
11422 : {
11423 64 : resetPQExpBuffer(tag);
11424 64 : appendPQExpBuffer(tag, "%s %s", reltypename,
11425 64 : fmtId(tbinfo->dobj.name));
11426 :
11427 64 : resetPQExpBuffer(query);
11428 64 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11429 64 : fmtQualifiedDumpable(tbinfo));
11430 64 : appendStringLiteralAH(query, descr, fout);
11431 64 : appendPQExpBufferStr(query, ";\n");
11432 :
11433 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11434 64 : ARCHIVE_OPTS(.tag = tag->data,
11435 : .namespace = tbinfo->dobj.namespace->dobj.name,
11436 : .owner = tbinfo->rolname,
11437 : .description = "COMMENT",
11438 : .section = SECTION_NONE,
11439 : .createStmt = query->data,
11440 : .deps = &(tbinfo->dobj.dumpId),
11441 : .nDeps = 1));
11442 : }
11443 212 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11444 : {
11445 212 : resetPQExpBuffer(tag);
11446 212 : appendPQExpBuffer(tag, "COLUMN %s.",
11447 212 : fmtId(tbinfo->dobj.name));
11448 212 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11449 :
11450 212 : resetPQExpBuffer(query);
11451 212 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11452 212 : fmtQualifiedDumpable(tbinfo));
11453 212 : appendPQExpBuffer(query, "%s IS ",
11454 212 : fmtId(tbinfo->attnames[objsubid - 1]));
11455 212 : appendStringLiteralAH(query, descr, fout);
11456 212 : appendPQExpBufferStr(query, ";\n");
11457 :
11458 212 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11459 212 : ARCHIVE_OPTS(.tag = tag->data,
11460 : .namespace = tbinfo->dobj.namespace->dobj.name,
11461 : .owner = tbinfo->rolname,
11462 : .description = "COMMENT",
11463 : .section = SECTION_NONE,
11464 : .createStmt = query->data,
11465 : .deps = &(tbinfo->dobj.dumpId),
11466 : .nDeps = 1));
11467 : }
11468 :
11469 276 : comments++;
11470 276 : ncomments--;
11471 : }
11472 :
11473 148 : destroyPQExpBuffer(query);
11474 148 : destroyPQExpBuffer(tag);
11475 : }
11476 :
11477 : /*
11478 : * findComments --
11479 : *
11480 : * Find the comment(s), if any, associated with the given object. All the
11481 : * objsubid values associated with the given classoid/objoid are found with
11482 : * one search.
11483 : */
11484 : static int
11485 13168 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11486 : {
11487 13168 : CommentItem *middle = NULL;
11488 : CommentItem *low;
11489 : CommentItem *high;
11490 : int nmatch;
11491 :
11492 : /*
11493 : * Do binary search to find some item matching the object.
11494 : */
11495 13168 : low = &comments[0];
11496 13168 : high = &comments[ncomments - 1];
11497 131054 : while (low <= high)
11498 : {
11499 130962 : middle = low + (high - low) / 2;
11500 :
11501 130962 : if (classoid < middle->classoid)
11502 14876 : high = middle - 1;
11503 116086 : else if (classoid > middle->classoid)
11504 14266 : low = middle + 1;
11505 101820 : else if (objoid < middle->objoid)
11506 43102 : high = middle - 1;
11507 58718 : else if (objoid > middle->objoid)
11508 45642 : low = middle + 1;
11509 : else
11510 13076 : break; /* found a match */
11511 : }
11512 :
11513 13168 : if (low > high) /* no matches */
11514 : {
11515 92 : *items = NULL;
11516 92 : return 0;
11517 : }
11518 :
11519 : /*
11520 : * Now determine how many items match the object. The search loop
11521 : * invariant still holds: only items between low and high inclusive could
11522 : * match.
11523 : */
11524 13076 : nmatch = 1;
11525 13204 : while (middle > low)
11526 : {
11527 6142 : if (classoid != middle[-1].classoid ||
11528 5844 : objoid != middle[-1].objoid)
11529 : break;
11530 128 : middle--;
11531 128 : nmatch++;
11532 : }
11533 :
11534 13076 : *items = middle;
11535 :
11536 13076 : middle += nmatch;
11537 13076 : while (middle <= high)
11538 : {
11539 7058 : if (classoid != middle->classoid ||
11540 6392 : objoid != middle->objoid)
11541 : break;
11542 0 : middle++;
11543 0 : nmatch++;
11544 : }
11545 :
11546 13076 : return nmatch;
11547 : }
11548 :
11549 : /*
11550 : * collectComments --
11551 : *
11552 : * Construct a table of all comments available for database objects;
11553 : * also set the has-comment component flag for each relevant object.
11554 : *
11555 : * We used to do per-object queries for the comments, but it's much faster
11556 : * to pull them all over at once, and on most databases the memory cost
11557 : * isn't high.
11558 : *
11559 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11560 : */
11561 : static void
11562 372 : collectComments(Archive *fout)
11563 : {
11564 : PGresult *res;
11565 : PQExpBuffer query;
11566 : int i_description;
11567 : int i_classoid;
11568 : int i_objoid;
11569 : int i_objsubid;
11570 : int ntups;
11571 : int i;
11572 : DumpableObject *dobj;
11573 :
11574 372 : query = createPQExpBuffer();
11575 :
11576 372 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11577 : "FROM pg_catalog.pg_description "
11578 : "ORDER BY classoid, objoid, objsubid");
11579 :
11580 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11581 :
11582 : /* Construct lookup table containing OIDs in numeric form */
11583 :
11584 372 : i_description = PQfnumber(res, "description");
11585 372 : i_classoid = PQfnumber(res, "classoid");
11586 372 : i_objoid = PQfnumber(res, "objoid");
11587 372 : i_objsubid = PQfnumber(res, "objsubid");
11588 :
11589 372 : ntups = PQntuples(res);
11590 :
11591 372 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11592 372 : ncomments = 0;
11593 372 : dobj = NULL;
11594 :
11595 1984466 : for (i = 0; i < ntups; i++)
11596 : {
11597 : CatalogId objId;
11598 : int subid;
11599 :
11600 1984094 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11601 1984094 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11602 1984094 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11603 :
11604 : /* We needn't remember comments that don't match any dumpable object */
11605 1984094 : if (dobj == NULL ||
11606 713914 : dobj->catId.tableoid != objId.tableoid ||
11607 709348 : dobj->catId.oid != objId.oid)
11608 1983914 : dobj = findObjectByCatalogId(objId);
11609 1984094 : if (dobj == NULL)
11610 1269820 : continue;
11611 :
11612 : /*
11613 : * Comments on columns of composite types are linked to the type's
11614 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11615 : * in the type's own DumpableObject.
11616 : */
11617 714274 : if (subid != 0 && dobj->objType == DO_TABLE &&
11618 388 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11619 90 : {
11620 : TypeInfo *cTypeInfo;
11621 :
11622 90 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11623 90 : if (cTypeInfo)
11624 90 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11625 : }
11626 : else
11627 714184 : dobj->components |= DUMP_COMPONENT_COMMENT;
11628 :
11629 714274 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11630 714274 : comments[ncomments].classoid = objId.tableoid;
11631 714274 : comments[ncomments].objoid = objId.oid;
11632 714274 : comments[ncomments].objsubid = subid;
11633 714274 : ncomments++;
11634 : }
11635 :
11636 372 : PQclear(res);
11637 372 : destroyPQExpBuffer(query);
11638 372 : }
11639 :
11640 : /*
11641 : * dumpDumpableObject
11642 : *
11643 : * This routine and its subsidiaries are responsible for creating
11644 : * ArchiveEntries (TOC objects) for each object to be dumped.
11645 : */
11646 : static void
11647 1379460 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11648 : {
11649 : /*
11650 : * Clear any dump-request bits for components that don't exist for this
11651 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11652 : * request for every kind of object.)
11653 : */
11654 1379460 : dobj->dump &= dobj->components;
11655 :
11656 : /* Now, short-circuit if there's nothing to be done here. */
11657 1379460 : if (dobj->dump == 0)
11658 1227370 : return;
11659 :
11660 152090 : switch (dobj->objType)
11661 : {
11662 970 : case DO_NAMESPACE:
11663 970 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11664 970 : break;
11665 48 : case DO_EXTENSION:
11666 48 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11667 48 : break;
11668 1840 : case DO_TYPE:
11669 1840 : dumpType(fout, (const TypeInfo *) dobj);
11670 1840 : break;
11671 146 : case DO_SHELL_TYPE:
11672 146 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11673 146 : break;
11674 3636 : case DO_FUNC:
11675 3636 : dumpFunc(fout, (const FuncInfo *) dobj);
11676 3636 : break;
11677 584 : case DO_AGG:
11678 584 : dumpAgg(fout, (const AggInfo *) dobj);
11679 584 : break;
11680 5008 : case DO_OPERATOR:
11681 5008 : dumpOpr(fout, (const OprInfo *) dobj);
11682 5008 : break;
11683 160 : case DO_ACCESS_METHOD:
11684 160 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11685 160 : break;
11686 1320 : case DO_OPCLASS:
11687 1320 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11688 1320 : break;
11689 1098 : case DO_OPFAMILY:
11690 1098 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11691 1098 : break;
11692 5074 : case DO_COLLATION:
11693 5074 : dumpCollation(fout, (const CollInfo *) dobj);
11694 5074 : break;
11695 844 : case DO_CONVERSION:
11696 844 : dumpConversion(fout, (const ConvInfo *) dobj);
11697 844 : break;
11698 62034 : case DO_TABLE:
11699 62034 : dumpTable(fout, (const TableInfo *) dobj);
11700 62034 : break;
11701 2762 : case DO_TABLE_ATTACH:
11702 2762 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11703 2762 : break;
11704 2064 : case DO_ATTRDEF:
11705 2064 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11706 2064 : break;
11707 5148 : case DO_INDEX:
11708 5148 : dumpIndex(fout, (const IndxInfo *) dobj);
11709 5148 : break;
11710 1128 : case DO_INDEX_ATTACH:
11711 1128 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11712 1128 : break;
11713 266 : case DO_STATSEXT:
11714 266 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11715 266 : break;
11716 690 : case DO_REFRESH_MATVIEW:
11717 690 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11718 690 : break;
11719 2258 : case DO_RULE:
11720 2258 : dumpRule(fout, (const RuleInfo *) dobj);
11721 2258 : break;
11722 1046 : case DO_TRIGGER:
11723 1046 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11724 1046 : break;
11725 84 : case DO_EVENT_TRIGGER:
11726 84 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11727 84 : break;
11728 4604 : case DO_CONSTRAINT:
11729 4604 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11730 4604 : break;
11731 342 : case DO_FK_CONSTRAINT:
11732 342 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11733 342 : break;
11734 164 : case DO_PROCLANG:
11735 164 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11736 164 : break;
11737 134 : case DO_CAST:
11738 134 : dumpCast(fout, (const CastInfo *) dobj);
11739 134 : break;
11740 84 : case DO_TRANSFORM:
11741 84 : dumpTransform(fout, (const TransformInfo *) dobj);
11742 84 : break;
11743 786 : case DO_SEQUENCE_SET:
11744 786 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11745 786 : break;
11746 8436 : case DO_TABLE_DATA:
11747 8436 : dumpTableData(fout, (const TableDataInfo *) dobj);
11748 8436 : break;
11749 28090 : case DO_DUMMY_TYPE:
11750 : /* table rowtypes and array types are never dumped separately */
11751 28090 : break;
11752 82 : case DO_TSPARSER:
11753 82 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11754 82 : break;
11755 346 : case DO_TSDICT:
11756 346 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11757 346 : break;
11758 106 : case DO_TSTEMPLATE:
11759 106 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11760 106 : break;
11761 296 : case DO_TSCONFIG:
11762 296 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11763 296 : break;
11764 104 : case DO_FDW:
11765 104 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11766 104 : break;
11767 112 : case DO_FOREIGN_SERVER:
11768 112 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11769 112 : break;
11770 320 : case DO_DEFAULT_ACL:
11771 320 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11772 320 : break;
11773 168 : case DO_LARGE_OBJECT:
11774 168 : dumpLO(fout, (const LoInfo *) dobj);
11775 168 : break;
11776 180 : case DO_LARGE_OBJECT_DATA:
11777 180 : if (dobj->dump & DUMP_COMPONENT_DATA)
11778 : {
11779 : LoInfo *loinfo;
11780 : TocEntry *te;
11781 :
11782 180 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11783 180 : if (loinfo == NULL)
11784 0 : pg_fatal("missing metadata for large objects \"%s\"",
11785 : dobj->name);
11786 :
11787 180 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11788 180 : ARCHIVE_OPTS(.tag = dobj->name,
11789 : .owner = loinfo->rolname,
11790 : .description = "BLOBS",
11791 : .section = SECTION_DATA,
11792 : .deps = dobj->dependencies,
11793 : .nDeps = dobj->nDeps,
11794 : .dumpFn = dumpLOs,
11795 : .dumpArg = loinfo));
11796 :
11797 : /*
11798 : * Set the TocEntry's dataLength in case we are doing a
11799 : * parallel dump and want to order dump jobs by table size.
11800 : * (We need some size estimate for every TocEntry with a
11801 : * DataDumper function.) We don't currently have any cheap
11802 : * way to estimate the size of LOs, but fortunately it doesn't
11803 : * matter too much as long as we get large batches of LOs
11804 : * processed reasonably early. Assume 8K per blob.
11805 : */
11806 180 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11807 : }
11808 180 : break;
11809 652 : case DO_POLICY:
11810 652 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11811 652 : break;
11812 570 : case DO_PUBLICATION:
11813 570 : dumpPublication(fout, (const PublicationInfo *) dobj);
11814 570 : break;
11815 568 : case DO_PUBLICATION_REL:
11816 568 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11817 568 : break;
11818 198 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11819 198 : dumpPublicationNamespace(fout,
11820 : (const PublicationSchemaInfo *) dobj);
11821 198 : break;
11822 220 : case DO_SUBSCRIPTION:
11823 220 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11824 220 : break;
11825 4 : case DO_SUBSCRIPTION_REL:
11826 4 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11827 4 : break;
11828 6572 : case DO_REL_STATS:
11829 6572 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11830 6572 : break;
11831 744 : case DO_PRE_DATA_BOUNDARY:
11832 : case DO_POST_DATA_BOUNDARY:
11833 : /* never dumped, nothing to do */
11834 744 : break;
11835 : }
11836 : }
11837 :
11838 : /*
11839 : * dumpNamespace
11840 : * writes out to fout the queries to recreate a user-defined namespace
11841 : */
11842 : static void
11843 970 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11844 : {
11845 970 : DumpOptions *dopt = fout->dopt;
11846 : PQExpBuffer q;
11847 : PQExpBuffer delq;
11848 : char *qnspname;
11849 :
11850 : /* Do nothing if not dumping schema */
11851 970 : if (!dopt->dumpSchema)
11852 56 : return;
11853 :
11854 914 : q = createPQExpBuffer();
11855 914 : delq = createPQExpBuffer();
11856 :
11857 914 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11858 :
11859 914 : if (nspinfo->create)
11860 : {
11861 612 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11862 612 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11863 : }
11864 : else
11865 : {
11866 : /* see selectDumpableNamespace() */
11867 302 : appendPQExpBufferStr(delq,
11868 : "-- *not* dropping schema, since initdb creates it\n");
11869 302 : appendPQExpBufferStr(q,
11870 : "-- *not* creating schema, since initdb creates it\n");
11871 : }
11872 :
11873 914 : if (dopt->binary_upgrade)
11874 180 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11875 : "SCHEMA", qnspname, NULL);
11876 :
11877 914 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11878 370 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11879 370 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11880 : .owner = nspinfo->rolname,
11881 : .description = "SCHEMA",
11882 : .section = SECTION_PRE_DATA,
11883 : .createStmt = q->data,
11884 : .dropStmt = delq->data));
11885 :
11886 : /* Dump Schema Comments and Security Labels */
11887 914 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11888 : {
11889 312 : const char *initdb_comment = NULL;
11890 :
11891 312 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11892 234 : initdb_comment = "standard public schema";
11893 312 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11894 312 : NULL, nspinfo->rolname,
11895 312 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11896 : initdb_comment);
11897 : }
11898 :
11899 914 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11900 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11901 0 : NULL, nspinfo->rolname,
11902 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11903 :
11904 914 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11905 710 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11906 : qnspname, NULL, NULL,
11907 710 : NULL, nspinfo->rolname, &nspinfo->dacl);
11908 :
11909 914 : free(qnspname);
11910 :
11911 914 : destroyPQExpBuffer(q);
11912 914 : destroyPQExpBuffer(delq);
11913 : }
11914 :
11915 : /*
11916 : * dumpExtension
11917 : * writes out to fout the queries to recreate an extension
11918 : */
11919 : static void
11920 48 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11921 : {
11922 48 : DumpOptions *dopt = fout->dopt;
11923 : PQExpBuffer q;
11924 : PQExpBuffer delq;
11925 : char *qextname;
11926 :
11927 : /* Do nothing if not dumping schema */
11928 48 : if (!dopt->dumpSchema)
11929 2 : return;
11930 :
11931 46 : q = createPQExpBuffer();
11932 46 : delq = createPQExpBuffer();
11933 :
11934 46 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11935 :
11936 46 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11937 :
11938 46 : if (!dopt->binary_upgrade)
11939 : {
11940 : /*
11941 : * In a regular dump, we simply create the extension, intentionally
11942 : * not specifying a version, so that the destination installation's
11943 : * default version is used.
11944 : *
11945 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11946 : * types; but there are various scenarios in which it's convenient to
11947 : * manually create the desired extension before restoring, so we
11948 : * prefer to allow it to exist already.
11949 : */
11950 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11951 34 : qextname, fmtId(extinfo->namespace));
11952 : }
11953 : else
11954 : {
11955 : /*
11956 : * In binary-upgrade mode, it's critical to reproduce the state of the
11957 : * database exactly, so our procedure is to create an empty extension,
11958 : * restore all the contained objects normally, and add them to the
11959 : * extension one by one. This function performs just the first of
11960 : * those steps. binary_upgrade_extension_member() takes care of
11961 : * adding member objects as they're created.
11962 : */
11963 : int i;
11964 : int n;
11965 :
11966 12 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11967 :
11968 : /*
11969 : * We unconditionally create the extension, so we must drop it if it
11970 : * exists. This could happen if the user deleted 'plpgsql' and then
11971 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11972 : */
11973 12 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11974 :
11975 12 : appendPQExpBufferStr(q,
11976 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11977 12 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11978 12 : appendPQExpBufferStr(q, ", ");
11979 12 : appendStringLiteralAH(q, extinfo->namespace, fout);
11980 12 : appendPQExpBufferStr(q, ", ");
11981 12 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11982 12 : appendStringLiteralAH(q, extinfo->extversion, fout);
11983 12 : appendPQExpBufferStr(q, ", ");
11984 :
11985 : /*
11986 : * Note that we're pushing extconfig (an OID array) back into
11987 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
11988 : * preserved in binary upgrade.
11989 : */
11990 12 : if (strlen(extinfo->extconfig) > 2)
11991 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
11992 : else
11993 10 : appendPQExpBufferStr(q, "NULL");
11994 12 : appendPQExpBufferStr(q, ", ");
11995 12 : if (strlen(extinfo->extcondition) > 2)
11996 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
11997 : else
11998 10 : appendPQExpBufferStr(q, "NULL");
11999 12 : appendPQExpBufferStr(q, ", ");
12000 12 : appendPQExpBufferStr(q, "ARRAY[");
12001 12 : n = 0;
12002 24 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12003 : {
12004 : DumpableObject *extobj;
12005 :
12006 12 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12007 12 : if (extobj && extobj->objType == DO_EXTENSION)
12008 : {
12009 0 : if (n++ > 0)
12010 0 : appendPQExpBufferChar(q, ',');
12011 0 : appendStringLiteralAH(q, extobj->name, fout);
12012 : }
12013 : }
12014 12 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12015 12 : appendPQExpBufferStr(q, ");\n");
12016 : }
12017 :
12018 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12019 46 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12020 46 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12021 : .description = "EXTENSION",
12022 : .section = SECTION_PRE_DATA,
12023 : .createStmt = q->data,
12024 : .dropStmt = delq->data));
12025 :
12026 : /* Dump Extension Comments */
12027 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12028 46 : dumpComment(fout, "EXTENSION", qextname,
12029 : NULL, "",
12030 46 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12031 :
12032 46 : free(qextname);
12033 :
12034 46 : destroyPQExpBuffer(q);
12035 46 : destroyPQExpBuffer(delq);
12036 : }
12037 :
12038 : /*
12039 : * dumpType
12040 : * writes out to fout the queries to recreate a user-defined type
12041 : */
12042 : static void
12043 1840 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12044 : {
12045 1840 : DumpOptions *dopt = fout->dopt;
12046 :
12047 : /* Do nothing if not dumping schema */
12048 1840 : if (!dopt->dumpSchema)
12049 98 : return;
12050 :
12051 : /* Dump out in proper style */
12052 1742 : if (tyinfo->typtype == TYPTYPE_BASE)
12053 560 : dumpBaseType(fout, tyinfo);
12054 1182 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12055 304 : dumpDomain(fout, tyinfo);
12056 878 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12057 260 : dumpCompositeType(fout, tyinfo);
12058 618 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12059 170 : dumpEnumType(fout, tyinfo);
12060 448 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12061 224 : dumpRangeType(fout, tyinfo);
12062 224 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12063 74 : dumpUndefinedType(fout, tyinfo);
12064 : else
12065 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12066 : tyinfo->dobj.name);
12067 : }
12068 :
12069 : /*
12070 : * dumpEnumType
12071 : * writes out to fout the queries to recreate a user-defined enum type
12072 : */
12073 : static void
12074 170 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12075 : {
12076 170 : DumpOptions *dopt = fout->dopt;
12077 170 : PQExpBuffer q = createPQExpBuffer();
12078 170 : PQExpBuffer delq = createPQExpBuffer();
12079 170 : PQExpBuffer query = createPQExpBuffer();
12080 : PGresult *res;
12081 : int num,
12082 : i;
12083 : Oid enum_oid;
12084 : char *qtypname;
12085 : char *qualtypname;
12086 : char *label;
12087 : int i_enumlabel;
12088 : int i_oid;
12089 :
12090 170 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12091 : {
12092 : /* Set up query for enum-specific details */
12093 80 : appendPQExpBufferStr(query,
12094 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12095 : "SELECT oid, enumlabel "
12096 : "FROM pg_catalog.pg_enum "
12097 : "WHERE enumtypid = $1 "
12098 : "ORDER BY enumsortorder");
12099 :
12100 80 : ExecuteSqlStatement(fout, query->data);
12101 :
12102 80 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12103 : }
12104 :
12105 170 : printfPQExpBuffer(query,
12106 : "EXECUTE dumpEnumType('%u')",
12107 170 : tyinfo->dobj.catId.oid);
12108 :
12109 170 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12110 :
12111 170 : num = PQntuples(res);
12112 :
12113 170 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12114 170 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12115 :
12116 : /*
12117 : * CASCADE shouldn't be required here as for normal types since the I/O
12118 : * functions are generic and do not get dropped.
12119 : */
12120 170 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12121 :
12122 170 : if (dopt->binary_upgrade)
12123 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12124 12 : tyinfo->dobj.catId.oid,
12125 : false, false);
12126 :
12127 170 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12128 : qualtypname);
12129 :
12130 170 : if (!dopt->binary_upgrade)
12131 : {
12132 158 : i_enumlabel = PQfnumber(res, "enumlabel");
12133 :
12134 : /* Labels with server-assigned oids */
12135 964 : for (i = 0; i < num; i++)
12136 : {
12137 806 : label = PQgetvalue(res, i, i_enumlabel);
12138 806 : if (i > 0)
12139 648 : appendPQExpBufferChar(q, ',');
12140 806 : appendPQExpBufferStr(q, "\n ");
12141 806 : appendStringLiteralAH(q, label, fout);
12142 : }
12143 : }
12144 :
12145 170 : appendPQExpBufferStr(q, "\n);\n");
12146 :
12147 170 : if (dopt->binary_upgrade)
12148 : {
12149 12 : i_oid = PQfnumber(res, "oid");
12150 12 : i_enumlabel = PQfnumber(res, "enumlabel");
12151 :
12152 : /* Labels with dump-assigned (preserved) oids */
12153 124 : for (i = 0; i < num; i++)
12154 : {
12155 112 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12156 112 : label = PQgetvalue(res, i, i_enumlabel);
12157 :
12158 112 : if (i == 0)
12159 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12160 112 : appendPQExpBuffer(q,
12161 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12162 : enum_oid);
12163 112 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12164 112 : appendStringLiteralAH(q, label, fout);
12165 112 : appendPQExpBufferStr(q, ";\n\n");
12166 : }
12167 : }
12168 :
12169 170 : if (dopt->binary_upgrade)
12170 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12171 : "TYPE", qtypname,
12172 12 : tyinfo->dobj.namespace->dobj.name);
12173 :
12174 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12175 170 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12176 170 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12177 : .namespace = tyinfo->dobj.namespace->dobj.name,
12178 : .owner = tyinfo->rolname,
12179 : .description = "TYPE",
12180 : .section = SECTION_PRE_DATA,
12181 : .createStmt = q->data,
12182 : .dropStmt = delq->data));
12183 :
12184 : /* Dump Type Comments and Security Labels */
12185 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12186 64 : dumpComment(fout, "TYPE", qtypname,
12187 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12188 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12189 :
12190 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12191 0 : dumpSecLabel(fout, "TYPE", qtypname,
12192 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12193 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12194 :
12195 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12196 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12197 : qtypname, NULL,
12198 64 : tyinfo->dobj.namespace->dobj.name,
12199 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12200 :
12201 170 : PQclear(res);
12202 170 : destroyPQExpBuffer(q);
12203 170 : destroyPQExpBuffer(delq);
12204 170 : destroyPQExpBuffer(query);
12205 170 : free(qtypname);
12206 170 : free(qualtypname);
12207 170 : }
12208 :
12209 : /*
12210 : * dumpRangeType
12211 : * writes out to fout the queries to recreate a user-defined range type
12212 : */
12213 : static void
12214 224 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12215 : {
12216 224 : DumpOptions *dopt = fout->dopt;
12217 224 : PQExpBuffer q = createPQExpBuffer();
12218 224 : PQExpBuffer delq = createPQExpBuffer();
12219 224 : PQExpBuffer query = createPQExpBuffer();
12220 : PGresult *res;
12221 : Oid collationOid;
12222 : char *qtypname;
12223 : char *qualtypname;
12224 : char *procname;
12225 :
12226 224 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12227 : {
12228 : /* Set up query for range-specific details */
12229 80 : appendPQExpBufferStr(query,
12230 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12231 :
12232 80 : appendPQExpBufferStr(query,
12233 : "SELECT ");
12234 :
12235 80 : if (fout->remoteVersion >= 140000)
12236 80 : appendPQExpBufferStr(query,
12237 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12238 : else
12239 0 : appendPQExpBufferStr(query,
12240 : "NULL AS rngmultitype, ");
12241 :
12242 80 : appendPQExpBufferStr(query,
12243 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12244 : "opc.opcname AS opcname, "
12245 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12246 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12247 : "opc.opcdefault, "
12248 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12249 : " ELSE rngcollation END AS collation, "
12250 : "rngcanonical, rngsubdiff "
12251 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12252 : " pg_catalog.pg_opclass opc "
12253 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12254 : "rngtypid = $1");
12255 :
12256 80 : ExecuteSqlStatement(fout, query->data);
12257 :
12258 80 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12259 : }
12260 :
12261 224 : printfPQExpBuffer(query,
12262 : "EXECUTE dumpRangeType('%u')",
12263 224 : tyinfo->dobj.catId.oid);
12264 :
12265 224 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12266 :
12267 224 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12268 224 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12269 :
12270 : /*
12271 : * CASCADE shouldn't be required here as for normal types since the I/O
12272 : * functions are generic and do not get dropped.
12273 : */
12274 224 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12275 :
12276 224 : if (dopt->binary_upgrade)
12277 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12278 16 : tyinfo->dobj.catId.oid,
12279 : false, true);
12280 :
12281 224 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12282 : qualtypname);
12283 :
12284 224 : appendPQExpBuffer(q, "\n subtype = %s",
12285 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12286 :
12287 224 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12288 224 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12289 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12290 :
12291 : /* print subtype_opclass only if not default for subtype */
12292 224 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12293 : {
12294 64 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12295 64 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12296 :
12297 64 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12298 : fmtId(nspname));
12299 64 : appendPQExpBufferStr(q, fmtId(opcname));
12300 : }
12301 :
12302 224 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12303 224 : if (OidIsValid(collationOid))
12304 : {
12305 74 : CollInfo *coll = findCollationByOid(collationOid);
12306 :
12307 74 : if (coll)
12308 74 : appendPQExpBuffer(q, ",\n collation = %s",
12309 74 : fmtQualifiedDumpable(coll));
12310 : }
12311 :
12312 224 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12313 224 : if (strcmp(procname, "-") != 0)
12314 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12315 :
12316 224 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12317 224 : if (strcmp(procname, "-") != 0)
12318 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12319 :
12320 224 : appendPQExpBufferStr(q, "\n);\n");
12321 :
12322 224 : if (dopt->binary_upgrade)
12323 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12324 : "TYPE", qtypname,
12325 16 : tyinfo->dobj.namespace->dobj.name);
12326 :
12327 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12328 224 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12329 224 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12330 : .namespace = tyinfo->dobj.namespace->dobj.name,
12331 : .owner = tyinfo->rolname,
12332 : .description = "TYPE",
12333 : .section = SECTION_PRE_DATA,
12334 : .createStmt = q->data,
12335 : .dropStmt = delq->data));
12336 :
12337 : /* Dump Type Comments and Security Labels */
12338 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12339 100 : dumpComment(fout, "TYPE", qtypname,
12340 100 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12341 100 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12342 :
12343 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12344 0 : dumpSecLabel(fout, "TYPE", qtypname,
12345 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12346 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12347 :
12348 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12349 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12350 : qtypname, NULL,
12351 64 : tyinfo->dobj.namespace->dobj.name,
12352 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12353 :
12354 224 : PQclear(res);
12355 224 : destroyPQExpBuffer(q);
12356 224 : destroyPQExpBuffer(delq);
12357 224 : destroyPQExpBuffer(query);
12358 224 : free(qtypname);
12359 224 : free(qualtypname);
12360 224 : }
12361 :
12362 : /*
12363 : * dumpUndefinedType
12364 : * writes out to fout the queries to recreate a !typisdefined type
12365 : *
12366 : * This is a shell type, but we use different terminology to distinguish
12367 : * this case from where we have to emit a shell type definition to break
12368 : * circular dependencies. An undefined type shouldn't ever have anything
12369 : * depending on it.
12370 : */
12371 : static void
12372 74 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12373 : {
12374 74 : DumpOptions *dopt = fout->dopt;
12375 74 : PQExpBuffer q = createPQExpBuffer();
12376 74 : PQExpBuffer delq = createPQExpBuffer();
12377 : char *qtypname;
12378 : char *qualtypname;
12379 :
12380 74 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12381 74 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12382 :
12383 74 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12384 :
12385 74 : if (dopt->binary_upgrade)
12386 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12387 4 : tyinfo->dobj.catId.oid,
12388 : false, false);
12389 :
12390 74 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12391 : qualtypname);
12392 :
12393 74 : if (dopt->binary_upgrade)
12394 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12395 : "TYPE", qtypname,
12396 4 : tyinfo->dobj.namespace->dobj.name);
12397 :
12398 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12399 74 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12400 74 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12401 : .namespace = tyinfo->dobj.namespace->dobj.name,
12402 : .owner = tyinfo->rolname,
12403 : .description = "TYPE",
12404 : .section = SECTION_PRE_DATA,
12405 : .createStmt = q->data,
12406 : .dropStmt = delq->data));
12407 :
12408 : /* Dump Type Comments and Security Labels */
12409 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12410 64 : dumpComment(fout, "TYPE", qtypname,
12411 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12412 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12413 :
12414 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12415 0 : dumpSecLabel(fout, "TYPE", qtypname,
12416 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12417 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12418 :
12419 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12420 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12421 : qtypname, NULL,
12422 0 : tyinfo->dobj.namespace->dobj.name,
12423 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12424 :
12425 74 : destroyPQExpBuffer(q);
12426 74 : destroyPQExpBuffer(delq);
12427 74 : free(qtypname);
12428 74 : free(qualtypname);
12429 74 : }
12430 :
12431 : /*
12432 : * dumpBaseType
12433 : * writes out to fout the queries to recreate a user-defined base type
12434 : */
12435 : static void
12436 560 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12437 : {
12438 560 : DumpOptions *dopt = fout->dopt;
12439 560 : PQExpBuffer q = createPQExpBuffer();
12440 560 : PQExpBuffer delq = createPQExpBuffer();
12441 560 : PQExpBuffer query = createPQExpBuffer();
12442 : PGresult *res;
12443 : char *qtypname;
12444 : char *qualtypname;
12445 : char *typlen;
12446 : char *typinput;
12447 : char *typoutput;
12448 : char *typreceive;
12449 : char *typsend;
12450 : char *typmodin;
12451 : char *typmodout;
12452 : char *typanalyze;
12453 : char *typsubscript;
12454 : Oid typreceiveoid;
12455 : Oid typsendoid;
12456 : Oid typmodinoid;
12457 : Oid typmodoutoid;
12458 : Oid typanalyzeoid;
12459 : Oid typsubscriptoid;
12460 : char *typcategory;
12461 : char *typispreferred;
12462 : char *typdelim;
12463 : char *typbyval;
12464 : char *typalign;
12465 : char *typstorage;
12466 : char *typcollatable;
12467 : char *typdefault;
12468 560 : bool typdefault_is_literal = false;
12469 :
12470 560 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12471 : {
12472 : /* Set up query for type-specific details */
12473 80 : appendPQExpBufferStr(query,
12474 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12475 : "SELECT typlen, "
12476 : "typinput, typoutput, typreceive, typsend, "
12477 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12478 : "typsend::pg_catalog.oid AS typsendoid, "
12479 : "typanalyze, "
12480 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12481 : "typdelim, typbyval, typalign, typstorage, "
12482 : "typmodin, typmodout, "
12483 : "typmodin::pg_catalog.oid AS typmodinoid, "
12484 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12485 : "typcategory, typispreferred, "
12486 : "(typcollation <> 0) AS typcollatable, "
12487 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12488 :
12489 80 : if (fout->remoteVersion >= 140000)
12490 80 : appendPQExpBufferStr(query,
12491 : "typsubscript, "
12492 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12493 : else
12494 0 : appendPQExpBufferStr(query,
12495 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12496 :
12497 80 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12498 : "WHERE oid = $1");
12499 :
12500 80 : ExecuteSqlStatement(fout, query->data);
12501 :
12502 80 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12503 : }
12504 :
12505 560 : printfPQExpBuffer(query,
12506 : "EXECUTE dumpBaseType('%u')",
12507 560 : tyinfo->dobj.catId.oid);
12508 :
12509 560 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12510 :
12511 560 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12512 560 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12513 560 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12514 560 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12515 560 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12516 560 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12517 560 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12518 560 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12519 560 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12520 560 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12521 560 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12522 560 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12523 560 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12524 560 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12525 560 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12526 560 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12527 560 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12528 560 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12529 560 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12530 560 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12531 560 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12532 560 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12533 560 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12534 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12535 560 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12536 : {
12537 84 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12538 84 : typdefault_is_literal = true; /* it needs quotes */
12539 : }
12540 : else
12541 476 : typdefault = NULL;
12542 :
12543 560 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12544 560 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12545 :
12546 : /*
12547 : * The reason we include CASCADE is that the circular dependency between
12548 : * the type and its I/O functions makes it impossible to drop the type any
12549 : * other way.
12550 : */
12551 560 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12552 :
12553 : /*
12554 : * We might already have a shell type, but setting pg_type_oid is
12555 : * harmless, and in any case we'd better set the array type OID.
12556 : */
12557 560 : if (dopt->binary_upgrade)
12558 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12559 16 : tyinfo->dobj.catId.oid,
12560 : false, false);
12561 :
12562 560 : appendPQExpBuffer(q,
12563 : "CREATE TYPE %s (\n"
12564 : " INTERNALLENGTH = %s",
12565 : qualtypname,
12566 560 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12567 :
12568 : /* regproc result is sufficiently quoted already */
12569 560 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12570 560 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12571 560 : if (OidIsValid(typreceiveoid))
12572 414 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12573 560 : if (OidIsValid(typsendoid))
12574 414 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12575 560 : if (OidIsValid(typmodinoid))
12576 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12577 560 : if (OidIsValid(typmodoutoid))
12578 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12579 560 : if (OidIsValid(typanalyzeoid))
12580 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12581 :
12582 560 : if (strcmp(typcollatable, "t") == 0)
12583 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12584 :
12585 560 : if (typdefault != NULL)
12586 : {
12587 84 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12588 84 : if (typdefault_is_literal)
12589 84 : appendStringLiteralAH(q, typdefault, fout);
12590 : else
12591 0 : appendPQExpBufferStr(q, typdefault);
12592 : }
12593 :
12594 560 : if (OidIsValid(typsubscriptoid))
12595 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12596 :
12597 560 : if (OidIsValid(tyinfo->typelem))
12598 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12599 52 : getFormattedTypeName(fout, tyinfo->typelem,
12600 : zeroIsError));
12601 :
12602 560 : if (strcmp(typcategory, "U") != 0)
12603 : {
12604 316 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12605 316 : appendStringLiteralAH(q, typcategory, fout);
12606 : }
12607 :
12608 560 : if (strcmp(typispreferred, "t") == 0)
12609 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12610 :
12611 560 : if (typdelim && strcmp(typdelim, ",") != 0)
12612 : {
12613 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12614 6 : appendStringLiteralAH(q, typdelim, fout);
12615 : }
12616 :
12617 560 : if (*typalign == TYPALIGN_CHAR)
12618 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12619 536 : else if (*typalign == TYPALIGN_SHORT)
12620 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12621 524 : else if (*typalign == TYPALIGN_INT)
12622 374 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12623 150 : else if (*typalign == TYPALIGN_DOUBLE)
12624 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12625 :
12626 560 : if (*typstorage == TYPSTORAGE_PLAIN)
12627 410 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12628 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12629 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12630 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12631 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12632 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12633 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12634 :
12635 560 : if (strcmp(typbyval, "t") == 0)
12636 268 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12637 :
12638 560 : appendPQExpBufferStr(q, "\n);\n");
12639 :
12640 560 : if (dopt->binary_upgrade)
12641 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12642 : "TYPE", qtypname,
12643 16 : tyinfo->dobj.namespace->dobj.name);
12644 :
12645 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12646 560 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12647 560 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12648 : .namespace = tyinfo->dobj.namespace->dobj.name,
12649 : .owner = tyinfo->rolname,
12650 : .description = "TYPE",
12651 : .section = SECTION_PRE_DATA,
12652 : .createStmt = q->data,
12653 : .dropStmt = delq->data));
12654 :
12655 : /* Dump Type Comments and Security Labels */
12656 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12657 490 : dumpComment(fout, "TYPE", qtypname,
12658 490 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12659 490 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12660 :
12661 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12662 0 : dumpSecLabel(fout, "TYPE", qtypname,
12663 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12664 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12665 :
12666 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12667 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12668 : qtypname, NULL,
12669 64 : tyinfo->dobj.namespace->dobj.name,
12670 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12671 :
12672 560 : PQclear(res);
12673 560 : destroyPQExpBuffer(q);
12674 560 : destroyPQExpBuffer(delq);
12675 560 : destroyPQExpBuffer(query);
12676 560 : free(qtypname);
12677 560 : free(qualtypname);
12678 560 : }
12679 :
12680 : /*
12681 : * dumpDomain
12682 : * writes out to fout the queries to recreate a user-defined domain
12683 : */
12684 : static void
12685 304 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12686 : {
12687 304 : DumpOptions *dopt = fout->dopt;
12688 304 : PQExpBuffer q = createPQExpBuffer();
12689 304 : PQExpBuffer delq = createPQExpBuffer();
12690 304 : PQExpBuffer query = createPQExpBuffer();
12691 : PGresult *res;
12692 : int i;
12693 : char *qtypname;
12694 : char *qualtypname;
12695 : char *typnotnull;
12696 : char *typdefn;
12697 : char *typdefault;
12698 : Oid typcollation;
12699 304 : bool typdefault_is_literal = false;
12700 :
12701 304 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12702 : {
12703 : /* Set up query for domain-specific details */
12704 74 : appendPQExpBufferStr(query,
12705 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12706 :
12707 74 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12708 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12709 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12710 : "t.typdefault, "
12711 : "CASE WHEN t.typcollation <> u.typcollation "
12712 : "THEN t.typcollation ELSE 0 END AS typcollation "
12713 : "FROM pg_catalog.pg_type t "
12714 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12715 : "WHERE t.oid = $1");
12716 :
12717 74 : ExecuteSqlStatement(fout, query->data);
12718 :
12719 74 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12720 : }
12721 :
12722 304 : printfPQExpBuffer(query,
12723 : "EXECUTE dumpDomain('%u')",
12724 304 : tyinfo->dobj.catId.oid);
12725 :
12726 304 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12727 :
12728 304 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12729 304 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12730 304 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12731 74 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12732 230 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12733 : {
12734 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12735 0 : typdefault_is_literal = true; /* it needs quotes */
12736 : }
12737 : else
12738 230 : typdefault = NULL;
12739 304 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12740 :
12741 304 : if (dopt->binary_upgrade)
12742 50 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12743 50 : tyinfo->dobj.catId.oid,
12744 : true, /* force array type */
12745 : false); /* force multirange type */
12746 :
12747 304 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12748 304 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12749 :
12750 304 : appendPQExpBuffer(q,
12751 : "CREATE DOMAIN %s AS %s",
12752 : qualtypname,
12753 : typdefn);
12754 :
12755 : /* Print collation only if different from base type's collation */
12756 304 : if (OidIsValid(typcollation))
12757 : {
12758 : CollInfo *coll;
12759 :
12760 64 : coll = findCollationByOid(typcollation);
12761 64 : if (coll)
12762 64 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12763 : }
12764 :
12765 : /*
12766 : * Print a not-null constraint if there's one. In servers older than 17
12767 : * these don't have names, so just print it unadorned; in newer ones they
12768 : * do, but most of the time it's going to be the standard generated one,
12769 : * so omit the name in that case also.
12770 : */
12771 304 : if (typnotnull[0] == 't')
12772 : {
12773 94 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12774 0 : appendPQExpBufferStr(q, " NOT NULL");
12775 : else
12776 : {
12777 94 : ConstraintInfo *notnull = tyinfo->notnull;
12778 :
12779 94 : if (!notnull->separate)
12780 : {
12781 : char *default_name;
12782 :
12783 : /* XXX should match ChooseConstraintName better */
12784 94 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12785 :
12786 94 : if (strcmp(default_name, notnull->dobj.name) == 0)
12787 30 : appendPQExpBufferStr(q, " NOT NULL");
12788 : else
12789 64 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12790 64 : fmtId(notnull->dobj.name), notnull->condef);
12791 94 : free(default_name);
12792 : }
12793 : }
12794 : }
12795 :
12796 304 : if (typdefault != NULL)
12797 : {
12798 74 : appendPQExpBufferStr(q, " DEFAULT ");
12799 74 : if (typdefault_is_literal)
12800 0 : appendStringLiteralAH(q, typdefault, fout);
12801 : else
12802 74 : appendPQExpBufferStr(q, typdefault);
12803 : }
12804 :
12805 304 : PQclear(res);
12806 :
12807 : /*
12808 : * Add any CHECK constraints for the domain
12809 : */
12810 518 : for (i = 0; i < tyinfo->nDomChecks; i++)
12811 : {
12812 214 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12813 :
12814 214 : if (!domcheck->separate && domcheck->contype == 'c')
12815 204 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12816 204 : fmtId(domcheck->dobj.name), domcheck->condef);
12817 : }
12818 :
12819 304 : appendPQExpBufferStr(q, ";\n");
12820 :
12821 304 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12822 :
12823 304 : if (dopt->binary_upgrade)
12824 50 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12825 : "DOMAIN", qtypname,
12826 50 : tyinfo->dobj.namespace->dobj.name);
12827 :
12828 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12829 304 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12830 304 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12831 : .namespace = tyinfo->dobj.namespace->dobj.name,
12832 : .owner = tyinfo->rolname,
12833 : .description = "DOMAIN",
12834 : .section = SECTION_PRE_DATA,
12835 : .createStmt = q->data,
12836 : .dropStmt = delq->data));
12837 :
12838 : /* Dump Domain Comments and Security Labels */
12839 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12840 0 : dumpComment(fout, "DOMAIN", qtypname,
12841 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12842 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12843 :
12844 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12845 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12846 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12847 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12848 :
12849 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12850 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12851 : qtypname, NULL,
12852 64 : tyinfo->dobj.namespace->dobj.name,
12853 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12854 :
12855 : /* Dump any per-constraint comments */
12856 518 : for (i = 0; i < tyinfo->nDomChecks; i++)
12857 : {
12858 214 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12859 : PQExpBuffer conprefix;
12860 :
12861 : /* but only if the constraint itself was dumped here */
12862 214 : if (domcheck->separate)
12863 10 : continue;
12864 :
12865 204 : conprefix = createPQExpBuffer();
12866 204 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12867 204 : fmtId(domcheck->dobj.name));
12868 :
12869 204 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12870 64 : dumpComment(fout, conprefix->data, qtypname,
12871 64 : tyinfo->dobj.namespace->dobj.name,
12872 64 : tyinfo->rolname,
12873 64 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12874 :
12875 204 : destroyPQExpBuffer(conprefix);
12876 : }
12877 :
12878 : /*
12879 : * And a comment on the not-null constraint, if there's one -- but only if
12880 : * the constraint itself was dumped here
12881 : */
12882 304 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12883 : {
12884 94 : PQExpBuffer conprefix = createPQExpBuffer();
12885 :
12886 94 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12887 94 : fmtId(tyinfo->notnull->dobj.name));
12888 :
12889 94 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12890 64 : dumpComment(fout, conprefix->data, qtypname,
12891 64 : tyinfo->dobj.namespace->dobj.name,
12892 64 : tyinfo->rolname,
12893 64 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12894 94 : destroyPQExpBuffer(conprefix);
12895 : }
12896 :
12897 304 : destroyPQExpBuffer(q);
12898 304 : destroyPQExpBuffer(delq);
12899 304 : destroyPQExpBuffer(query);
12900 304 : free(qtypname);
12901 304 : free(qualtypname);
12902 304 : }
12903 :
12904 : /*
12905 : * dumpCompositeType
12906 : * writes out to fout the queries to recreate a user-defined stand-alone
12907 : * composite type
12908 : */
12909 : static void
12910 260 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12911 : {
12912 260 : DumpOptions *dopt = fout->dopt;
12913 260 : PQExpBuffer q = createPQExpBuffer();
12914 260 : PQExpBuffer dropped = createPQExpBuffer();
12915 260 : PQExpBuffer delq = createPQExpBuffer();
12916 260 : PQExpBuffer query = createPQExpBuffer();
12917 : PGresult *res;
12918 : char *qtypname;
12919 : char *qualtypname;
12920 : int ntups;
12921 : int i_attname;
12922 : int i_atttypdefn;
12923 : int i_attlen;
12924 : int i_attalign;
12925 : int i_attisdropped;
12926 : int i_attcollation;
12927 : int i;
12928 : int actual_atts;
12929 :
12930 260 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12931 : {
12932 : /*
12933 : * Set up query for type-specific details.
12934 : *
12935 : * Since we only want to dump COLLATE clauses for attributes whose
12936 : * collation is different from their type's default, we use a CASE
12937 : * here to suppress uninteresting attcollations cheaply. atttypid
12938 : * will be 0 for dropped columns; collation does not matter for those.
12939 : */
12940 110 : appendPQExpBufferStr(query,
12941 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12942 : "SELECT a.attname, a.attnum, "
12943 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12944 : "a.attlen, a.attalign, a.attisdropped, "
12945 : "CASE WHEN a.attcollation <> at.typcollation "
12946 : "THEN a.attcollation ELSE 0 END AS attcollation "
12947 : "FROM pg_catalog.pg_type ct "
12948 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12949 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12950 : "WHERE ct.oid = $1 "
12951 : "ORDER BY a.attnum");
12952 :
12953 110 : ExecuteSqlStatement(fout, query->data);
12954 :
12955 110 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12956 : }
12957 :
12958 260 : printfPQExpBuffer(query,
12959 : "EXECUTE dumpCompositeType('%u')",
12960 260 : tyinfo->dobj.catId.oid);
12961 :
12962 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12963 :
12964 260 : ntups = PQntuples(res);
12965 :
12966 260 : i_attname = PQfnumber(res, "attname");
12967 260 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12968 260 : i_attlen = PQfnumber(res, "attlen");
12969 260 : i_attalign = PQfnumber(res, "attalign");
12970 260 : i_attisdropped = PQfnumber(res, "attisdropped");
12971 260 : i_attcollation = PQfnumber(res, "attcollation");
12972 :
12973 260 : if (dopt->binary_upgrade)
12974 : {
12975 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12976 36 : tyinfo->dobj.catId.oid,
12977 : false, false);
12978 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12979 : }
12980 :
12981 260 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12982 260 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12983 :
12984 260 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12985 : qualtypname);
12986 :
12987 260 : actual_atts = 0;
12988 824 : for (i = 0; i < ntups; i++)
12989 : {
12990 : char *attname;
12991 : char *atttypdefn;
12992 : char *attlen;
12993 : char *attalign;
12994 : bool attisdropped;
12995 : Oid attcollation;
12996 :
12997 564 : attname = PQgetvalue(res, i, i_attname);
12998 564 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
12999 564 : attlen = PQgetvalue(res, i, i_attlen);
13000 564 : attalign = PQgetvalue(res, i, i_attalign);
13001 564 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13002 564 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13003 :
13004 564 : if (attisdropped && !dopt->binary_upgrade)
13005 16 : continue;
13006 :
13007 : /* Format properly if not first attr */
13008 548 : if (actual_atts++ > 0)
13009 288 : appendPQExpBufferChar(q, ',');
13010 548 : appendPQExpBufferStr(q, "\n\t");
13011 :
13012 548 : if (!attisdropped)
13013 : {
13014 544 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13015 :
13016 : /* Add collation if not default for the column type */
13017 544 : if (OidIsValid(attcollation))
13018 : {
13019 : CollInfo *coll;
13020 :
13021 0 : coll = findCollationByOid(attcollation);
13022 0 : if (coll)
13023 0 : appendPQExpBuffer(q, " COLLATE %s",
13024 0 : fmtQualifiedDumpable(coll));
13025 : }
13026 : }
13027 : else
13028 : {
13029 : /*
13030 : * This is a dropped attribute and we're in binary_upgrade mode.
13031 : * Insert a placeholder for it in the CREATE TYPE command, and set
13032 : * length and alignment with direct UPDATE to the catalogs
13033 : * afterwards. See similar code in dumpTableSchema().
13034 : */
13035 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13036 :
13037 : /* stash separately for insertion after the CREATE TYPE */
13038 4 : appendPQExpBufferStr(dropped,
13039 : "\n-- For binary upgrade, recreate dropped column.\n");
13040 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13041 : "SET attlen = %s, "
13042 : "attalign = '%s', attbyval = false\n"
13043 : "WHERE attname = ", attlen, attalign);
13044 4 : appendStringLiteralAH(dropped, attname, fout);
13045 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13046 4 : appendStringLiteralAH(dropped, qualtypname, fout);
13047 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13048 :
13049 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13050 : qualtypname);
13051 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13052 : fmtId(attname));
13053 : }
13054 : }
13055 260 : appendPQExpBufferStr(q, "\n);\n");
13056 260 : appendPQExpBufferStr(q, dropped->data);
13057 :
13058 260 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13059 :
13060 260 : if (dopt->binary_upgrade)
13061 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13062 : "TYPE", qtypname,
13063 36 : tyinfo->dobj.namespace->dobj.name);
13064 :
13065 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13066 226 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13067 226 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13068 : .namespace = tyinfo->dobj.namespace->dobj.name,
13069 : .owner = tyinfo->rolname,
13070 : .description = "TYPE",
13071 : .section = SECTION_PRE_DATA,
13072 : .createStmt = q->data,
13073 : .dropStmt = delq->data));
13074 :
13075 :
13076 : /* Dump Type Comments and Security Labels */
13077 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13078 64 : dumpComment(fout, "TYPE", qtypname,
13079 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13080 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13081 :
13082 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13083 0 : dumpSecLabel(fout, "TYPE", qtypname,
13084 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13085 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13086 :
13087 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13088 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13089 : qtypname, NULL,
13090 36 : tyinfo->dobj.namespace->dobj.name,
13091 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
13092 :
13093 : /* Dump any per-column comments */
13094 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13095 64 : dumpCompositeTypeColComments(fout, tyinfo, res);
13096 :
13097 260 : PQclear(res);
13098 260 : destroyPQExpBuffer(q);
13099 260 : destroyPQExpBuffer(dropped);
13100 260 : destroyPQExpBuffer(delq);
13101 260 : destroyPQExpBuffer(query);
13102 260 : free(qtypname);
13103 260 : free(qualtypname);
13104 260 : }
13105 :
13106 : /*
13107 : * dumpCompositeTypeColComments
13108 : * writes out to fout the queries to recreate comments on the columns of
13109 : * a user-defined stand-alone composite type.
13110 : *
13111 : * The caller has already made a query to collect the names and attnums
13112 : * of the type's columns, so we just pass that result into here rather
13113 : * than reading them again.
13114 : */
13115 : static void
13116 64 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13117 : PGresult *res)
13118 : {
13119 : CommentItem *comments;
13120 : int ncomments;
13121 : PQExpBuffer query;
13122 : PQExpBuffer target;
13123 : int i;
13124 : int ntups;
13125 : int i_attname;
13126 : int i_attnum;
13127 : int i_attisdropped;
13128 :
13129 : /* do nothing, if --no-comments is supplied */
13130 64 : if (fout->dopt->no_comments)
13131 0 : return;
13132 :
13133 : /* Search for comments associated with type's pg_class OID */
13134 64 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13135 : &comments);
13136 :
13137 : /* If no comments exist, we're done */
13138 64 : if (ncomments <= 0)
13139 0 : return;
13140 :
13141 : /* Build COMMENT ON statements */
13142 64 : query = createPQExpBuffer();
13143 64 : target = createPQExpBuffer();
13144 :
13145 64 : ntups = PQntuples(res);
13146 64 : i_attnum = PQfnumber(res, "attnum");
13147 64 : i_attname = PQfnumber(res, "attname");
13148 64 : i_attisdropped = PQfnumber(res, "attisdropped");
13149 128 : while (ncomments > 0)
13150 : {
13151 : const char *attname;
13152 :
13153 64 : attname = NULL;
13154 64 : for (i = 0; i < ntups; i++)
13155 : {
13156 64 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13157 64 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13158 : {
13159 64 : attname = PQgetvalue(res, i, i_attname);
13160 64 : break;
13161 : }
13162 : }
13163 64 : if (attname) /* just in case we don't find it */
13164 : {
13165 64 : const char *descr = comments->descr;
13166 :
13167 64 : resetPQExpBuffer(target);
13168 64 : appendPQExpBuffer(target, "COLUMN %s.",
13169 64 : fmtId(tyinfo->dobj.name));
13170 64 : appendPQExpBufferStr(target, fmtId(attname));
13171 :
13172 64 : resetPQExpBuffer(query);
13173 64 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13174 64 : fmtQualifiedDumpable(tyinfo));
13175 64 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13176 64 : appendStringLiteralAH(query, descr, fout);
13177 64 : appendPQExpBufferStr(query, ";\n");
13178 :
13179 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13180 64 : ARCHIVE_OPTS(.tag = target->data,
13181 : .namespace = tyinfo->dobj.namespace->dobj.name,
13182 : .owner = tyinfo->rolname,
13183 : .description = "COMMENT",
13184 : .section = SECTION_NONE,
13185 : .createStmt = query->data,
13186 : .deps = &(tyinfo->dobj.dumpId),
13187 : .nDeps = 1));
13188 : }
13189 :
13190 64 : comments++;
13191 64 : ncomments--;
13192 : }
13193 :
13194 64 : destroyPQExpBuffer(query);
13195 64 : destroyPQExpBuffer(target);
13196 : }
13197 :
13198 : /*
13199 : * dumpShellType
13200 : * writes out to fout the queries to create a shell type
13201 : *
13202 : * We dump a shell definition in advance of the I/O functions for the type.
13203 : */
13204 : static void
13205 146 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13206 : {
13207 146 : DumpOptions *dopt = fout->dopt;
13208 : PQExpBuffer q;
13209 :
13210 : /* Do nothing if not dumping schema */
13211 146 : if (!dopt->dumpSchema)
13212 12 : return;
13213 :
13214 134 : q = createPQExpBuffer();
13215 :
13216 : /*
13217 : * Note the lack of a DROP command for the shell type; any required DROP
13218 : * is driven off the base type entry, instead. This interacts with
13219 : * _printTocEntry()'s use of the presence of a DROP command to decide
13220 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13221 : * the shell type's owner immediately on creation; that should happen only
13222 : * after it's filled in, otherwise the backend complains.
13223 : */
13224 :
13225 134 : if (dopt->binary_upgrade)
13226 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13227 16 : stinfo->baseType->dobj.catId.oid,
13228 : false, false);
13229 :
13230 134 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13231 134 : fmtQualifiedDumpable(stinfo));
13232 :
13233 134 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13234 134 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13235 134 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13236 : .namespace = stinfo->dobj.namespace->dobj.name,
13237 : .owner = stinfo->baseType->rolname,
13238 : .description = "SHELL TYPE",
13239 : .section = SECTION_PRE_DATA,
13240 : .createStmt = q->data));
13241 :
13242 134 : destroyPQExpBuffer(q);
13243 : }
13244 :
13245 : /*
13246 : * dumpProcLang
13247 : * writes out to fout the queries to recreate a user-defined
13248 : * procedural language
13249 : */
13250 : static void
13251 164 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13252 : {
13253 164 : DumpOptions *dopt = fout->dopt;
13254 : PQExpBuffer defqry;
13255 : PQExpBuffer delqry;
13256 : bool useParams;
13257 : char *qlanname;
13258 : FuncInfo *funcInfo;
13259 164 : FuncInfo *inlineInfo = NULL;
13260 164 : FuncInfo *validatorInfo = NULL;
13261 :
13262 : /* Do nothing if not dumping schema */
13263 164 : if (!dopt->dumpSchema)
13264 26 : return;
13265 :
13266 : /*
13267 : * Try to find the support function(s). It is not an error if we don't
13268 : * find them --- if the functions are in the pg_catalog schema, as is
13269 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13270 : * we will emit a parameterless CREATE LANGUAGE command, which will
13271 : * require PL template knowledge in the backend to reload.)
13272 : */
13273 :
13274 138 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13275 138 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13276 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
13277 :
13278 138 : if (OidIsValid(plang->laninline))
13279 : {
13280 76 : inlineInfo = findFuncByOid(plang->laninline);
13281 76 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13282 2 : inlineInfo = NULL;
13283 : }
13284 :
13285 138 : if (OidIsValid(plang->lanvalidator))
13286 : {
13287 76 : validatorInfo = findFuncByOid(plang->lanvalidator);
13288 76 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13289 2 : validatorInfo = NULL;
13290 : }
13291 :
13292 : /*
13293 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13294 : * parameters. Otherwise, we'll write a parameterless command, which will
13295 : * be interpreted as CREATE EXTENSION.
13296 : */
13297 60 : useParams = (funcInfo != NULL &&
13298 258 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13299 60 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13300 :
13301 138 : defqry = createPQExpBuffer();
13302 138 : delqry = createPQExpBuffer();
13303 :
13304 138 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13305 :
13306 138 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13307 : qlanname);
13308 :
13309 138 : if (useParams)
13310 : {
13311 60 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13312 60 : plang->lanpltrusted ? "TRUSTED " : "",
13313 : qlanname);
13314 60 : appendPQExpBuffer(defqry, " HANDLER %s",
13315 60 : fmtQualifiedDumpable(funcInfo));
13316 60 : if (OidIsValid(plang->laninline))
13317 0 : appendPQExpBuffer(defqry, " INLINE %s",
13318 0 : fmtQualifiedDumpable(inlineInfo));
13319 60 : if (OidIsValid(plang->lanvalidator))
13320 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13321 0 : fmtQualifiedDumpable(validatorInfo));
13322 : }
13323 : else
13324 : {
13325 : /*
13326 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13327 : * command will not fail if the language is preinstalled in the target
13328 : * database.
13329 : *
13330 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13331 : * EXISTS; perhaps we should emit that instead? But it might just add
13332 : * confusion.
13333 : */
13334 78 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13335 : qlanname);
13336 : }
13337 138 : appendPQExpBufferStr(defqry, ";\n");
13338 :
13339 138 : if (dopt->binary_upgrade)
13340 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13341 : "LANGUAGE", qlanname, NULL);
13342 :
13343 138 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13344 62 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13345 62 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13346 : .owner = plang->lanowner,
13347 : .description = "PROCEDURAL LANGUAGE",
13348 : .section = SECTION_PRE_DATA,
13349 : .createStmt = defqry->data,
13350 : .dropStmt = delqry->data,
13351 : ));
13352 :
13353 : /* Dump Proc Lang Comments and Security Labels */
13354 138 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13355 0 : dumpComment(fout, "LANGUAGE", qlanname,
13356 0 : NULL, plang->lanowner,
13357 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13358 :
13359 138 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13360 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13361 0 : NULL, plang->lanowner,
13362 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13363 :
13364 138 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13365 76 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13366 : qlanname, NULL, NULL,
13367 76 : NULL, plang->lanowner, &plang->dacl);
13368 :
13369 138 : free(qlanname);
13370 :
13371 138 : destroyPQExpBuffer(defqry);
13372 138 : destroyPQExpBuffer(delqry);
13373 : }
13374 :
13375 : /*
13376 : * format_function_arguments: generate function name and argument list
13377 : *
13378 : * This is used when we can rely on pg_get_function_arguments to format
13379 : * the argument list. Note, however, that pg_get_function_arguments
13380 : * does not special-case zero-argument aggregates.
13381 : */
13382 : static char *
13383 8164 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13384 : {
13385 : PQExpBufferData fn;
13386 :
13387 8164 : initPQExpBuffer(&fn);
13388 8164 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13389 8164 : if (is_agg && finfo->nargs == 0)
13390 160 : appendPQExpBufferStr(&fn, "(*)");
13391 : else
13392 8004 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13393 8164 : return fn.data;
13394 : }
13395 :
13396 : /*
13397 : * format_function_signature: generate function name and argument list
13398 : *
13399 : * Only a minimal list of input argument types is generated; this is
13400 : * sufficient to reference the function, but not to define it.
13401 : *
13402 : * If honor_quotes is false then the function name is never quoted.
13403 : * This is appropriate for use in TOC tags, but not in SQL commands.
13404 : */
13405 : static char *
13406 4298 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13407 : {
13408 : PQExpBufferData fn;
13409 : int j;
13410 :
13411 4298 : initPQExpBuffer(&fn);
13412 4298 : if (honor_quotes)
13413 786 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13414 : else
13415 3512 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13416 7890 : for (j = 0; j < finfo->nargs; j++)
13417 : {
13418 3592 : if (j > 0)
13419 844 : appendPQExpBufferStr(&fn, ", ");
13420 :
13421 3592 : appendPQExpBufferStr(&fn,
13422 3592 : getFormattedTypeName(fout, finfo->argtypes[j],
13423 : zeroIsError));
13424 : }
13425 4298 : appendPQExpBufferChar(&fn, ')');
13426 4298 : return fn.data;
13427 : }
13428 :
13429 :
13430 : /*
13431 : * dumpFunc:
13432 : * dump out one function
13433 : */
13434 : static void
13435 3636 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13436 : {
13437 3636 : DumpOptions *dopt = fout->dopt;
13438 : PQExpBuffer query;
13439 : PQExpBuffer q;
13440 : PQExpBuffer delqry;
13441 : PQExpBuffer asPart;
13442 : PGresult *res;
13443 : char *funcsig; /* identity signature */
13444 3636 : char *funcfullsig = NULL; /* full signature */
13445 : char *funcsig_tag;
13446 : char *qual_funcsig;
13447 : char *proretset;
13448 : char *prosrc;
13449 : char *probin;
13450 : char *prosqlbody;
13451 : char *funcargs;
13452 : char *funciargs;
13453 : char *funcresult;
13454 : char *protrftypes;
13455 : char *prokind;
13456 : char *provolatile;
13457 : char *proisstrict;
13458 : char *prosecdef;
13459 : char *proleakproof;
13460 : char *proconfig;
13461 : char *procost;
13462 : char *prorows;
13463 : char *prosupport;
13464 : char *proparallel;
13465 : char *lanname;
13466 3636 : char **configitems = NULL;
13467 3636 : int nconfigitems = 0;
13468 : const char *keyword;
13469 :
13470 : /* Do nothing if not dumping schema */
13471 3636 : if (!dopt->dumpSchema)
13472 124 : return;
13473 :
13474 3512 : query = createPQExpBuffer();
13475 3512 : q = createPQExpBuffer();
13476 3512 : delqry = createPQExpBuffer();
13477 3512 : asPart = createPQExpBuffer();
13478 :
13479 3512 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13480 : {
13481 : /* Set up query for function-specific details */
13482 132 : appendPQExpBufferStr(query,
13483 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13484 :
13485 132 : appendPQExpBufferStr(query,
13486 : "SELECT\n"
13487 : "proretset,\n"
13488 : "prosrc,\n"
13489 : "probin,\n"
13490 : "provolatile,\n"
13491 : "proisstrict,\n"
13492 : "prosecdef,\n"
13493 : "lanname,\n"
13494 : "proconfig,\n"
13495 : "procost,\n"
13496 : "prorows,\n"
13497 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13498 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13499 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13500 : "proleakproof,\n");
13501 :
13502 132 : if (fout->remoteVersion >= 90500)
13503 132 : appendPQExpBufferStr(query,
13504 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13505 : else
13506 0 : appendPQExpBufferStr(query,
13507 : "NULL AS protrftypes,\n");
13508 :
13509 132 : if (fout->remoteVersion >= 90600)
13510 132 : appendPQExpBufferStr(query,
13511 : "proparallel,\n");
13512 : else
13513 0 : appendPQExpBufferStr(query,
13514 : "'u' AS proparallel,\n");
13515 :
13516 132 : if (fout->remoteVersion >= 110000)
13517 132 : appendPQExpBufferStr(query,
13518 : "prokind,\n");
13519 : else
13520 0 : appendPQExpBufferStr(query,
13521 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13522 :
13523 132 : if (fout->remoteVersion >= 120000)
13524 132 : appendPQExpBufferStr(query,
13525 : "prosupport,\n");
13526 : else
13527 0 : appendPQExpBufferStr(query,
13528 : "'-' AS prosupport,\n");
13529 :
13530 132 : if (fout->remoteVersion >= 140000)
13531 132 : appendPQExpBufferStr(query,
13532 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13533 : else
13534 0 : appendPQExpBufferStr(query,
13535 : "NULL AS prosqlbody\n");
13536 :
13537 132 : appendPQExpBufferStr(query,
13538 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13539 : "WHERE p.oid = $1 "
13540 : "AND l.oid = p.prolang");
13541 :
13542 132 : ExecuteSqlStatement(fout, query->data);
13543 :
13544 132 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13545 : }
13546 :
13547 3512 : printfPQExpBuffer(query,
13548 : "EXECUTE dumpFunc('%u')",
13549 3512 : finfo->dobj.catId.oid);
13550 :
13551 3512 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13552 :
13553 3512 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13554 3512 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13555 : {
13556 3416 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13557 3416 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13558 3416 : prosqlbody = NULL;
13559 : }
13560 : else
13561 : {
13562 96 : prosrc = NULL;
13563 96 : probin = NULL;
13564 96 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13565 : }
13566 3512 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13567 3512 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13568 3512 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13569 3512 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13570 3512 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13571 3512 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13572 3512 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13573 3512 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13574 3512 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13575 3512 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13576 3512 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13577 3512 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13578 3512 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13579 3512 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13580 3512 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13581 :
13582 : /*
13583 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13584 : * is used.
13585 : */
13586 3512 : if (prosqlbody)
13587 : {
13588 96 : appendPQExpBufferStr(asPart, prosqlbody);
13589 : }
13590 3416 : else if (probin[0] != '\0')
13591 : {
13592 274 : appendPQExpBufferStr(asPart, "AS ");
13593 274 : appendStringLiteralAH(asPart, probin, fout);
13594 274 : if (prosrc[0] != '\0')
13595 : {
13596 274 : appendPQExpBufferStr(asPart, ", ");
13597 :
13598 : /*
13599 : * where we have bin, use dollar quoting if allowed and src
13600 : * contains quote or backslash; else use regular quoting.
13601 : */
13602 274 : if (dopt->disable_dollar_quoting ||
13603 274 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13604 274 : appendStringLiteralAH(asPart, prosrc, fout);
13605 : else
13606 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13607 : }
13608 : }
13609 : else
13610 : {
13611 3142 : appendPQExpBufferStr(asPart, "AS ");
13612 : /* with no bin, dollar quote src unconditionally if allowed */
13613 3142 : if (dopt->disable_dollar_quoting)
13614 0 : appendStringLiteralAH(asPart, prosrc, fout);
13615 : else
13616 3142 : appendStringLiteralDQ(asPart, prosrc, NULL);
13617 : }
13618 :
13619 3512 : if (*proconfig)
13620 : {
13621 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13622 0 : pg_fatal("could not parse %s array", "proconfig");
13623 : }
13624 : else
13625 : {
13626 3482 : configitems = NULL;
13627 3482 : nconfigitems = 0;
13628 : }
13629 :
13630 3512 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13631 3512 : funcsig = format_function_arguments(finfo, funciargs, false);
13632 :
13633 3512 : funcsig_tag = format_function_signature(fout, finfo, false);
13634 :
13635 3512 : qual_funcsig = psprintf("%s.%s",
13636 3512 : fmtId(finfo->dobj.namespace->dobj.name),
13637 : funcsig);
13638 :
13639 3512 : if (prokind[0] == PROKIND_PROCEDURE)
13640 184 : keyword = "PROCEDURE";
13641 : else
13642 3328 : keyword = "FUNCTION"; /* works for window functions too */
13643 :
13644 3512 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13645 : keyword, qual_funcsig);
13646 :
13647 7024 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13648 : keyword,
13649 3512 : fmtId(finfo->dobj.namespace->dobj.name),
13650 : funcfullsig ? funcfullsig :
13651 : funcsig);
13652 :
13653 3512 : if (prokind[0] == PROKIND_PROCEDURE)
13654 : /* no result type to output */ ;
13655 3328 : else if (funcresult)
13656 3328 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13657 : else
13658 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13659 0 : (proretset[0] == 't') ? "SETOF " : "",
13660 0 : getFormattedTypeName(fout, finfo->prorettype,
13661 : zeroIsError));
13662 :
13663 3512 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13664 :
13665 3512 : if (*protrftypes)
13666 : {
13667 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13668 : int i;
13669 :
13670 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13671 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13672 0 : for (i = 0; typeids[i]; i++)
13673 : {
13674 0 : if (i != 0)
13675 0 : appendPQExpBufferStr(q, ", ");
13676 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13677 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13678 : }
13679 :
13680 0 : free(typeids);
13681 : }
13682 :
13683 3512 : if (prokind[0] == PROKIND_WINDOW)
13684 10 : appendPQExpBufferStr(q, " WINDOW");
13685 :
13686 3512 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13687 : {
13688 702 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13689 660 : appendPQExpBufferStr(q, " IMMUTABLE");
13690 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
13691 42 : appendPQExpBufferStr(q, " STABLE");
13692 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13693 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13694 : finfo->dobj.name);
13695 : }
13696 :
13697 3512 : if (proisstrict[0] == 't')
13698 706 : appendPQExpBufferStr(q, " STRICT");
13699 :
13700 3512 : if (prosecdef[0] == 't')
13701 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13702 :
13703 3512 : if (proleakproof[0] == 't')
13704 20 : appendPQExpBufferStr(q, " LEAKPROOF");
13705 :
13706 : /*
13707 : * COST and ROWS are emitted only if present and not default, so as not to
13708 : * break backwards-compatibility of the dump without need. Keep this code
13709 : * in sync with the defaults in functioncmds.c.
13710 : */
13711 3512 : if (strcmp(procost, "0") != 0)
13712 : {
13713 3512 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13714 : {
13715 : /* default cost is 1 */
13716 732 : if (strcmp(procost, "1") != 0)
13717 0 : appendPQExpBuffer(q, " COST %s", procost);
13718 : }
13719 : else
13720 : {
13721 : /* default cost is 100 */
13722 2780 : if (strcmp(procost, "100") != 0)
13723 12 : appendPQExpBuffer(q, " COST %s", procost);
13724 : }
13725 : }
13726 3512 : if (proretset[0] == 't' &&
13727 374 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13728 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13729 :
13730 3512 : if (strcmp(prosupport, "-") != 0)
13731 : {
13732 : /* We rely on regprocout to provide quoting and qualification */
13733 84 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13734 : }
13735 :
13736 3512 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13737 : {
13738 232 : if (proparallel[0] == PROPARALLEL_SAFE)
13739 222 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13740 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13741 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13742 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13743 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13744 : finfo->dobj.name);
13745 : }
13746 :
13747 3592 : for (int i = 0; i < nconfigitems; i++)
13748 : {
13749 : /* we feel free to scribble on configitems[] here */
13750 80 : char *configitem = configitems[i];
13751 : char *pos;
13752 :
13753 80 : pos = strchr(configitem, '=');
13754 80 : if (pos == NULL)
13755 0 : continue;
13756 80 : *pos++ = '\0';
13757 80 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13758 :
13759 : /*
13760 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13761 : * by flatten_set_variable_args() before they were put into the
13762 : * proconfig array. However, because the quoting rules used there
13763 : * aren't exactly like SQL's, we have to break the list value apart
13764 : * and then quote the elements as string literals. (The elements may
13765 : * be double-quoted as-is, but we can't just feed them to the SQL
13766 : * parser; it would do the wrong thing with elements that are
13767 : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13768 : * case for empty lists.
13769 : *
13770 : * Variables that are not so marked should just be emitted as simple
13771 : * string literals. If the variable is not known to
13772 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13773 : * to use GUC_LIST_QUOTE for extension variables.
13774 : */
13775 80 : if (variable_is_guc_list_quote(configitem))
13776 : {
13777 : char **namelist;
13778 : char **nameptr;
13779 :
13780 : /* Parse string into list of identifiers */
13781 : /* this shouldn't fail really */
13782 30 : if (SplitGUCList(pos, ',', &namelist))
13783 : {
13784 : /* Special case: represent an empty list as NULL */
13785 30 : if (*namelist == NULL)
13786 10 : appendPQExpBufferStr(q, "NULL");
13787 80 : for (nameptr = namelist; *nameptr; nameptr++)
13788 : {
13789 50 : if (nameptr != namelist)
13790 30 : appendPQExpBufferStr(q, ", ");
13791 50 : appendStringLiteralAH(q, *nameptr, fout);
13792 : }
13793 : }
13794 30 : pg_free(namelist);
13795 : }
13796 : else
13797 50 : appendStringLiteralAH(q, pos, fout);
13798 : }
13799 :
13800 3512 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13801 :
13802 3512 : append_depends_on_extension(fout, q, &finfo->dobj,
13803 : "pg_catalog.pg_proc", keyword,
13804 : qual_funcsig);
13805 :
13806 3512 : if (dopt->binary_upgrade)
13807 586 : binary_upgrade_extension_member(q, &finfo->dobj,
13808 : keyword, funcsig,
13809 586 : finfo->dobj.namespace->dobj.name);
13810 :
13811 3512 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13812 3320 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13813 3320 : ARCHIVE_OPTS(.tag = funcsig_tag,
13814 : .namespace = finfo->dobj.namespace->dobj.name,
13815 : .owner = finfo->rolname,
13816 : .description = keyword,
13817 : .section = finfo->postponed_def ?
13818 : SECTION_POST_DATA : SECTION_PRE_DATA,
13819 : .createStmt = q->data,
13820 : .dropStmt = delqry->data));
13821 :
13822 : /* Dump Function Comments and Security Labels */
13823 3512 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13824 18 : dumpComment(fout, keyword, funcsig,
13825 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13826 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13827 :
13828 3512 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13829 0 : dumpSecLabel(fout, keyword, funcsig,
13830 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13831 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13832 :
13833 3512 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13834 200 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13835 : funcsig, NULL,
13836 200 : finfo->dobj.namespace->dobj.name,
13837 200 : NULL, finfo->rolname, &finfo->dacl);
13838 :
13839 3512 : PQclear(res);
13840 :
13841 3512 : destroyPQExpBuffer(query);
13842 3512 : destroyPQExpBuffer(q);
13843 3512 : destroyPQExpBuffer(delqry);
13844 3512 : destroyPQExpBuffer(asPart);
13845 3512 : free(funcsig);
13846 3512 : free(funcfullsig);
13847 3512 : free(funcsig_tag);
13848 3512 : free(qual_funcsig);
13849 3512 : free(configitems);
13850 : }
13851 :
13852 :
13853 : /*
13854 : * Dump a user-defined cast
13855 : */
13856 : static void
13857 134 : dumpCast(Archive *fout, const CastInfo *cast)
13858 : {
13859 134 : DumpOptions *dopt = fout->dopt;
13860 : PQExpBuffer defqry;
13861 : PQExpBuffer delqry;
13862 : PQExpBuffer labelq;
13863 : PQExpBuffer castargs;
13864 134 : FuncInfo *funcInfo = NULL;
13865 : const char *sourceType;
13866 : const char *targetType;
13867 :
13868 : /* Do nothing if not dumping schema */
13869 134 : if (!dopt->dumpSchema)
13870 12 : return;
13871 :
13872 : /* Cannot dump if we don't have the cast function's info */
13873 122 : if (OidIsValid(cast->castfunc))
13874 : {
13875 72 : funcInfo = findFuncByOid(cast->castfunc);
13876 72 : if (funcInfo == NULL)
13877 0 : pg_fatal("could not find function definition for function with OID %u",
13878 : cast->castfunc);
13879 : }
13880 :
13881 122 : defqry = createPQExpBuffer();
13882 122 : delqry = createPQExpBuffer();
13883 122 : labelq = createPQExpBuffer();
13884 122 : castargs = createPQExpBuffer();
13885 :
13886 122 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13887 122 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13888 122 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13889 : sourceType, targetType);
13890 :
13891 122 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13892 : sourceType, targetType);
13893 :
13894 122 : switch (cast->castmethod)
13895 : {
13896 50 : case COERCION_METHOD_BINARY:
13897 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13898 50 : break;
13899 0 : case COERCION_METHOD_INOUT:
13900 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13901 0 : break;
13902 72 : case COERCION_METHOD_FUNCTION:
13903 72 : if (funcInfo)
13904 : {
13905 72 : char *fsig = format_function_signature(fout, funcInfo, true);
13906 :
13907 : /*
13908 : * Always qualify the function name (format_function_signature
13909 : * won't qualify it).
13910 : */
13911 72 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13912 72 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13913 72 : free(fsig);
13914 : }
13915 : else
13916 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13917 72 : break;
13918 0 : default:
13919 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13920 : }
13921 :
13922 122 : if (cast->castcontext == 'a')
13923 62 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13924 60 : else if (cast->castcontext == 'i')
13925 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13926 122 : appendPQExpBufferStr(defqry, ";\n");
13927 :
13928 122 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13929 : sourceType, targetType);
13930 :
13931 122 : appendPQExpBuffer(castargs, "(%s AS %s)",
13932 : sourceType, targetType);
13933 :
13934 122 : if (dopt->binary_upgrade)
13935 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13936 14 : "CAST", castargs->data, NULL);
13937 :
13938 122 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13939 122 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13940 122 : ARCHIVE_OPTS(.tag = labelq->data,
13941 : .description = "CAST",
13942 : .section = SECTION_PRE_DATA,
13943 : .createStmt = defqry->data,
13944 : .dropStmt = delqry->data));
13945 :
13946 : /* Dump Cast Comments */
13947 122 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13948 0 : dumpComment(fout, "CAST", castargs->data,
13949 : NULL, "",
13950 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13951 :
13952 122 : destroyPQExpBuffer(defqry);
13953 122 : destroyPQExpBuffer(delqry);
13954 122 : destroyPQExpBuffer(labelq);
13955 122 : destroyPQExpBuffer(castargs);
13956 : }
13957 :
13958 : /*
13959 : * Dump a transform
13960 : */
13961 : static void
13962 84 : dumpTransform(Archive *fout, const TransformInfo *transform)
13963 : {
13964 84 : DumpOptions *dopt = fout->dopt;
13965 : PQExpBuffer defqry;
13966 : PQExpBuffer delqry;
13967 : PQExpBuffer labelq;
13968 : PQExpBuffer transformargs;
13969 84 : FuncInfo *fromsqlFuncInfo = NULL;
13970 84 : FuncInfo *tosqlFuncInfo = NULL;
13971 : char *lanname;
13972 : const char *transformType;
13973 :
13974 : /* Do nothing if not dumping schema */
13975 84 : if (!dopt->dumpSchema)
13976 12 : return;
13977 :
13978 : /* Cannot dump if we don't have the transform functions' info */
13979 72 : if (OidIsValid(transform->trffromsql))
13980 : {
13981 72 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13982 72 : if (fromsqlFuncInfo == NULL)
13983 0 : pg_fatal("could not find function definition for function with OID %u",
13984 : transform->trffromsql);
13985 : }
13986 72 : if (OidIsValid(transform->trftosql))
13987 : {
13988 72 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
13989 72 : if (tosqlFuncInfo == NULL)
13990 0 : pg_fatal("could not find function definition for function with OID %u",
13991 : transform->trftosql);
13992 : }
13993 :
13994 72 : defqry = createPQExpBuffer();
13995 72 : delqry = createPQExpBuffer();
13996 72 : labelq = createPQExpBuffer();
13997 72 : transformargs = createPQExpBuffer();
13998 :
13999 72 : lanname = get_language_name(fout, transform->trflang);
14000 72 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14001 :
14002 72 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14003 : transformType, lanname);
14004 :
14005 72 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14006 : transformType, lanname);
14007 :
14008 72 : if (!transform->trffromsql && !transform->trftosql)
14009 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14010 :
14011 72 : if (transform->trffromsql)
14012 : {
14013 72 : if (fromsqlFuncInfo)
14014 : {
14015 72 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14016 :
14017 : /*
14018 : * Always qualify the function name (format_function_signature
14019 : * won't qualify it).
14020 : */
14021 72 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14022 72 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14023 72 : free(fsig);
14024 : }
14025 : else
14026 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14027 : }
14028 :
14029 72 : if (transform->trftosql)
14030 : {
14031 72 : if (transform->trffromsql)
14032 72 : appendPQExpBufferStr(defqry, ", ");
14033 :
14034 72 : if (tosqlFuncInfo)
14035 : {
14036 72 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14037 :
14038 : /*
14039 : * Always qualify the function name (format_function_signature
14040 : * won't qualify it).
14041 : */
14042 72 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14043 72 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14044 72 : free(fsig);
14045 : }
14046 : else
14047 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14048 : }
14049 :
14050 72 : appendPQExpBufferStr(defqry, ");\n");
14051 :
14052 72 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14053 : transformType, lanname);
14054 :
14055 72 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14056 : transformType, lanname);
14057 :
14058 72 : if (dopt->binary_upgrade)
14059 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
14060 4 : "TRANSFORM", transformargs->data, NULL);
14061 :
14062 72 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14063 72 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14064 72 : ARCHIVE_OPTS(.tag = labelq->data,
14065 : .description = "TRANSFORM",
14066 : .section = SECTION_PRE_DATA,
14067 : .createStmt = defqry->data,
14068 : .dropStmt = delqry->data,
14069 : .deps = transform->dobj.dependencies,
14070 : .nDeps = transform->dobj.nDeps));
14071 :
14072 : /* Dump Transform Comments */
14073 72 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14074 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14075 : NULL, "",
14076 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14077 :
14078 72 : free(lanname);
14079 72 : destroyPQExpBuffer(defqry);
14080 72 : destroyPQExpBuffer(delqry);
14081 72 : destroyPQExpBuffer(labelq);
14082 72 : destroyPQExpBuffer(transformargs);
14083 : }
14084 :
14085 :
14086 : /*
14087 : * dumpOpr
14088 : * write out a single operator definition
14089 : */
14090 : static void
14091 5008 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14092 : {
14093 5008 : DumpOptions *dopt = fout->dopt;
14094 : PQExpBuffer query;
14095 : PQExpBuffer q;
14096 : PQExpBuffer delq;
14097 : PQExpBuffer oprid;
14098 : PQExpBuffer details;
14099 : PGresult *res;
14100 : int i_oprkind;
14101 : int i_oprcode;
14102 : int i_oprleft;
14103 : int i_oprright;
14104 : int i_oprcom;
14105 : int i_oprnegate;
14106 : int i_oprrest;
14107 : int i_oprjoin;
14108 : int i_oprcanmerge;
14109 : int i_oprcanhash;
14110 : char *oprkind;
14111 : char *oprcode;
14112 : char *oprleft;
14113 : char *oprright;
14114 : char *oprcom;
14115 : char *oprnegate;
14116 : char *oprrest;
14117 : char *oprjoin;
14118 : char *oprcanmerge;
14119 : char *oprcanhash;
14120 : char *oprregproc;
14121 : char *oprref;
14122 :
14123 : /* Do nothing if not dumping schema */
14124 5008 : if (!dopt->dumpSchema)
14125 12 : return;
14126 :
14127 : /*
14128 : * some operators are invalid because they were the result of user
14129 : * defining operators before commutators exist
14130 : */
14131 4996 : if (!OidIsValid(oprinfo->oprcode))
14132 28 : return;
14133 :
14134 4968 : query = createPQExpBuffer();
14135 4968 : q = createPQExpBuffer();
14136 4968 : delq = createPQExpBuffer();
14137 4968 : oprid = createPQExpBuffer();
14138 4968 : details = createPQExpBuffer();
14139 :
14140 4968 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14141 : {
14142 : /* Set up query for operator-specific details */
14143 80 : appendPQExpBufferStr(query,
14144 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14145 : "SELECT oprkind, "
14146 : "oprcode::pg_catalog.regprocedure, "
14147 : "oprleft::pg_catalog.regtype, "
14148 : "oprright::pg_catalog.regtype, "
14149 : "oprcom, "
14150 : "oprnegate, "
14151 : "oprrest::pg_catalog.regprocedure, "
14152 : "oprjoin::pg_catalog.regprocedure, "
14153 : "oprcanmerge, oprcanhash "
14154 : "FROM pg_catalog.pg_operator "
14155 : "WHERE oid = $1");
14156 :
14157 80 : ExecuteSqlStatement(fout, query->data);
14158 :
14159 80 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14160 : }
14161 :
14162 4968 : printfPQExpBuffer(query,
14163 : "EXECUTE dumpOpr('%u')",
14164 4968 : oprinfo->dobj.catId.oid);
14165 :
14166 4968 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14167 :
14168 4968 : i_oprkind = PQfnumber(res, "oprkind");
14169 4968 : i_oprcode = PQfnumber(res, "oprcode");
14170 4968 : i_oprleft = PQfnumber(res, "oprleft");
14171 4968 : i_oprright = PQfnumber(res, "oprright");
14172 4968 : i_oprcom = PQfnumber(res, "oprcom");
14173 4968 : i_oprnegate = PQfnumber(res, "oprnegate");
14174 4968 : i_oprrest = PQfnumber(res, "oprrest");
14175 4968 : i_oprjoin = PQfnumber(res, "oprjoin");
14176 4968 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14177 4968 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14178 :
14179 4968 : oprkind = PQgetvalue(res, 0, i_oprkind);
14180 4968 : oprcode = PQgetvalue(res, 0, i_oprcode);
14181 4968 : oprleft = PQgetvalue(res, 0, i_oprleft);
14182 4968 : oprright = PQgetvalue(res, 0, i_oprright);
14183 4968 : oprcom = PQgetvalue(res, 0, i_oprcom);
14184 4968 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14185 4968 : oprrest = PQgetvalue(res, 0, i_oprrest);
14186 4968 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14187 4968 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14188 4968 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14189 :
14190 : /* In PG14 upwards postfix operator support does not exist anymore. */
14191 4968 : if (strcmp(oprkind, "r") == 0)
14192 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14193 : oprcode);
14194 :
14195 4968 : oprregproc = convertRegProcReference(oprcode);
14196 4968 : if (oprregproc)
14197 : {
14198 4968 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14199 4968 : free(oprregproc);
14200 : }
14201 :
14202 4968 : appendPQExpBuffer(oprid, "%s (",
14203 4968 : oprinfo->dobj.name);
14204 :
14205 : /*
14206 : * right unary means there's a left arg and left unary means there's a
14207 : * right arg. (Although the "r" case is dead code for PG14 and later,
14208 : * continue to support it in case we're dumping from an old server.)
14209 : */
14210 4968 : if (strcmp(oprkind, "r") == 0 ||
14211 4968 : strcmp(oprkind, "b") == 0)
14212 : {
14213 4682 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14214 4682 : appendPQExpBufferStr(oprid, oprleft);
14215 : }
14216 : else
14217 286 : appendPQExpBufferStr(oprid, "NONE");
14218 :
14219 4968 : if (strcmp(oprkind, "l") == 0 ||
14220 4682 : strcmp(oprkind, "b") == 0)
14221 : {
14222 4968 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14223 4968 : appendPQExpBuffer(oprid, ", %s)", oprright);
14224 : }
14225 : else
14226 0 : appendPQExpBufferStr(oprid, ", NONE)");
14227 :
14228 4968 : oprref = getFormattedOperatorName(oprcom);
14229 4968 : if (oprref)
14230 : {
14231 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14232 3322 : free(oprref);
14233 : }
14234 :
14235 4968 : oprref = getFormattedOperatorName(oprnegate);
14236 4968 : if (oprref)
14237 : {
14238 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14239 2326 : free(oprref);
14240 : }
14241 :
14242 4968 : if (strcmp(oprcanmerge, "t") == 0)
14243 370 : appendPQExpBufferStr(details, ",\n MERGES");
14244 :
14245 4968 : if (strcmp(oprcanhash, "t") == 0)
14246 276 : appendPQExpBufferStr(details, ",\n HASHES");
14247 :
14248 4968 : oprregproc = convertRegProcReference(oprrest);
14249 4968 : if (oprregproc)
14250 : {
14251 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14252 3028 : free(oprregproc);
14253 : }
14254 :
14255 4968 : oprregproc = convertRegProcReference(oprjoin);
14256 4968 : if (oprregproc)
14257 : {
14258 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14259 3028 : free(oprregproc);
14260 : }
14261 :
14262 4968 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14263 4968 : fmtId(oprinfo->dobj.namespace->dobj.name),
14264 : oprid->data);
14265 :
14266 4968 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14267 4968 : fmtId(oprinfo->dobj.namespace->dobj.name),
14268 4968 : oprinfo->dobj.name, details->data);
14269 :
14270 4968 : if (dopt->binary_upgrade)
14271 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14272 24 : "OPERATOR", oprid->data,
14273 24 : oprinfo->dobj.namespace->dobj.name);
14274 :
14275 4968 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14276 4968 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14277 4968 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14278 : .namespace = oprinfo->dobj.namespace->dobj.name,
14279 : .owner = oprinfo->rolname,
14280 : .description = "OPERATOR",
14281 : .section = SECTION_PRE_DATA,
14282 : .createStmt = q->data,
14283 : .dropStmt = delq->data));
14284 :
14285 : /* Dump Operator Comments */
14286 4968 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14287 4794 : dumpComment(fout, "OPERATOR", oprid->data,
14288 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14289 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14290 :
14291 4968 : PQclear(res);
14292 :
14293 4968 : destroyPQExpBuffer(query);
14294 4968 : destroyPQExpBuffer(q);
14295 4968 : destroyPQExpBuffer(delq);
14296 4968 : destroyPQExpBuffer(oprid);
14297 4968 : destroyPQExpBuffer(details);
14298 : }
14299 :
14300 : /*
14301 : * Convert a function reference obtained from pg_operator
14302 : *
14303 : * Returns allocated string of what to print, or NULL if function references
14304 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14305 : *
14306 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14307 : * part.
14308 : */
14309 : static char *
14310 14904 : convertRegProcReference(const char *proc)
14311 : {
14312 : char *name;
14313 : char *paren;
14314 : bool inquote;
14315 :
14316 : /* In all cases "-" means a null reference */
14317 14904 : if (strcmp(proc, "-") == 0)
14318 3880 : return NULL;
14319 :
14320 11024 : name = pg_strdup(proc);
14321 : /* find non-double-quoted left paren */
14322 11024 : inquote = false;
14323 132864 : for (paren = name; *paren; paren++)
14324 : {
14325 132864 : if (*paren == '(' && !inquote)
14326 : {
14327 11024 : *paren = '\0';
14328 11024 : break;
14329 : }
14330 121840 : if (*paren == '"')
14331 100 : inquote = !inquote;
14332 : }
14333 11024 : return name;
14334 : }
14335 :
14336 : /*
14337 : * getFormattedOperatorName - retrieve the operator name for the
14338 : * given operator OID (presented in string form).
14339 : *
14340 : * Returns an allocated string, or NULL if the given OID is invalid.
14341 : * Caller is responsible for free'ing result string.
14342 : *
14343 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14344 : * useful in commands where the operator's argument types can be inferred from
14345 : * context. We always schema-qualify the name, though. The predecessor to
14346 : * this code tried to skip the schema qualification if possible, but that led
14347 : * to wrong results in corner cases, such as if an operator and its negator
14348 : * are in different schemas.
14349 : */
14350 : static char *
14351 10506 : getFormattedOperatorName(const char *oproid)
14352 : {
14353 : OprInfo *oprInfo;
14354 :
14355 : /* In all cases "0" means a null reference */
14356 10506 : if (strcmp(oproid, "0") == 0)
14357 4858 : return NULL;
14358 :
14359 5648 : oprInfo = findOprByOid(atooid(oproid));
14360 5648 : if (oprInfo == NULL)
14361 : {
14362 0 : pg_log_warning("could not find operator with OID %s",
14363 : oproid);
14364 0 : return NULL;
14365 : }
14366 :
14367 5648 : return psprintf("OPERATOR(%s.%s)",
14368 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
14369 : oprInfo->dobj.name);
14370 : }
14371 :
14372 : /*
14373 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14374 : *
14375 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14376 : * argument lists of these functions are predetermined. Note that the
14377 : * caller should ensure we are in the proper schema, because the results
14378 : * are search path dependent!
14379 : */
14380 : static char *
14381 410 : convertTSFunction(Archive *fout, Oid funcOid)
14382 : {
14383 : char *result;
14384 : char query[128];
14385 : PGresult *res;
14386 :
14387 410 : snprintf(query, sizeof(query),
14388 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14389 410 : res = ExecuteSqlQueryForSingleRow(fout, query);
14390 :
14391 410 : result = pg_strdup(PQgetvalue(res, 0, 0));
14392 :
14393 410 : PQclear(res);
14394 :
14395 410 : return result;
14396 : }
14397 :
14398 : /*
14399 : * dumpAccessMethod
14400 : * write out a single access method definition
14401 : */
14402 : static void
14403 160 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14404 : {
14405 160 : DumpOptions *dopt = fout->dopt;
14406 : PQExpBuffer q;
14407 : PQExpBuffer delq;
14408 : char *qamname;
14409 :
14410 : /* Do nothing if not dumping schema */
14411 160 : if (!dopt->dumpSchema)
14412 24 : return;
14413 :
14414 136 : q = createPQExpBuffer();
14415 136 : delq = createPQExpBuffer();
14416 :
14417 136 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14418 :
14419 136 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14420 :
14421 136 : switch (aminfo->amtype)
14422 : {
14423 64 : case AMTYPE_INDEX:
14424 64 : appendPQExpBufferStr(q, "TYPE INDEX ");
14425 64 : break;
14426 72 : case AMTYPE_TABLE:
14427 72 : appendPQExpBufferStr(q, "TYPE TABLE ");
14428 72 : break;
14429 0 : default:
14430 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14431 : aminfo->amtype, qamname);
14432 0 : destroyPQExpBuffer(q);
14433 0 : destroyPQExpBuffer(delq);
14434 0 : free(qamname);
14435 0 : return;
14436 : }
14437 :
14438 136 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14439 :
14440 136 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14441 : qamname);
14442 :
14443 136 : if (dopt->binary_upgrade)
14444 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14445 : "ACCESS METHOD", qamname, NULL);
14446 :
14447 136 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14448 136 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14449 136 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14450 : .description = "ACCESS METHOD",
14451 : .section = SECTION_PRE_DATA,
14452 : .createStmt = q->data,
14453 : .dropStmt = delq->data));
14454 :
14455 : /* Dump Access Method Comments */
14456 136 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14457 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14458 : NULL, "",
14459 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14460 :
14461 136 : destroyPQExpBuffer(q);
14462 136 : destroyPQExpBuffer(delq);
14463 136 : free(qamname);
14464 : }
14465 :
14466 : /*
14467 : * dumpOpclass
14468 : * write out a single operator class definition
14469 : */
14470 : static void
14471 1320 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14472 : {
14473 1320 : DumpOptions *dopt = fout->dopt;
14474 : PQExpBuffer query;
14475 : PQExpBuffer q;
14476 : PQExpBuffer delq;
14477 : PQExpBuffer nameusing;
14478 : PGresult *res;
14479 : int ntups;
14480 : int i_opcintype;
14481 : int i_opckeytype;
14482 : int i_opcdefault;
14483 : int i_opcfamily;
14484 : int i_opcfamilyname;
14485 : int i_opcfamilynsp;
14486 : int i_amname;
14487 : int i_amopstrategy;
14488 : int i_amopopr;
14489 : int i_sortfamily;
14490 : int i_sortfamilynsp;
14491 : int i_amprocnum;
14492 : int i_amproc;
14493 : int i_amproclefttype;
14494 : int i_amprocrighttype;
14495 : char *opcintype;
14496 : char *opckeytype;
14497 : char *opcdefault;
14498 : char *opcfamily;
14499 : char *opcfamilyname;
14500 : char *opcfamilynsp;
14501 : char *amname;
14502 : char *amopstrategy;
14503 : char *amopopr;
14504 : char *sortfamily;
14505 : char *sortfamilynsp;
14506 : char *amprocnum;
14507 : char *amproc;
14508 : char *amproclefttype;
14509 : char *amprocrighttype;
14510 : bool needComma;
14511 : int i;
14512 :
14513 : /* Do nothing if not dumping schema */
14514 1320 : if (!dopt->dumpSchema)
14515 36 : return;
14516 :
14517 1284 : query = createPQExpBuffer();
14518 1284 : q = createPQExpBuffer();
14519 1284 : delq = createPQExpBuffer();
14520 1284 : nameusing = createPQExpBuffer();
14521 :
14522 : /* Get additional fields from the pg_opclass row */
14523 1284 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14524 : "opckeytype::pg_catalog.regtype, "
14525 : "opcdefault, opcfamily, "
14526 : "opfname AS opcfamilyname, "
14527 : "nspname AS opcfamilynsp, "
14528 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14529 : "FROM pg_catalog.pg_opclass c "
14530 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14531 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14532 : "WHERE c.oid = '%u'::pg_catalog.oid",
14533 1284 : opcinfo->dobj.catId.oid);
14534 :
14535 1284 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14536 :
14537 1284 : i_opcintype = PQfnumber(res, "opcintype");
14538 1284 : i_opckeytype = PQfnumber(res, "opckeytype");
14539 1284 : i_opcdefault = PQfnumber(res, "opcdefault");
14540 1284 : i_opcfamily = PQfnumber(res, "opcfamily");
14541 1284 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14542 1284 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14543 1284 : i_amname = PQfnumber(res, "amname");
14544 :
14545 : /* opcintype may still be needed after we PQclear res */
14546 1284 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14547 1284 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14548 1284 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14549 : /* opcfamily will still be needed after we PQclear res */
14550 1284 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14551 1284 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14552 1284 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14553 : /* amname will still be needed after we PQclear res */
14554 1284 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14555 :
14556 1284 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14557 1284 : fmtQualifiedDumpable(opcinfo));
14558 1284 : appendPQExpBuffer(delq, " USING %s;\n",
14559 : fmtId(amname));
14560 :
14561 : /* Build the fixed portion of the CREATE command */
14562 1284 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14563 1284 : fmtQualifiedDumpable(opcinfo));
14564 1284 : if (strcmp(opcdefault, "t") == 0)
14565 714 : appendPQExpBufferStr(q, "DEFAULT ");
14566 1284 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14567 : opcintype,
14568 : fmtId(amname));
14569 1284 : if (strlen(opcfamilyname) > 0)
14570 : {
14571 1284 : appendPQExpBufferStr(q, " FAMILY ");
14572 1284 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14573 1284 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14574 : }
14575 1284 : appendPQExpBufferStr(q, " AS\n ");
14576 :
14577 1284 : needComma = false;
14578 :
14579 1284 : if (strcmp(opckeytype, "-") != 0)
14580 : {
14581 504 : appendPQExpBuffer(q, "STORAGE %s",
14582 : opckeytype);
14583 504 : needComma = true;
14584 : }
14585 :
14586 1284 : PQclear(res);
14587 :
14588 : /*
14589 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14590 : *
14591 : * Print only those opfamily members that are tied to the opclass by
14592 : * pg_depend entries.
14593 : */
14594 1284 : resetPQExpBuffer(query);
14595 1284 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14596 : "amopopr::pg_catalog.regoperator, "
14597 : "opfname AS sortfamily, "
14598 : "nspname AS sortfamilynsp "
14599 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14600 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14601 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14602 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14603 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14604 : "AND refobjid = '%u'::pg_catalog.oid "
14605 : "AND amopfamily = '%s'::pg_catalog.oid "
14606 : "ORDER BY amopstrategy",
14607 1284 : opcinfo->dobj.catId.oid,
14608 : opcfamily);
14609 :
14610 1284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14611 :
14612 1284 : ntups = PQntuples(res);
14613 :
14614 1284 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14615 1284 : i_amopopr = PQfnumber(res, "amopopr");
14616 1284 : i_sortfamily = PQfnumber(res, "sortfamily");
14617 1284 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14618 :
14619 1688 : for (i = 0; i < ntups; i++)
14620 : {
14621 404 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14622 404 : amopopr = PQgetvalue(res, i, i_amopopr);
14623 404 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14624 404 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14625 :
14626 404 : if (needComma)
14627 256 : appendPQExpBufferStr(q, " ,\n ");
14628 :
14629 404 : appendPQExpBuffer(q, "OPERATOR %s %s",
14630 : amopstrategy, amopopr);
14631 :
14632 404 : if (strlen(sortfamily) > 0)
14633 : {
14634 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14635 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14636 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14637 : }
14638 :
14639 404 : needComma = true;
14640 : }
14641 :
14642 1284 : PQclear(res);
14643 :
14644 : /*
14645 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14646 : *
14647 : * Print only those opfamily members that are tied to the opclass by
14648 : * pg_depend entries.
14649 : *
14650 : * We print the amproclefttype/amprocrighttype even though in most cases
14651 : * the backend could deduce the right values, because of the corner case
14652 : * of a btree sort support function for a cross-type comparison.
14653 : */
14654 1284 : resetPQExpBuffer(query);
14655 :
14656 1284 : appendPQExpBuffer(query, "SELECT amprocnum, "
14657 : "amproc::pg_catalog.regprocedure, "
14658 : "amproclefttype::pg_catalog.regtype, "
14659 : "amprocrighttype::pg_catalog.regtype "
14660 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14661 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14662 : "AND refobjid = '%u'::pg_catalog.oid "
14663 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14664 : "AND objid = ap.oid "
14665 : "ORDER BY amprocnum",
14666 1284 : opcinfo->dobj.catId.oid);
14667 :
14668 1284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14669 :
14670 1284 : ntups = PQntuples(res);
14671 :
14672 1284 : i_amprocnum = PQfnumber(res, "amprocnum");
14673 1284 : i_amproc = PQfnumber(res, "amproc");
14674 1284 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14675 1284 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14676 :
14677 1348 : for (i = 0; i < ntups; i++)
14678 : {
14679 64 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14680 64 : amproc = PQgetvalue(res, i, i_amproc);
14681 64 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14682 64 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14683 :
14684 64 : if (needComma)
14685 64 : appendPQExpBufferStr(q, " ,\n ");
14686 :
14687 64 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14688 :
14689 64 : if (*amproclefttype && *amprocrighttype)
14690 64 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14691 :
14692 64 : appendPQExpBuffer(q, " %s", amproc);
14693 :
14694 64 : needComma = true;
14695 : }
14696 :
14697 1284 : PQclear(res);
14698 :
14699 : /*
14700 : * If needComma is still false it means we haven't added anything after
14701 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14702 : * clause with the same datatype. This isn't sanctioned by the
14703 : * documentation, but actually DefineOpClass will treat it as a no-op.
14704 : */
14705 1284 : if (!needComma)
14706 632 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14707 :
14708 1284 : appendPQExpBufferStr(q, ";\n");
14709 :
14710 1284 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14711 1284 : appendPQExpBuffer(nameusing, " USING %s",
14712 : fmtId(amname));
14713 :
14714 1284 : if (dopt->binary_upgrade)
14715 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14716 12 : "OPERATOR CLASS", nameusing->data,
14717 12 : opcinfo->dobj.namespace->dobj.name);
14718 :
14719 1284 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14720 1284 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14721 1284 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14722 : .namespace = opcinfo->dobj.namespace->dobj.name,
14723 : .owner = opcinfo->rolname,
14724 : .description = "OPERATOR CLASS",
14725 : .section = SECTION_PRE_DATA,
14726 : .createStmt = q->data,
14727 : .dropStmt = delq->data));
14728 :
14729 : /* Dump Operator Class Comments */
14730 1284 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14731 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14732 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14733 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14734 :
14735 1284 : free(opcintype);
14736 1284 : free(opcfamily);
14737 1284 : free(amname);
14738 1284 : destroyPQExpBuffer(query);
14739 1284 : destroyPQExpBuffer(q);
14740 1284 : destroyPQExpBuffer(delq);
14741 1284 : destroyPQExpBuffer(nameusing);
14742 : }
14743 :
14744 : /*
14745 : * dumpOpfamily
14746 : * write out a single operator family definition
14747 : *
14748 : * Note: this also dumps any "loose" operator members that aren't bound to a
14749 : * specific opclass within the opfamily.
14750 : */
14751 : static void
14752 1098 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14753 : {
14754 1098 : DumpOptions *dopt = fout->dopt;
14755 : PQExpBuffer query;
14756 : PQExpBuffer q;
14757 : PQExpBuffer delq;
14758 : PQExpBuffer nameusing;
14759 : PGresult *res;
14760 : PGresult *res_ops;
14761 : PGresult *res_procs;
14762 : int ntups;
14763 : int i_amname;
14764 : int i_amopstrategy;
14765 : int i_amopopr;
14766 : int i_sortfamily;
14767 : int i_sortfamilynsp;
14768 : int i_amprocnum;
14769 : int i_amproc;
14770 : int i_amproclefttype;
14771 : int i_amprocrighttype;
14772 : char *amname;
14773 : char *amopstrategy;
14774 : char *amopopr;
14775 : char *sortfamily;
14776 : char *sortfamilynsp;
14777 : char *amprocnum;
14778 : char *amproc;
14779 : char *amproclefttype;
14780 : char *amprocrighttype;
14781 : bool needComma;
14782 : int i;
14783 :
14784 : /* Do nothing if not dumping schema */
14785 1098 : if (!dopt->dumpSchema)
14786 24 : return;
14787 :
14788 1074 : query = createPQExpBuffer();
14789 1074 : q = createPQExpBuffer();
14790 1074 : delq = createPQExpBuffer();
14791 1074 : nameusing = createPQExpBuffer();
14792 :
14793 : /*
14794 : * Fetch only those opfamily members that are tied directly to the
14795 : * opfamily by pg_depend entries.
14796 : */
14797 1074 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14798 : "amopopr::pg_catalog.regoperator, "
14799 : "opfname AS sortfamily, "
14800 : "nspname AS sortfamilynsp "
14801 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14802 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14803 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14804 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14805 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14806 : "AND refobjid = '%u'::pg_catalog.oid "
14807 : "AND amopfamily = '%u'::pg_catalog.oid "
14808 : "ORDER BY amopstrategy",
14809 1074 : opfinfo->dobj.catId.oid,
14810 1074 : opfinfo->dobj.catId.oid);
14811 :
14812 1074 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14813 :
14814 1074 : resetPQExpBuffer(query);
14815 :
14816 1074 : appendPQExpBuffer(query, "SELECT amprocnum, "
14817 : "amproc::pg_catalog.regprocedure, "
14818 : "amproclefttype::pg_catalog.regtype, "
14819 : "amprocrighttype::pg_catalog.regtype "
14820 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14821 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14822 : "AND refobjid = '%u'::pg_catalog.oid "
14823 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14824 : "AND objid = ap.oid "
14825 : "ORDER BY amprocnum",
14826 1074 : opfinfo->dobj.catId.oid);
14827 :
14828 1074 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14829 :
14830 : /* Get additional fields from the pg_opfamily row */
14831 1074 : resetPQExpBuffer(query);
14832 :
14833 1074 : appendPQExpBuffer(query, "SELECT "
14834 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14835 : "FROM pg_catalog.pg_opfamily "
14836 : "WHERE oid = '%u'::pg_catalog.oid",
14837 1074 : opfinfo->dobj.catId.oid);
14838 :
14839 1074 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14840 :
14841 1074 : i_amname = PQfnumber(res, "amname");
14842 :
14843 : /* amname will still be needed after we PQclear res */
14844 1074 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14845 :
14846 1074 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14847 1074 : fmtQualifiedDumpable(opfinfo));
14848 1074 : appendPQExpBuffer(delq, " USING %s;\n",
14849 : fmtId(amname));
14850 :
14851 : /* Build the fixed portion of the CREATE command */
14852 1074 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14853 1074 : fmtQualifiedDumpable(opfinfo));
14854 1074 : appendPQExpBuffer(q, " USING %s;\n",
14855 : fmtId(amname));
14856 :
14857 1074 : PQclear(res);
14858 :
14859 : /* Do we need an ALTER to add loose members? */
14860 1074 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14861 : {
14862 94 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14863 94 : fmtQualifiedDumpable(opfinfo));
14864 94 : appendPQExpBuffer(q, " USING %s ADD\n ",
14865 : fmtId(amname));
14866 :
14867 94 : needComma = false;
14868 :
14869 : /*
14870 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14871 : */
14872 94 : ntups = PQntuples(res_ops);
14873 :
14874 94 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14875 94 : i_amopopr = PQfnumber(res_ops, "amopopr");
14876 94 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14877 94 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14878 :
14879 414 : for (i = 0; i < ntups; i++)
14880 : {
14881 320 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14882 320 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14883 320 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14884 320 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14885 :
14886 320 : if (needComma)
14887 256 : appendPQExpBufferStr(q, " ,\n ");
14888 :
14889 320 : appendPQExpBuffer(q, "OPERATOR %s %s",
14890 : amopstrategy, amopopr);
14891 :
14892 320 : if (strlen(sortfamily) > 0)
14893 : {
14894 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14895 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14896 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14897 : }
14898 :
14899 320 : needComma = true;
14900 : }
14901 :
14902 : /*
14903 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14904 : */
14905 94 : ntups = PQntuples(res_procs);
14906 :
14907 94 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14908 94 : i_amproc = PQfnumber(res_procs, "amproc");
14909 94 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14910 94 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14911 :
14912 444 : for (i = 0; i < ntups; i++)
14913 : {
14914 350 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14915 350 : amproc = PQgetvalue(res_procs, i, i_amproc);
14916 350 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14917 350 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14918 :
14919 350 : if (needComma)
14920 320 : appendPQExpBufferStr(q, " ,\n ");
14921 :
14922 350 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14923 : amprocnum, amproclefttype, amprocrighttype,
14924 : amproc);
14925 :
14926 350 : needComma = true;
14927 : }
14928 :
14929 94 : appendPQExpBufferStr(q, ";\n");
14930 : }
14931 :
14932 1074 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14933 1074 : appendPQExpBuffer(nameusing, " USING %s",
14934 : fmtId(amname));
14935 :
14936 1074 : if (dopt->binary_upgrade)
14937 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14938 18 : "OPERATOR FAMILY", nameusing->data,
14939 18 : opfinfo->dobj.namespace->dobj.name);
14940 :
14941 1074 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14942 1074 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14943 1074 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14944 : .namespace = opfinfo->dobj.namespace->dobj.name,
14945 : .owner = opfinfo->rolname,
14946 : .description = "OPERATOR FAMILY",
14947 : .section = SECTION_PRE_DATA,
14948 : .createStmt = q->data,
14949 : .dropStmt = delq->data));
14950 :
14951 : /* Dump Operator Family Comments */
14952 1074 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14953 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14954 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14955 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14956 :
14957 1074 : free(amname);
14958 1074 : PQclear(res_ops);
14959 1074 : PQclear(res_procs);
14960 1074 : destroyPQExpBuffer(query);
14961 1074 : destroyPQExpBuffer(q);
14962 1074 : destroyPQExpBuffer(delq);
14963 1074 : destroyPQExpBuffer(nameusing);
14964 : }
14965 :
14966 : /*
14967 : * dumpCollation
14968 : * write out a single collation definition
14969 : */
14970 : static void
14971 5074 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14972 : {
14973 5074 : DumpOptions *dopt = fout->dopt;
14974 : PQExpBuffer query;
14975 : PQExpBuffer q;
14976 : PQExpBuffer delq;
14977 : char *qcollname;
14978 : PGresult *res;
14979 : int i_collprovider;
14980 : int i_collisdeterministic;
14981 : int i_collcollate;
14982 : int i_collctype;
14983 : int i_colllocale;
14984 : int i_collicurules;
14985 : const char *collprovider;
14986 : const char *collcollate;
14987 : const char *collctype;
14988 : const char *colllocale;
14989 : const char *collicurules;
14990 :
14991 : /* Do nothing if not dumping schema */
14992 5074 : if (!dopt->dumpSchema)
14993 24 : return;
14994 :
14995 5050 : query = createPQExpBuffer();
14996 5050 : q = createPQExpBuffer();
14997 5050 : delq = createPQExpBuffer();
14998 :
14999 5050 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15000 :
15001 : /* Get collation-specific details */
15002 5050 : appendPQExpBufferStr(query, "SELECT ");
15003 :
15004 5050 : if (fout->remoteVersion >= 100000)
15005 5050 : appendPQExpBufferStr(query,
15006 : "collprovider, "
15007 : "collversion, ");
15008 : else
15009 0 : appendPQExpBufferStr(query,
15010 : "'c' AS collprovider, "
15011 : "NULL AS collversion, ");
15012 :
15013 5050 : if (fout->remoteVersion >= 120000)
15014 5050 : appendPQExpBufferStr(query,
15015 : "collisdeterministic, ");
15016 : else
15017 0 : appendPQExpBufferStr(query,
15018 : "true AS collisdeterministic, ");
15019 :
15020 5050 : if (fout->remoteVersion >= 170000)
15021 5050 : appendPQExpBufferStr(query,
15022 : "colllocale, ");
15023 0 : else if (fout->remoteVersion >= 150000)
15024 0 : appendPQExpBufferStr(query,
15025 : "colliculocale AS colllocale, ");
15026 : else
15027 0 : appendPQExpBufferStr(query,
15028 : "NULL AS colllocale, ");
15029 :
15030 5050 : if (fout->remoteVersion >= 160000)
15031 5050 : appendPQExpBufferStr(query,
15032 : "collicurules, ");
15033 : else
15034 0 : appendPQExpBufferStr(query,
15035 : "NULL AS collicurules, ");
15036 :
15037 5050 : appendPQExpBuffer(query,
15038 : "collcollate, "
15039 : "collctype "
15040 : "FROM pg_catalog.pg_collation c "
15041 : "WHERE c.oid = '%u'::pg_catalog.oid",
15042 5050 : collinfo->dobj.catId.oid);
15043 :
15044 5050 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15045 :
15046 5050 : i_collprovider = PQfnumber(res, "collprovider");
15047 5050 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15048 5050 : i_collcollate = PQfnumber(res, "collcollate");
15049 5050 : i_collctype = PQfnumber(res, "collctype");
15050 5050 : i_colllocale = PQfnumber(res, "colllocale");
15051 5050 : i_collicurules = PQfnumber(res, "collicurules");
15052 :
15053 5050 : collprovider = PQgetvalue(res, 0, i_collprovider);
15054 :
15055 5050 : if (!PQgetisnull(res, 0, i_collcollate))
15056 92 : collcollate = PQgetvalue(res, 0, i_collcollate);
15057 : else
15058 4958 : collcollate = NULL;
15059 :
15060 5050 : if (!PQgetisnull(res, 0, i_collctype))
15061 92 : collctype = PQgetvalue(res, 0, i_collctype);
15062 : else
15063 4958 : collctype = NULL;
15064 :
15065 : /*
15066 : * Before version 15, collcollate and collctype were of type NAME and
15067 : * non-nullable. Treat empty strings as NULL for consistency.
15068 : */
15069 5050 : if (fout->remoteVersion < 150000)
15070 : {
15071 0 : if (collcollate[0] == '\0')
15072 0 : collcollate = NULL;
15073 0 : if (collctype[0] == '\0')
15074 0 : collctype = NULL;
15075 : }
15076 :
15077 5050 : if (!PQgetisnull(res, 0, i_colllocale))
15078 4952 : colllocale = PQgetvalue(res, 0, i_colllocale);
15079 : else
15080 98 : colllocale = NULL;
15081 :
15082 5050 : if (!PQgetisnull(res, 0, i_collicurules))
15083 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15084 : else
15085 5050 : collicurules = NULL;
15086 :
15087 5050 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15088 5050 : fmtQualifiedDumpable(collinfo));
15089 :
15090 5050 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15091 5050 : fmtQualifiedDumpable(collinfo));
15092 :
15093 5050 : appendPQExpBufferStr(q, "provider = ");
15094 5050 : if (collprovider[0] == 'b')
15095 38 : appendPQExpBufferStr(q, "builtin");
15096 5012 : else if (collprovider[0] == 'c')
15097 92 : appendPQExpBufferStr(q, "libc");
15098 4920 : else if (collprovider[0] == 'i')
15099 4914 : appendPQExpBufferStr(q, "icu");
15100 6 : else if (collprovider[0] == 'd')
15101 : /* to allow dumping pg_catalog; not accepted on input */
15102 6 : appendPQExpBufferStr(q, "default");
15103 : else
15104 0 : pg_fatal("unrecognized collation provider: %s",
15105 : collprovider);
15106 :
15107 5050 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15108 0 : appendPQExpBufferStr(q, ", deterministic = false");
15109 :
15110 5050 : if (collprovider[0] == 'd')
15111 : {
15112 6 : if (collcollate || collctype || colllocale || collicurules)
15113 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15114 :
15115 : /* no locale -- the default collation cannot be reloaded anyway */
15116 : }
15117 5044 : else if (collprovider[0] == 'b')
15118 : {
15119 38 : if (collcollate || collctype || !colllocale || collicurules)
15120 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15121 :
15122 38 : appendPQExpBufferStr(q, ", locale = ");
15123 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15124 : fout);
15125 : }
15126 5006 : else if (collprovider[0] == 'i')
15127 : {
15128 4914 : if (fout->remoteVersion >= 150000)
15129 : {
15130 4914 : if (collcollate || collctype || !colllocale)
15131 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15132 :
15133 4914 : appendPQExpBufferStr(q, ", locale = ");
15134 4914 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15135 : fout);
15136 : }
15137 : else
15138 : {
15139 0 : if (!collcollate || !collctype || colllocale ||
15140 0 : strcmp(collcollate, collctype) != 0)
15141 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15142 :
15143 0 : appendPQExpBufferStr(q, ", locale = ");
15144 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15145 : }
15146 :
15147 4914 : if (collicurules)
15148 : {
15149 0 : appendPQExpBufferStr(q, ", rules = ");
15150 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15151 : }
15152 : }
15153 92 : else if (collprovider[0] == 'c')
15154 : {
15155 92 : if (colllocale || collicurules || !collcollate || !collctype)
15156 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15157 :
15158 92 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15159 : {
15160 92 : appendPQExpBufferStr(q, ", locale = ");
15161 92 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15162 : }
15163 : else
15164 : {
15165 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15166 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15167 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15168 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15169 : }
15170 : }
15171 : else
15172 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15173 :
15174 : /*
15175 : * For binary upgrade, carry over the collation version. For normal
15176 : * dump/restore, omit the version, so that it is computed upon restore.
15177 : */
15178 5050 : if (dopt->binary_upgrade)
15179 : {
15180 : int i_collversion;
15181 :
15182 10 : i_collversion = PQfnumber(res, "collversion");
15183 10 : if (!PQgetisnull(res, 0, i_collversion))
15184 : {
15185 8 : appendPQExpBufferStr(q, ", version = ");
15186 8 : appendStringLiteralAH(q,
15187 : PQgetvalue(res, 0, i_collversion),
15188 : fout);
15189 : }
15190 : }
15191 :
15192 5050 : appendPQExpBufferStr(q, ");\n");
15193 :
15194 5050 : if (dopt->binary_upgrade)
15195 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
15196 : "COLLATION", qcollname,
15197 10 : collinfo->dobj.namespace->dobj.name);
15198 :
15199 5050 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15200 5050 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15201 5050 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15202 : .namespace = collinfo->dobj.namespace->dobj.name,
15203 : .owner = collinfo->rolname,
15204 : .description = "COLLATION",
15205 : .section = SECTION_PRE_DATA,
15206 : .createStmt = q->data,
15207 : .dropStmt = delq->data));
15208 :
15209 : /* Dump Collation Comments */
15210 5050 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15211 4862 : dumpComment(fout, "COLLATION", qcollname,
15212 4862 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15213 4862 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15214 :
15215 5050 : PQclear(res);
15216 :
15217 5050 : destroyPQExpBuffer(query);
15218 5050 : destroyPQExpBuffer(q);
15219 5050 : destroyPQExpBuffer(delq);
15220 5050 : free(qcollname);
15221 : }
15222 :
15223 : /*
15224 : * dumpConversion
15225 : * write out a single conversion definition
15226 : */
15227 : static void
15228 844 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15229 : {
15230 844 : DumpOptions *dopt = fout->dopt;
15231 : PQExpBuffer query;
15232 : PQExpBuffer q;
15233 : PQExpBuffer delq;
15234 : char *qconvname;
15235 : PGresult *res;
15236 : int i_conforencoding;
15237 : int i_contoencoding;
15238 : int i_conproc;
15239 : int i_condefault;
15240 : const char *conforencoding;
15241 : const char *contoencoding;
15242 : const char *conproc;
15243 : bool condefault;
15244 :
15245 : /* Do nothing if not dumping schema */
15246 844 : if (!dopt->dumpSchema)
15247 12 : return;
15248 :
15249 832 : query = createPQExpBuffer();
15250 832 : q = createPQExpBuffer();
15251 832 : delq = createPQExpBuffer();
15252 :
15253 832 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15254 :
15255 : /* Get conversion-specific details */
15256 832 : appendPQExpBuffer(query, "SELECT "
15257 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15258 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15259 : "conproc, condefault "
15260 : "FROM pg_catalog.pg_conversion c "
15261 : "WHERE c.oid = '%u'::pg_catalog.oid",
15262 832 : convinfo->dobj.catId.oid);
15263 :
15264 832 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15265 :
15266 832 : i_conforencoding = PQfnumber(res, "conforencoding");
15267 832 : i_contoencoding = PQfnumber(res, "contoencoding");
15268 832 : i_conproc = PQfnumber(res, "conproc");
15269 832 : i_condefault = PQfnumber(res, "condefault");
15270 :
15271 832 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15272 832 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15273 832 : conproc = PQgetvalue(res, 0, i_conproc);
15274 832 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15275 :
15276 832 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15277 832 : fmtQualifiedDumpable(convinfo));
15278 :
15279 832 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15280 : (condefault) ? "DEFAULT " : "",
15281 832 : fmtQualifiedDumpable(convinfo));
15282 832 : appendStringLiteralAH(q, conforencoding, fout);
15283 832 : appendPQExpBufferStr(q, " TO ");
15284 832 : appendStringLiteralAH(q, contoencoding, fout);
15285 : /* regproc output is already sufficiently quoted */
15286 832 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15287 :
15288 832 : if (dopt->binary_upgrade)
15289 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
15290 : "CONVERSION", qconvname,
15291 2 : convinfo->dobj.namespace->dobj.name);
15292 :
15293 832 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15294 832 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15295 832 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15296 : .namespace = convinfo->dobj.namespace->dobj.name,
15297 : .owner = convinfo->rolname,
15298 : .description = "CONVERSION",
15299 : .section = SECTION_PRE_DATA,
15300 : .createStmt = q->data,
15301 : .dropStmt = delq->data));
15302 :
15303 : /* Dump Conversion Comments */
15304 832 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15305 832 : dumpComment(fout, "CONVERSION", qconvname,
15306 832 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15307 832 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15308 :
15309 832 : PQclear(res);
15310 :
15311 832 : destroyPQExpBuffer(query);
15312 832 : destroyPQExpBuffer(q);
15313 832 : destroyPQExpBuffer(delq);
15314 832 : free(qconvname);
15315 : }
15316 :
15317 : /*
15318 : * format_aggregate_signature: generate aggregate name and argument list
15319 : *
15320 : * The argument type names are qualified if needed. The aggregate name
15321 : * is never qualified.
15322 : */
15323 : static char *
15324 570 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15325 : {
15326 : PQExpBufferData buf;
15327 : int j;
15328 :
15329 570 : initPQExpBuffer(&buf);
15330 570 : if (honor_quotes)
15331 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15332 : else
15333 570 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15334 :
15335 570 : if (agginfo->aggfn.nargs == 0)
15336 80 : appendPQExpBufferStr(&buf, "(*)");
15337 : else
15338 : {
15339 490 : appendPQExpBufferChar(&buf, '(');
15340 1070 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15341 580 : appendPQExpBuffer(&buf, "%s%s",
15342 : (j > 0) ? ", " : "",
15343 : getFormattedTypeName(fout,
15344 580 : agginfo->aggfn.argtypes[j],
15345 : zeroIsError));
15346 490 : appendPQExpBufferChar(&buf, ')');
15347 : }
15348 570 : return buf.data;
15349 : }
15350 :
15351 : /*
15352 : * dumpAgg
15353 : * write out a single aggregate definition
15354 : */
15355 : static void
15356 584 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15357 : {
15358 584 : DumpOptions *dopt = fout->dopt;
15359 : PQExpBuffer query;
15360 : PQExpBuffer q;
15361 : PQExpBuffer delq;
15362 : PQExpBuffer details;
15363 : char *aggsig; /* identity signature */
15364 584 : char *aggfullsig = NULL; /* full signature */
15365 : char *aggsig_tag;
15366 : PGresult *res;
15367 : int i_agginitval;
15368 : int i_aggminitval;
15369 : const char *aggtransfn;
15370 : const char *aggfinalfn;
15371 : const char *aggcombinefn;
15372 : const char *aggserialfn;
15373 : const char *aggdeserialfn;
15374 : const char *aggmtransfn;
15375 : const char *aggminvtransfn;
15376 : const char *aggmfinalfn;
15377 : bool aggfinalextra;
15378 : bool aggmfinalextra;
15379 : char aggfinalmodify;
15380 : char aggmfinalmodify;
15381 : const char *aggsortop;
15382 : char *aggsortconvop;
15383 : char aggkind;
15384 : const char *aggtranstype;
15385 : const char *aggtransspace;
15386 : const char *aggmtranstype;
15387 : const char *aggmtransspace;
15388 : const char *agginitval;
15389 : const char *aggminitval;
15390 : const char *proparallel;
15391 : char defaultfinalmodify;
15392 :
15393 : /* Do nothing if not dumping schema */
15394 584 : if (!dopt->dumpSchema)
15395 14 : return;
15396 :
15397 570 : query = createPQExpBuffer();
15398 570 : q = createPQExpBuffer();
15399 570 : delq = createPQExpBuffer();
15400 570 : details = createPQExpBuffer();
15401 :
15402 570 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15403 : {
15404 : /* Set up query for aggregate-specific details */
15405 110 : appendPQExpBufferStr(query,
15406 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15407 :
15408 110 : appendPQExpBufferStr(query,
15409 : "SELECT "
15410 : "aggtransfn,\n"
15411 : "aggfinalfn,\n"
15412 : "aggtranstype::pg_catalog.regtype,\n"
15413 : "agginitval,\n"
15414 : "aggsortop,\n"
15415 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15416 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15417 :
15418 110 : if (fout->remoteVersion >= 90400)
15419 110 : appendPQExpBufferStr(query,
15420 : "aggkind,\n"
15421 : "aggmtransfn,\n"
15422 : "aggminvtransfn,\n"
15423 : "aggmfinalfn,\n"
15424 : "aggmtranstype::pg_catalog.regtype,\n"
15425 : "aggfinalextra,\n"
15426 : "aggmfinalextra,\n"
15427 : "aggtransspace,\n"
15428 : "aggmtransspace,\n"
15429 : "aggminitval,\n");
15430 : else
15431 0 : appendPQExpBufferStr(query,
15432 : "'n' AS aggkind,\n"
15433 : "'-' AS aggmtransfn,\n"
15434 : "'-' AS aggminvtransfn,\n"
15435 : "'-' AS aggmfinalfn,\n"
15436 : "0 AS aggmtranstype,\n"
15437 : "false AS aggfinalextra,\n"
15438 : "false AS aggmfinalextra,\n"
15439 : "0 AS aggtransspace,\n"
15440 : "0 AS aggmtransspace,\n"
15441 : "NULL AS aggminitval,\n");
15442 :
15443 110 : if (fout->remoteVersion >= 90600)
15444 110 : appendPQExpBufferStr(query,
15445 : "aggcombinefn,\n"
15446 : "aggserialfn,\n"
15447 : "aggdeserialfn,\n"
15448 : "proparallel,\n");
15449 : else
15450 0 : appendPQExpBufferStr(query,
15451 : "'-' AS aggcombinefn,\n"
15452 : "'-' AS aggserialfn,\n"
15453 : "'-' AS aggdeserialfn,\n"
15454 : "'u' AS proparallel,\n");
15455 :
15456 110 : if (fout->remoteVersion >= 110000)
15457 110 : appendPQExpBufferStr(query,
15458 : "aggfinalmodify,\n"
15459 : "aggmfinalmodify\n");
15460 : else
15461 0 : appendPQExpBufferStr(query,
15462 : "'0' AS aggfinalmodify,\n"
15463 : "'0' AS aggmfinalmodify\n");
15464 :
15465 110 : appendPQExpBufferStr(query,
15466 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15467 : "WHERE a.aggfnoid = p.oid "
15468 : "AND p.oid = $1");
15469 :
15470 110 : ExecuteSqlStatement(fout, query->data);
15471 :
15472 110 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15473 : }
15474 :
15475 570 : printfPQExpBuffer(query,
15476 : "EXECUTE dumpAgg('%u')",
15477 570 : agginfo->aggfn.dobj.catId.oid);
15478 :
15479 570 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15480 :
15481 570 : i_agginitval = PQfnumber(res, "agginitval");
15482 570 : i_aggminitval = PQfnumber(res, "aggminitval");
15483 :
15484 570 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15485 570 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15486 570 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15487 570 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15488 570 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15489 570 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15490 570 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15491 570 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15492 570 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15493 570 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15494 570 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15495 570 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15496 570 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15497 570 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15498 570 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15499 570 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15500 570 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15501 570 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15502 570 : agginitval = PQgetvalue(res, 0, i_agginitval);
15503 570 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15504 570 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15505 :
15506 : {
15507 : char *funcargs;
15508 : char *funciargs;
15509 :
15510 570 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15511 570 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15512 570 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15513 570 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15514 : }
15515 :
15516 570 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15517 :
15518 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15519 570 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15520 : /* replace omitted flags for old versions */
15521 570 : if (aggfinalmodify == '0')
15522 0 : aggfinalmodify = defaultfinalmodify;
15523 570 : if (aggmfinalmodify == '0')
15524 0 : aggmfinalmodify = defaultfinalmodify;
15525 :
15526 : /* regproc and regtype output is already sufficiently quoted */
15527 570 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15528 : aggtransfn, aggtranstype);
15529 :
15530 570 : if (strcmp(aggtransspace, "0") != 0)
15531 : {
15532 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15533 : aggtransspace);
15534 : }
15535 :
15536 570 : if (!PQgetisnull(res, 0, i_agginitval))
15537 : {
15538 414 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15539 414 : appendStringLiteralAH(details, agginitval, fout);
15540 : }
15541 :
15542 570 : if (strcmp(aggfinalfn, "-") != 0)
15543 : {
15544 264 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15545 : aggfinalfn);
15546 264 : if (aggfinalextra)
15547 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15548 264 : if (aggfinalmodify != defaultfinalmodify)
15549 : {
15550 64 : switch (aggfinalmodify)
15551 : {
15552 0 : case AGGMODIFY_READ_ONLY:
15553 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15554 0 : break;
15555 64 : case AGGMODIFY_SHAREABLE:
15556 64 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15557 64 : break;
15558 0 : case AGGMODIFY_READ_WRITE:
15559 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15560 0 : break;
15561 0 : default:
15562 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15563 : agginfo->aggfn.dobj.name);
15564 : break;
15565 : }
15566 : }
15567 : }
15568 :
15569 570 : if (strcmp(aggcombinefn, "-") != 0)
15570 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15571 :
15572 570 : if (strcmp(aggserialfn, "-") != 0)
15573 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15574 :
15575 570 : if (strcmp(aggdeserialfn, "-") != 0)
15576 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15577 :
15578 570 : if (strcmp(aggmtransfn, "-") != 0)
15579 : {
15580 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15581 : aggmtransfn,
15582 : aggminvtransfn,
15583 : aggmtranstype);
15584 : }
15585 :
15586 570 : if (strcmp(aggmtransspace, "0") != 0)
15587 : {
15588 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15589 : aggmtransspace);
15590 : }
15591 :
15592 570 : if (!PQgetisnull(res, 0, i_aggminitval))
15593 : {
15594 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15595 20 : appendStringLiteralAH(details, aggminitval, fout);
15596 : }
15597 :
15598 570 : if (strcmp(aggmfinalfn, "-") != 0)
15599 : {
15600 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15601 : aggmfinalfn);
15602 0 : if (aggmfinalextra)
15603 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15604 0 : if (aggmfinalmodify != defaultfinalmodify)
15605 : {
15606 0 : switch (aggmfinalmodify)
15607 : {
15608 0 : case AGGMODIFY_READ_ONLY:
15609 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15610 0 : break;
15611 0 : case AGGMODIFY_SHAREABLE:
15612 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15613 0 : break;
15614 0 : case AGGMODIFY_READ_WRITE:
15615 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15616 0 : break;
15617 0 : default:
15618 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15619 : agginfo->aggfn.dobj.name);
15620 : break;
15621 : }
15622 : }
15623 : }
15624 :
15625 570 : aggsortconvop = getFormattedOperatorName(aggsortop);
15626 570 : if (aggsortconvop)
15627 : {
15628 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15629 : aggsortconvop);
15630 0 : free(aggsortconvop);
15631 : }
15632 :
15633 570 : if (aggkind == AGGKIND_HYPOTHETICAL)
15634 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15635 :
15636 570 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15637 : {
15638 10 : if (proparallel[0] == PROPARALLEL_SAFE)
15639 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15640 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15641 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15642 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15643 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15644 : agginfo->aggfn.dobj.name);
15645 : }
15646 :
15647 570 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15648 570 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15649 : aggsig);
15650 :
15651 1140 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15652 570 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15653 : aggfullsig ? aggfullsig : aggsig, details->data);
15654 :
15655 570 : if (dopt->binary_upgrade)
15656 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15657 : "AGGREGATE", aggsig,
15658 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15659 :
15660 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15661 536 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15662 536 : agginfo->aggfn.dobj.dumpId,
15663 536 : ARCHIVE_OPTS(.tag = aggsig_tag,
15664 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15665 : .owner = agginfo->aggfn.rolname,
15666 : .description = "AGGREGATE",
15667 : .section = SECTION_PRE_DATA,
15668 : .createStmt = q->data,
15669 : .dropStmt = delq->data));
15670 :
15671 : /* Dump Aggregate Comments */
15672 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15673 20 : dumpComment(fout, "AGGREGATE", aggsig,
15674 20 : agginfo->aggfn.dobj.namespace->dobj.name,
15675 20 : agginfo->aggfn.rolname,
15676 20 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15677 :
15678 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15679 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15680 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15681 0 : agginfo->aggfn.rolname,
15682 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15683 :
15684 : /*
15685 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15686 : * command look like a function's GRANT; in particular this affects the
15687 : * syntax for zero-argument aggregates and ordered-set aggregates.
15688 : */
15689 570 : free(aggsig);
15690 :
15691 570 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15692 :
15693 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15694 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15695 : "FUNCTION", aggsig, NULL,
15696 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15697 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15698 :
15699 570 : free(aggsig);
15700 570 : free(aggfullsig);
15701 570 : free(aggsig_tag);
15702 :
15703 570 : PQclear(res);
15704 :
15705 570 : destroyPQExpBuffer(query);
15706 570 : destroyPQExpBuffer(q);
15707 570 : destroyPQExpBuffer(delq);
15708 570 : destroyPQExpBuffer(details);
15709 : }
15710 :
15711 : /*
15712 : * dumpTSParser
15713 : * write out a single text search parser
15714 : */
15715 : static void
15716 82 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15717 : {
15718 82 : DumpOptions *dopt = fout->dopt;
15719 : PQExpBuffer q;
15720 : PQExpBuffer delq;
15721 : char *qprsname;
15722 :
15723 : /* Do nothing if not dumping schema */
15724 82 : if (!dopt->dumpSchema)
15725 12 : return;
15726 :
15727 70 : q = createPQExpBuffer();
15728 70 : delq = createPQExpBuffer();
15729 :
15730 70 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15731 :
15732 70 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15733 70 : fmtQualifiedDumpable(prsinfo));
15734 :
15735 70 : appendPQExpBuffer(q, " START = %s,\n",
15736 70 : convertTSFunction(fout, prsinfo->prsstart));
15737 70 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15738 70 : convertTSFunction(fout, prsinfo->prstoken));
15739 70 : appendPQExpBuffer(q, " END = %s,\n",
15740 70 : convertTSFunction(fout, prsinfo->prsend));
15741 70 : if (prsinfo->prsheadline != InvalidOid)
15742 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15743 6 : convertTSFunction(fout, prsinfo->prsheadline));
15744 70 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15745 70 : convertTSFunction(fout, prsinfo->prslextype));
15746 :
15747 70 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15748 70 : fmtQualifiedDumpable(prsinfo));
15749 :
15750 70 : if (dopt->binary_upgrade)
15751 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15752 : "TEXT SEARCH PARSER", qprsname,
15753 2 : prsinfo->dobj.namespace->dobj.name);
15754 :
15755 70 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15756 70 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15757 70 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15758 : .namespace = prsinfo->dobj.namespace->dobj.name,
15759 : .description = "TEXT SEARCH PARSER",
15760 : .section = SECTION_PRE_DATA,
15761 : .createStmt = q->data,
15762 : .dropStmt = delq->data));
15763 :
15764 : /* Dump Parser Comments */
15765 70 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15766 70 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15767 70 : prsinfo->dobj.namespace->dobj.name, "",
15768 70 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15769 :
15770 70 : destroyPQExpBuffer(q);
15771 70 : destroyPQExpBuffer(delq);
15772 70 : free(qprsname);
15773 : }
15774 :
15775 : /*
15776 : * dumpTSDictionary
15777 : * write out a single text search dictionary
15778 : */
15779 : static void
15780 346 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15781 : {
15782 346 : DumpOptions *dopt = fout->dopt;
15783 : PQExpBuffer q;
15784 : PQExpBuffer delq;
15785 : PQExpBuffer query;
15786 : char *qdictname;
15787 : PGresult *res;
15788 : char *nspname;
15789 : char *tmplname;
15790 :
15791 : /* Do nothing if not dumping schema */
15792 346 : if (!dopt->dumpSchema)
15793 12 : return;
15794 :
15795 334 : q = createPQExpBuffer();
15796 334 : delq = createPQExpBuffer();
15797 334 : query = createPQExpBuffer();
15798 :
15799 334 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15800 :
15801 : /* Fetch name and namespace of the dictionary's template */
15802 334 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15803 : "FROM pg_ts_template p, pg_namespace n "
15804 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15805 334 : dictinfo->dicttemplate);
15806 334 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15807 334 : nspname = PQgetvalue(res, 0, 0);
15808 334 : tmplname = PQgetvalue(res, 0, 1);
15809 :
15810 334 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15811 334 : fmtQualifiedDumpable(dictinfo));
15812 :
15813 334 : appendPQExpBufferStr(q, " TEMPLATE = ");
15814 334 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15815 334 : appendPQExpBufferStr(q, fmtId(tmplname));
15816 :
15817 334 : PQclear(res);
15818 :
15819 : /* the dictinitoption can be dumped straight into the command */
15820 334 : if (dictinfo->dictinitoption)
15821 264 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15822 :
15823 334 : appendPQExpBufferStr(q, " );\n");
15824 :
15825 334 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15826 334 : fmtQualifiedDumpable(dictinfo));
15827 :
15828 334 : if (dopt->binary_upgrade)
15829 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15830 : "TEXT SEARCH DICTIONARY", qdictname,
15831 20 : dictinfo->dobj.namespace->dobj.name);
15832 :
15833 334 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15834 334 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15835 334 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15836 : .namespace = dictinfo->dobj.namespace->dobj.name,
15837 : .owner = dictinfo->rolname,
15838 : .description = "TEXT SEARCH DICTIONARY",
15839 : .section = SECTION_PRE_DATA,
15840 : .createStmt = q->data,
15841 : .dropStmt = delq->data));
15842 :
15843 : /* Dump Dictionary Comments */
15844 334 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15845 244 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15846 244 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15847 244 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15848 :
15849 334 : destroyPQExpBuffer(q);
15850 334 : destroyPQExpBuffer(delq);
15851 334 : destroyPQExpBuffer(query);
15852 334 : free(qdictname);
15853 : }
15854 :
15855 : /*
15856 : * dumpTSTemplate
15857 : * write out a single text search template
15858 : */
15859 : static void
15860 106 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15861 : {
15862 106 : DumpOptions *dopt = fout->dopt;
15863 : PQExpBuffer q;
15864 : PQExpBuffer delq;
15865 : char *qtmplname;
15866 :
15867 : /* Do nothing if not dumping schema */
15868 106 : if (!dopt->dumpSchema)
15869 12 : return;
15870 :
15871 94 : q = createPQExpBuffer();
15872 94 : delq = createPQExpBuffer();
15873 :
15874 94 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15875 :
15876 94 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15877 94 : fmtQualifiedDumpable(tmplinfo));
15878 :
15879 94 : if (tmplinfo->tmplinit != InvalidOid)
15880 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15881 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15882 94 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15883 94 : convertTSFunction(fout, tmplinfo->tmpllexize));
15884 :
15885 94 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15886 94 : fmtQualifiedDumpable(tmplinfo));
15887 :
15888 94 : if (dopt->binary_upgrade)
15889 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15890 : "TEXT SEARCH TEMPLATE", qtmplname,
15891 2 : tmplinfo->dobj.namespace->dobj.name);
15892 :
15893 94 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15894 94 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15895 94 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15896 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15897 : .description = "TEXT SEARCH TEMPLATE",
15898 : .section = SECTION_PRE_DATA,
15899 : .createStmt = q->data,
15900 : .dropStmt = delq->data));
15901 :
15902 : /* Dump Template Comments */
15903 94 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15904 94 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15905 94 : tmplinfo->dobj.namespace->dobj.name, "",
15906 94 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15907 :
15908 94 : destroyPQExpBuffer(q);
15909 94 : destroyPQExpBuffer(delq);
15910 94 : free(qtmplname);
15911 : }
15912 :
15913 : /*
15914 : * dumpTSConfig
15915 : * write out a single text search configuration
15916 : */
15917 : static void
15918 296 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15919 : {
15920 296 : DumpOptions *dopt = fout->dopt;
15921 : PQExpBuffer q;
15922 : PQExpBuffer delq;
15923 : PQExpBuffer query;
15924 : char *qcfgname;
15925 : PGresult *res;
15926 : char *nspname;
15927 : char *prsname;
15928 : int ntups,
15929 : i;
15930 : int i_tokenname;
15931 : int i_dictname;
15932 :
15933 : /* Do nothing if not dumping schema */
15934 296 : if (!dopt->dumpSchema)
15935 12 : return;
15936 :
15937 284 : q = createPQExpBuffer();
15938 284 : delq = createPQExpBuffer();
15939 284 : query = createPQExpBuffer();
15940 :
15941 284 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15942 :
15943 : /* Fetch name and namespace of the config's parser */
15944 284 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15945 : "FROM pg_ts_parser p, pg_namespace n "
15946 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15947 284 : cfginfo->cfgparser);
15948 284 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15949 284 : nspname = PQgetvalue(res, 0, 0);
15950 284 : prsname = PQgetvalue(res, 0, 1);
15951 :
15952 284 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15953 284 : fmtQualifiedDumpable(cfginfo));
15954 :
15955 284 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15956 284 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15957 :
15958 284 : PQclear(res);
15959 :
15960 284 : resetPQExpBuffer(query);
15961 284 : appendPQExpBuffer(query,
15962 : "SELECT\n"
15963 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15964 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15965 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15966 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15967 : "WHERE m.mapcfg = '%u'\n"
15968 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15969 284 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15970 :
15971 284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15972 284 : ntups = PQntuples(res);
15973 :
15974 284 : i_tokenname = PQfnumber(res, "tokenname");
15975 284 : i_dictname = PQfnumber(res, "dictname");
15976 :
15977 5950 : for (i = 0; i < ntups; i++)
15978 : {
15979 5666 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15980 5666 : char *dictname = PQgetvalue(res, i, i_dictname);
15981 :
15982 5666 : if (i == 0 ||
15983 5382 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15984 : {
15985 : /* starting a new token type, so start a new command */
15986 5396 : if (i > 0)
15987 5112 : appendPQExpBufferStr(q, ";\n");
15988 5396 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
15989 5396 : fmtQualifiedDumpable(cfginfo));
15990 : /* tokenname needs quoting, dictname does NOT */
15991 5396 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
15992 : fmtId(tokenname), dictname);
15993 : }
15994 : else
15995 270 : appendPQExpBuffer(q, ", %s", dictname);
15996 : }
15997 :
15998 284 : if (ntups > 0)
15999 284 : appendPQExpBufferStr(q, ";\n");
16000 :
16001 284 : PQclear(res);
16002 :
16003 284 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16004 284 : fmtQualifiedDumpable(cfginfo));
16005 :
16006 284 : if (dopt->binary_upgrade)
16007 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
16008 : "TEXT SEARCH CONFIGURATION", qcfgname,
16009 10 : cfginfo->dobj.namespace->dobj.name);
16010 :
16011 284 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16012 284 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16013 284 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16014 : .namespace = cfginfo->dobj.namespace->dobj.name,
16015 : .owner = cfginfo->rolname,
16016 : .description = "TEXT SEARCH CONFIGURATION",
16017 : .section = SECTION_PRE_DATA,
16018 : .createStmt = q->data,
16019 : .dropStmt = delq->data));
16020 :
16021 : /* Dump Configuration Comments */
16022 284 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16023 244 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16024 244 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16025 244 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16026 :
16027 284 : destroyPQExpBuffer(q);
16028 284 : destroyPQExpBuffer(delq);
16029 284 : destroyPQExpBuffer(query);
16030 284 : free(qcfgname);
16031 : }
16032 :
16033 : /*
16034 : * dumpForeignDataWrapper
16035 : * write out a single foreign-data wrapper definition
16036 : */
16037 : static void
16038 104 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16039 : {
16040 104 : DumpOptions *dopt = fout->dopt;
16041 : PQExpBuffer q;
16042 : PQExpBuffer delq;
16043 : char *qfdwname;
16044 :
16045 : /* Do nothing if not dumping schema */
16046 104 : if (!dopt->dumpSchema)
16047 14 : return;
16048 :
16049 90 : q = createPQExpBuffer();
16050 90 : delq = createPQExpBuffer();
16051 :
16052 90 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16053 :
16054 90 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16055 : qfdwname);
16056 :
16057 90 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16058 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16059 :
16060 90 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16061 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16062 :
16063 90 : if (strlen(fdwinfo->fdwoptions) > 0)
16064 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16065 :
16066 90 : appendPQExpBufferStr(q, ";\n");
16067 :
16068 90 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16069 : qfdwname);
16070 :
16071 90 : if (dopt->binary_upgrade)
16072 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16073 : "FOREIGN DATA WRAPPER", qfdwname,
16074 : NULL);
16075 :
16076 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16077 90 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16078 90 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16079 : .owner = fdwinfo->rolname,
16080 : .description = "FOREIGN DATA WRAPPER",
16081 : .section = SECTION_PRE_DATA,
16082 : .createStmt = q->data,
16083 : .dropStmt = delq->data));
16084 :
16085 : /* Dump Foreign Data Wrapper Comments */
16086 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16087 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16088 0 : NULL, fdwinfo->rolname,
16089 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16090 :
16091 : /* Handle the ACL */
16092 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16093 62 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16094 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16095 62 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16096 :
16097 90 : free(qfdwname);
16098 :
16099 90 : destroyPQExpBuffer(q);
16100 90 : destroyPQExpBuffer(delq);
16101 : }
16102 :
16103 : /*
16104 : * dumpForeignServer
16105 : * write out a foreign server definition
16106 : */
16107 : static void
16108 112 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16109 : {
16110 112 : DumpOptions *dopt = fout->dopt;
16111 : PQExpBuffer q;
16112 : PQExpBuffer delq;
16113 : PQExpBuffer query;
16114 : PGresult *res;
16115 : char *qsrvname;
16116 : char *fdwname;
16117 :
16118 : /* Do nothing if not dumping schema */
16119 112 : if (!dopt->dumpSchema)
16120 18 : return;
16121 :
16122 94 : q = createPQExpBuffer();
16123 94 : delq = createPQExpBuffer();
16124 94 : query = createPQExpBuffer();
16125 :
16126 94 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16127 :
16128 : /* look up the foreign-data wrapper */
16129 94 : appendPQExpBuffer(query, "SELECT fdwname "
16130 : "FROM pg_foreign_data_wrapper w "
16131 : "WHERE w.oid = '%u'",
16132 94 : srvinfo->srvfdw);
16133 94 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16134 94 : fdwname = PQgetvalue(res, 0, 0);
16135 :
16136 94 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16137 94 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16138 : {
16139 0 : appendPQExpBufferStr(q, " TYPE ");
16140 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16141 : }
16142 94 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16143 : {
16144 0 : appendPQExpBufferStr(q, " VERSION ");
16145 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16146 : }
16147 :
16148 94 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16149 94 : appendPQExpBufferStr(q, fmtId(fdwname));
16150 :
16151 94 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16152 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16153 :
16154 94 : appendPQExpBufferStr(q, ";\n");
16155 :
16156 94 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16157 : qsrvname);
16158 :
16159 94 : if (dopt->binary_upgrade)
16160 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16161 : "SERVER", qsrvname, NULL);
16162 :
16163 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16164 94 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16165 94 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16166 : .owner = srvinfo->rolname,
16167 : .description = "SERVER",
16168 : .section = SECTION_PRE_DATA,
16169 : .createStmt = q->data,
16170 : .dropStmt = delq->data));
16171 :
16172 : /* Dump Foreign Server Comments */
16173 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16174 0 : dumpComment(fout, "SERVER", qsrvname,
16175 0 : NULL, srvinfo->rolname,
16176 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16177 :
16178 : /* Handle the ACL */
16179 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16180 62 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16181 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16182 62 : NULL, srvinfo->rolname, &srvinfo->dacl);
16183 :
16184 : /* Dump user mappings */
16185 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16186 94 : dumpUserMappings(fout,
16187 94 : srvinfo->dobj.name, NULL,
16188 94 : srvinfo->rolname,
16189 94 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16190 :
16191 94 : PQclear(res);
16192 :
16193 94 : free(qsrvname);
16194 :
16195 94 : destroyPQExpBuffer(q);
16196 94 : destroyPQExpBuffer(delq);
16197 94 : destroyPQExpBuffer(query);
16198 : }
16199 :
16200 : /*
16201 : * dumpUserMappings
16202 : *
16203 : * This routine is used to dump any user mappings associated with the
16204 : * server handed to this routine. Should be called after ArchiveEntry()
16205 : * for the server.
16206 : */
16207 : static void
16208 94 : dumpUserMappings(Archive *fout,
16209 : const char *servername, const char *namespace,
16210 : const char *owner,
16211 : CatalogId catalogId, DumpId dumpId)
16212 : {
16213 : PQExpBuffer q;
16214 : PQExpBuffer delq;
16215 : PQExpBuffer query;
16216 : PQExpBuffer tag;
16217 : PGresult *res;
16218 : int ntups;
16219 : int i_usename;
16220 : int i_umoptions;
16221 : int i;
16222 :
16223 94 : q = createPQExpBuffer();
16224 94 : tag = createPQExpBuffer();
16225 94 : delq = createPQExpBuffer();
16226 94 : query = createPQExpBuffer();
16227 :
16228 : /*
16229 : * We read from the publicly accessible view pg_user_mappings, so as not
16230 : * to fail if run by a non-superuser. Note that the view will show
16231 : * umoptions as null if the user hasn't got privileges for the associated
16232 : * server; this means that pg_dump will dump such a mapping, but with no
16233 : * OPTIONS clause. A possible alternative is to skip such mappings
16234 : * altogether, but it's not clear that that's an improvement.
16235 : */
16236 94 : appendPQExpBuffer(query,
16237 : "SELECT usename, "
16238 : "array_to_string(ARRAY("
16239 : "SELECT quote_ident(option_name) || ' ' || "
16240 : "quote_literal(option_value) "
16241 : "FROM pg_options_to_table(umoptions) "
16242 : "ORDER BY option_name"
16243 : "), E',\n ') AS umoptions "
16244 : "FROM pg_user_mappings "
16245 : "WHERE srvid = '%u' "
16246 : "ORDER BY usename",
16247 : catalogId.oid);
16248 :
16249 94 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16250 :
16251 94 : ntups = PQntuples(res);
16252 94 : i_usename = PQfnumber(res, "usename");
16253 94 : i_umoptions = PQfnumber(res, "umoptions");
16254 :
16255 156 : for (i = 0; i < ntups; i++)
16256 : {
16257 : char *usename;
16258 : char *umoptions;
16259 :
16260 62 : usename = PQgetvalue(res, i, i_usename);
16261 62 : umoptions = PQgetvalue(res, i, i_umoptions);
16262 :
16263 62 : resetPQExpBuffer(q);
16264 62 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16265 62 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16266 :
16267 62 : if (umoptions && strlen(umoptions) > 0)
16268 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16269 :
16270 62 : appendPQExpBufferStr(q, ";\n");
16271 :
16272 62 : resetPQExpBuffer(delq);
16273 62 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16274 62 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16275 :
16276 62 : resetPQExpBuffer(tag);
16277 62 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16278 : usename, servername);
16279 :
16280 62 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16281 62 : ARCHIVE_OPTS(.tag = tag->data,
16282 : .namespace = namespace,
16283 : .owner = owner,
16284 : .description = "USER MAPPING",
16285 : .section = SECTION_PRE_DATA,
16286 : .createStmt = q->data,
16287 : .dropStmt = delq->data));
16288 : }
16289 :
16290 94 : PQclear(res);
16291 :
16292 94 : destroyPQExpBuffer(query);
16293 94 : destroyPQExpBuffer(delq);
16294 94 : destroyPQExpBuffer(tag);
16295 94 : destroyPQExpBuffer(q);
16296 94 : }
16297 :
16298 : /*
16299 : * Write out default privileges information
16300 : */
16301 : static void
16302 320 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16303 : {
16304 320 : DumpOptions *dopt = fout->dopt;
16305 : PQExpBuffer q;
16306 : PQExpBuffer tag;
16307 : const char *type;
16308 :
16309 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16310 320 : if (!dopt->dumpSchema || dopt->aclsSkip)
16311 56 : return;
16312 :
16313 264 : q = createPQExpBuffer();
16314 264 : tag = createPQExpBuffer();
16315 :
16316 264 : switch (daclinfo->defaclobjtype)
16317 : {
16318 122 : case DEFACLOBJ_RELATION:
16319 122 : type = "TABLES";
16320 122 : break;
16321 0 : case DEFACLOBJ_SEQUENCE:
16322 0 : type = "SEQUENCES";
16323 0 : break;
16324 122 : case DEFACLOBJ_FUNCTION:
16325 122 : type = "FUNCTIONS";
16326 122 : break;
16327 20 : case DEFACLOBJ_TYPE:
16328 20 : type = "TYPES";
16329 20 : break;
16330 0 : case DEFACLOBJ_NAMESPACE:
16331 0 : type = "SCHEMAS";
16332 0 : break;
16333 0 : case DEFACLOBJ_LARGEOBJECT:
16334 0 : type = "LARGE OBJECTS";
16335 0 : break;
16336 0 : default:
16337 : /* shouldn't get here */
16338 0 : pg_fatal("unrecognized object type in default privileges: %d",
16339 : (int) daclinfo->defaclobjtype);
16340 : type = ""; /* keep compiler quiet */
16341 : }
16342 :
16343 264 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16344 :
16345 : /* build the actual command(s) for this tuple */
16346 264 : if (!buildDefaultACLCommands(type,
16347 264 : daclinfo->dobj.namespace != NULL ?
16348 124 : daclinfo->dobj.namespace->dobj.name : NULL,
16349 264 : daclinfo->dacl.acl,
16350 264 : daclinfo->dacl.acldefault,
16351 264 : daclinfo->defaclrole,
16352 : fout->remoteVersion,
16353 : q))
16354 0 : pg_fatal("could not parse default ACL list (%s)",
16355 : daclinfo->dacl.acl);
16356 :
16357 264 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16358 264 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16359 264 : ARCHIVE_OPTS(.tag = tag->data,
16360 : .namespace = daclinfo->dobj.namespace ?
16361 : daclinfo->dobj.namespace->dobj.name : NULL,
16362 : .owner = daclinfo->defaclrole,
16363 : .description = "DEFAULT ACL",
16364 : .section = SECTION_POST_DATA,
16365 : .createStmt = q->data));
16366 :
16367 264 : destroyPQExpBuffer(tag);
16368 264 : destroyPQExpBuffer(q);
16369 : }
16370 :
16371 : /*----------
16372 : * Write out grant/revoke information
16373 : *
16374 : * 'objDumpId' is the dump ID of the underlying object.
16375 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16376 : * or InvalidDumpId if there is no need for a second dependency.
16377 : * 'type' must be one of
16378 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16379 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16380 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16381 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16382 : * (Currently we assume that subname is only provided for table columns.)
16383 : * 'nspname' is the namespace the object is in (NULL if none).
16384 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16385 : * to use the default for the object type.
16386 : * 'owner' is the owner, NULL if there is no owner (for languages).
16387 : * 'dacl' is the DumpableAcl struct for the object.
16388 : *
16389 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16390 : * no ACL entry was created.
16391 : *----------
16392 : */
16393 : static DumpId
16394 57348 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16395 : const char *type, const char *name, const char *subname,
16396 : const char *nspname, const char *tag, const char *owner,
16397 : const DumpableAcl *dacl)
16398 : {
16399 57348 : DumpId aclDumpId = InvalidDumpId;
16400 57348 : DumpOptions *dopt = fout->dopt;
16401 57348 : const char *acls = dacl->acl;
16402 57348 : const char *acldefault = dacl->acldefault;
16403 57348 : char privtype = dacl->privtype;
16404 57348 : const char *initprivs = dacl->initprivs;
16405 : const char *baseacls;
16406 : PQExpBuffer sql;
16407 :
16408 : /* Do nothing if ACL dump is not enabled */
16409 57348 : if (dopt->aclsSkip)
16410 652 : return InvalidDumpId;
16411 :
16412 : /* --data-only skips ACLs *except* large object ACLs */
16413 56696 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16414 0 : return InvalidDumpId;
16415 :
16416 56696 : sql = createPQExpBuffer();
16417 :
16418 : /*
16419 : * In binary upgrade mode, we don't run an extension's script but instead
16420 : * dump out the objects independently and then recreate them. To preserve
16421 : * any initial privileges which were set on extension objects, we need to
16422 : * compute the set of GRANT and REVOKE commands necessary to get from the
16423 : * default privileges of an object to its initial privileges as recorded
16424 : * in pg_init_privs.
16425 : *
16426 : * At restore time, we apply these commands after having called
16427 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16428 : * copy the results into pg_init_privs. This is how we preserve the
16429 : * contents of that catalog across binary upgrades.
16430 : */
16431 56696 : if (dopt->binary_upgrade && privtype == 'e' &&
16432 26 : initprivs && *initprivs != '\0')
16433 : {
16434 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16435 26 : if (!buildACLCommands(name, subname, nspname, type,
16436 : initprivs, acldefault, owner,
16437 : "", fout->remoteVersion, sql))
16438 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16439 : initprivs, acldefault, name, type);
16440 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16441 : }
16442 :
16443 : /*
16444 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16445 : * actual current ACL, starting from the initprivs if given, else from the
16446 : * object-type-specific default. Also, while buildACLCommands will assume
16447 : * that a NULL/empty acls string means it needn't do anything, what that
16448 : * actually represents is the object-type-specific default; so we need to
16449 : * substitute the acldefault string to get the right results in that case.
16450 : */
16451 56696 : if (initprivs && *initprivs != '\0')
16452 : {
16453 53244 : baseacls = initprivs;
16454 53244 : if (acls == NULL || *acls == '\0')
16455 34 : acls = acldefault;
16456 : }
16457 : else
16458 3452 : baseacls = acldefault;
16459 :
16460 56696 : if (!buildACLCommands(name, subname, nspname, type,
16461 : acls, baseacls, owner,
16462 : "", fout->remoteVersion, sql))
16463 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16464 : acls, baseacls, name, type);
16465 :
16466 56696 : if (sql->len > 0)
16467 : {
16468 3568 : PQExpBuffer tagbuf = createPQExpBuffer();
16469 : DumpId aclDeps[2];
16470 3568 : int nDeps = 0;
16471 :
16472 3568 : if (tag)
16473 0 : appendPQExpBufferStr(tagbuf, tag);
16474 3568 : else if (subname)
16475 2094 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16476 : else
16477 1474 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16478 :
16479 3568 : aclDeps[nDeps++] = objDumpId;
16480 3568 : if (altDumpId != InvalidDumpId)
16481 1926 : aclDeps[nDeps++] = altDumpId;
16482 :
16483 3568 : aclDumpId = createDumpId();
16484 :
16485 3568 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16486 3568 : ARCHIVE_OPTS(.tag = tagbuf->data,
16487 : .namespace = nspname,
16488 : .owner = owner,
16489 : .description = "ACL",
16490 : .section = SECTION_NONE,
16491 : .createStmt = sql->data,
16492 : .deps = aclDeps,
16493 : .nDeps = nDeps));
16494 :
16495 3568 : destroyPQExpBuffer(tagbuf);
16496 : }
16497 :
16498 56696 : destroyPQExpBuffer(sql);
16499 :
16500 56696 : return aclDumpId;
16501 : }
16502 :
16503 : /*
16504 : * dumpSecLabel
16505 : *
16506 : * This routine is used to dump any security labels associated with the
16507 : * object handed to this routine. The routine takes the object type
16508 : * and object name (ready to print, except for schema decoration), plus
16509 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16510 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16511 : * plus the dump ID for the object (for setting a dependency).
16512 : * If a matching pg_seclabel entry is found, it is dumped.
16513 : *
16514 : * Note: although this routine takes a dumpId for dependency purposes,
16515 : * that purpose is just to mark the dependency in the emitted dump file
16516 : * for possible future use by pg_restore. We do NOT use it for determining
16517 : * ordering of the label in the dump file, because this routine is called
16518 : * after dependency sorting occurs. This routine should be called just after
16519 : * calling ArchiveEntry() for the specified object.
16520 : */
16521 : static void
16522 20 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16523 : const char *namespace, const char *owner,
16524 : CatalogId catalogId, int subid, DumpId dumpId)
16525 : {
16526 20 : DumpOptions *dopt = fout->dopt;
16527 : SecLabelItem *labels;
16528 : int nlabels;
16529 : int i;
16530 : PQExpBuffer query;
16531 :
16532 : /* do nothing, if --no-security-labels is supplied */
16533 20 : if (dopt->no_security_labels)
16534 0 : return;
16535 :
16536 : /*
16537 : * Security labels are schema not data ... except large object labels are
16538 : * data
16539 : */
16540 20 : if (strcmp(type, "LARGE OBJECT") != 0)
16541 : {
16542 0 : if (!dopt->dumpSchema)
16543 0 : return;
16544 : }
16545 : else
16546 : {
16547 : /* We do dump large object security labels in binary-upgrade mode */
16548 20 : if (!dopt->dumpData && !dopt->binary_upgrade)
16549 0 : return;
16550 : }
16551 :
16552 : /* Search for security labels associated with catalogId, using table */
16553 20 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16554 :
16555 20 : query = createPQExpBuffer();
16556 :
16557 30 : for (i = 0; i < nlabels; i++)
16558 : {
16559 : /*
16560 : * Ignore label entries for which the subid doesn't match.
16561 : */
16562 10 : if (labels[i].objsubid != subid)
16563 0 : continue;
16564 :
16565 10 : appendPQExpBuffer(query,
16566 : "SECURITY LABEL FOR %s ON %s ",
16567 10 : fmtId(labels[i].provider), type);
16568 10 : if (namespace && *namespace)
16569 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16570 10 : appendPQExpBuffer(query, "%s IS ", name);
16571 10 : appendStringLiteralAH(query, labels[i].label, fout);
16572 10 : appendPQExpBufferStr(query, ";\n");
16573 : }
16574 :
16575 20 : if (query->len > 0)
16576 : {
16577 10 : PQExpBuffer tag = createPQExpBuffer();
16578 :
16579 10 : appendPQExpBuffer(tag, "%s %s", type, name);
16580 10 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16581 10 : ARCHIVE_OPTS(.tag = tag->data,
16582 : .namespace = namespace,
16583 : .owner = owner,
16584 : .description = "SECURITY LABEL",
16585 : .section = SECTION_NONE,
16586 : .createStmt = query->data,
16587 : .deps = &dumpId,
16588 : .nDeps = 1));
16589 10 : destroyPQExpBuffer(tag);
16590 : }
16591 :
16592 20 : destroyPQExpBuffer(query);
16593 : }
16594 :
16595 : /*
16596 : * dumpTableSecLabel
16597 : *
16598 : * As above, but dump security label for both the specified table (or view)
16599 : * and its columns.
16600 : */
16601 : static void
16602 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16603 : {
16604 0 : DumpOptions *dopt = fout->dopt;
16605 : SecLabelItem *labels;
16606 : int nlabels;
16607 : int i;
16608 : PQExpBuffer query;
16609 : PQExpBuffer target;
16610 :
16611 : /* do nothing, if --no-security-labels is supplied */
16612 0 : if (dopt->no_security_labels)
16613 0 : return;
16614 :
16615 : /* SecLabel are SCHEMA not data */
16616 0 : if (!dopt->dumpSchema)
16617 0 : return;
16618 :
16619 : /* Search for comments associated with relation, using table */
16620 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16621 0 : tbinfo->dobj.catId.oid,
16622 : &labels);
16623 :
16624 : /* If security labels exist, build SECURITY LABEL statements */
16625 0 : if (nlabels <= 0)
16626 0 : return;
16627 :
16628 0 : query = createPQExpBuffer();
16629 0 : target = createPQExpBuffer();
16630 :
16631 0 : for (i = 0; i < nlabels; i++)
16632 : {
16633 : const char *colname;
16634 0 : const char *provider = labels[i].provider;
16635 0 : const char *label = labels[i].label;
16636 0 : int objsubid = labels[i].objsubid;
16637 :
16638 0 : resetPQExpBuffer(target);
16639 0 : if (objsubid == 0)
16640 : {
16641 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16642 0 : fmtQualifiedDumpable(tbinfo));
16643 : }
16644 : else
16645 : {
16646 0 : colname = getAttrName(objsubid, tbinfo);
16647 : /* first fmtXXX result must be consumed before calling again */
16648 0 : appendPQExpBuffer(target, "COLUMN %s",
16649 0 : fmtQualifiedDumpable(tbinfo));
16650 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16651 : }
16652 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16653 : fmtId(provider), target->data);
16654 0 : appendStringLiteralAH(query, label, fout);
16655 0 : appendPQExpBufferStr(query, ";\n");
16656 : }
16657 0 : if (query->len > 0)
16658 : {
16659 0 : resetPQExpBuffer(target);
16660 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16661 0 : fmtId(tbinfo->dobj.name));
16662 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16663 0 : ARCHIVE_OPTS(.tag = target->data,
16664 : .namespace = tbinfo->dobj.namespace->dobj.name,
16665 : .owner = tbinfo->rolname,
16666 : .description = "SECURITY LABEL",
16667 : .section = SECTION_NONE,
16668 : .createStmt = query->data,
16669 : .deps = &(tbinfo->dobj.dumpId),
16670 : .nDeps = 1));
16671 : }
16672 0 : destroyPQExpBuffer(query);
16673 0 : destroyPQExpBuffer(target);
16674 : }
16675 :
16676 : /*
16677 : * findSecLabels
16678 : *
16679 : * Find the security label(s), if any, associated with the given object.
16680 : * All the objsubid values associated with the given classoid/objoid are
16681 : * found with one search.
16682 : */
16683 : static int
16684 20 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16685 : {
16686 20 : SecLabelItem *middle = NULL;
16687 : SecLabelItem *low;
16688 : SecLabelItem *high;
16689 : int nmatch;
16690 :
16691 20 : if (nseclabels <= 0) /* no labels, so no match is possible */
16692 : {
16693 0 : *items = NULL;
16694 0 : return 0;
16695 : }
16696 :
16697 : /*
16698 : * Do binary search to find some item matching the object.
16699 : */
16700 20 : low = &seclabels[0];
16701 20 : high = &seclabels[nseclabels - 1];
16702 30 : while (low <= high)
16703 : {
16704 20 : middle = low + (high - low) / 2;
16705 :
16706 20 : if (classoid < middle->classoid)
16707 0 : high = middle - 1;
16708 20 : else if (classoid > middle->classoid)
16709 0 : low = middle + 1;
16710 20 : else if (objoid < middle->objoid)
16711 10 : high = middle - 1;
16712 10 : else if (objoid > middle->objoid)
16713 0 : low = middle + 1;
16714 : else
16715 10 : break; /* found a match */
16716 : }
16717 :
16718 20 : if (low > high) /* no matches */
16719 : {
16720 10 : *items = NULL;
16721 10 : return 0;
16722 : }
16723 :
16724 : /*
16725 : * Now determine how many items match the object. The search loop
16726 : * invariant still holds: only items between low and high inclusive could
16727 : * match.
16728 : */
16729 10 : nmatch = 1;
16730 10 : while (middle > low)
16731 : {
16732 0 : if (classoid != middle[-1].classoid ||
16733 0 : objoid != middle[-1].objoid)
16734 : break;
16735 0 : middle--;
16736 0 : nmatch++;
16737 : }
16738 :
16739 10 : *items = middle;
16740 :
16741 10 : middle += nmatch;
16742 10 : while (middle <= high)
16743 : {
16744 0 : if (classoid != middle->classoid ||
16745 0 : objoid != middle->objoid)
16746 : break;
16747 0 : middle++;
16748 0 : nmatch++;
16749 : }
16750 :
16751 10 : return nmatch;
16752 : }
16753 :
16754 : /*
16755 : * collectSecLabels
16756 : *
16757 : * Construct a table of all security labels available for database objects;
16758 : * also set the has-seclabel component flag for each relevant object.
16759 : *
16760 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16761 : */
16762 : static void
16763 372 : collectSecLabels(Archive *fout)
16764 : {
16765 : PGresult *res;
16766 : PQExpBuffer query;
16767 : int i_label;
16768 : int i_provider;
16769 : int i_classoid;
16770 : int i_objoid;
16771 : int i_objsubid;
16772 : int ntups;
16773 : int i;
16774 : DumpableObject *dobj;
16775 :
16776 372 : query = createPQExpBuffer();
16777 :
16778 372 : appendPQExpBufferStr(query,
16779 : "SELECT label, provider, classoid, objoid, objsubid "
16780 : "FROM pg_catalog.pg_seclabels "
16781 : "ORDER BY classoid, objoid, objsubid");
16782 :
16783 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16784 :
16785 : /* Construct lookup table containing OIDs in numeric form */
16786 372 : i_label = PQfnumber(res, "label");
16787 372 : i_provider = PQfnumber(res, "provider");
16788 372 : i_classoid = PQfnumber(res, "classoid");
16789 372 : i_objoid = PQfnumber(res, "objoid");
16790 372 : i_objsubid = PQfnumber(res, "objsubid");
16791 :
16792 372 : ntups = PQntuples(res);
16793 :
16794 372 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16795 372 : nseclabels = 0;
16796 372 : dobj = NULL;
16797 :
16798 382 : for (i = 0; i < ntups; i++)
16799 : {
16800 : CatalogId objId;
16801 : int subid;
16802 :
16803 10 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16804 10 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16805 10 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16806 :
16807 : /* We needn't remember labels that don't match any dumpable object */
16808 10 : if (dobj == NULL ||
16809 0 : dobj->catId.tableoid != objId.tableoid ||
16810 0 : dobj->catId.oid != objId.oid)
16811 10 : dobj = findObjectByCatalogId(objId);
16812 10 : if (dobj == NULL)
16813 0 : continue;
16814 :
16815 : /*
16816 : * Labels on columns of composite types are linked to the type's
16817 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16818 : * in the type's own DumpableObject.
16819 : */
16820 10 : if (subid != 0 && dobj->objType == DO_TABLE &&
16821 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16822 0 : {
16823 : TypeInfo *cTypeInfo;
16824 :
16825 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16826 0 : if (cTypeInfo)
16827 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16828 : }
16829 : else
16830 10 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16831 :
16832 10 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16833 10 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16834 10 : seclabels[nseclabels].classoid = objId.tableoid;
16835 10 : seclabels[nseclabels].objoid = objId.oid;
16836 10 : seclabels[nseclabels].objsubid = subid;
16837 10 : nseclabels++;
16838 : }
16839 :
16840 372 : PQclear(res);
16841 372 : destroyPQExpBuffer(query);
16842 372 : }
16843 :
16844 : /*
16845 : * dumpTable
16846 : * write out to fout the declarations (not data) of a user-defined table
16847 : */
16848 : static void
16849 62034 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16850 : {
16851 62034 : DumpOptions *dopt = fout->dopt;
16852 62034 : DumpId tableAclDumpId = InvalidDumpId;
16853 : char *namecopy;
16854 :
16855 : /* Do nothing if not dumping schema */
16856 62034 : if (!dopt->dumpSchema)
16857 3016 : return;
16858 :
16859 59018 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16860 : {
16861 13160 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16862 750 : dumpSequence(fout, tbinfo);
16863 : else
16864 12410 : dumpTableSchema(fout, tbinfo);
16865 : }
16866 :
16867 : /* Handle the ACL here */
16868 59018 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16869 59018 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16870 : {
16871 47300 : const char *objtype =
16872 47300 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16873 :
16874 : tableAclDumpId =
16875 47300 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16876 : objtype, namecopy, NULL,
16877 47300 : tbinfo->dobj.namespace->dobj.name,
16878 47300 : NULL, tbinfo->rolname, &tbinfo->dacl);
16879 : }
16880 :
16881 : /*
16882 : * Handle column ACLs, if any. Note: we pull these with a separate query
16883 : * rather than trying to fetch them during getTableAttrs, so that we won't
16884 : * miss ACLs on system columns. Doing it this way also allows us to dump
16885 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16886 : */
16887 59018 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16888 : {
16889 562 : PQExpBuffer query = createPQExpBuffer();
16890 : PGresult *res;
16891 : int i;
16892 :
16893 562 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16894 : {
16895 : /* Set up query for column ACLs */
16896 320 : appendPQExpBufferStr(query,
16897 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16898 :
16899 320 : if (fout->remoteVersion >= 90600)
16900 : {
16901 : /*
16902 : * In principle we should call acldefault('c', relowner) to
16903 : * get the default ACL for a column. However, we don't
16904 : * currently store the numeric OID of the relowner in
16905 : * TableInfo. We could convert the owner name using regrole,
16906 : * but that creates a risk of failure due to concurrent role
16907 : * renames. Given that the default ACL for columns is empty
16908 : * and is likely to stay that way, it's not worth extra cycles
16909 : * and risk to avoid hard-wiring that knowledge here.
16910 : */
16911 320 : appendPQExpBufferStr(query,
16912 : "SELECT at.attname, "
16913 : "at.attacl, "
16914 : "'{}' AS acldefault, "
16915 : "pip.privtype, pip.initprivs "
16916 : "FROM pg_catalog.pg_attribute at "
16917 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16918 : "(at.attrelid = pip.objoid "
16919 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16920 : "AND at.attnum = pip.objsubid) "
16921 : "WHERE at.attrelid = $1 AND "
16922 : "NOT at.attisdropped "
16923 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16924 : "ORDER BY at.attnum");
16925 : }
16926 : else
16927 : {
16928 0 : appendPQExpBufferStr(query,
16929 : "SELECT attname, attacl, '{}' AS acldefault, "
16930 : "NULL AS privtype, NULL AS initprivs "
16931 : "FROM pg_catalog.pg_attribute "
16932 : "WHERE attrelid = $1 AND NOT attisdropped "
16933 : "AND attacl IS NOT NULL "
16934 : "ORDER BY attnum");
16935 : }
16936 :
16937 320 : ExecuteSqlStatement(fout, query->data);
16938 :
16939 320 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16940 : }
16941 :
16942 562 : printfPQExpBuffer(query,
16943 : "EXECUTE getColumnACLs('%u')",
16944 562 : tbinfo->dobj.catId.oid);
16945 :
16946 562 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16947 :
16948 8936 : for (i = 0; i < PQntuples(res); i++)
16949 : {
16950 8374 : char *attname = PQgetvalue(res, i, 0);
16951 8374 : char *attacl = PQgetvalue(res, i, 1);
16952 8374 : char *acldefault = PQgetvalue(res, i, 2);
16953 8374 : char privtype = *(PQgetvalue(res, i, 3));
16954 8374 : char *initprivs = PQgetvalue(res, i, 4);
16955 : DumpableAcl coldacl;
16956 : char *attnamecopy;
16957 :
16958 8374 : coldacl.acl = attacl;
16959 8374 : coldacl.acldefault = acldefault;
16960 8374 : coldacl.privtype = privtype;
16961 8374 : coldacl.initprivs = initprivs;
16962 8374 : attnamecopy = pg_strdup(fmtId(attname));
16963 :
16964 : /*
16965 : * Column's GRANT type is always TABLE. Each column ACL depends
16966 : * on the table-level ACL, since we can restore column ACLs in
16967 : * parallel but the table-level ACL has to be done first.
16968 : */
16969 8374 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16970 : "TABLE", namecopy, attnamecopy,
16971 8374 : tbinfo->dobj.namespace->dobj.name,
16972 8374 : NULL, tbinfo->rolname, &coldacl);
16973 8374 : free(attnamecopy);
16974 : }
16975 562 : PQclear(res);
16976 562 : destroyPQExpBuffer(query);
16977 : }
16978 :
16979 59018 : free(namecopy);
16980 : }
16981 :
16982 : /*
16983 : * Create the AS clause for a view or materialized view. The semicolon is
16984 : * stripped because a materialized view must add a WITH NO DATA clause.
16985 : *
16986 : * This returns a new buffer which must be freed by the caller.
16987 : */
16988 : static PQExpBuffer
16989 1736 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
16990 : {
16991 1736 : PQExpBuffer query = createPQExpBuffer();
16992 1736 : PQExpBuffer result = createPQExpBuffer();
16993 : PGresult *res;
16994 : int len;
16995 :
16996 : /* Fetch the view definition */
16997 1736 : appendPQExpBuffer(query,
16998 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
16999 1736 : tbinfo->dobj.catId.oid);
17000 :
17001 1736 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17002 :
17003 1736 : if (PQntuples(res) != 1)
17004 : {
17005 0 : if (PQntuples(res) < 1)
17006 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
17007 : tbinfo->dobj.name);
17008 : else
17009 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17010 : tbinfo->dobj.name);
17011 : }
17012 :
17013 1736 : len = PQgetlength(res, 0, 0);
17014 :
17015 1736 : if (len == 0)
17016 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17017 : tbinfo->dobj.name);
17018 :
17019 : /* Strip off the trailing semicolon so that other things may follow. */
17020 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17021 1736 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17022 :
17023 1736 : PQclear(res);
17024 1736 : destroyPQExpBuffer(query);
17025 :
17026 1736 : return result;
17027 : }
17028 :
17029 : /*
17030 : * Create a dummy AS clause for a view. This is used when the real view
17031 : * definition has to be postponed because of circular dependencies.
17032 : * We must duplicate the view's external properties -- column names and types
17033 : * (including collation) -- so that it works for subsequent references.
17034 : *
17035 : * This returns a new buffer which must be freed by the caller.
17036 : */
17037 : static PQExpBuffer
17038 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17039 : {
17040 40 : PQExpBuffer result = createPQExpBuffer();
17041 : int j;
17042 :
17043 40 : appendPQExpBufferStr(result, "SELECT");
17044 :
17045 80 : for (j = 0; j < tbinfo->numatts; j++)
17046 : {
17047 40 : if (j > 0)
17048 20 : appendPQExpBufferChar(result, ',');
17049 40 : appendPQExpBufferStr(result, "\n ");
17050 :
17051 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17052 :
17053 : /*
17054 : * Must add collation if not default for the type, because CREATE OR
17055 : * REPLACE VIEW won't change it
17056 : */
17057 40 : if (OidIsValid(tbinfo->attcollation[j]))
17058 : {
17059 : CollInfo *coll;
17060 :
17061 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17062 0 : if (coll)
17063 0 : appendPQExpBuffer(result, " COLLATE %s",
17064 0 : fmtQualifiedDumpable(coll));
17065 : }
17066 :
17067 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17068 : }
17069 :
17070 40 : return result;
17071 : }
17072 :
17073 : /*
17074 : * dumpTableSchema
17075 : * write the declaration (not data) of one user-defined table or view
17076 : */
17077 : static void
17078 12410 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17079 : {
17080 12410 : DumpOptions *dopt = fout->dopt;
17081 12410 : PQExpBuffer q = createPQExpBuffer();
17082 12410 : PQExpBuffer delq = createPQExpBuffer();
17083 12410 : PQExpBuffer extra = createPQExpBuffer();
17084 : char *qrelname;
17085 : char *qualrelname;
17086 : int numParents;
17087 : TableInfo **parents;
17088 : int actual_atts; /* number of attrs in this CREATE statement */
17089 : const char *reltypename;
17090 : char *storage;
17091 : int j,
17092 : k;
17093 :
17094 : /* We had better have loaded per-column details about this table */
17095 : Assert(tbinfo->interesting);
17096 :
17097 12410 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17098 12410 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17099 :
17100 12410 : if (tbinfo->hasoids)
17101 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17102 : qrelname);
17103 :
17104 12410 : if (dopt->binary_upgrade)
17105 1732 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17106 :
17107 : /* Is it a table or a view? */
17108 12410 : if (tbinfo->relkind == RELKIND_VIEW)
17109 : {
17110 : PQExpBuffer result;
17111 :
17112 : /*
17113 : * Note: keep this code in sync with the is_view case in dumpRule()
17114 : */
17115 :
17116 1066 : reltypename = "VIEW";
17117 :
17118 1066 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17119 :
17120 1066 : if (dopt->binary_upgrade)
17121 104 : binary_upgrade_set_pg_class_oids(fout, q,
17122 104 : tbinfo->dobj.catId.oid);
17123 :
17124 1066 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17125 :
17126 1066 : if (tbinfo->dummy_view)
17127 20 : result = createDummyViewAsClause(fout, tbinfo);
17128 : else
17129 : {
17130 1046 : if (nonemptyReloptions(tbinfo->reloptions))
17131 : {
17132 122 : appendPQExpBufferStr(q, " WITH (");
17133 122 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17134 122 : appendPQExpBufferChar(q, ')');
17135 : }
17136 1046 : result = createViewAsClause(fout, tbinfo);
17137 : }
17138 1066 : appendPQExpBuffer(q, " AS\n%s", result->data);
17139 1066 : destroyPQExpBuffer(result);
17140 :
17141 1066 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17142 64 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17143 1066 : appendPQExpBufferStr(q, ";\n");
17144 : }
17145 : else
17146 : {
17147 11344 : char *partkeydef = NULL;
17148 11344 : char *ftoptions = NULL;
17149 11344 : char *srvname = NULL;
17150 11344 : const char *foreign = "";
17151 :
17152 : /*
17153 : * Set reltypename, and collect any relkind-specific data that we
17154 : * didn't fetch during getTables().
17155 : */
17156 11344 : switch (tbinfo->relkind)
17157 : {
17158 1132 : case RELKIND_PARTITIONED_TABLE:
17159 : {
17160 1132 : PQExpBuffer query = createPQExpBuffer();
17161 : PGresult *res;
17162 :
17163 1132 : reltypename = "TABLE";
17164 :
17165 : /* retrieve partition key definition */
17166 1132 : appendPQExpBuffer(query,
17167 : "SELECT pg_get_partkeydef('%u')",
17168 1132 : tbinfo->dobj.catId.oid);
17169 1132 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17170 1132 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17171 1132 : PQclear(res);
17172 1132 : destroyPQExpBuffer(query);
17173 1132 : break;
17174 : }
17175 68 : case RELKIND_FOREIGN_TABLE:
17176 : {
17177 68 : PQExpBuffer query = createPQExpBuffer();
17178 : PGresult *res;
17179 : int i_srvname;
17180 : int i_ftoptions;
17181 :
17182 68 : reltypename = "FOREIGN TABLE";
17183 :
17184 : /* retrieve name of foreign server and generic options */
17185 68 : appendPQExpBuffer(query,
17186 : "SELECT fs.srvname, "
17187 : "pg_catalog.array_to_string(ARRAY("
17188 : "SELECT pg_catalog.quote_ident(option_name) || "
17189 : "' ' || pg_catalog.quote_literal(option_value) "
17190 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17191 : "ORDER BY option_name"
17192 : "), E',\n ') AS ftoptions "
17193 : "FROM pg_catalog.pg_foreign_table ft "
17194 : "JOIN pg_catalog.pg_foreign_server fs "
17195 : "ON (fs.oid = ft.ftserver) "
17196 : "WHERE ft.ftrelid = '%u'",
17197 68 : tbinfo->dobj.catId.oid);
17198 68 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17199 68 : i_srvname = PQfnumber(res, "srvname");
17200 68 : i_ftoptions = PQfnumber(res, "ftoptions");
17201 68 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17202 68 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17203 68 : PQclear(res);
17204 68 : destroyPQExpBuffer(query);
17205 :
17206 68 : foreign = "FOREIGN ";
17207 68 : break;
17208 : }
17209 670 : case RELKIND_MATVIEW:
17210 670 : reltypename = "MATERIALIZED VIEW";
17211 670 : break;
17212 9474 : default:
17213 9474 : reltypename = "TABLE";
17214 9474 : break;
17215 : }
17216 :
17217 11344 : numParents = tbinfo->numParents;
17218 11344 : parents = tbinfo->parents;
17219 :
17220 11344 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17221 :
17222 11344 : if (dopt->binary_upgrade)
17223 1628 : binary_upgrade_set_pg_class_oids(fout, q,
17224 1628 : tbinfo->dobj.catId.oid);
17225 :
17226 : /*
17227 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17228 : * ignore it when dumping if it was set in this case.
17229 : */
17230 11344 : appendPQExpBuffer(q, "CREATE %s%s %s",
17231 11344 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17232 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17233 : "UNLOGGED " : "",
17234 : reltypename,
17235 : qualrelname);
17236 :
17237 : /*
17238 : * Attach to type, if reloftype; except in case of a binary upgrade,
17239 : * we dump the table normally and attach it to the type afterward.
17240 : */
17241 11344 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17242 48 : appendPQExpBuffer(q, " OF %s",
17243 48 : getFormattedTypeName(fout, tbinfo->reloftype,
17244 : zeroIsError));
17245 :
17246 11344 : if (tbinfo->relkind != RELKIND_MATVIEW)
17247 : {
17248 : /* Dump the attributes */
17249 10674 : actual_atts = 0;
17250 50246 : for (j = 0; j < tbinfo->numatts; j++)
17251 : {
17252 : /*
17253 : * Normally, dump if it's locally defined in this table, and
17254 : * not dropped. But for binary upgrade, we'll dump all the
17255 : * columns, and then fix up the dropped and nonlocal cases
17256 : * below.
17257 : */
17258 39572 : if (shouldPrintColumn(dopt, tbinfo, j))
17259 : {
17260 : bool print_default;
17261 : bool print_notnull;
17262 :
17263 : /*
17264 : * Default value --- suppress if to be printed separately
17265 : * or not at all.
17266 : */
17267 77298 : print_default = (tbinfo->attrdefs[j] != NULL &&
17268 39598 : tbinfo->attrdefs[j]->dobj.dump &&
17269 1992 : !tbinfo->attrdefs[j]->separate);
17270 :
17271 : /*
17272 : * Not Null constraint --- print it if it is locally
17273 : * defined, or if binary upgrade. (In the latter case, we
17274 : * reset conislocal below.)
17275 : */
17276 42036 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17277 4430 : (tbinfo->notnull_islocal[j] ||
17278 1214 : dopt->binary_upgrade ||
17279 1046 : tbinfo->ispartition));
17280 :
17281 : /*
17282 : * Skip column if fully defined by reloftype, except in
17283 : * binary upgrade
17284 : */
17285 37606 : if (OidIsValid(tbinfo->reloftype) &&
17286 100 : !print_default && !print_notnull &&
17287 60 : !dopt->binary_upgrade)
17288 48 : continue;
17289 :
17290 : /* Format properly if not first attr */
17291 37558 : if (actual_atts == 0)
17292 10022 : appendPQExpBufferStr(q, " (");
17293 : else
17294 27536 : appendPQExpBufferChar(q, ',');
17295 37558 : appendPQExpBufferStr(q, "\n ");
17296 37558 : actual_atts++;
17297 :
17298 : /* Attribute name */
17299 37558 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17300 :
17301 37558 : if (tbinfo->attisdropped[j])
17302 : {
17303 : /*
17304 : * ALTER TABLE DROP COLUMN clears
17305 : * pg_attribute.atttypid, so we will not have gotten a
17306 : * valid type name; insert INTEGER as a stopgap. We'll
17307 : * clean things up later.
17308 : */
17309 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17310 : /* and skip to the next column */
17311 168 : continue;
17312 : }
17313 :
17314 : /*
17315 : * Attribute type; print it except when creating a typed
17316 : * table ('OF type_name'), but in binary-upgrade mode,
17317 : * print it in that case too.
17318 : */
17319 37390 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17320 : {
17321 37358 : appendPQExpBuffer(q, " %s",
17322 37358 : tbinfo->atttypnames[j]);
17323 : }
17324 :
17325 37390 : if (print_default)
17326 : {
17327 1732 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17328 554 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17329 554 : tbinfo->attrdefs[j]->adef_expr);
17330 1178 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17331 446 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17332 446 : tbinfo->attrdefs[j]->adef_expr);
17333 : else
17334 732 : appendPQExpBuffer(q, " DEFAULT %s",
17335 732 : tbinfo->attrdefs[j]->adef_expr);
17336 : }
17337 :
17338 37390 : if (print_notnull)
17339 : {
17340 4368 : if (tbinfo->notnull_constrs[j][0] == '\0')
17341 3100 : appendPQExpBufferStr(q, " NOT NULL");
17342 : else
17343 1268 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17344 1268 : fmtId(tbinfo->notnull_constrs[j]));
17345 :
17346 4368 : if (tbinfo->notnull_noinh[j])
17347 0 : appendPQExpBufferStr(q, " NO INHERIT");
17348 : }
17349 :
17350 : /* Add collation if not default for the type */
17351 37390 : if (OidIsValid(tbinfo->attcollation[j]))
17352 : {
17353 : CollInfo *coll;
17354 :
17355 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
17356 394 : if (coll)
17357 394 : appendPQExpBuffer(q, " COLLATE %s",
17358 394 : fmtQualifiedDumpable(coll));
17359 : }
17360 : }
17361 :
17362 : /*
17363 : * On the other hand, if we choose not to print a column
17364 : * (likely because it is created by inheritance), but the
17365 : * column has a locally-defined not-null constraint, we need
17366 : * to dump the constraint as a standalone object.
17367 : *
17368 : * This syntax isn't SQL-conforming, but if you wanted
17369 : * standard output you wouldn't be creating non-standard
17370 : * objects to begin with.
17371 : */
17372 39356 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17373 1966 : !tbinfo->attisdropped[j] &&
17374 1240 : tbinfo->notnull_constrs[j] != NULL &&
17375 354 : tbinfo->notnull_islocal[j])
17376 : {
17377 : /* Format properly if not first attr */
17378 118 : if (actual_atts == 0)
17379 110 : appendPQExpBufferStr(q, " (");
17380 : else
17381 8 : appendPQExpBufferChar(q, ',');
17382 118 : appendPQExpBufferStr(q, "\n ");
17383 118 : actual_atts++;
17384 :
17385 118 : if (tbinfo->notnull_constrs[j][0] == '\0')
17386 16 : appendPQExpBuffer(q, "NOT NULL %s",
17387 16 : fmtId(tbinfo->attnames[j]));
17388 : else
17389 204 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17390 102 : tbinfo->notnull_constrs[j],
17391 102 : fmtId(tbinfo->attnames[j]));
17392 : }
17393 : }
17394 :
17395 : /*
17396 : * Add non-inherited CHECK constraints, if any.
17397 : *
17398 : * For partitions, we need to include check constraints even if
17399 : * they're not defined locally, because the ALTER TABLE ATTACH
17400 : * PARTITION that we'll emit later expects the constraint to be
17401 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17402 : */
17403 11820 : for (j = 0; j < tbinfo->ncheck; j++)
17404 : {
17405 1146 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17406 :
17407 1146 : if (constr->separate ||
17408 1006 : (!constr->conislocal && !tbinfo->ispartition))
17409 214 : continue;
17410 :
17411 932 : if (actual_atts == 0)
17412 32 : appendPQExpBufferStr(q, " (\n ");
17413 : else
17414 900 : appendPQExpBufferStr(q, ",\n ");
17415 :
17416 932 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17417 932 : fmtId(constr->dobj.name));
17418 932 : appendPQExpBufferStr(q, constr->condef);
17419 :
17420 932 : actual_atts++;
17421 : }
17422 :
17423 10674 : if (actual_atts)
17424 10164 : appendPQExpBufferStr(q, "\n)");
17425 510 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17426 : {
17427 : /*
17428 : * No attributes? we must have a parenthesized attribute list,
17429 : * even though empty, when not using the OF TYPE syntax.
17430 : */
17431 486 : appendPQExpBufferStr(q, " (\n)");
17432 : }
17433 :
17434 : /*
17435 : * Emit the INHERITS clause (not for partitions), except in
17436 : * binary-upgrade mode.
17437 : */
17438 10674 : if (numParents > 0 && !tbinfo->ispartition &&
17439 978 : !dopt->binary_upgrade)
17440 : {
17441 850 : appendPQExpBufferStr(q, "\nINHERITS (");
17442 1842 : for (k = 0; k < numParents; k++)
17443 : {
17444 992 : TableInfo *parentRel = parents[k];
17445 :
17446 992 : if (k > 0)
17447 142 : appendPQExpBufferStr(q, ", ");
17448 992 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17449 : }
17450 850 : appendPQExpBufferChar(q, ')');
17451 : }
17452 :
17453 10674 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17454 1132 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17455 :
17456 10674 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17457 68 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17458 : }
17459 :
17460 22404 : if (nonemptyReloptions(tbinfo->reloptions) ||
17461 11060 : nonemptyReloptions(tbinfo->toast_reloptions))
17462 : {
17463 284 : bool addcomma = false;
17464 :
17465 284 : appendPQExpBufferStr(q, "\nWITH (");
17466 284 : if (nonemptyReloptions(tbinfo->reloptions))
17467 : {
17468 284 : addcomma = true;
17469 284 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17470 : }
17471 284 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17472 : {
17473 10 : if (addcomma)
17474 10 : appendPQExpBufferStr(q, ", ");
17475 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17476 : fout);
17477 : }
17478 284 : appendPQExpBufferChar(q, ')');
17479 : }
17480 :
17481 : /* Dump generic options if any */
17482 11344 : if (ftoptions && ftoptions[0])
17483 64 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17484 :
17485 : /*
17486 : * For materialized views, create the AS clause just like a view. At
17487 : * this point, we always mark the view as not populated.
17488 : */
17489 11344 : if (tbinfo->relkind == RELKIND_MATVIEW)
17490 : {
17491 : PQExpBuffer result;
17492 :
17493 670 : result = createViewAsClause(fout, tbinfo);
17494 670 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17495 : result->data);
17496 670 : destroyPQExpBuffer(result);
17497 : }
17498 : else
17499 10674 : appendPQExpBufferStr(q, ";\n");
17500 :
17501 : /* Materialized views can depend on extensions */
17502 11344 : if (tbinfo->relkind == RELKIND_MATVIEW)
17503 670 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17504 : "pg_catalog.pg_class",
17505 : "MATERIALIZED VIEW",
17506 : qualrelname);
17507 :
17508 : /*
17509 : * in binary upgrade mode, update the catalog with any missing values
17510 : * that might be present.
17511 : */
17512 11344 : if (dopt->binary_upgrade)
17513 : {
17514 7906 : for (j = 0; j < tbinfo->numatts; j++)
17515 : {
17516 6278 : if (tbinfo->attmissingval[j][0] != '\0')
17517 : {
17518 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17519 4 : appendPQExpBufferStr(q,
17520 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17521 4 : appendStringLiteralAH(q, qualrelname, fout);
17522 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17523 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17524 4 : appendPQExpBufferChar(q, ',');
17525 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17526 4 : appendPQExpBufferStr(q, ");\n\n");
17527 : }
17528 : }
17529 : }
17530 :
17531 : /*
17532 : * To create binary-compatible heap files, we have to ensure the same
17533 : * physical column order, including dropped columns, as in the
17534 : * original. Therefore, we create dropped columns above and drop them
17535 : * here, also updating their attlen/attalign values so that the
17536 : * dropped column can be skipped properly. (We do not bother with
17537 : * restoring the original attbyval setting.) Also, inheritance
17538 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17539 : * using an INHERITS clause --- the latter would possibly mess up the
17540 : * column order. That also means we have to take care about setting
17541 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17542 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17543 : *
17544 : * We process foreign and partitioned tables here, even though they
17545 : * lack heap storage, because they can participate in inheritance
17546 : * relationships and we want this stuff to be consistent across the
17547 : * inheritance tree. We can exclude indexes, toast tables, sequences
17548 : * and matviews, even though they have storage, because we don't
17549 : * support altering or dropping columns in them, nor can they be part
17550 : * of inheritance trees.
17551 : */
17552 11344 : if (dopt->binary_upgrade &&
17553 1628 : (tbinfo->relkind == RELKIND_RELATION ||
17554 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17555 216 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17556 : {
17557 : bool firstitem;
17558 : bool firstitem_extra;
17559 :
17560 : /*
17561 : * Drop any dropped columns. Merge the pg_attribute manipulations
17562 : * into a single SQL command, so that we don't cause repeated
17563 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17564 : * relcache bloat while dropping N columns.
17565 : */
17566 1594 : resetPQExpBuffer(extra);
17567 1594 : firstitem = true;
17568 7830 : for (j = 0; j < tbinfo->numatts; j++)
17569 : {
17570 6236 : if (tbinfo->attisdropped[j])
17571 : {
17572 168 : if (firstitem)
17573 : {
17574 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17575 : "UPDATE pg_catalog.pg_attribute\n"
17576 : "SET attlen = v.dlen, "
17577 : "attalign = v.dalign, "
17578 : "attbyval = false\n"
17579 : "FROM (VALUES ");
17580 76 : firstitem = false;
17581 : }
17582 : else
17583 92 : appendPQExpBufferStr(q, ",\n ");
17584 168 : appendPQExpBufferChar(q, '(');
17585 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17586 168 : appendPQExpBuffer(q, ", %d, '%c')",
17587 168 : tbinfo->attlen[j],
17588 168 : tbinfo->attalign[j]);
17589 : /* The ALTER ... DROP COLUMN commands must come after */
17590 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17591 : foreign, qualrelname);
17592 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17593 168 : fmtId(tbinfo->attnames[j]));
17594 : }
17595 : }
17596 1594 : if (!firstitem)
17597 : {
17598 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17599 : "WHERE attrelid = ");
17600 76 : appendStringLiteralAH(q, qualrelname, fout);
17601 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17602 : " AND attname = v.dname;\n");
17603 : /* Now we can issue the actual DROP COLUMN commands */
17604 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17605 : }
17606 :
17607 : /*
17608 : * Fix up inherited columns. As above, do the pg_attribute
17609 : * manipulations in a single SQL command.
17610 : */
17611 1594 : firstitem = true;
17612 7830 : for (j = 0; j < tbinfo->numatts; j++)
17613 : {
17614 6236 : if (!tbinfo->attisdropped[j] &&
17615 6068 : !tbinfo->attislocal[j])
17616 : {
17617 1208 : if (firstitem)
17618 : {
17619 534 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17620 534 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17621 : "SET attislocal = false\n"
17622 : "WHERE attrelid = ");
17623 534 : appendStringLiteralAH(q, qualrelname, fout);
17624 534 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17625 : " AND attname IN (");
17626 534 : firstitem = false;
17627 : }
17628 : else
17629 674 : appendPQExpBufferStr(q, ", ");
17630 1208 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17631 : }
17632 : }
17633 1594 : if (!firstitem)
17634 534 : appendPQExpBufferStr(q, ");\n");
17635 :
17636 : /*
17637 : * Fix up not-null constraints that come from inheritance. As
17638 : * above, do the pg_constraint manipulations in a single SQL
17639 : * command. (Actually, two in special cases, if we're doing an
17640 : * upgrade from < 18).
17641 : */
17642 1594 : firstitem = true;
17643 1594 : firstitem_extra = true;
17644 1594 : resetPQExpBuffer(extra);
17645 7830 : for (j = 0; j < tbinfo->numatts; j++)
17646 : {
17647 : /*
17648 : * If a not-null constraint comes from inheritance, reset
17649 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17650 : * below. Special hack: in versions < 18, columns with no
17651 : * local definition need their constraint to be matched by
17652 : * column number in conkeys instead of by constraint name,
17653 : * because the latter is not available. (We distinguish the
17654 : * case because the constraint name is the empty string.)
17655 : */
17656 6236 : if (tbinfo->notnull_constrs[j] != NULL &&
17657 582 : !tbinfo->notnull_islocal[j])
17658 : {
17659 168 : if (tbinfo->notnull_constrs[j][0] != '\0')
17660 : {
17661 142 : if (firstitem)
17662 : {
17663 122 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17664 : "SET conislocal = false\n"
17665 : "WHERE contype = 'n' AND conrelid = ");
17666 122 : appendStringLiteralAH(q, qualrelname, fout);
17667 122 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17668 : "conname IN (");
17669 122 : firstitem = false;
17670 : }
17671 : else
17672 20 : appendPQExpBufferStr(q, ", ");
17673 142 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17674 : }
17675 : else
17676 : {
17677 26 : if (firstitem_extra)
17678 : {
17679 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17680 : "SET conislocal = false\n"
17681 : "WHERE contype = 'n' AND conrelid = ");
17682 26 : appendStringLiteralAH(extra, qualrelname, fout);
17683 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17684 : "conkey IN (");
17685 26 : firstitem_extra = false;
17686 : }
17687 : else
17688 0 : appendPQExpBufferStr(extra, ", ");
17689 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17690 : }
17691 : }
17692 : }
17693 1594 : if (!firstitem)
17694 122 : appendPQExpBufferStr(q, ");\n");
17695 1594 : if (!firstitem_extra)
17696 26 : appendPQExpBufferStr(extra, ");\n");
17697 :
17698 1594 : if (extra->len > 0)
17699 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17700 :
17701 : /*
17702 : * Add inherited CHECK constraints, if any.
17703 : *
17704 : * For partitions, they were already dumped, and conislocal
17705 : * doesn't need fixing.
17706 : *
17707 : * As above, issue only one direct manipulation of pg_constraint.
17708 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17709 : * commands into one as well, refrain for now due to concern about
17710 : * possible backend memory bloat if there are many such
17711 : * constraints.
17712 : */
17713 1594 : resetPQExpBuffer(extra);
17714 1594 : firstitem = true;
17715 1718 : for (k = 0; k < tbinfo->ncheck; k++)
17716 : {
17717 124 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17718 :
17719 124 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17720 120 : continue;
17721 :
17722 4 : if (firstitem)
17723 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17724 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17725 : foreign, qualrelname,
17726 4 : fmtId(constr->dobj.name),
17727 : constr->condef);
17728 : /* Update pg_constraint after all the ALTER TABLEs */
17729 4 : if (firstitem)
17730 : {
17731 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17732 : "SET conislocal = false\n"
17733 : "WHERE contype = 'c' AND conrelid = ");
17734 4 : appendStringLiteralAH(extra, qualrelname, fout);
17735 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17736 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17737 4 : firstitem = false;
17738 : }
17739 : else
17740 0 : appendPQExpBufferStr(extra, ", ");
17741 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17742 : }
17743 1594 : if (!firstitem)
17744 : {
17745 4 : appendPQExpBufferStr(extra, ");\n");
17746 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17747 : }
17748 :
17749 1594 : if (numParents > 0 && !tbinfo->ispartition)
17750 : {
17751 128 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17752 278 : for (k = 0; k < numParents; k++)
17753 : {
17754 150 : TableInfo *parentRel = parents[k];
17755 :
17756 150 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17757 : qualrelname,
17758 150 : fmtQualifiedDumpable(parentRel));
17759 : }
17760 : }
17761 :
17762 1594 : if (OidIsValid(tbinfo->reloftype))
17763 : {
17764 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17765 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17766 : qualrelname,
17767 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17768 : zeroIsError));
17769 : }
17770 : }
17771 :
17772 : /*
17773 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17774 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17775 : * TOAST tables semi-independently, here we see them only as children
17776 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17777 : * child toast table is handled below.)
17778 : */
17779 11344 : if (dopt->binary_upgrade &&
17780 1628 : (tbinfo->relkind == RELKIND_RELATION ||
17781 218 : tbinfo->relkind == RELKIND_MATVIEW))
17782 : {
17783 1444 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17784 1444 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17785 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17786 : "WHERE oid = ",
17787 1444 : tbinfo->frozenxid, tbinfo->minmxid);
17788 1444 : appendStringLiteralAH(q, qualrelname, fout);
17789 1444 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17790 :
17791 1444 : if (tbinfo->toast_oid)
17792 : {
17793 : /*
17794 : * The toast table will have the same OID at restore, so we
17795 : * can safely target it by OID.
17796 : */
17797 554 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17798 554 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17799 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17800 : "WHERE oid = '%u';\n",
17801 554 : tbinfo->toast_frozenxid,
17802 554 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17803 : }
17804 : }
17805 :
17806 : /*
17807 : * In binary_upgrade mode, restore matviews' populated status by
17808 : * poking pg_class directly. This is pretty ugly, but we can't use
17809 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17810 : * matview is not populated even though this matview is; in any case,
17811 : * we want to transfer the matview's heap storage, not run REFRESH.
17812 : */
17813 11344 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17814 34 : tbinfo->relispopulated)
17815 : {
17816 30 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17817 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17818 : "SET relispopulated = 't'\n"
17819 : "WHERE oid = ");
17820 30 : appendStringLiteralAH(q, qualrelname, fout);
17821 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17822 : }
17823 :
17824 : /*
17825 : * Dump additional per-column properties that we can't handle in the
17826 : * main CREATE TABLE command.
17827 : */
17828 51734 : for (j = 0; j < tbinfo->numatts; j++)
17829 : {
17830 : /* None of this applies to dropped columns */
17831 40390 : if (tbinfo->attisdropped[j])
17832 894 : continue;
17833 :
17834 : /*
17835 : * Dump per-column statistics information. We only issue an ALTER
17836 : * TABLE statement if the attstattarget entry for this column is
17837 : * not the default value.
17838 : */
17839 39496 : if (tbinfo->attstattarget[j] >= 0)
17840 64 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17841 : foreign, qualrelname,
17842 64 : fmtId(tbinfo->attnames[j]),
17843 64 : tbinfo->attstattarget[j]);
17844 :
17845 : /*
17846 : * Dump per-column storage information. The statement is only
17847 : * dumped if the storage has been changed from the type's default.
17848 : */
17849 39496 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17850 : {
17851 158 : switch (tbinfo->attstorage[j])
17852 : {
17853 20 : case TYPSTORAGE_PLAIN:
17854 20 : storage = "PLAIN";
17855 20 : break;
17856 74 : case TYPSTORAGE_EXTERNAL:
17857 74 : storage = "EXTERNAL";
17858 74 : break;
17859 0 : case TYPSTORAGE_EXTENDED:
17860 0 : storage = "EXTENDED";
17861 0 : break;
17862 64 : case TYPSTORAGE_MAIN:
17863 64 : storage = "MAIN";
17864 64 : break;
17865 0 : default:
17866 0 : storage = NULL;
17867 : }
17868 :
17869 : /*
17870 : * Only dump the statement if it's a storage type we recognize
17871 : */
17872 158 : if (storage != NULL)
17873 158 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17874 : foreign, qualrelname,
17875 158 : fmtId(tbinfo->attnames[j]),
17876 : storage);
17877 : }
17878 :
17879 : /*
17880 : * Dump per-column compression, if it's been set.
17881 : */
17882 39496 : if (!dopt->no_toast_compression)
17883 : {
17884 : const char *cmname;
17885 :
17886 39308 : switch (tbinfo->attcompression[j])
17887 : {
17888 142 : case 'p':
17889 142 : cmname = "pglz";
17890 142 : break;
17891 78 : case 'l':
17892 78 : cmname = "lz4";
17893 78 : break;
17894 39088 : default:
17895 39088 : cmname = NULL;
17896 39088 : break;
17897 : }
17898 :
17899 39308 : if (cmname != NULL)
17900 220 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17901 : foreign, qualrelname,
17902 220 : fmtId(tbinfo->attnames[j]),
17903 : cmname);
17904 : }
17905 :
17906 : /*
17907 : * Dump per-column attributes.
17908 : */
17909 39496 : if (tbinfo->attoptions[j][0] != '\0')
17910 64 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17911 : foreign, qualrelname,
17912 64 : fmtId(tbinfo->attnames[j]),
17913 64 : tbinfo->attoptions[j]);
17914 :
17915 : /*
17916 : * Dump per-column fdw options.
17917 : */
17918 39496 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17919 68 : tbinfo->attfdwoptions[j][0] != '\0')
17920 64 : appendPQExpBuffer(q,
17921 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17922 : " %s\n"
17923 : ");\n",
17924 : qualrelname,
17925 64 : fmtId(tbinfo->attnames[j]),
17926 64 : tbinfo->attfdwoptions[j]);
17927 : } /* end loop over columns */
17928 :
17929 11344 : free(partkeydef);
17930 11344 : free(ftoptions);
17931 11344 : free(srvname);
17932 : }
17933 :
17934 : /*
17935 : * dump properties we only have ALTER TABLE syntax for
17936 : */
17937 12410 : if ((tbinfo->relkind == RELKIND_RELATION ||
17938 2936 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17939 1804 : tbinfo->relkind == RELKIND_MATVIEW) &&
17940 11276 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17941 : {
17942 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17943 : {
17944 : /* nothing to do, will be set when the index is dumped */
17945 : }
17946 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17947 : {
17948 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17949 : qualrelname);
17950 : }
17951 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17952 : {
17953 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17954 : qualrelname);
17955 : }
17956 : }
17957 :
17958 12410 : if (tbinfo->forcerowsec)
17959 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17960 : qualrelname);
17961 :
17962 12410 : if (dopt->binary_upgrade)
17963 1732 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17964 : reltypename, qrelname,
17965 1732 : tbinfo->dobj.namespace->dobj.name);
17966 :
17967 12410 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17968 : {
17969 12410 : char *tablespace = NULL;
17970 12410 : char *tableam = NULL;
17971 :
17972 : /*
17973 : * _selectTablespace() relies on tablespace-enabled objects in the
17974 : * default tablespace to have a tablespace of "" (empty string) versus
17975 : * non-tablespace-enabled objects to have a tablespace of NULL.
17976 : * getTables() sets tbinfo->reltablespace to "" for the default
17977 : * tablespace (not NULL).
17978 : */
17979 12410 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17980 11276 : tablespace = tbinfo->reltablespace;
17981 :
17982 12410 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17983 2266 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17984 11276 : tableam = tbinfo->amname;
17985 :
17986 12410 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
17987 12410 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
17988 : .namespace = tbinfo->dobj.namespace->dobj.name,
17989 : .tablespace = tablespace,
17990 : .tableam = tableam,
17991 : .relkind = tbinfo->relkind,
17992 : .owner = tbinfo->rolname,
17993 : .description = reltypename,
17994 : .section = tbinfo->postponed_def ?
17995 : SECTION_POST_DATA : SECTION_PRE_DATA,
17996 : .createStmt = q->data,
17997 : .dropStmt = delq->data));
17998 : }
17999 :
18000 : /* Dump Table Comments */
18001 12410 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18002 148 : dumpTableComment(fout, tbinfo, reltypename);
18003 :
18004 : /* Dump Table Security Labels */
18005 12410 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18006 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18007 :
18008 : /*
18009 : * Dump comments for not-null constraints that aren't to be dumped
18010 : * separately (those are processed by collectComments/dumpComment).
18011 : */
18012 12410 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18013 12410 : fout->remoteVersion >= 180000)
18014 : {
18015 12410 : PQExpBuffer comment = NULL;
18016 12410 : PQExpBuffer tag = NULL;
18017 :
18018 59610 : for (j = 0; j < tbinfo->numatts; j++)
18019 : {
18020 47200 : if (tbinfo->notnull_constrs[j] != NULL &&
18021 4784 : tbinfo->notnull_comment[j] != NULL)
18022 : {
18023 84 : if (comment == NULL)
18024 : {
18025 84 : comment = createPQExpBuffer();
18026 84 : tag = createPQExpBuffer();
18027 : }
18028 : else
18029 : {
18030 0 : resetPQExpBuffer(comment);
18031 0 : resetPQExpBuffer(tag);
18032 : }
18033 :
18034 84 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18035 84 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18036 84 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18037 84 : appendPQExpBufferStr(comment, ";\n");
18038 :
18039 84 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18040 84 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18041 :
18042 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18043 84 : ARCHIVE_OPTS(.tag = tag->data,
18044 : .namespace = tbinfo->dobj.namespace->dobj.name,
18045 : .owner = tbinfo->rolname,
18046 : .description = "COMMENT",
18047 : .section = SECTION_NONE,
18048 : .createStmt = comment->data,
18049 : .deps = &(tbinfo->dobj.dumpId),
18050 : .nDeps = 1));
18051 : }
18052 : }
18053 :
18054 12410 : destroyPQExpBuffer(comment);
18055 12410 : destroyPQExpBuffer(tag);
18056 : }
18057 :
18058 : /* Dump comments on inlined table constraints */
18059 13556 : for (j = 0; j < tbinfo->ncheck; j++)
18060 : {
18061 1146 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18062 :
18063 1146 : if (constr->separate || !constr->conislocal)
18064 488 : continue;
18065 :
18066 658 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18067 74 : dumpTableConstraintComment(fout, constr);
18068 : }
18069 :
18070 12410 : destroyPQExpBuffer(q);
18071 12410 : destroyPQExpBuffer(delq);
18072 12410 : destroyPQExpBuffer(extra);
18073 12410 : free(qrelname);
18074 12410 : free(qualrelname);
18075 12410 : }
18076 :
18077 : /*
18078 : * dumpTableAttach
18079 : * write to fout the commands to attach a child partition
18080 : *
18081 : * Child partitions are always made by creating them separately
18082 : * and then using ATTACH PARTITION, rather than using
18083 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18084 : * any possible discrepancy in column layout, to allow assigning the
18085 : * correct tablespace if different, and so that it's possible to restore
18086 : * a partition without restoring its parent. (You'll get an error from
18087 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18088 : * using "pg_restore -L" if you prefer.) The last point motivates
18089 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18090 : * rather than emitting it within the child partition's ArchiveEntry.
18091 : */
18092 : static void
18093 2762 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18094 : {
18095 2762 : DumpOptions *dopt = fout->dopt;
18096 : PQExpBuffer q;
18097 : PGresult *res;
18098 : char *partbound;
18099 :
18100 : /* Do nothing if not dumping schema */
18101 2762 : if (!dopt->dumpSchema)
18102 108 : return;
18103 :
18104 2654 : q = createPQExpBuffer();
18105 :
18106 2654 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18107 : {
18108 : /* Set up query for partbound details */
18109 86 : appendPQExpBufferStr(q,
18110 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18111 :
18112 86 : appendPQExpBufferStr(q,
18113 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18114 : "FROM pg_class c "
18115 : "WHERE c.oid = $1");
18116 :
18117 86 : ExecuteSqlStatement(fout, q->data);
18118 :
18119 86 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18120 : }
18121 :
18122 2654 : printfPQExpBuffer(q,
18123 : "EXECUTE dumpTableAttach('%u')",
18124 2654 : attachinfo->partitionTbl->dobj.catId.oid);
18125 :
18126 2654 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18127 2654 : partbound = PQgetvalue(res, 0, 0);
18128 :
18129 : /* Perform ALTER TABLE on the parent */
18130 2654 : printfPQExpBuffer(q,
18131 : "ALTER TABLE ONLY %s ",
18132 2654 : fmtQualifiedDumpable(attachinfo->parentTbl));
18133 2654 : appendPQExpBuffer(q,
18134 : "ATTACH PARTITION %s %s;\n",
18135 2654 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18136 : partbound);
18137 :
18138 : /*
18139 : * There is no point in creating a drop query as the drop is done by table
18140 : * drop. (If you think to change this, see also _printTocEntry().)
18141 : * Although this object doesn't really have ownership as such, set the
18142 : * owner field anyway to ensure that the command is run by the correct
18143 : * role at restore time.
18144 : */
18145 2654 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18146 2654 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18147 : .namespace = attachinfo->dobj.namespace->dobj.name,
18148 : .owner = attachinfo->partitionTbl->rolname,
18149 : .description = "TABLE ATTACH",
18150 : .section = SECTION_PRE_DATA,
18151 : .createStmt = q->data));
18152 :
18153 2654 : PQclear(res);
18154 2654 : destroyPQExpBuffer(q);
18155 : }
18156 :
18157 : /*
18158 : * dumpAttrDef --- dump an attribute's default-value declaration
18159 : */
18160 : static void
18161 2064 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18162 : {
18163 2064 : DumpOptions *dopt = fout->dopt;
18164 2064 : TableInfo *tbinfo = adinfo->adtable;
18165 2064 : int adnum = adinfo->adnum;
18166 : PQExpBuffer q;
18167 : PQExpBuffer delq;
18168 : char *qualrelname;
18169 : char *tag;
18170 : char *foreign;
18171 :
18172 : /* Do nothing if not dumping schema */
18173 2064 : if (!dopt->dumpSchema)
18174 0 : return;
18175 :
18176 : /* Skip if not "separate"; it was dumped in the table's definition */
18177 2064 : if (!adinfo->separate)
18178 1732 : return;
18179 :
18180 332 : q = createPQExpBuffer();
18181 332 : delq = createPQExpBuffer();
18182 :
18183 332 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18184 :
18185 332 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18186 :
18187 332 : appendPQExpBuffer(q,
18188 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18189 332 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18190 332 : adinfo->adef_expr);
18191 :
18192 332 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18193 : foreign, qualrelname,
18194 332 : fmtId(tbinfo->attnames[adnum - 1]));
18195 :
18196 332 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18197 :
18198 332 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18199 332 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18200 332 : ARCHIVE_OPTS(.tag = tag,
18201 : .namespace = tbinfo->dobj.namespace->dobj.name,
18202 : .owner = tbinfo->rolname,
18203 : .description = "DEFAULT",
18204 : .section = SECTION_PRE_DATA,
18205 : .createStmt = q->data,
18206 : .dropStmt = delq->data));
18207 :
18208 332 : free(tag);
18209 332 : destroyPQExpBuffer(q);
18210 332 : destroyPQExpBuffer(delq);
18211 332 : free(qualrelname);
18212 : }
18213 :
18214 : /*
18215 : * getAttrName: extract the correct name for an attribute
18216 : *
18217 : * The array tblInfo->attnames[] only provides names of user attributes;
18218 : * if a system attribute number is supplied, we have to fake it.
18219 : * We also do a little bit of bounds checking for safety's sake.
18220 : */
18221 : static const char *
18222 4078 : getAttrName(int attrnum, const TableInfo *tblInfo)
18223 : {
18224 4078 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18225 4078 : return tblInfo->attnames[attrnum - 1];
18226 0 : switch (attrnum)
18227 : {
18228 0 : case SelfItemPointerAttributeNumber:
18229 0 : return "ctid";
18230 0 : case MinTransactionIdAttributeNumber:
18231 0 : return "xmin";
18232 0 : case MinCommandIdAttributeNumber:
18233 0 : return "cmin";
18234 0 : case MaxTransactionIdAttributeNumber:
18235 0 : return "xmax";
18236 0 : case MaxCommandIdAttributeNumber:
18237 0 : return "cmax";
18238 0 : case TableOidAttributeNumber:
18239 0 : return "tableoid";
18240 : }
18241 0 : pg_fatal("invalid column number %d for table \"%s\"",
18242 : attrnum, tblInfo->dobj.name);
18243 : return NULL; /* keep compiler quiet */
18244 : }
18245 :
18246 : /*
18247 : * dumpIndex
18248 : * write out to fout a user-defined index
18249 : */
18250 : static void
18251 5148 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18252 : {
18253 5148 : DumpOptions *dopt = fout->dopt;
18254 5148 : TableInfo *tbinfo = indxinfo->indextable;
18255 5148 : bool is_constraint = (indxinfo->indexconstraint != 0);
18256 : PQExpBuffer q;
18257 : PQExpBuffer delq;
18258 : char *qindxname;
18259 : char *qqindxname;
18260 :
18261 : /* Do nothing if not dumping schema */
18262 5148 : if (!dopt->dumpSchema)
18263 234 : return;
18264 :
18265 4914 : q = createPQExpBuffer();
18266 4914 : delq = createPQExpBuffer();
18267 :
18268 4914 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18269 4914 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18270 :
18271 : /*
18272 : * If there's an associated constraint, don't dump the index per se, but
18273 : * do dump any comment for it. (This is safe because dependency ordering
18274 : * will have ensured the constraint is emitted first.) Note that the
18275 : * emitted comment has to be shown as depending on the constraint, not the
18276 : * index, in such cases.
18277 : */
18278 4914 : if (!is_constraint)
18279 : {
18280 2070 : char *indstatcols = indxinfo->indstatcols;
18281 2070 : char *indstatvals = indxinfo->indstatvals;
18282 2070 : char **indstatcolsarray = NULL;
18283 2070 : char **indstatvalsarray = NULL;
18284 2070 : int nstatcols = 0;
18285 2070 : int nstatvals = 0;
18286 :
18287 2070 : if (dopt->binary_upgrade)
18288 312 : binary_upgrade_set_pg_class_oids(fout, q,
18289 312 : indxinfo->dobj.catId.oid);
18290 :
18291 : /* Plain secondary index */
18292 2070 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18293 :
18294 : /*
18295 : * Append ALTER TABLE commands as needed to set properties that we
18296 : * only have ALTER TABLE syntax for. Keep this in sync with the
18297 : * similar code in dumpConstraint!
18298 : */
18299 :
18300 : /* If the index is clustered, we need to record that. */
18301 2070 : if (indxinfo->indisclustered)
18302 : {
18303 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18304 0 : fmtQualifiedDumpable(tbinfo));
18305 : /* index name is not qualified in this syntax */
18306 0 : appendPQExpBuffer(q, " ON %s;\n",
18307 : qindxname);
18308 : }
18309 :
18310 : /*
18311 : * If the index has any statistics on some of its columns, generate
18312 : * the associated ALTER INDEX queries.
18313 : */
18314 2070 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18315 : {
18316 : int j;
18317 :
18318 64 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18319 0 : pg_fatal("could not parse index statistic columns");
18320 64 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18321 0 : pg_fatal("could not parse index statistic values");
18322 64 : if (nstatcols != nstatvals)
18323 0 : pg_fatal("mismatched number of columns and values for index statistics");
18324 :
18325 192 : for (j = 0; j < nstatcols; j++)
18326 : {
18327 128 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18328 :
18329 : /*
18330 : * Note that this is a column number, so no quotes should be
18331 : * used.
18332 : */
18333 128 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18334 128 : indstatcolsarray[j]);
18335 128 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18336 128 : indstatvalsarray[j]);
18337 : }
18338 : }
18339 :
18340 : /* Indexes can depend on extensions */
18341 2070 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18342 : "pg_catalog.pg_class",
18343 : "INDEX", qqindxname);
18344 :
18345 : /* If the index defines identity, we need to record that. */
18346 2070 : if (indxinfo->indisreplident)
18347 : {
18348 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18349 0 : fmtQualifiedDumpable(tbinfo));
18350 : /* index name is not qualified in this syntax */
18351 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18352 : qindxname);
18353 : }
18354 :
18355 : /*
18356 : * If this index is a member of a partitioned index, the backend will
18357 : * not allow us to drop it separately, so don't try. It will go away
18358 : * automatically when we drop either the index's table or the
18359 : * partitioned index. (If, in a selective restore with --clean, we
18360 : * drop neither of those, then this index will not be dropped either.
18361 : * But that's fine, and even if you think it's not, the backend won't
18362 : * let us do differently.)
18363 : */
18364 2070 : if (indxinfo->parentidx == 0)
18365 1706 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18366 :
18367 2070 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18368 2070 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18369 2070 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18370 : .namespace = tbinfo->dobj.namespace->dobj.name,
18371 : .tablespace = indxinfo->tablespace,
18372 : .owner = tbinfo->rolname,
18373 : .description = "INDEX",
18374 : .section = SECTION_POST_DATA,
18375 : .createStmt = q->data,
18376 : .dropStmt = delq->data));
18377 :
18378 2070 : free(indstatcolsarray);
18379 2070 : free(indstatvalsarray);
18380 : }
18381 :
18382 : /* Dump Index Comments */
18383 4914 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18384 30 : dumpComment(fout, "INDEX", qindxname,
18385 30 : tbinfo->dobj.namespace->dobj.name,
18386 : tbinfo->rolname,
18387 : indxinfo->dobj.catId, 0,
18388 : is_constraint ? indxinfo->indexconstraint :
18389 : indxinfo->dobj.dumpId);
18390 :
18391 4914 : destroyPQExpBuffer(q);
18392 4914 : destroyPQExpBuffer(delq);
18393 4914 : free(qindxname);
18394 4914 : free(qqindxname);
18395 : }
18396 :
18397 : /*
18398 : * dumpIndexAttach
18399 : * write out to fout a partitioned-index attachment clause
18400 : */
18401 : static void
18402 1128 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18403 : {
18404 : /* Do nothing if not dumping schema */
18405 1128 : if (!fout->dopt->dumpSchema)
18406 96 : return;
18407 :
18408 1032 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18409 : {
18410 1032 : PQExpBuffer q = createPQExpBuffer();
18411 :
18412 1032 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18413 1032 : fmtQualifiedDumpable(attachinfo->parentIdx));
18414 1032 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18415 1032 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18416 :
18417 : /*
18418 : * There is no need for a dropStmt since the drop is done implicitly
18419 : * when we drop either the index's table or the partitioned index.
18420 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18421 : * there's no way to do it anyway. (If you think to change this,
18422 : * consider also what to do with --if-exists.)
18423 : *
18424 : * Although this object doesn't really have ownership as such, set the
18425 : * owner field anyway to ensure that the command is run by the correct
18426 : * role at restore time.
18427 : */
18428 1032 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18429 1032 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18430 : .namespace = attachinfo->dobj.namespace->dobj.name,
18431 : .owner = attachinfo->parentIdx->indextable->rolname,
18432 : .description = "INDEX ATTACH",
18433 : .section = SECTION_POST_DATA,
18434 : .createStmt = q->data));
18435 :
18436 1032 : destroyPQExpBuffer(q);
18437 : }
18438 : }
18439 :
18440 : /*
18441 : * dumpStatisticsExt
18442 : * write out to fout an extended statistics object
18443 : */
18444 : static void
18445 266 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18446 : {
18447 266 : DumpOptions *dopt = fout->dopt;
18448 : PQExpBuffer q;
18449 : PQExpBuffer delq;
18450 : PQExpBuffer query;
18451 : char *qstatsextname;
18452 : PGresult *res;
18453 : char *stxdef;
18454 :
18455 : /* Do nothing if not dumping schema */
18456 266 : if (!dopt->dumpSchema)
18457 36 : return;
18458 :
18459 230 : q = createPQExpBuffer();
18460 230 : delq = createPQExpBuffer();
18461 230 : query = createPQExpBuffer();
18462 :
18463 230 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18464 :
18465 230 : appendPQExpBuffer(query, "SELECT "
18466 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18467 230 : statsextinfo->dobj.catId.oid);
18468 :
18469 230 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18470 :
18471 230 : stxdef = PQgetvalue(res, 0, 0);
18472 :
18473 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18474 230 : appendPQExpBuffer(q, "%s;\n", stxdef);
18475 :
18476 : /*
18477 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18478 : * for this statistics object is not the default value.
18479 : */
18480 230 : if (statsextinfo->stattarget >= 0)
18481 : {
18482 64 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18483 64 : fmtQualifiedDumpable(statsextinfo));
18484 64 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18485 64 : statsextinfo->stattarget);
18486 : }
18487 :
18488 230 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18489 230 : fmtQualifiedDumpable(statsextinfo));
18490 :
18491 230 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18492 230 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18493 230 : statsextinfo->dobj.dumpId,
18494 230 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18495 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18496 : .owner = statsextinfo->rolname,
18497 : .description = "STATISTICS",
18498 : .section = SECTION_POST_DATA,
18499 : .createStmt = q->data,
18500 : .dropStmt = delq->data));
18501 :
18502 : /* Dump Statistics Comments */
18503 230 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18504 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18505 0 : statsextinfo->dobj.namespace->dobj.name,
18506 0 : statsextinfo->rolname,
18507 : statsextinfo->dobj.catId, 0,
18508 0 : statsextinfo->dobj.dumpId);
18509 :
18510 230 : PQclear(res);
18511 230 : destroyPQExpBuffer(q);
18512 230 : destroyPQExpBuffer(delq);
18513 230 : destroyPQExpBuffer(query);
18514 230 : free(qstatsextname);
18515 : }
18516 :
18517 : /*
18518 : * dumpConstraint
18519 : * write out to fout a user-defined constraint
18520 : */
18521 : static void
18522 4946 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18523 : {
18524 4946 : DumpOptions *dopt = fout->dopt;
18525 4946 : TableInfo *tbinfo = coninfo->contable;
18526 : PQExpBuffer q;
18527 : PQExpBuffer delq;
18528 4946 : char *tag = NULL;
18529 : char *foreign;
18530 :
18531 : /* Do nothing if not dumping schema */
18532 4946 : if (!dopt->dumpSchema)
18533 196 : return;
18534 :
18535 4750 : q = createPQExpBuffer();
18536 4750 : delq = createPQExpBuffer();
18537 :
18538 9192 : foreign = tbinfo &&
18539 4750 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18540 :
18541 4750 : if (coninfo->contype == 'p' ||
18542 2384 : coninfo->contype == 'u' ||
18543 1926 : coninfo->contype == 'x')
18544 2844 : {
18545 : /* Index-related constraint */
18546 : IndxInfo *indxinfo;
18547 : int k;
18548 :
18549 2844 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18550 :
18551 2844 : if (indxinfo == NULL)
18552 0 : pg_fatal("missing index for constraint \"%s\"",
18553 : coninfo->dobj.name);
18554 :
18555 2844 : if (dopt->binary_upgrade)
18556 292 : binary_upgrade_set_pg_class_oids(fout, q,
18557 : indxinfo->dobj.catId.oid);
18558 :
18559 2844 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18560 2844 : fmtQualifiedDumpable(tbinfo));
18561 2844 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18562 2844 : fmtId(coninfo->dobj.name));
18563 :
18564 2844 : if (coninfo->condef)
18565 : {
18566 : /* pg_get_constraintdef should have provided everything */
18567 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18568 : }
18569 : else
18570 : {
18571 2824 : appendPQExpBufferStr(q,
18572 2824 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18573 :
18574 : /*
18575 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18576 : * indexes. Being able to create this was fixed, but we need to
18577 : * make the index distinct in order to be able to restore the
18578 : * dump.
18579 : */
18580 2824 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18581 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18582 2824 : appendPQExpBufferStr(q, " (");
18583 6822 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18584 : {
18585 3998 : int indkey = (int) indxinfo->indkeys[k];
18586 : const char *attname;
18587 :
18588 3998 : if (indkey == InvalidAttrNumber)
18589 0 : break;
18590 3998 : attname = getAttrName(indkey, tbinfo);
18591 :
18592 3998 : appendPQExpBuffer(q, "%s%s",
18593 : (k == 0) ? "" : ", ",
18594 : fmtId(attname));
18595 : }
18596 2824 : if (coninfo->conperiod)
18597 208 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18598 :
18599 2824 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18600 40 : appendPQExpBufferStr(q, ") INCLUDE (");
18601 :
18602 2904 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18603 : {
18604 80 : int indkey = (int) indxinfo->indkeys[k];
18605 : const char *attname;
18606 :
18607 80 : if (indkey == InvalidAttrNumber)
18608 0 : break;
18609 80 : attname = getAttrName(indkey, tbinfo);
18610 :
18611 160 : appendPQExpBuffer(q, "%s%s",
18612 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18613 : fmtId(attname));
18614 : }
18615 :
18616 2824 : appendPQExpBufferChar(q, ')');
18617 :
18618 2824 : if (nonemptyReloptions(indxinfo->indreloptions))
18619 : {
18620 0 : appendPQExpBufferStr(q, " WITH (");
18621 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18622 0 : appendPQExpBufferChar(q, ')');
18623 : }
18624 :
18625 2824 : if (coninfo->condeferrable)
18626 : {
18627 50 : appendPQExpBufferStr(q, " DEFERRABLE");
18628 50 : if (coninfo->condeferred)
18629 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18630 : }
18631 :
18632 2824 : appendPQExpBufferStr(q, ";\n");
18633 : }
18634 :
18635 : /*
18636 : * Append ALTER TABLE commands as needed to set properties that we
18637 : * only have ALTER TABLE syntax for. Keep this in sync with the
18638 : * similar code in dumpIndex!
18639 : */
18640 :
18641 : /* If the index is clustered, we need to record that. */
18642 2844 : if (indxinfo->indisclustered)
18643 : {
18644 64 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18645 64 : fmtQualifiedDumpable(tbinfo));
18646 : /* index name is not qualified in this syntax */
18647 64 : appendPQExpBuffer(q, " ON %s;\n",
18648 64 : fmtId(indxinfo->dobj.name));
18649 : }
18650 :
18651 : /* If the index defines identity, we need to record that. */
18652 2844 : if (indxinfo->indisreplident)
18653 : {
18654 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18655 0 : fmtQualifiedDumpable(tbinfo));
18656 : /* index name is not qualified in this syntax */
18657 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18658 0 : fmtId(indxinfo->dobj.name));
18659 : }
18660 :
18661 : /* Indexes can depend on extensions */
18662 2844 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18663 : "pg_catalog.pg_class", "INDEX",
18664 2844 : fmtQualifiedDumpable(indxinfo));
18665 :
18666 2844 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18667 2844 : fmtQualifiedDumpable(tbinfo));
18668 2844 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18669 2844 : fmtId(coninfo->dobj.name));
18670 :
18671 2844 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18672 :
18673 2844 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18674 2844 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18675 2844 : ARCHIVE_OPTS(.tag = tag,
18676 : .namespace = tbinfo->dobj.namespace->dobj.name,
18677 : .tablespace = indxinfo->tablespace,
18678 : .owner = tbinfo->rolname,
18679 : .description = "CONSTRAINT",
18680 : .section = SECTION_POST_DATA,
18681 : .createStmt = q->data,
18682 : .dropStmt = delq->data));
18683 : }
18684 1906 : else if (coninfo->contype == 'f')
18685 : {
18686 : char *only;
18687 :
18688 : /*
18689 : * Foreign keys on partitioned tables are always declared as
18690 : * inheriting to partitions; for all other cases, emit them as
18691 : * applying ONLY directly to the named table, because that's how they
18692 : * work for regular inherited tables.
18693 : */
18694 318 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18695 :
18696 : /*
18697 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18698 : * current table data is not processed
18699 : */
18700 318 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18701 318 : only, fmtQualifiedDumpable(tbinfo));
18702 318 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18703 318 : fmtId(coninfo->dobj.name),
18704 318 : coninfo->condef);
18705 :
18706 318 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18707 318 : only, fmtQualifiedDumpable(tbinfo));
18708 318 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18709 318 : fmtId(coninfo->dobj.name));
18710 :
18711 318 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18712 :
18713 318 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18714 318 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18715 318 : ARCHIVE_OPTS(.tag = tag,
18716 : .namespace = tbinfo->dobj.namespace->dobj.name,
18717 : .owner = tbinfo->rolname,
18718 : .description = "FK CONSTRAINT",
18719 : .section = SECTION_POST_DATA,
18720 : .createStmt = q->data,
18721 : .dropStmt = delq->data));
18722 : }
18723 1588 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18724 : {
18725 : /* CHECK or invalid not-null constraint on a table */
18726 :
18727 : /* Ignore if not to be dumped separately, or if it was inherited */
18728 1280 : if (coninfo->separate && coninfo->conislocal)
18729 : {
18730 : const char *keyword;
18731 :
18732 214 : if (coninfo->contype == 'c')
18733 90 : keyword = "CHECK CONSTRAINT";
18734 : else
18735 124 : keyword = "CONSTRAINT";
18736 :
18737 : /* not ONLY since we want it to propagate to children */
18738 214 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18739 214 : fmtQualifiedDumpable(tbinfo));
18740 214 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18741 214 : fmtId(coninfo->dobj.name),
18742 214 : coninfo->condef);
18743 :
18744 214 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18745 214 : fmtQualifiedDumpable(tbinfo));
18746 214 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18747 214 : fmtId(coninfo->dobj.name));
18748 :
18749 214 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18750 :
18751 214 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18752 214 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18753 214 : ARCHIVE_OPTS(.tag = tag,
18754 : .namespace = tbinfo->dobj.namespace->dobj.name,
18755 : .owner = tbinfo->rolname,
18756 : .description = keyword,
18757 : .section = SECTION_POST_DATA,
18758 : .createStmt = q->data,
18759 : .dropStmt = delq->data));
18760 : }
18761 : }
18762 308 : else if (tbinfo == NULL)
18763 : {
18764 : /* CHECK, NOT NULL constraint on a domain */
18765 308 : TypeInfo *tyinfo = coninfo->condomain;
18766 :
18767 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18768 :
18769 : /* Ignore if not to be dumped separately */
18770 308 : if (coninfo->separate)
18771 : {
18772 : const char *keyword;
18773 :
18774 10 : if (coninfo->contype == 'c')
18775 10 : keyword = "CHECK CONSTRAINT";
18776 : else
18777 0 : keyword = "CONSTRAINT";
18778 :
18779 10 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18780 10 : fmtQualifiedDumpable(tyinfo));
18781 10 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18782 10 : fmtId(coninfo->dobj.name),
18783 10 : coninfo->condef);
18784 :
18785 10 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18786 10 : fmtQualifiedDumpable(tyinfo));
18787 10 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18788 10 : fmtId(coninfo->dobj.name));
18789 :
18790 10 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18791 :
18792 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18793 10 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18794 10 : ARCHIVE_OPTS(.tag = tag,
18795 : .namespace = tyinfo->dobj.namespace->dobj.name,
18796 : .owner = tyinfo->rolname,
18797 : .description = keyword,
18798 : .section = SECTION_POST_DATA,
18799 : .createStmt = q->data,
18800 : .dropStmt = delq->data));
18801 :
18802 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18803 : {
18804 10 : PQExpBuffer conprefix = createPQExpBuffer();
18805 10 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18806 :
18807 10 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18808 10 : fmtId(coninfo->dobj.name));
18809 :
18810 10 : dumpComment(fout, conprefix->data, qtypname,
18811 10 : tyinfo->dobj.namespace->dobj.name,
18812 : tyinfo->rolname,
18813 10 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18814 10 : destroyPQExpBuffer(conprefix);
18815 10 : free(qtypname);
18816 : }
18817 : }
18818 : }
18819 : else
18820 : {
18821 0 : pg_fatal("unrecognized constraint type: %c",
18822 : coninfo->contype);
18823 : }
18824 :
18825 : /* Dump Constraint Comments --- only works for table constraints */
18826 4750 : if (tbinfo && coninfo->separate &&
18827 3436 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18828 94 : dumpTableConstraintComment(fout, coninfo);
18829 :
18830 4750 : free(tag);
18831 4750 : destroyPQExpBuffer(q);
18832 4750 : destroyPQExpBuffer(delq);
18833 : }
18834 :
18835 : /*
18836 : * dumpTableConstraintComment --- dump a constraint's comment if any
18837 : *
18838 : * This is split out because we need the function in two different places
18839 : * depending on whether the constraint is dumped as part of CREATE TABLE
18840 : * or as a separate ALTER command.
18841 : */
18842 : static void
18843 168 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18844 : {
18845 168 : TableInfo *tbinfo = coninfo->contable;
18846 168 : PQExpBuffer conprefix = createPQExpBuffer();
18847 : char *qtabname;
18848 :
18849 168 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18850 :
18851 168 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18852 168 : fmtId(coninfo->dobj.name));
18853 :
18854 168 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18855 168 : dumpComment(fout, conprefix->data, qtabname,
18856 168 : tbinfo->dobj.namespace->dobj.name,
18857 : tbinfo->rolname,
18858 : coninfo->dobj.catId, 0,
18859 168 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18860 :
18861 168 : destroyPQExpBuffer(conprefix);
18862 168 : free(qtabname);
18863 168 : }
18864 :
18865 : static inline SeqType
18866 1276 : parse_sequence_type(const char *name)
18867 : {
18868 2850 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18869 : {
18870 2850 : if (strcmp(SeqTypeNames[i], name) == 0)
18871 1276 : return (SeqType) i;
18872 : }
18873 :
18874 0 : pg_fatal("unrecognized sequence type: %s", name);
18875 : return (SeqType) 0; /* keep compiler quiet */
18876 : }
18877 :
18878 : /*
18879 : * bsearch() comparator for SequenceItem
18880 : */
18881 : static int
18882 5908 : SequenceItemCmp(const void *p1, const void *p2)
18883 : {
18884 5908 : SequenceItem v1 = *((const SequenceItem *) p1);
18885 5908 : SequenceItem v2 = *((const SequenceItem *) p2);
18886 :
18887 5908 : return pg_cmp_u32(v1.oid, v2.oid);
18888 : }
18889 :
18890 : /*
18891 : * collectSequences
18892 : *
18893 : * Construct a table of sequence information. This table is sorted by OID for
18894 : * speed in lookup.
18895 : */
18896 : static void
18897 372 : collectSequences(Archive *fout)
18898 : {
18899 : PGresult *res;
18900 : const char *query;
18901 :
18902 : /*
18903 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18904 : * some extra effort, we might be able to use the sorted table for those
18905 : * versions, but for now it seems unlikely to be worth it.
18906 : *
18907 : * Since version 18, we can gather the sequence data in this query with
18908 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18909 : */
18910 372 : if (fout->remoteVersion < 100000)
18911 0 : return;
18912 372 : else if (fout->remoteVersion < 180000 ||
18913 372 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18914 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18915 : "seqstart, seqincrement, "
18916 : "seqmax, seqmin, "
18917 : "seqcache, seqcycle, "
18918 : "NULL, 'f' "
18919 : "FROM pg_catalog.pg_sequence "
18920 : "ORDER BY seqrelid";
18921 : else
18922 356 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18923 : "seqstart, seqincrement, "
18924 : "seqmax, seqmin, "
18925 : "seqcache, seqcycle, "
18926 : "last_value, is_called "
18927 : "FROM pg_catalog.pg_sequence, "
18928 : "pg_get_sequence_data(seqrelid) "
18929 : "ORDER BY seqrelid;";
18930 :
18931 372 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18932 :
18933 372 : nsequences = PQntuples(res);
18934 372 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18935 :
18936 1648 : for (int i = 0; i < nsequences; i++)
18937 : {
18938 1276 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18939 1276 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18940 1276 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18941 1276 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18942 1276 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18943 1276 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18944 1276 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18945 1276 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18946 1276 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18947 1276 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18948 : }
18949 :
18950 372 : PQclear(res);
18951 : }
18952 :
18953 : /*
18954 : * dumpSequence
18955 : * write the declaration (not data) of one user-defined sequence
18956 : */
18957 : static void
18958 750 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18959 : {
18960 750 : DumpOptions *dopt = fout->dopt;
18961 : SequenceItem *seq;
18962 : bool is_ascending;
18963 : int64 default_minv,
18964 : default_maxv;
18965 750 : PQExpBuffer query = createPQExpBuffer();
18966 750 : PQExpBuffer delqry = createPQExpBuffer();
18967 : char *qseqname;
18968 750 : TableInfo *owning_tab = NULL;
18969 :
18970 750 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18971 :
18972 : /*
18973 : * For versions >= 10, the sequence information is gathered in a sorted
18974 : * table before any calls to dumpSequence(). See collectSequences() for
18975 : * more information.
18976 : */
18977 750 : if (fout->remoteVersion >= 100000)
18978 : {
18979 750 : SequenceItem key = {0};
18980 :
18981 : Assert(sequences);
18982 :
18983 750 : key.oid = tbinfo->dobj.catId.oid;
18984 750 : seq = bsearch(&key, sequences, nsequences,
18985 : sizeof(SequenceItem), SequenceItemCmp);
18986 : }
18987 : else
18988 : {
18989 : PGresult *res;
18990 :
18991 : /*
18992 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
18993 : *
18994 : * Note: it might seem that 'bigint' potentially needs to be
18995 : * schema-qualified, but actually that's a keyword.
18996 : */
18997 0 : appendPQExpBuffer(query,
18998 : "SELECT 'bigint' AS sequence_type, "
18999 : "start_value, increment_by, max_value, min_value, "
19000 : "cache_value, is_cycled FROM %s",
19001 0 : fmtQualifiedDumpable(tbinfo));
19002 :
19003 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19004 :
19005 0 : if (PQntuples(res) != 1)
19006 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19007 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19008 : PQntuples(res)),
19009 : tbinfo->dobj.name, PQntuples(res));
19010 :
19011 0 : seq = pg_malloc0(sizeof(SequenceItem));
19012 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19013 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19014 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19015 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19016 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19017 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19018 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19019 :
19020 0 : PQclear(res);
19021 : }
19022 :
19023 : /* Calculate default limits for a sequence of this type */
19024 750 : is_ascending = (seq->incby >= 0);
19025 750 : if (seq->seqtype == SEQTYPE_SMALLINT)
19026 : {
19027 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19028 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19029 : }
19030 700 : else if (seq->seqtype == SEQTYPE_INTEGER)
19031 : {
19032 568 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19033 568 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19034 : }
19035 132 : else if (seq->seqtype == SEQTYPE_BIGINT)
19036 : {
19037 132 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19038 132 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19039 : }
19040 : else
19041 : {
19042 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19043 : default_minv = default_maxv = 0; /* keep compiler quiet */
19044 : }
19045 :
19046 : /*
19047 : * Identity sequences are not to be dropped separately.
19048 : */
19049 750 : if (!tbinfo->is_identity_sequence)
19050 : {
19051 466 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19052 466 : fmtQualifiedDumpable(tbinfo));
19053 : }
19054 :
19055 750 : resetPQExpBuffer(query);
19056 :
19057 750 : if (dopt->binary_upgrade)
19058 : {
19059 132 : binary_upgrade_set_pg_class_oids(fout, query,
19060 132 : tbinfo->dobj.catId.oid);
19061 :
19062 : /*
19063 : * In older PG versions a sequence will have a pg_type entry, but v14
19064 : * and up don't use that, so don't attempt to preserve the type OID.
19065 : */
19066 : }
19067 :
19068 750 : if (tbinfo->is_identity_sequence)
19069 : {
19070 284 : owning_tab = findTableByOid(tbinfo->owning_tab);
19071 :
19072 284 : appendPQExpBuffer(query,
19073 : "ALTER TABLE %s ",
19074 284 : fmtQualifiedDumpable(owning_tab));
19075 284 : appendPQExpBuffer(query,
19076 : "ALTER COLUMN %s ADD GENERATED ",
19077 284 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19078 284 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19079 204 : appendPQExpBufferStr(query, "ALWAYS");
19080 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19081 80 : appendPQExpBufferStr(query, "BY DEFAULT");
19082 284 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19083 284 : fmtQualifiedDumpable(tbinfo));
19084 :
19085 : /*
19086 : * Emit persistence option only if it's different from the owning
19087 : * table's. This avoids using this new syntax unnecessarily.
19088 : */
19089 284 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19090 20 : appendPQExpBuffer(query, " %s\n",
19091 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19092 : "UNLOGGED" : "LOGGED");
19093 : }
19094 : else
19095 : {
19096 466 : appendPQExpBuffer(query,
19097 : "CREATE %sSEQUENCE %s\n",
19098 466 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19099 : "UNLOGGED " : "",
19100 466 : fmtQualifiedDumpable(tbinfo));
19101 :
19102 466 : if (seq->seqtype != SEQTYPE_BIGINT)
19103 364 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19104 : }
19105 :
19106 750 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19107 :
19108 750 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19109 :
19110 750 : if (seq->minv != default_minv)
19111 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19112 : else
19113 720 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19114 :
19115 750 : if (seq->maxv != default_maxv)
19116 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19117 : else
19118 720 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19119 :
19120 750 : appendPQExpBuffer(query,
19121 : " CACHE " INT64_FORMAT "%s",
19122 750 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19123 :
19124 750 : if (tbinfo->is_identity_sequence)
19125 284 : appendPQExpBufferStr(query, "\n);\n");
19126 : else
19127 466 : appendPQExpBufferStr(query, ";\n");
19128 :
19129 : /* binary_upgrade: no need to clear TOAST table oid */
19130 :
19131 750 : if (dopt->binary_upgrade)
19132 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19133 : "SEQUENCE", qseqname,
19134 132 : tbinfo->dobj.namespace->dobj.name);
19135 :
19136 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19137 750 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19138 750 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19139 : .namespace = tbinfo->dobj.namespace->dobj.name,
19140 : .owner = tbinfo->rolname,
19141 : .description = "SEQUENCE",
19142 : .section = SECTION_PRE_DATA,
19143 : .createStmt = query->data,
19144 : .dropStmt = delqry->data));
19145 :
19146 : /*
19147 : * If the sequence is owned by a table column, emit the ALTER for it as a
19148 : * separate TOC entry immediately following the sequence's own entry. It's
19149 : * OK to do this rather than using full sorting logic, because the
19150 : * dependency that tells us it's owned will have forced the table to be
19151 : * created first. We can't just include the ALTER in the TOC entry
19152 : * because it will fail if we haven't reassigned the sequence owner to
19153 : * match the table's owner.
19154 : *
19155 : * We need not schema-qualify the table reference because both sequence
19156 : * and table must be in the same schema.
19157 : */
19158 750 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19159 : {
19160 274 : owning_tab = findTableByOid(tbinfo->owning_tab);
19161 :
19162 274 : if (owning_tab == NULL)
19163 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19164 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19165 :
19166 274 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19167 : {
19168 270 : resetPQExpBuffer(query);
19169 270 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19170 270 : fmtQualifiedDumpable(tbinfo));
19171 270 : appendPQExpBuffer(query, " OWNED BY %s",
19172 270 : fmtQualifiedDumpable(owning_tab));
19173 270 : appendPQExpBuffer(query, ".%s;\n",
19174 270 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19175 :
19176 270 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19177 270 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19178 270 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19179 : .namespace = tbinfo->dobj.namespace->dobj.name,
19180 : .owner = tbinfo->rolname,
19181 : .description = "SEQUENCE OWNED BY",
19182 : .section = SECTION_PRE_DATA,
19183 : .createStmt = query->data,
19184 : .deps = &(tbinfo->dobj.dumpId),
19185 : .nDeps = 1));
19186 : }
19187 : }
19188 :
19189 : /* Dump Sequence Comments and Security Labels */
19190 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19191 0 : dumpComment(fout, "SEQUENCE", qseqname,
19192 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19193 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19194 :
19195 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19196 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19197 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19198 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19199 :
19200 750 : if (fout->remoteVersion < 100000)
19201 0 : pg_free(seq);
19202 750 : destroyPQExpBuffer(query);
19203 750 : destroyPQExpBuffer(delqry);
19204 750 : free(qseqname);
19205 750 : }
19206 :
19207 : /*
19208 : * dumpSequenceData
19209 : * write the data of one user-defined sequence
19210 : */
19211 : static void
19212 786 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19213 : {
19214 786 : TableInfo *tbinfo = tdinfo->tdtable;
19215 : int64 last;
19216 : bool called;
19217 786 : PQExpBuffer query = createPQExpBuffer();
19218 :
19219 : /*
19220 : * For versions >= 18, the sequence information is gathered in the sorted
19221 : * array before any calls to dumpSequenceData(). See collectSequences()
19222 : * for more information.
19223 : *
19224 : * For older versions, we have to query the sequence relations
19225 : * individually.
19226 : */
19227 786 : if (fout->remoteVersion < 180000)
19228 : {
19229 : PGresult *res;
19230 :
19231 0 : appendPQExpBuffer(query,
19232 : "SELECT last_value, is_called FROM %s",
19233 0 : fmtQualifiedDumpable(tbinfo));
19234 :
19235 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19236 :
19237 0 : if (PQntuples(res) != 1)
19238 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19239 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19240 : PQntuples(res)),
19241 : tbinfo->dobj.name, PQntuples(res));
19242 :
19243 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19244 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19245 :
19246 0 : PQclear(res);
19247 : }
19248 : else
19249 : {
19250 786 : SequenceItem key = {0};
19251 : SequenceItem *entry;
19252 :
19253 : Assert(sequences);
19254 : Assert(tbinfo->dobj.catId.oid);
19255 :
19256 786 : key.oid = tbinfo->dobj.catId.oid;
19257 786 : entry = bsearch(&key, sequences, nsequences,
19258 : sizeof(SequenceItem), SequenceItemCmp);
19259 :
19260 786 : last = entry->last_value;
19261 786 : called = entry->is_called;
19262 : }
19263 :
19264 786 : resetPQExpBuffer(query);
19265 786 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19266 786 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19267 786 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19268 : last, (called ? "true" : "false"));
19269 :
19270 786 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19271 786 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19272 786 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19273 : .namespace = tbinfo->dobj.namespace->dobj.name,
19274 : .owner = tbinfo->rolname,
19275 : .description = "SEQUENCE SET",
19276 : .section = SECTION_DATA,
19277 : .createStmt = query->data,
19278 : .deps = &(tbinfo->dobj.dumpId),
19279 : .nDeps = 1));
19280 :
19281 786 : destroyPQExpBuffer(query);
19282 786 : }
19283 :
19284 : /*
19285 : * dumpTrigger
19286 : * write the declaration of one user-defined table trigger
19287 : */
19288 : static void
19289 1046 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19290 : {
19291 1046 : DumpOptions *dopt = fout->dopt;
19292 1046 : TableInfo *tbinfo = tginfo->tgtable;
19293 : PQExpBuffer query;
19294 : PQExpBuffer delqry;
19295 : PQExpBuffer trigprefix;
19296 : PQExpBuffer trigidentity;
19297 : char *qtabname;
19298 : char *tag;
19299 :
19300 : /* Do nothing if not dumping schema */
19301 1046 : if (!dopt->dumpSchema)
19302 62 : return;
19303 :
19304 984 : query = createPQExpBuffer();
19305 984 : delqry = createPQExpBuffer();
19306 984 : trigprefix = createPQExpBuffer();
19307 984 : trigidentity = createPQExpBuffer();
19308 :
19309 984 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19310 :
19311 984 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19312 984 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19313 :
19314 984 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19315 984 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19316 :
19317 : /* Triggers can depend on extensions */
19318 984 : append_depends_on_extension(fout, query, &tginfo->dobj,
19319 : "pg_catalog.pg_trigger", "TRIGGER",
19320 984 : trigidentity->data);
19321 :
19322 984 : if (tginfo->tgispartition)
19323 : {
19324 : Assert(tbinfo->ispartition);
19325 :
19326 : /*
19327 : * Partition triggers only appear here because their 'tgenabled' flag
19328 : * differs from its parent's. The trigger is created already, so
19329 : * remove the CREATE and replace it with an ALTER. (Clear out the
19330 : * DROP query too, so that pg_dump --create does not cause errors.)
19331 : */
19332 218 : resetPQExpBuffer(query);
19333 218 : resetPQExpBuffer(delqry);
19334 218 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19335 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19336 218 : fmtQualifiedDumpable(tbinfo));
19337 218 : switch (tginfo->tgenabled)
19338 : {
19339 76 : case 'f':
19340 : case 'D':
19341 76 : appendPQExpBufferStr(query, "DISABLE");
19342 76 : break;
19343 0 : case 't':
19344 : case 'O':
19345 0 : appendPQExpBufferStr(query, "ENABLE");
19346 0 : break;
19347 66 : case 'R':
19348 66 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19349 66 : break;
19350 76 : case 'A':
19351 76 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19352 76 : break;
19353 : }
19354 218 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19355 218 : fmtId(tginfo->dobj.name));
19356 : }
19357 766 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19358 : {
19359 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19360 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19361 0 : fmtQualifiedDumpable(tbinfo));
19362 0 : switch (tginfo->tgenabled)
19363 : {
19364 0 : case 'D':
19365 : case 'f':
19366 0 : appendPQExpBufferStr(query, "DISABLE");
19367 0 : break;
19368 0 : case 'A':
19369 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19370 0 : break;
19371 0 : case 'R':
19372 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19373 0 : break;
19374 0 : default:
19375 0 : appendPQExpBufferStr(query, "ENABLE");
19376 0 : break;
19377 : }
19378 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19379 0 : fmtId(tginfo->dobj.name));
19380 : }
19381 :
19382 984 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19383 984 : fmtId(tginfo->dobj.name));
19384 :
19385 984 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19386 :
19387 984 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19388 984 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19389 984 : ARCHIVE_OPTS(.tag = tag,
19390 : .namespace = tbinfo->dobj.namespace->dobj.name,
19391 : .owner = tbinfo->rolname,
19392 : .description = "TRIGGER",
19393 : .section = SECTION_POST_DATA,
19394 : .createStmt = query->data,
19395 : .dropStmt = delqry->data));
19396 :
19397 984 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19398 0 : dumpComment(fout, trigprefix->data, qtabname,
19399 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19400 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19401 :
19402 984 : free(tag);
19403 984 : destroyPQExpBuffer(query);
19404 984 : destroyPQExpBuffer(delqry);
19405 984 : destroyPQExpBuffer(trigprefix);
19406 984 : destroyPQExpBuffer(trigidentity);
19407 984 : free(qtabname);
19408 : }
19409 :
19410 : /*
19411 : * dumpEventTrigger
19412 : * write the declaration of one user-defined event trigger
19413 : */
19414 : static void
19415 84 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19416 : {
19417 84 : DumpOptions *dopt = fout->dopt;
19418 : PQExpBuffer query;
19419 : PQExpBuffer delqry;
19420 : char *qevtname;
19421 :
19422 : /* Do nothing if not dumping schema */
19423 84 : if (!dopt->dumpSchema)
19424 12 : return;
19425 :
19426 72 : query = createPQExpBuffer();
19427 72 : delqry = createPQExpBuffer();
19428 :
19429 72 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19430 :
19431 72 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19432 72 : appendPQExpBufferStr(query, qevtname);
19433 72 : appendPQExpBufferStr(query, " ON ");
19434 72 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19435 :
19436 72 : if (strcmp("", evtinfo->evttags) != 0)
19437 : {
19438 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19439 10 : appendPQExpBufferStr(query, evtinfo->evttags);
19440 10 : appendPQExpBufferChar(query, ')');
19441 : }
19442 :
19443 72 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19444 72 : appendPQExpBufferStr(query, evtinfo->evtfname);
19445 72 : appendPQExpBufferStr(query, "();\n");
19446 :
19447 72 : if (evtinfo->evtenabled != 'O')
19448 : {
19449 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19450 : qevtname);
19451 0 : switch (evtinfo->evtenabled)
19452 : {
19453 0 : case 'D':
19454 0 : appendPQExpBufferStr(query, "DISABLE");
19455 0 : break;
19456 0 : case 'A':
19457 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19458 0 : break;
19459 0 : case 'R':
19460 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19461 0 : break;
19462 0 : default:
19463 0 : appendPQExpBufferStr(query, "ENABLE");
19464 0 : break;
19465 : }
19466 0 : appendPQExpBufferStr(query, ";\n");
19467 : }
19468 :
19469 72 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19470 : qevtname);
19471 :
19472 72 : if (dopt->binary_upgrade)
19473 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19474 : "EVENT TRIGGER", qevtname, NULL);
19475 :
19476 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19477 72 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19478 72 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19479 : .owner = evtinfo->evtowner,
19480 : .description = "EVENT TRIGGER",
19481 : .section = SECTION_POST_DATA,
19482 : .createStmt = query->data,
19483 : .dropStmt = delqry->data));
19484 :
19485 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19486 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19487 0 : NULL, evtinfo->evtowner,
19488 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19489 :
19490 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19491 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19492 0 : NULL, evtinfo->evtowner,
19493 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19494 :
19495 72 : destroyPQExpBuffer(query);
19496 72 : destroyPQExpBuffer(delqry);
19497 72 : free(qevtname);
19498 : }
19499 :
19500 : /*
19501 : * dumpRule
19502 : * Dump a rule
19503 : */
19504 : static void
19505 2258 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19506 : {
19507 2258 : DumpOptions *dopt = fout->dopt;
19508 2258 : TableInfo *tbinfo = rinfo->ruletable;
19509 : bool is_view;
19510 : PQExpBuffer query;
19511 : PQExpBuffer cmd;
19512 : PQExpBuffer delcmd;
19513 : PQExpBuffer ruleprefix;
19514 : char *qtabname;
19515 : PGresult *res;
19516 : char *tag;
19517 :
19518 : /* Do nothing if not dumping schema */
19519 2258 : if (!dopt->dumpSchema)
19520 120 : return;
19521 :
19522 : /*
19523 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19524 : * we do not want to dump it as a separate object.
19525 : */
19526 2138 : if (!rinfo->separate)
19527 1716 : return;
19528 :
19529 : /*
19530 : * If it's an ON SELECT rule, we want to print it as a view definition,
19531 : * instead of a rule.
19532 : */
19533 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19534 :
19535 422 : query = createPQExpBuffer();
19536 422 : cmd = createPQExpBuffer();
19537 422 : delcmd = createPQExpBuffer();
19538 422 : ruleprefix = createPQExpBuffer();
19539 :
19540 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19541 :
19542 422 : if (is_view)
19543 : {
19544 : PQExpBuffer result;
19545 :
19546 : /*
19547 : * We need OR REPLACE here because we'll be replacing a dummy view.
19548 : * Otherwise this should look largely like the regular view dump code.
19549 : */
19550 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19551 20 : fmtQualifiedDumpable(tbinfo));
19552 20 : if (nonemptyReloptions(tbinfo->reloptions))
19553 : {
19554 0 : appendPQExpBufferStr(cmd, " WITH (");
19555 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19556 0 : appendPQExpBufferChar(cmd, ')');
19557 : }
19558 20 : result = createViewAsClause(fout, tbinfo);
19559 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19560 20 : destroyPQExpBuffer(result);
19561 20 : if (tbinfo->checkoption != NULL)
19562 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19563 : tbinfo->checkoption);
19564 20 : appendPQExpBufferStr(cmd, ";\n");
19565 : }
19566 : else
19567 : {
19568 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19569 402 : appendPQExpBuffer(query,
19570 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19571 402 : rinfo->dobj.catId.oid);
19572 :
19573 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19574 :
19575 402 : if (PQntuples(res) != 1)
19576 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19577 : rinfo->dobj.name, tbinfo->dobj.name);
19578 :
19579 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19580 :
19581 402 : PQclear(res);
19582 : }
19583 :
19584 : /*
19585 : * Add the command to alter the rules replication firing semantics if it
19586 : * differs from the default.
19587 : */
19588 422 : if (rinfo->ev_enabled != 'O')
19589 : {
19590 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19591 30 : switch (rinfo->ev_enabled)
19592 : {
19593 0 : case 'A':
19594 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19595 0 : fmtId(rinfo->dobj.name));
19596 0 : break;
19597 0 : case 'R':
19598 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19599 0 : fmtId(rinfo->dobj.name));
19600 0 : break;
19601 30 : case 'D':
19602 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19603 30 : fmtId(rinfo->dobj.name));
19604 30 : break;
19605 : }
19606 : }
19607 :
19608 422 : if (is_view)
19609 : {
19610 : /*
19611 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19612 : * REPLACE VIEW to replace the rule with something with minimal
19613 : * dependencies.
19614 : */
19615 : PQExpBuffer result;
19616 :
19617 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19618 20 : fmtQualifiedDumpable(tbinfo));
19619 20 : result = createDummyViewAsClause(fout, tbinfo);
19620 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19621 20 : destroyPQExpBuffer(result);
19622 : }
19623 : else
19624 : {
19625 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19626 402 : fmtId(rinfo->dobj.name));
19627 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
19628 402 : fmtQualifiedDumpable(tbinfo));
19629 : }
19630 :
19631 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19632 422 : fmtId(rinfo->dobj.name));
19633 :
19634 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19635 :
19636 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19637 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19638 422 : ARCHIVE_OPTS(.tag = tag,
19639 : .namespace = tbinfo->dobj.namespace->dobj.name,
19640 : .owner = tbinfo->rolname,
19641 : .description = "RULE",
19642 : .section = SECTION_POST_DATA,
19643 : .createStmt = cmd->data,
19644 : .dropStmt = delcmd->data));
19645 :
19646 : /* Dump rule comments */
19647 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19648 0 : dumpComment(fout, ruleprefix->data, qtabname,
19649 0 : tbinfo->dobj.namespace->dobj.name,
19650 : tbinfo->rolname,
19651 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19652 :
19653 422 : free(tag);
19654 422 : destroyPQExpBuffer(query);
19655 422 : destroyPQExpBuffer(cmd);
19656 422 : destroyPQExpBuffer(delcmd);
19657 422 : destroyPQExpBuffer(ruleprefix);
19658 422 : free(qtabname);
19659 : }
19660 :
19661 : /*
19662 : * getExtensionMembership --- obtain extension membership data
19663 : *
19664 : * We need to identify objects that are extension members as soon as they're
19665 : * loaded, so that we can correctly determine whether they need to be dumped.
19666 : * Generally speaking, extension member objects will get marked as *not* to
19667 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19668 : * command. However, in binary upgrade mode we still need to dump the members
19669 : * individually.
19670 : */
19671 : void
19672 374 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19673 : int numExtensions)
19674 : {
19675 : PQExpBuffer query;
19676 : PGresult *res;
19677 : int ntups,
19678 : i;
19679 : int i_classid,
19680 : i_objid,
19681 : i_refobjid;
19682 : ExtensionInfo *ext;
19683 :
19684 : /* Nothing to do if no extensions */
19685 374 : if (numExtensions == 0)
19686 0 : return;
19687 :
19688 374 : query = createPQExpBuffer();
19689 :
19690 : /* refclassid constraint is redundant but may speed the search */
19691 374 : appendPQExpBufferStr(query, "SELECT "
19692 : "classid, objid, refobjid "
19693 : "FROM pg_depend "
19694 : "WHERE refclassid = 'pg_extension'::regclass "
19695 : "AND deptype = 'e' "
19696 : "ORDER BY 3");
19697 :
19698 374 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19699 :
19700 374 : ntups = PQntuples(res);
19701 :
19702 374 : i_classid = PQfnumber(res, "classid");
19703 374 : i_objid = PQfnumber(res, "objid");
19704 374 : i_refobjid = PQfnumber(res, "refobjid");
19705 :
19706 : /*
19707 : * Since we ordered the SELECT by referenced ID, we can expect that
19708 : * multiple entries for the same extension will appear together; this
19709 : * saves on searches.
19710 : */
19711 374 : ext = NULL;
19712 :
19713 3080 : for (i = 0; i < ntups; i++)
19714 : {
19715 : CatalogId objId;
19716 : Oid extId;
19717 :
19718 2706 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19719 2706 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19720 2706 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19721 :
19722 2706 : if (ext == NULL ||
19723 2332 : ext->dobj.catId.oid != extId)
19724 434 : ext = findExtensionByOid(extId);
19725 :
19726 2706 : if (ext == NULL)
19727 : {
19728 : /* shouldn't happen */
19729 0 : pg_log_warning("could not find referenced extension %u", extId);
19730 0 : continue;
19731 : }
19732 :
19733 2706 : recordExtensionMembership(objId, ext);
19734 : }
19735 :
19736 374 : PQclear(res);
19737 :
19738 374 : destroyPQExpBuffer(query);
19739 : }
19740 :
19741 : /*
19742 : * processExtensionTables --- deal with extension configuration tables
19743 : *
19744 : * There are two parts to this process:
19745 : *
19746 : * 1. Identify and create dump records for extension configuration tables.
19747 : *
19748 : * Extensions can mark tables as "configuration", which means that the user
19749 : * is able and expected to modify those tables after the extension has been
19750 : * loaded. For these tables, we dump out only the data- the structure is
19751 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19752 : * foreign keys, which brings us to-
19753 : *
19754 : * 2. Record FK dependencies between configuration tables.
19755 : *
19756 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19757 : * the data is loaded, we have to work out what the best order for reloading
19758 : * the data is, to avoid FK violations when the tables are restored. This is
19759 : * not perfect- we can't handle circular dependencies and if any exist they
19760 : * will cause an invalid dump to be produced (though at least all of the data
19761 : * is included for a user to manually restore). This is currently documented
19762 : * but perhaps we can provide a better solution in the future.
19763 : */
19764 : void
19765 372 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19766 : int numExtensions)
19767 : {
19768 372 : DumpOptions *dopt = fout->dopt;
19769 : PQExpBuffer query;
19770 : PGresult *res;
19771 : int ntups,
19772 : i;
19773 : int i_conrelid,
19774 : i_confrelid;
19775 :
19776 : /* Nothing to do if no extensions */
19777 372 : if (numExtensions == 0)
19778 0 : return;
19779 :
19780 : /*
19781 : * Identify extension configuration tables and create TableDataInfo
19782 : * objects for them, ensuring their data will be dumped even though the
19783 : * tables themselves won't be.
19784 : *
19785 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19786 : * user data in a configuration table is treated like schema data. This
19787 : * seems appropriate since system data in a config table would get
19788 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19789 : * list of extensions to be included, none of its data is dumped.
19790 : */
19791 804 : for (i = 0; i < numExtensions; i++)
19792 : {
19793 432 : ExtensionInfo *curext = &(extinfo[i]);
19794 432 : char *extconfig = curext->extconfig;
19795 432 : char *extcondition = curext->extcondition;
19796 432 : char **extconfigarray = NULL;
19797 432 : char **extconditionarray = NULL;
19798 432 : int nconfigitems = 0;
19799 432 : int nconditionitems = 0;
19800 :
19801 : /*
19802 : * Check if this extension is listed as to include in the dump. If
19803 : * not, any table data associated with it is discarded.
19804 : */
19805 432 : if (extension_include_oids.head != NULL &&
19806 16 : !simple_oid_list_member(&extension_include_oids,
19807 : curext->dobj.catId.oid))
19808 12 : continue;
19809 :
19810 : /*
19811 : * Check if this extension is listed as to exclude in the dump. If
19812 : * yes, any table data associated with it is discarded.
19813 : */
19814 432 : if (extension_exclude_oids.head != NULL &&
19815 8 : simple_oid_list_member(&extension_exclude_oids,
19816 : curext->dobj.catId.oid))
19817 4 : continue;
19818 :
19819 420 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19820 : {
19821 : int j;
19822 :
19823 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19824 0 : pg_fatal("could not parse %s array", "extconfig");
19825 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19826 0 : pg_fatal("could not parse %s array", "extcondition");
19827 40 : if (nconfigitems != nconditionitems)
19828 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19829 :
19830 120 : for (j = 0; j < nconfigitems; j++)
19831 : {
19832 : TableInfo *configtbl;
19833 80 : Oid configtbloid = atooid(extconfigarray[j]);
19834 80 : bool dumpobj =
19835 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19836 :
19837 80 : configtbl = findTableByOid(configtbloid);
19838 80 : if (configtbl == NULL)
19839 0 : continue;
19840 :
19841 : /*
19842 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19843 : * unless the table or its schema is explicitly included
19844 : */
19845 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19846 : {
19847 : /* check table explicitly requested */
19848 4 : if (table_include_oids.head != NULL &&
19849 0 : simple_oid_list_member(&table_include_oids,
19850 : configtbloid))
19851 0 : dumpobj = true;
19852 :
19853 : /* check table's schema explicitly requested */
19854 4 : if (configtbl->dobj.namespace->dobj.dump &
19855 : DUMP_COMPONENT_DATA)
19856 4 : dumpobj = true;
19857 : }
19858 :
19859 : /* check table excluded by an exclusion switch */
19860 88 : if (table_exclude_oids.head != NULL &&
19861 8 : simple_oid_list_member(&table_exclude_oids,
19862 : configtbloid))
19863 2 : dumpobj = false;
19864 :
19865 : /* check schema excluded by an exclusion switch */
19866 80 : if (simple_oid_list_member(&schema_exclude_oids,
19867 80 : configtbl->dobj.namespace->dobj.catId.oid))
19868 0 : dumpobj = false;
19869 :
19870 80 : if (dumpobj)
19871 : {
19872 78 : makeTableDataInfo(dopt, configtbl);
19873 78 : if (configtbl->dataObj != NULL)
19874 : {
19875 78 : if (strlen(extconditionarray[j]) > 0)
19876 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19877 : }
19878 : }
19879 : }
19880 : }
19881 420 : if (extconfigarray)
19882 40 : free(extconfigarray);
19883 420 : if (extconditionarray)
19884 40 : free(extconditionarray);
19885 : }
19886 :
19887 : /*
19888 : * Now that all the TableDataInfo objects have been created for all the
19889 : * extensions, check their FK dependencies and register them to try and
19890 : * dump the data out in an order that they can be restored in.
19891 : *
19892 : * Note that this is not a problem for user tables as their FKs are
19893 : * recreated after the data has been loaded.
19894 : */
19895 :
19896 372 : query = createPQExpBuffer();
19897 :
19898 372 : printfPQExpBuffer(query,
19899 : "SELECT conrelid, confrelid "
19900 : "FROM pg_constraint "
19901 : "JOIN pg_depend ON (objid = confrelid) "
19902 : "WHERE contype = 'f' "
19903 : "AND refclassid = 'pg_extension'::regclass "
19904 : "AND classid = 'pg_class'::regclass;");
19905 :
19906 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19907 372 : ntups = PQntuples(res);
19908 :
19909 372 : i_conrelid = PQfnumber(res, "conrelid");
19910 372 : i_confrelid = PQfnumber(res, "confrelid");
19911 :
19912 : /* Now get the dependencies and register them */
19913 372 : for (i = 0; i < ntups; i++)
19914 : {
19915 : Oid conrelid,
19916 : confrelid;
19917 : TableInfo *reftable,
19918 : *contable;
19919 :
19920 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19921 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19922 0 : contable = findTableByOid(conrelid);
19923 0 : reftable = findTableByOid(confrelid);
19924 :
19925 0 : if (reftable == NULL ||
19926 0 : reftable->dataObj == NULL ||
19927 0 : contable == NULL ||
19928 0 : contable->dataObj == NULL)
19929 0 : continue;
19930 :
19931 : /*
19932 : * Make referencing TABLE_DATA object depend on the referenced table's
19933 : * TABLE_DATA object.
19934 : */
19935 0 : addObjectDependency(&contable->dataObj->dobj,
19936 0 : reftable->dataObj->dobj.dumpId);
19937 : }
19938 372 : PQclear(res);
19939 372 : destroyPQExpBuffer(query);
19940 : }
19941 :
19942 : /*
19943 : * getDependencies --- obtain available dependency data
19944 : */
19945 : static void
19946 372 : getDependencies(Archive *fout)
19947 : {
19948 : PQExpBuffer query;
19949 : PGresult *res;
19950 : int ntups,
19951 : i;
19952 : int i_classid,
19953 : i_objid,
19954 : i_refclassid,
19955 : i_refobjid,
19956 : i_deptype;
19957 : DumpableObject *dobj,
19958 : *refdobj;
19959 :
19960 372 : pg_log_info("reading dependency data");
19961 :
19962 372 : query = createPQExpBuffer();
19963 :
19964 : /*
19965 : * Messy query to collect the dependency data we need. Note that we
19966 : * ignore the sub-object column, so that dependencies of or on a column
19967 : * look the same as dependencies of or on a whole table.
19968 : *
19969 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19970 : * already processed by getExtensionMembership.
19971 : */
19972 372 : appendPQExpBufferStr(query, "SELECT "
19973 : "classid, objid, refclassid, refobjid, deptype "
19974 : "FROM pg_depend "
19975 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19976 :
19977 : /*
19978 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19979 : * have to translate their dependencies into dependencies of their parent
19980 : * opfamily. Ignore internal dependencies though, as those will point to
19981 : * their parent opclass, which we needn't consider here (and if we did,
19982 : * it'd just result in circular dependencies). Also, "loose" opfamily
19983 : * entries will have dependencies on their parent opfamily, which we
19984 : * should drop since they'd likewise become useless self-dependencies.
19985 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
19986 : */
19987 372 : appendPQExpBufferStr(query, "UNION ALL\n"
19988 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
19989 : "FROM pg_depend d, pg_amop o "
19990 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19991 : "classid = 'pg_amop'::regclass AND objid = o.oid "
19992 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
19993 :
19994 : /* Likewise for pg_amproc entries */
19995 372 : appendPQExpBufferStr(query, "UNION ALL\n"
19996 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
19997 : "FROM pg_depend d, pg_amproc p "
19998 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
19999 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20000 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20001 :
20002 : /* Sort the output for efficiency below */
20003 372 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20004 :
20005 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20006 :
20007 372 : ntups = PQntuples(res);
20008 :
20009 372 : i_classid = PQfnumber(res, "classid");
20010 372 : i_objid = PQfnumber(res, "objid");
20011 372 : i_refclassid = PQfnumber(res, "refclassid");
20012 372 : i_refobjid = PQfnumber(res, "refobjid");
20013 372 : i_deptype = PQfnumber(res, "deptype");
20014 :
20015 : /*
20016 : * Since we ordered the SELECT by referencing ID, we can expect that
20017 : * multiple entries for the same object will appear together; this saves
20018 : * on searches.
20019 : */
20020 372 : dobj = NULL;
20021 :
20022 803794 : for (i = 0; i < ntups; i++)
20023 : {
20024 : CatalogId objId;
20025 : CatalogId refobjId;
20026 : char deptype;
20027 :
20028 803422 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20029 803422 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20030 803422 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20031 803422 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20032 803422 : deptype = *(PQgetvalue(res, i, i_deptype));
20033 :
20034 803422 : if (dobj == NULL ||
20035 754316 : dobj->catId.tableoid != objId.tableoid ||
20036 750090 : dobj->catId.oid != objId.oid)
20037 350816 : dobj = findObjectByCatalogId(objId);
20038 :
20039 : /*
20040 : * Failure to find objects mentioned in pg_depend is not unexpected,
20041 : * since for example we don't collect info about TOAST tables.
20042 : */
20043 803422 : if (dobj == NULL)
20044 : {
20045 : #ifdef NOT_USED
20046 : pg_log_warning("no referencing object %u %u",
20047 : objId.tableoid, objId.oid);
20048 : #endif
20049 50442 : continue;
20050 : }
20051 :
20052 754688 : refdobj = findObjectByCatalogId(refobjId);
20053 :
20054 754688 : if (refdobj == NULL)
20055 : {
20056 : #ifdef NOT_USED
20057 : pg_log_warning("no referenced object %u %u",
20058 : refobjId.tableoid, refobjId.oid);
20059 : #endif
20060 1708 : continue;
20061 : }
20062 :
20063 : /*
20064 : * For 'x' dependencies, mark the object for later; we still add the
20065 : * normal dependency, for possible ordering purposes. Currently
20066 : * pg_dump_sort.c knows to put extensions ahead of all object types
20067 : * that could possibly depend on them, but this is safer.
20068 : */
20069 752980 : if (deptype == 'x')
20070 88 : dobj->depends_on_ext = true;
20071 :
20072 : /*
20073 : * Ordinarily, table rowtypes have implicit dependencies on their
20074 : * tables. However, for a composite type the implicit dependency goes
20075 : * the other way in pg_depend; which is the right thing for DROP but
20076 : * it doesn't produce the dependency ordering we need. So in that one
20077 : * case, we reverse the direction of the dependency.
20078 : */
20079 752980 : if (deptype == 'i' &&
20080 211492 : dobj->objType == DO_TABLE &&
20081 2488 : refdobj->objType == DO_TYPE)
20082 364 : addObjectDependency(refdobj, dobj->dumpId);
20083 : else
20084 : /* normal case */
20085 752616 : addObjectDependency(dobj, refdobj->dumpId);
20086 : }
20087 :
20088 372 : PQclear(res);
20089 :
20090 372 : destroyPQExpBuffer(query);
20091 372 : }
20092 :
20093 :
20094 : /*
20095 : * createBoundaryObjects - create dummy DumpableObjects to represent
20096 : * dump section boundaries.
20097 : */
20098 : static DumpableObject *
20099 372 : createBoundaryObjects(void)
20100 : {
20101 : DumpableObject *dobjs;
20102 :
20103 372 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20104 :
20105 372 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20106 372 : dobjs[0].catId = nilCatalogId;
20107 372 : AssignDumpId(dobjs + 0);
20108 372 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20109 :
20110 372 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20111 372 : dobjs[1].catId = nilCatalogId;
20112 372 : AssignDumpId(dobjs + 1);
20113 372 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20114 :
20115 372 : return dobjs;
20116 : }
20117 :
20118 : /*
20119 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20120 : * section boundaries.
20121 : */
20122 : static void
20123 372 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20124 : DumpableObject *boundaryObjs)
20125 : {
20126 372 : DumpableObject *preDataBound = boundaryObjs + 0;
20127 372 : DumpableObject *postDataBound = boundaryObjs + 1;
20128 : int i;
20129 :
20130 1379832 : for (i = 0; i < numObjs; i++)
20131 : {
20132 1379460 : DumpableObject *dobj = dobjs[i];
20133 :
20134 : /*
20135 : * The classification of object types here must match the SECTION_xxx
20136 : * values assigned during subsequent ArchiveEntry calls!
20137 : */
20138 1379460 : switch (dobj->objType)
20139 : {
20140 1288402 : case DO_NAMESPACE:
20141 : case DO_EXTENSION:
20142 : case DO_TYPE:
20143 : case DO_SHELL_TYPE:
20144 : case DO_FUNC:
20145 : case DO_AGG:
20146 : case DO_OPERATOR:
20147 : case DO_ACCESS_METHOD:
20148 : case DO_OPCLASS:
20149 : case DO_OPFAMILY:
20150 : case DO_COLLATION:
20151 : case DO_CONVERSION:
20152 : case DO_TABLE:
20153 : case DO_TABLE_ATTACH:
20154 : case DO_ATTRDEF:
20155 : case DO_PROCLANG:
20156 : case DO_CAST:
20157 : case DO_DUMMY_TYPE:
20158 : case DO_TSPARSER:
20159 : case DO_TSDICT:
20160 : case DO_TSTEMPLATE:
20161 : case DO_TSCONFIG:
20162 : case DO_FDW:
20163 : case DO_FOREIGN_SERVER:
20164 : case DO_TRANSFORM:
20165 : /* Pre-data objects: must come before the pre-data boundary */
20166 1288402 : addObjectDependency(preDataBound, dobj->dumpId);
20167 1288402 : break;
20168 9582 : case DO_TABLE_DATA:
20169 : case DO_SEQUENCE_SET:
20170 : case DO_LARGE_OBJECT:
20171 : case DO_LARGE_OBJECT_DATA:
20172 : /* Data objects: must come between the boundaries */
20173 9582 : addObjectDependency(dobj, preDataBound->dumpId);
20174 9582 : addObjectDependency(postDataBound, dobj->dumpId);
20175 9582 : break;
20176 11412 : case DO_INDEX:
20177 : case DO_INDEX_ATTACH:
20178 : case DO_STATSEXT:
20179 : case DO_REFRESH_MATVIEW:
20180 : case DO_TRIGGER:
20181 : case DO_EVENT_TRIGGER:
20182 : case DO_DEFAULT_ACL:
20183 : case DO_POLICY:
20184 : case DO_PUBLICATION:
20185 : case DO_PUBLICATION_REL:
20186 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20187 : case DO_SUBSCRIPTION:
20188 : case DO_SUBSCRIPTION_REL:
20189 : /* Post-data objects: must come after the post-data boundary */
20190 11412 : addObjectDependency(dobj, postDataBound->dumpId);
20191 11412 : break;
20192 57714 : case DO_RULE:
20193 : /* Rules are post-data, but only if dumped separately */
20194 57714 : if (((RuleInfo *) dobj)->separate)
20195 1290 : addObjectDependency(dobj, postDataBound->dumpId);
20196 57714 : break;
20197 5034 : case DO_CONSTRAINT:
20198 : case DO_FK_CONSTRAINT:
20199 : /* Constraints are post-data, but only if dumped separately */
20200 5034 : if (((ConstraintInfo *) dobj)->separate)
20201 3618 : addObjectDependency(dobj, postDataBound->dumpId);
20202 5034 : break;
20203 372 : case DO_PRE_DATA_BOUNDARY:
20204 : /* nothing to do */
20205 372 : break;
20206 372 : case DO_POST_DATA_BOUNDARY:
20207 : /* must come after the pre-data boundary */
20208 372 : addObjectDependency(dobj, preDataBound->dumpId);
20209 372 : break;
20210 6572 : case DO_REL_STATS:
20211 : /* stats section varies by parent object type, DATA or POST */
20212 6572 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20213 : {
20214 4224 : addObjectDependency(dobj, preDataBound->dumpId);
20215 4224 : addObjectDependency(postDataBound, dobj->dumpId);
20216 : }
20217 : else
20218 2348 : addObjectDependency(dobj, postDataBound->dumpId);
20219 6572 : break;
20220 : }
20221 : }
20222 372 : }
20223 :
20224 :
20225 : /*
20226 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20227 : *
20228 : * The raw dependency data obtained by getDependencies() is not terribly
20229 : * useful in an archive dump, because in many cases there are dependency
20230 : * chains linking through objects that don't appear explicitly in the dump.
20231 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20232 : * will depend on other objects --- but the rule will not appear as a separate
20233 : * object in the dump. We need to adjust the view's dependencies to include
20234 : * whatever the rule depends on that is included in the dump.
20235 : *
20236 : * Just to make things more complicated, there are also "special" dependencies
20237 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20238 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20239 : * its table. In these cases we must leave the dependencies strictly as-is
20240 : * even if they refer to not-to-be-dumped objects.
20241 : *
20242 : * To handle this, the convention is that "special" dependencies are created
20243 : * during ArchiveEntry calls, and an archive TOC item that has any such
20244 : * entries will not be touched here. Otherwise, we recursively search the
20245 : * DumpableObject data structures to build the correct dependencies for each
20246 : * archive TOC item.
20247 : */
20248 : static void
20249 112 : BuildArchiveDependencies(Archive *fout)
20250 : {
20251 112 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20252 : TocEntry *te;
20253 :
20254 : /* Scan all TOC entries in the archive */
20255 13216 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20256 : {
20257 : DumpableObject *dobj;
20258 : DumpId *dependencies;
20259 : int nDeps;
20260 : int allocDeps;
20261 :
20262 : /* No need to process entries that will not be dumped */
20263 13104 : if (te->reqs == 0)
20264 6400 : continue;
20265 : /* Ignore entries that already have "special" dependencies */
20266 13078 : if (te->nDeps > 0)
20267 5518 : continue;
20268 : /* Otherwise, look up the item's original DumpableObject, if any */
20269 7560 : dobj = findObjectByDumpId(te->dumpId);
20270 7560 : if (dobj == NULL)
20271 672 : continue;
20272 : /* No work if it has no dependencies */
20273 6888 : if (dobj->nDeps <= 0)
20274 184 : continue;
20275 : /* Set up work array */
20276 6704 : allocDeps = 64;
20277 6704 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20278 6704 : nDeps = 0;
20279 : /* Recursively find all dumpable dependencies */
20280 6704 : findDumpableDependencies(AH, dobj,
20281 : &dependencies, &nDeps, &allocDeps);
20282 : /* And save 'em ... */
20283 6704 : if (nDeps > 0)
20284 : {
20285 4982 : dependencies = (DumpId *) pg_realloc(dependencies,
20286 : nDeps * sizeof(DumpId));
20287 4982 : te->dependencies = dependencies;
20288 4982 : te->nDeps = nDeps;
20289 : }
20290 : else
20291 1722 : free(dependencies);
20292 : }
20293 112 : }
20294 :
20295 : /* Recursive search subroutine for BuildArchiveDependencies */
20296 : static void
20297 16468 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20298 : DumpId **dependencies, int *nDeps, int *allocDeps)
20299 : {
20300 : int i;
20301 :
20302 : /*
20303 : * Ignore section boundary objects: if we search through them, we'll
20304 : * report lots of bogus dependencies.
20305 : */
20306 16468 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20307 16434 : dobj->objType == DO_POST_DATA_BOUNDARY)
20308 2770 : return;
20309 :
20310 33982 : for (i = 0; i < dobj->nDeps; i++)
20311 : {
20312 20284 : DumpId depid = dobj->dependencies[i];
20313 :
20314 20284 : if (TocIDRequired(AH, depid) != 0)
20315 : {
20316 : /* Object will be dumped, so just reference it as a dependency */
20317 10520 : if (*nDeps >= *allocDeps)
20318 : {
20319 0 : *allocDeps *= 2;
20320 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20321 0 : *allocDeps * sizeof(DumpId));
20322 : }
20323 10520 : (*dependencies)[*nDeps] = depid;
20324 10520 : (*nDeps)++;
20325 : }
20326 : else
20327 : {
20328 : /*
20329 : * Object will not be dumped, so recursively consider its deps. We
20330 : * rely on the assumption that sortDumpableObjects already broke
20331 : * any dependency loops, else we might recurse infinitely.
20332 : */
20333 9764 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20334 :
20335 9764 : if (otherdobj)
20336 9764 : findDumpableDependencies(AH, otherdobj,
20337 : dependencies, nDeps, allocDeps);
20338 : }
20339 : }
20340 : }
20341 :
20342 :
20343 : /*
20344 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20345 : * given type OID.
20346 : *
20347 : * This does not guarantee to schema-qualify the output, so it should not
20348 : * be used to create the target object name for CREATE or ALTER commands.
20349 : *
20350 : * Note that the result is cached and must not be freed by the caller.
20351 : */
20352 : static const char *
20353 4600 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20354 : {
20355 : TypeInfo *typeInfo;
20356 : char *result;
20357 : PQExpBuffer query;
20358 : PGresult *res;
20359 :
20360 4600 : if (oid == 0)
20361 : {
20362 0 : if ((opts & zeroAsStar) != 0)
20363 0 : return "*";
20364 0 : else if ((opts & zeroAsNone) != 0)
20365 0 : return "NONE";
20366 : }
20367 :
20368 : /* see if we have the result cached in the type's TypeInfo record */
20369 4600 : typeInfo = findTypeByOid(oid);
20370 4600 : if (typeInfo && typeInfo->ftypname)
20371 3666 : return typeInfo->ftypname;
20372 :
20373 934 : query = createPQExpBuffer();
20374 934 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20375 : oid);
20376 :
20377 934 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20378 :
20379 : /* result of format_type is already quoted */
20380 934 : result = pg_strdup(PQgetvalue(res, 0, 0));
20381 :
20382 934 : PQclear(res);
20383 934 : destroyPQExpBuffer(query);
20384 :
20385 : /*
20386 : * Cache the result for re-use in later requests, if possible. If we
20387 : * don't have a TypeInfo for the type, the string will be leaked once the
20388 : * caller is done with it ... but that case really should not happen, so
20389 : * leaking if it does seems acceptable.
20390 : */
20391 934 : if (typeInfo)
20392 934 : typeInfo->ftypname = result;
20393 :
20394 934 : return result;
20395 : }
20396 :
20397 : /*
20398 : * Return a column list clause for the given relation.
20399 : *
20400 : * Special case: if there are no undropped columns in the relation, return
20401 : * "", not an invalid "()" column list.
20402 : */
20403 : static const char *
20404 16388 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20405 : {
20406 16388 : int numatts = ti->numatts;
20407 16388 : char **attnames = ti->attnames;
20408 16388 : bool *attisdropped = ti->attisdropped;
20409 16388 : char *attgenerated = ti->attgenerated;
20410 : bool needComma;
20411 : int i;
20412 :
20413 16388 : appendPQExpBufferChar(buffer, '(');
20414 16388 : needComma = false;
20415 79976 : for (i = 0; i < numatts; i++)
20416 : {
20417 63588 : if (attisdropped[i])
20418 1196 : continue;
20419 62392 : if (attgenerated[i])
20420 2208 : continue;
20421 60184 : if (needComma)
20422 44244 : appendPQExpBufferStr(buffer, ", ");
20423 60184 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20424 60184 : needComma = true;
20425 : }
20426 :
20427 16388 : if (!needComma)
20428 448 : return ""; /* no undropped columns */
20429 :
20430 15940 : appendPQExpBufferChar(buffer, ')');
20431 15940 : return buffer->data;
20432 : }
20433 :
20434 : /*
20435 : * Check if a reloptions array is nonempty.
20436 : */
20437 : static bool
20438 26862 : nonemptyReloptions(const char *reloptions)
20439 : {
20440 : /* Don't want to print it if it's just "{}" */
20441 26862 : return (reloptions != NULL && strlen(reloptions) > 2);
20442 : }
20443 :
20444 : /*
20445 : * Format a reloptions array and append it to the given buffer.
20446 : *
20447 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20448 : */
20449 : static void
20450 416 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20451 : const char *prefix, Archive *fout)
20452 : {
20453 : bool res;
20454 :
20455 416 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20456 416 : fout->std_strings);
20457 416 : if (!res)
20458 0 : pg_log_warning("could not parse %s array", "reloptions");
20459 416 : }
20460 :
20461 : /*
20462 : * read_dump_filters - retrieve object identifier patterns from file
20463 : *
20464 : * Parse the specified filter file for include and exclude patterns, and add
20465 : * them to the relevant lists. If the filename is "-" then filters will be
20466 : * read from STDIN rather than a file.
20467 : */
20468 : static void
20469 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20470 : {
20471 : FilterStateData fstate;
20472 : char *objname;
20473 : FilterCommandType comtype;
20474 : FilterObjectType objtype;
20475 :
20476 52 : filter_init(&fstate, filename, exit_nicely);
20477 :
20478 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20479 : {
20480 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20481 : {
20482 34 : switch (objtype)
20483 : {
20484 0 : case FILTER_OBJECT_TYPE_NONE:
20485 0 : break;
20486 0 : case FILTER_OBJECT_TYPE_DATABASE:
20487 : case FILTER_OBJECT_TYPE_FUNCTION:
20488 : case FILTER_OBJECT_TYPE_INDEX:
20489 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20490 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20491 : case FILTER_OBJECT_TYPE_TRIGGER:
20492 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20493 : "include",
20494 : filter_object_type_name(objtype));
20495 0 : exit_nicely(1);
20496 : break; /* unreachable */
20497 :
20498 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20499 2 : simple_string_list_append(&extension_include_patterns, objname);
20500 2 : break;
20501 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20502 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20503 2 : break;
20504 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20505 2 : simple_string_list_append(&schema_include_patterns, objname);
20506 2 : dopt->include_everything = false;
20507 2 : break;
20508 26 : case FILTER_OBJECT_TYPE_TABLE:
20509 26 : simple_string_list_append(&table_include_patterns, objname);
20510 26 : dopt->include_everything = false;
20511 26 : break;
20512 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20513 2 : simple_string_list_append(&table_include_patterns_and_children,
20514 : objname);
20515 2 : dopt->include_everything = false;
20516 2 : break;
20517 : }
20518 : }
20519 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20520 : {
20521 18 : switch (objtype)
20522 : {
20523 0 : case FILTER_OBJECT_TYPE_NONE:
20524 0 : break;
20525 2 : case FILTER_OBJECT_TYPE_DATABASE:
20526 : case FILTER_OBJECT_TYPE_FUNCTION:
20527 : case FILTER_OBJECT_TYPE_INDEX:
20528 : case FILTER_OBJECT_TYPE_TRIGGER:
20529 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20530 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20531 : "exclude",
20532 : filter_object_type_name(objtype));
20533 2 : exit_nicely(1);
20534 : break;
20535 :
20536 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20537 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20538 2 : break;
20539 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20540 2 : simple_string_list_append(&tabledata_exclude_patterns,
20541 : objname);
20542 2 : break;
20543 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20544 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20545 : objname);
20546 2 : break;
20547 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20548 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20549 4 : break;
20550 4 : case FILTER_OBJECT_TYPE_TABLE:
20551 4 : simple_string_list_append(&table_exclude_patterns, objname);
20552 4 : break;
20553 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20554 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20555 : objname);
20556 2 : break;
20557 : }
20558 : }
20559 : else
20560 : {
20561 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20562 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20563 : }
20564 :
20565 64 : if (objname)
20566 50 : free(objname);
20567 : }
20568 :
20569 44 : filter_free(&fstate);
20570 44 : }
|