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 596 : main(int argc, char **argv)
422 : {
423 : int c;
424 596 : const char *filename = NULL;
425 596 : 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 596 : bool g_verbose = false;
436 596 : const char *dumpencoding = NULL;
437 596 : const char *dumpsnapshot = NULL;
438 596 : char *use_role = NULL;
439 596 : int numWorkers = 1;
440 596 : int plainText = 0;
441 596 : ArchiveFormat archiveFormat = archUnknown;
442 : ArchiveMode archiveMode;
443 596 : pg_compress_specification compression_spec = {0};
444 596 : char *compression_detail = NULL;
445 596 : char *compression_algorithm_str = "none";
446 596 : char *error_detail = NULL;
447 596 : bool user_compression_defined = false;
448 596 : DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
449 596 : bool data_only = false;
450 596 : bool schema_only = false;
451 596 : bool statistics_only = false;
452 596 : bool with_statistics = false;
453 596 : bool no_data = false;
454 596 : bool no_schema = false;
455 596 : 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 596 : pg_logging_init(argv[0]);
546 596 : pg_logging_set_level(PG_LOG_WARNING);
547 596 : 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 596 : init_parallel_dump_utils();
554 :
555 596 : progname = get_progname(argv[0]);
556 :
557 596 : if (argc > 1)
558 : {
559 596 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
560 : {
561 2 : help(progname);
562 2 : exit_nicely(0);
563 : }
564 594 : if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
565 : {
566 128 : puts("pg_dump (PostgreSQL) " PG_VERSION);
567 128 : exit_nicely(0);
568 : }
569 : }
570 :
571 466 : InitDumpOptions(&dopt);
572 :
573 2636 : while ((c = getopt_long(argc, argv, "abBcCd:e:E:f:F:h:j:n:N:Op:RsS:t:T:U:vwWxXZ:",
574 2636 : long_options, &optindex)) != -1)
575 : {
576 2186 : 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 386 : case 'f':
612 386 : filename = pg_strdup(optarg);
613 386 : break;
614 :
615 226 : case 'F':
616 226 : format = pg_strdup(optarg);
617 226 : break;
618 :
619 72 : case 'h': /* server host */
620 72 : dopt.cparams.pghost = pg_strdup(optarg);
621 72 : 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 150 : case 'p': /* server port */
644 150 : dopt.cparams.pgport = pg_strdup(optarg);
645 150 : 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 76 : case 'U':
669 76 : dopt.cparams.username = pg_strdup(optarg);
670 76 : 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 270 : case 0:
696 : /* This covers the long options. */
697 270 : 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 300 : case 7: /* no-sync */
720 300 : dosync = false;
721 300 : 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 76 : case 19:
786 76 : no_data = true;
787 76 : 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 180 : case 22:
798 180 : with_statistics = true;
799 180 : 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 450 : if (optind < argc && dopt.cparams.dbname == NULL)
817 378 : dopt.cparams.dbname = argv[optind++];
818 :
819 : /* Complain if any arguments remain */
820 450 : 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 448 : 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 448 : if (data_only && schema_only)
834 2 : pg_fatal("options %s and %s cannot be used together",
835 : "-s/--schema-only", "-a/--data-only");
836 446 : if (schema_only && statistics_only)
837 2 : pg_fatal("options %s and %s cannot be used together",
838 : "-s/--schema-only", "--statistics-only");
839 444 : if (data_only && statistics_only)
840 2 : pg_fatal("options %s and %s cannot be used together",
841 : "-a/--data-only", "--statistics-only");
842 :
843 : /* reject conflicting "-only" and "no-" options */
844 442 : if (data_only && no_data)
845 0 : pg_fatal("options %s and %s cannot be used together",
846 : "-a/--data-only", "--no-data");
847 442 : if (schema_only && no_schema)
848 0 : pg_fatal("options %s and %s cannot be used together",
849 : "-s/--schema-only", "--no-schema");
850 442 : if (statistics_only && no_statistics)
851 2 : pg_fatal("options %s and %s cannot be used together",
852 : "--statistics-only", "--no-statistics");
853 :
854 : /* reject conflicting "no-" options */
855 440 : if (with_statistics && no_statistics)
856 0 : pg_fatal("options %s and %s cannot be used together",
857 : "--statistics", "--no-statistics");
858 :
859 : /* reject conflicting "-only" options */
860 440 : if (data_only && with_statistics)
861 0 : pg_fatal("options %s and %s cannot be used together",
862 : "-a/--data-only", "--statistics");
863 440 : if (schema_only && with_statistics)
864 2 : pg_fatal("options %s and %s cannot be used together",
865 : "-s/--schema-only", "--statistics");
866 :
867 438 : if (schema_only && foreign_servers_include_patterns.head != NULL)
868 2 : pg_fatal("options %s and %s cannot be used together",
869 : "-s/--schema-only", "--include-foreign-data");
870 :
871 436 : if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
872 2 : pg_fatal("option %s is not supported with parallel backup",
873 : "--include-foreign-data");
874 :
875 434 : if (data_only && dopt.outputClean)
876 2 : pg_fatal("options %s and %s cannot be used together",
877 : "-c/--clean", "-a/--data-only");
878 :
879 432 : if (dopt.if_exists && !dopt.outputClean)
880 2 : pg_fatal("option %s requires option %s",
881 : "--if-exists", "-c/--clean");
882 :
883 : /*
884 : * Set derivative flags. Ambiguous or nonsensical combinations, e.g.
885 : * "--schema-only --no-schema", will have already caused an error in one
886 : * of the checks above.
887 : */
888 430 : dopt.dumpData = ((dopt.dumpData && !schema_only && !statistics_only) ||
889 860 : data_only) && !no_data;
890 430 : dopt.dumpSchema = ((dopt.dumpSchema && !data_only && !statistics_only) ||
891 860 : schema_only) && !no_schema;
892 430 : dopt.dumpStatistics = ((dopt.dumpStatistics && !schema_only && !data_only) ||
893 860 : (statistics_only || with_statistics)) && !no_statistics;
894 :
895 :
896 : /*
897 : * --inserts are already implied above if --column-inserts or
898 : * --rows-per-insert were specified.
899 : */
900 430 : if (dopt.do_nothing && dopt.dump_inserts == 0)
901 2 : pg_fatal("option %s requires option %s, %s, or %s",
902 : "--on-conflict-do-nothing",
903 : "--inserts", "--rows-per-insert", "--column-inserts");
904 :
905 : /* Identify archive format to emit */
906 428 : archiveFormat = parseArchiveFormat(format, &archiveMode);
907 :
908 : /* archiveFormat specific setup */
909 426 : if (archiveFormat == archNull)
910 : {
911 308 : plainText = 1;
912 :
913 : /*
914 : * If you don't provide a restrict key, one will be appointed for you.
915 : */
916 308 : if (!dopt.restrict_key)
917 256 : dopt.restrict_key = generate_restrict_key();
918 308 : if (!dopt.restrict_key)
919 0 : pg_fatal("could not generate restrict key");
920 308 : if (!valid_restrict_key(dopt.restrict_key))
921 0 : pg_fatal("invalid restrict key");
922 : }
923 118 : else if (dopt.restrict_key)
924 0 : pg_fatal("option %s can only be used with %s",
925 : "--restrict-key", "--format=plain");
926 :
927 : /*
928 : * Custom and directory formats are compressed by default with gzip when
929 : * available, not the others. If gzip is not available, no compression is
930 : * done by default.
931 : */
932 426 : if ((archiveFormat == archCustom || archiveFormat == archDirectory) &&
933 112 : !user_compression_defined)
934 : {
935 : #ifdef HAVE_LIBZ
936 100 : compression_algorithm_str = "gzip";
937 : #else
938 : compression_algorithm_str = "none";
939 : #endif
940 : }
941 :
942 : /*
943 : * Compression options
944 : */
945 426 : if (!parse_compress_algorithm(compression_algorithm_str,
946 : &compression_algorithm))
947 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
948 : compression_algorithm_str);
949 :
950 424 : parse_compress_specification(compression_algorithm, compression_detail,
951 : &compression_spec);
952 424 : error_detail = validate_compress_specification(&compression_spec);
953 424 : if (error_detail != NULL)
954 6 : pg_fatal("invalid compression specification: %s",
955 : error_detail);
956 :
957 418 : error_detail = supports_compression(compression_spec);
958 418 : if (error_detail != NULL)
959 0 : pg_fatal("%s", error_detail);
960 :
961 : /*
962 : * Disable support for zstd workers for now - these are based on
963 : * threading, and it's unclear how it interacts with parallel dumps on
964 : * platforms where that relies on threads too (e.g. Windows).
965 : */
966 418 : if (compression_spec.options & PG_COMPRESSION_OPTION_WORKERS)
967 0 : pg_log_warning("compression option \"%s\" is not currently supported by pg_dump",
968 : "workers");
969 :
970 : /*
971 : * If emitting an archive format, we always want to emit a DATABASE item,
972 : * in case --create is specified at pg_restore time.
973 : */
974 418 : if (!plainText)
975 118 : dopt.outputCreateDB = 1;
976 :
977 : /* Parallel backup only in the directory archive format so far */
978 418 : if (archiveFormat != archDirectory && numWorkers > 1)
979 2 : pg_fatal("parallel backup only supported by the directory format");
980 :
981 : /* Open the output file */
982 416 : fout = CreateArchive(filename, archiveFormat, compression_spec,
983 : dosync, archiveMode, setupDumpWorker, sync_method);
984 :
985 : /* Make dump options accessible right away */
986 414 : SetArchiveOptions(fout, &dopt, NULL);
987 :
988 : /* Register the cleanup hook */
989 414 : on_exit_close_archive(fout);
990 :
991 : /* Let the archiver know how noisy to be */
992 414 : fout->verbose = g_verbose;
993 :
994 :
995 : /*
996 : * We allow the server to be back to 9.2, and up to any minor release of
997 : * our own major version. (See also version check in pg_dumpall.c.)
998 : */
999 414 : fout->minRemoteVersion = 90200;
1000 414 : fout->maxRemoteVersion = (PG_VERSION_NUM / 100) * 100 + 99;
1001 :
1002 414 : fout->numWorkers = numWorkers;
1003 :
1004 : /*
1005 : * Open the database using the Archiver, so it knows about it. Errors mean
1006 : * death.
1007 : */
1008 414 : ConnectDatabaseAhx(fout, &dopt.cparams, false);
1009 410 : setup_connection(fout, dumpencoding, dumpsnapshot, use_role);
1010 :
1011 : /*
1012 : * On hot standbys, never try to dump unlogged table data, since it will
1013 : * just throw an error.
1014 : */
1015 410 : if (fout->isStandby)
1016 8 : dopt.no_unlogged_table_data = true;
1017 :
1018 : /*
1019 : * Find the last built-in OID, if needed (prior to 8.1)
1020 : *
1021 : * With 8.1 and above, we can just use FirstNormalObjectId - 1.
1022 : */
1023 410 : g_last_builtin_oid = FirstNormalObjectId - 1;
1024 :
1025 410 : pg_log_info("last built-in OID is %u", g_last_builtin_oid);
1026 :
1027 : /* Expand schema selection patterns into OID lists */
1028 410 : if (schema_include_patterns.head != NULL)
1029 : {
1030 36 : expand_schema_name_patterns(fout, &schema_include_patterns,
1031 : &schema_include_oids,
1032 : strict_names);
1033 24 : if (schema_include_oids.head == NULL)
1034 2 : pg_fatal("no matching schemas were found");
1035 : }
1036 396 : expand_schema_name_patterns(fout, &schema_exclude_patterns,
1037 : &schema_exclude_oids,
1038 : false);
1039 : /* non-matching exclusion patterns aren't an error */
1040 :
1041 : /* Expand table selection patterns into OID lists */
1042 396 : expand_table_name_patterns(fout, &table_include_patterns,
1043 : &table_include_oids,
1044 : strict_names, false);
1045 386 : expand_table_name_patterns(fout, &table_include_patterns_and_children,
1046 : &table_include_oids,
1047 : strict_names, true);
1048 386 : if ((table_include_patterns.head != NULL ||
1049 364 : table_include_patterns_and_children.head != NULL) &&
1050 26 : table_include_oids.head == NULL)
1051 4 : pg_fatal("no matching tables were found");
1052 :
1053 382 : expand_table_name_patterns(fout, &table_exclude_patterns,
1054 : &table_exclude_oids,
1055 : false, false);
1056 382 : expand_table_name_patterns(fout, &table_exclude_patterns_and_children,
1057 : &table_exclude_oids,
1058 : false, true);
1059 :
1060 382 : expand_table_name_patterns(fout, &tabledata_exclude_patterns,
1061 : &tabledata_exclude_oids,
1062 : false, false);
1063 382 : expand_table_name_patterns(fout, &tabledata_exclude_patterns_and_children,
1064 : &tabledata_exclude_oids,
1065 : false, true);
1066 :
1067 382 : expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
1068 : &foreign_servers_include_oids);
1069 :
1070 : /* non-matching exclusion patterns aren't an error */
1071 :
1072 : /* Expand extension selection patterns into OID lists */
1073 380 : if (extension_include_patterns.head != NULL)
1074 : {
1075 10 : expand_extension_name_patterns(fout, &extension_include_patterns,
1076 : &extension_include_oids,
1077 : strict_names);
1078 10 : if (extension_include_oids.head == NULL)
1079 2 : pg_fatal("no matching extensions were found");
1080 : }
1081 378 : expand_extension_name_patterns(fout, &extension_exclude_patterns,
1082 : &extension_exclude_oids,
1083 : false);
1084 : /* non-matching exclusion patterns aren't an error */
1085 :
1086 : /*
1087 : * Dumping LOs is the default for dumps where an inclusion switch is not
1088 : * used (an "include everything" dump). -B can be used to exclude LOs
1089 : * from those dumps. -b can be used to include LOs even when an inclusion
1090 : * switch is used.
1091 : *
1092 : * -s means "schema only" and LOs are data, not schema, so we never
1093 : * include LOs when -s is used.
1094 : */
1095 378 : if (dopt.include_everything && dopt.dumpData && !dopt.dontOutputLOs)
1096 244 : dopt.outputLOs = true;
1097 :
1098 : /*
1099 : * Collect role names so we can map object owner OIDs to names.
1100 : */
1101 378 : collectRoleNames(fout);
1102 :
1103 : /*
1104 : * Now scan the database and create DumpableObject structs for all the
1105 : * objects we intend to dump.
1106 : */
1107 378 : tblinfo = getSchemaData(fout, &numTables);
1108 :
1109 376 : if (dopt.dumpData)
1110 : {
1111 292 : getTableData(&dopt, tblinfo, numTables, 0);
1112 292 : buildMatViewRefreshDependencies(fout);
1113 292 : if (!dopt.dumpSchema)
1114 14 : getTableDataFKConstraints();
1115 : }
1116 :
1117 376 : if (!dopt.dumpData && dopt.sequence_data)
1118 68 : getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE);
1119 :
1120 : /*
1121 : * For binary upgrade mode, dump pg_largeobject_metadata and the
1122 : * associated pg_shdepend rows. This is faster to restore than the
1123 : * equivalent set of large object commands. We can only do this for
1124 : * upgrades from v12 and newer; in older versions, pg_largeobject_metadata
1125 : * was created WITH OIDS, so the OID column is hidden and won't be dumped.
1126 : */
1127 376 : if (dopt.binary_upgrade && fout->remoteVersion >= 120000)
1128 : {
1129 76 : TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId);
1130 76 : TableInfo *shdepend = findTableByOid(SharedDependRelationId);
1131 :
1132 76 : makeTableDataInfo(&dopt, lo_metadata);
1133 76 : makeTableDataInfo(&dopt, shdepend);
1134 :
1135 : /*
1136 : * Save pg_largeobject_metadata's dump ID for use as a dependency for
1137 : * pg_shdepend and any large object comments/seclabels.
1138 : */
1139 76 : lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId;
1140 76 : addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId);
1141 :
1142 : /*
1143 : * Only dump large object shdepend rows for this database.
1144 : */
1145 76 : shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass "
1146 : "AND dbid = (SELECT oid FROM pg_database "
1147 : " WHERE datname = current_database())";
1148 :
1149 : /*
1150 : * If upgrading from v16 or newer, only dump large objects with
1151 : * comments/seclabels. For these upgrades, pg_upgrade can copy/link
1152 : * pg_largeobject_metadata's files (which is usually faster) but we
1153 : * still need to dump LOs with comments/seclabels here so that the
1154 : * subsequent COMMENT and SECURITY LABEL commands work. pg_upgrade
1155 : * can't copy/link the files from older versions because aclitem
1156 : * (needed by pg_largeobject_metadata.lomacl) changed its storage
1157 : * format in v16.
1158 : */
1159 76 : if (fout->remoteVersion >= 160000)
1160 76 : lo_metadata->dataObj->filtercond = "WHERE oid IN "
1161 : "(SELECT objoid FROM pg_description "
1162 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) " "
1163 : "UNION SELECT objoid FROM pg_seclabel "
1164 : "WHERE classoid = " CppAsString2(LargeObjectRelationId) ")";
1165 : }
1166 :
1167 : /*
1168 : * In binary-upgrade mode, we do not have to worry about the actual LO
1169 : * data or the associated metadata that resides in the pg_largeobject and
1170 : * pg_largeobject_metadata tables, respectively.
1171 : *
1172 : * However, we do need to collect LO information as there may be comments
1173 : * or other information on LOs that we do need to dump out.
1174 : */
1175 376 : if (dopt.outputLOs || dopt.binary_upgrade)
1176 320 : getLOs(fout);
1177 :
1178 : /*
1179 : * Collect dependency data to assist in ordering the objects.
1180 : */
1181 376 : getDependencies(fout);
1182 :
1183 : /*
1184 : * Collect ACLs, comments, and security labels, if wanted.
1185 : */
1186 376 : if (!dopt.aclsSkip)
1187 372 : getAdditionalACLs(fout);
1188 376 : if (!dopt.no_comments)
1189 376 : collectComments(fout);
1190 376 : if (!dopt.no_security_labels)
1191 376 : collectSecLabels(fout);
1192 :
1193 : /* For binary upgrade mode, collect required pg_class information. */
1194 376 : if (dopt.binary_upgrade)
1195 76 : collectBinaryUpgradeClassOids(fout);
1196 :
1197 : /* Collect sequence information. */
1198 376 : collectSequences(fout);
1199 :
1200 : /* Lastly, create dummy objects to represent the section boundaries */
1201 376 : boundaryObjs = createBoundaryObjects();
1202 :
1203 : /* Get pointers to all the known DumpableObjects */
1204 376 : getDumpableObjects(&dobjs, &numObjs);
1205 :
1206 : /*
1207 : * Add dummy dependencies to enforce the dump section ordering.
1208 : */
1209 376 : addBoundaryDependencies(dobjs, numObjs, boundaryObjs);
1210 :
1211 : /*
1212 : * Sort the objects into a safe dump order (no forward references).
1213 : *
1214 : * We rely on dependency information to help us determine a safe order, so
1215 : * the initial sort is mostly for cosmetic purposes: we sort by name to
1216 : * ensure that logically identical schemas will dump identically.
1217 : */
1218 376 : sortDumpableObjectsByTypeName(dobjs, numObjs);
1219 :
1220 376 : sortDumpableObjects(dobjs, numObjs,
1221 376 : boundaryObjs[0].dumpId, boundaryObjs[1].dumpId);
1222 :
1223 : /*
1224 : * Create archive TOC entries for all the objects to be dumped, in a safe
1225 : * order.
1226 : */
1227 :
1228 : /*
1229 : * First the special entries for ENCODING, STDSTRINGS, and SEARCHPATH.
1230 : */
1231 376 : dumpEncoding(fout);
1232 376 : dumpStdStrings(fout);
1233 376 : dumpSearchPath(fout);
1234 :
1235 : /* The database items are always next, unless we don't want them at all */
1236 376 : if (dopt.outputCreateDB)
1237 174 : dumpDatabase(fout);
1238 :
1239 : /* Now the rearrangeable objects. */
1240 1393768 : for (i = 0; i < numObjs; i++)
1241 1393392 : dumpDumpableObject(fout, dobjs[i]);
1242 :
1243 : /*
1244 : * Set up options info to ensure we dump what we want.
1245 : */
1246 376 : ropt = NewRestoreOptions();
1247 376 : ropt->filename = filename;
1248 :
1249 : /* if you change this list, see dumpOptionsFromRestoreOptions */
1250 376 : ropt->cparams.dbname = dopt.cparams.dbname ? pg_strdup(dopt.cparams.dbname) : NULL;
1251 376 : ropt->cparams.pgport = dopt.cparams.pgport ? pg_strdup(dopt.cparams.pgport) : NULL;
1252 376 : ropt->cparams.pghost = dopt.cparams.pghost ? pg_strdup(dopt.cparams.pghost) : NULL;
1253 376 : ropt->cparams.username = dopt.cparams.username ? pg_strdup(dopt.cparams.username) : NULL;
1254 376 : ropt->cparams.promptPassword = dopt.cparams.promptPassword;
1255 376 : ropt->dropSchema = dopt.outputClean;
1256 376 : ropt->dumpData = dopt.dumpData;
1257 376 : ropt->dumpSchema = dopt.dumpSchema;
1258 376 : ropt->dumpStatistics = dopt.dumpStatistics;
1259 376 : ropt->if_exists = dopt.if_exists;
1260 376 : ropt->column_inserts = dopt.column_inserts;
1261 376 : ropt->dumpSections = dopt.dumpSections;
1262 376 : ropt->aclsSkip = dopt.aclsSkip;
1263 376 : ropt->superuser = dopt.outputSuperuser;
1264 376 : ropt->createDB = dopt.outputCreateDB;
1265 376 : ropt->noOwner = dopt.outputNoOwner;
1266 376 : ropt->noTableAm = dopt.outputNoTableAm;
1267 376 : ropt->noTablespace = dopt.outputNoTablespaces;
1268 376 : ropt->disable_triggers = dopt.disable_triggers;
1269 376 : ropt->use_setsessauth = dopt.use_setsessauth;
1270 376 : ropt->disable_dollar_quoting = dopt.disable_dollar_quoting;
1271 376 : ropt->dump_inserts = dopt.dump_inserts;
1272 376 : ropt->no_comments = dopt.no_comments;
1273 376 : ropt->no_policies = dopt.no_policies;
1274 376 : ropt->no_publications = dopt.no_publications;
1275 376 : ropt->no_security_labels = dopt.no_security_labels;
1276 376 : ropt->no_subscriptions = dopt.no_subscriptions;
1277 376 : ropt->lockWaitTimeout = dopt.lockWaitTimeout;
1278 376 : ropt->include_everything = dopt.include_everything;
1279 376 : ropt->enable_row_security = dopt.enable_row_security;
1280 376 : ropt->sequence_data = dopt.sequence_data;
1281 376 : ropt->binary_upgrade = dopt.binary_upgrade;
1282 376 : ropt->restrict_key = dopt.restrict_key ? pg_strdup(dopt.restrict_key) : NULL;
1283 :
1284 376 : ropt->compression_spec = compression_spec;
1285 :
1286 376 : ropt->suppressDumpWarnings = true; /* We've already shown them */
1287 :
1288 376 : SetArchiveOptions(fout, &dopt, ropt);
1289 :
1290 : /* Mark which entries should be output */
1291 376 : ProcessArchiveRestoreOptions(fout);
1292 :
1293 : /*
1294 : * The archive's TOC entries are now marked as to which ones will actually
1295 : * be output, so we can set up their dependency lists properly. This isn't
1296 : * necessary for plain-text output, though.
1297 : */
1298 376 : if (!plainText)
1299 116 : BuildArchiveDependencies(fout);
1300 :
1301 : /*
1302 : * And finally we can do the actual output.
1303 : *
1304 : * Note: for non-plain-text output formats, the output file is written
1305 : * inside CloseArchive(). This is, um, bizarre; but not worth changing
1306 : * right now.
1307 : */
1308 376 : if (plainText)
1309 260 : RestoreArchive(fout);
1310 :
1311 374 : CloseArchive(fout);
1312 :
1313 374 : exit_nicely(0);
1314 : }
1315 :
1316 :
1317 : static void
1318 2 : help(const char *progname)
1319 : {
1320 2 : printf(_("%s exports a PostgreSQL database as an SQL script or to other formats.\n\n"), progname);
1321 2 : printf(_("Usage:\n"));
1322 2 : printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
1323 :
1324 2 : printf(_("\nGeneral options:\n"));
1325 2 : printf(_(" -f, --file=FILENAME output file or directory name\n"));
1326 2 : printf(_(" -F, --format=c|d|t|p output file format (custom, directory, tar,\n"
1327 : " plain text (default))\n"));
1328 2 : printf(_(" -j, --jobs=NUM use this many parallel jobs to dump\n"));
1329 2 : printf(_(" -v, --verbose verbose mode\n"));
1330 2 : printf(_(" -V, --version output version information, then exit\n"));
1331 2 : printf(_(" -Z, --compress=METHOD[:DETAIL]\n"
1332 : " compress as specified\n"));
1333 2 : printf(_(" --lock-wait-timeout=TIMEOUT fail after waiting TIMEOUT for a table lock\n"));
1334 2 : printf(_(" --no-sync do not wait for changes to be written safely to disk\n"));
1335 2 : printf(_(" --sync-method=METHOD set method for syncing files to disk\n"));
1336 2 : printf(_(" -?, --help show this help, then exit\n"));
1337 :
1338 2 : printf(_("\nOptions controlling the output content:\n"));
1339 2 : printf(_(" -a, --data-only dump only the data, not the schema or statistics\n"));
1340 2 : printf(_(" -b, --large-objects include large objects in dump\n"));
1341 2 : printf(_(" --blobs (same as --large-objects, deprecated)\n"));
1342 2 : printf(_(" -B, --no-large-objects exclude large objects in dump\n"));
1343 2 : printf(_(" --no-blobs (same as --no-large-objects, deprecated)\n"));
1344 2 : printf(_(" -c, --clean clean (drop) database objects before recreating\n"));
1345 2 : printf(_(" -C, --create include commands to create database in dump\n"));
1346 2 : printf(_(" -e, --extension=PATTERN dump the specified extension(s) only\n"));
1347 2 : printf(_(" -E, --encoding=ENCODING dump the data in encoding ENCODING\n"));
1348 2 : printf(_(" -n, --schema=PATTERN dump the specified schema(s) only\n"));
1349 2 : printf(_(" -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
1350 2 : printf(_(" -O, --no-owner skip restoration of object ownership in\n"
1351 : " plain-text format\n"));
1352 2 : printf(_(" -s, --schema-only dump only the schema, no data or statistics\n"));
1353 2 : printf(_(" -S, --superuser=NAME superuser user name to use in plain-text format\n"));
1354 2 : printf(_(" -t, --table=PATTERN dump only the specified table(s)\n"));
1355 2 : printf(_(" -T, --exclude-table=PATTERN do NOT dump the specified table(s)\n"));
1356 2 : printf(_(" -x, --no-privileges do not dump privileges (grant/revoke)\n"));
1357 2 : printf(_(" --binary-upgrade for use by upgrade utilities only\n"));
1358 2 : printf(_(" --column-inserts dump data as INSERT commands with column names\n"));
1359 2 : printf(_(" --disable-dollar-quoting disable dollar quoting, use SQL standard quoting\n"));
1360 2 : printf(_(" --disable-triggers disable triggers during data-only restore\n"));
1361 2 : printf(_(" --enable-row-security enable row security (dump only content user has\n"
1362 : " access to)\n"));
1363 2 : printf(_(" --exclude-extension=PATTERN do NOT dump the specified extension(s)\n"));
1364 2 : printf(_(" --exclude-table-and-children=PATTERN\n"
1365 : " do NOT dump the specified table(s), including\n"
1366 : " child and partition tables\n"));
1367 2 : printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
1368 2 : printf(_(" --exclude-table-data-and-children=PATTERN\n"
1369 : " do NOT dump data for the specified table(s),\n"
1370 : " including child and partition tables\n"));
1371 2 : printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
1372 2 : printf(_(" --filter=FILENAME include or exclude objects and data from dump\n"
1373 : " based on expressions in FILENAME\n"));
1374 2 : printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1375 2 : printf(_(" --include-foreign-data=PATTERN\n"
1376 : " include data of foreign tables on foreign\n"
1377 : " servers matching PATTERN\n"));
1378 2 : printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
1379 2 : printf(_(" --load-via-partition-root load partitions via the root table\n"));
1380 2 : printf(_(" --no-comments do not dump comment commands\n"));
1381 2 : printf(_(" --no-data do not dump data\n"));
1382 2 : printf(_(" --no-policies do not dump row security policies\n"));
1383 2 : printf(_(" --no-publications do not dump publications\n"));
1384 2 : printf(_(" --no-schema do not dump schema\n"));
1385 2 : printf(_(" --no-security-labels do not dump security label assignments\n"));
1386 2 : printf(_(" --no-statistics do not dump statistics\n"));
1387 2 : printf(_(" --no-subscriptions do not dump subscriptions\n"));
1388 2 : printf(_(" --no-table-access-method do not dump table access methods\n"));
1389 2 : printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
1390 2 : printf(_(" --no-toast-compression do not dump TOAST compression methods\n"));
1391 2 : printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
1392 2 : printf(_(" --on-conflict-do-nothing add ON CONFLICT DO NOTHING to INSERT commands\n"));
1393 2 : printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
1394 2 : printf(_(" --restrict-key=RESTRICT_KEY use provided string as psql \\restrict key\n"));
1395 2 : printf(_(" --rows-per-insert=NROWS number of rows per INSERT; implies --inserts\n"));
1396 2 : printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
1397 2 : printf(_(" --sequence-data include sequence data in dump\n"));
1398 2 : printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
1399 2 : printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
1400 2 : printf(_(" --statistics dump the statistics\n"));
1401 2 : printf(_(" --statistics-only dump only the statistics, not schema or data\n"));
1402 2 : printf(_(" --strict-names require table and/or schema include patterns to\n"
1403 : " match at least one entity each\n"));
1404 2 : printf(_(" --table-and-children=PATTERN dump only the specified table(s), including\n"
1405 : " child and partition tables\n"));
1406 2 : printf(_(" --use-set-session-authorization\n"
1407 : " use SET SESSION AUTHORIZATION commands instead of\n"
1408 : " ALTER OWNER commands to set ownership\n"));
1409 :
1410 2 : printf(_("\nConnection options:\n"));
1411 2 : printf(_(" -d, --dbname=DBNAME database to dump\n"));
1412 2 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
1413 2 : printf(_(" -p, --port=PORT database server port number\n"));
1414 2 : printf(_(" -U, --username=NAME connect as specified database user\n"));
1415 2 : printf(_(" -w, --no-password never prompt for password\n"));
1416 2 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
1417 2 : printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
1418 :
1419 2 : printf(_("\nIf no database name is supplied, then the PGDATABASE environment\n"
1420 : "variable value is used.\n\n"));
1421 2 : printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
1422 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
1423 2 : }
1424 :
1425 : static void
1426 442 : setup_connection(Archive *AH, const char *dumpencoding,
1427 : const char *dumpsnapshot, char *use_role)
1428 : {
1429 442 : DumpOptions *dopt = AH->dopt;
1430 442 : PGconn *conn = GetConnection(AH);
1431 : const char *std_strings;
1432 :
1433 442 : PQclear(ExecuteSqlQueryForSingleRow(AH, ALWAYS_SECURE_SEARCH_PATH_SQL));
1434 :
1435 : /*
1436 : * Set the client encoding if requested.
1437 : */
1438 442 : if (dumpencoding)
1439 : {
1440 36 : if (PQsetClientEncoding(conn, dumpencoding) < 0)
1441 0 : pg_fatal("invalid client encoding \"%s\" specified",
1442 : dumpencoding);
1443 : }
1444 :
1445 : /*
1446 : * Get the active encoding and the standard_conforming_strings setting, so
1447 : * we know how to escape strings.
1448 : */
1449 442 : AH->encoding = PQclientEncoding(conn);
1450 442 : setFmtEncoding(AH->encoding);
1451 :
1452 442 : std_strings = PQparameterStatus(conn, "standard_conforming_strings");
1453 442 : AH->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
1454 :
1455 : /*
1456 : * Set the role if requested. In a parallel dump worker, we'll be passed
1457 : * use_role == NULL, but AH->use_role is already set (if user specified it
1458 : * originally) and we should use that.
1459 : */
1460 442 : if (!use_role && AH->use_role)
1461 4 : use_role = AH->use_role;
1462 :
1463 : /* Set the role if requested */
1464 442 : if (use_role)
1465 : {
1466 10 : PQExpBuffer query = createPQExpBuffer();
1467 :
1468 10 : appendPQExpBuffer(query, "SET ROLE %s", fmtId(use_role));
1469 10 : ExecuteSqlStatement(AH, query->data);
1470 10 : destroyPQExpBuffer(query);
1471 :
1472 : /* save it for possible later use by parallel workers */
1473 10 : if (!AH->use_role)
1474 6 : AH->use_role = pg_strdup(use_role);
1475 : }
1476 :
1477 : /* Set the datestyle to ISO to ensure the dump's portability */
1478 442 : ExecuteSqlStatement(AH, "SET DATESTYLE = ISO");
1479 :
1480 : /* Likewise, avoid using sql_standard intervalstyle */
1481 442 : ExecuteSqlStatement(AH, "SET INTERVALSTYLE = POSTGRES");
1482 :
1483 : /*
1484 : * Use an explicitly specified extra_float_digits if it has been provided.
1485 : * Otherwise, set extra_float_digits so that we can dump float data
1486 : * exactly (given correctly implemented float I/O code, anyway).
1487 : */
1488 442 : if (have_extra_float_digits)
1489 : {
1490 0 : PQExpBuffer q = createPQExpBuffer();
1491 :
1492 0 : appendPQExpBuffer(q, "SET extra_float_digits TO %d",
1493 : extra_float_digits);
1494 0 : ExecuteSqlStatement(AH, q->data);
1495 0 : destroyPQExpBuffer(q);
1496 : }
1497 : else
1498 442 : ExecuteSqlStatement(AH, "SET extra_float_digits TO 3");
1499 :
1500 : /*
1501 : * Disable synchronized scanning, to prevent unpredictable changes in row
1502 : * ordering across a dump and reload.
1503 : */
1504 442 : ExecuteSqlStatement(AH, "SET synchronize_seqscans TO off");
1505 :
1506 : /*
1507 : * Disable timeouts if supported.
1508 : */
1509 442 : ExecuteSqlStatement(AH, "SET statement_timeout = 0");
1510 442 : if (AH->remoteVersion >= 90300)
1511 442 : ExecuteSqlStatement(AH, "SET lock_timeout = 0");
1512 442 : if (AH->remoteVersion >= 90600)
1513 442 : ExecuteSqlStatement(AH, "SET idle_in_transaction_session_timeout = 0");
1514 442 : if (AH->remoteVersion >= 170000)
1515 442 : ExecuteSqlStatement(AH, "SET transaction_timeout = 0");
1516 :
1517 : /*
1518 : * Quote all identifiers, if requested.
1519 : */
1520 442 : if (quote_all_identifiers)
1521 72 : ExecuteSqlStatement(AH, "SET quote_all_identifiers = true");
1522 :
1523 : /*
1524 : * Adjust row-security mode, if supported.
1525 : */
1526 442 : if (AH->remoteVersion >= 90500)
1527 : {
1528 442 : if (dopt->enable_row_security)
1529 0 : ExecuteSqlStatement(AH, "SET row_security = on");
1530 : else
1531 442 : ExecuteSqlStatement(AH, "SET row_security = off");
1532 : }
1533 :
1534 : /*
1535 : * For security reasons, we restrict the expansion of non-system views and
1536 : * access to foreign tables during the pg_dump process. This restriction
1537 : * is adjusted when dumping foreign table data.
1538 : */
1539 442 : set_restrict_relation_kind(AH, "view, foreign-table");
1540 :
1541 : /*
1542 : * Initialize prepared-query state to "nothing prepared". We do this here
1543 : * so that a parallel dump worker will have its own state.
1544 : */
1545 442 : AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
1546 :
1547 : /*
1548 : * Start transaction-snapshot mode transaction to dump consistent data.
1549 : */
1550 442 : ExecuteSqlStatement(AH, "BEGIN");
1551 :
1552 : /*
1553 : * To support the combination of serializable_deferrable with the jobs
1554 : * option we use REPEATABLE READ for the worker connections that are
1555 : * passed a snapshot. As long as the snapshot is acquired in a
1556 : * SERIALIZABLE, READ ONLY, DEFERRABLE transaction, its use within a
1557 : * REPEATABLE READ transaction provides the appropriate integrity
1558 : * guarantees. This is a kluge, but safe for back-patching.
1559 : */
1560 442 : if (dopt->serializable_deferrable && AH->sync_snapshot_id == NULL)
1561 0 : ExecuteSqlStatement(AH,
1562 : "SET TRANSACTION ISOLATION LEVEL "
1563 : "SERIALIZABLE, READ ONLY, DEFERRABLE");
1564 : else
1565 442 : ExecuteSqlStatement(AH,
1566 : "SET TRANSACTION ISOLATION LEVEL "
1567 : "REPEATABLE READ, READ ONLY");
1568 :
1569 : /*
1570 : * If user specified a snapshot to use, select that. In a parallel dump
1571 : * worker, we'll be passed dumpsnapshot == NULL, but AH->sync_snapshot_id
1572 : * is already set (if the server can handle it) and we should use that.
1573 : */
1574 442 : if (dumpsnapshot)
1575 0 : AH->sync_snapshot_id = pg_strdup(dumpsnapshot);
1576 :
1577 442 : if (AH->sync_snapshot_id)
1578 : {
1579 32 : PQExpBuffer query = createPQExpBuffer();
1580 :
1581 32 : appendPQExpBufferStr(query, "SET TRANSACTION SNAPSHOT ");
1582 32 : appendStringLiteralConn(query, AH->sync_snapshot_id, conn);
1583 32 : ExecuteSqlStatement(AH, query->data);
1584 32 : destroyPQExpBuffer(query);
1585 : }
1586 410 : else if (AH->numWorkers > 1)
1587 : {
1588 16 : if (AH->isStandby && AH->remoteVersion < 100000)
1589 0 : pg_fatal("parallel dumps from standby servers are not supported by this server version");
1590 16 : AH->sync_snapshot_id = get_synchronized_snapshot(AH);
1591 : }
1592 442 : }
1593 :
1594 : /* Set up connection for a parallel worker process */
1595 : static void
1596 32 : setupDumpWorker(Archive *AH)
1597 : {
1598 : /*
1599 : * We want to re-select all the same values the leader connection is
1600 : * using. We'll have inherited directly-usable values in
1601 : * AH->sync_snapshot_id and AH->use_role, but we need to translate the
1602 : * inherited encoding value back to a string to pass to setup_connection.
1603 : */
1604 32 : setup_connection(AH,
1605 : pg_encoding_to_char(AH->encoding),
1606 : NULL,
1607 : NULL);
1608 32 : }
1609 :
1610 : static char *
1611 16 : get_synchronized_snapshot(Archive *fout)
1612 : {
1613 16 : char *query = "SELECT pg_catalog.pg_export_snapshot()";
1614 : char *result;
1615 : PGresult *res;
1616 :
1617 16 : res = ExecuteSqlQueryForSingleRow(fout, query);
1618 16 : result = pg_strdup(PQgetvalue(res, 0, 0));
1619 16 : PQclear(res);
1620 :
1621 16 : return result;
1622 : }
1623 :
1624 : static ArchiveFormat
1625 428 : parseArchiveFormat(const char *format, ArchiveMode *mode)
1626 : {
1627 : ArchiveFormat archiveFormat;
1628 :
1629 428 : *mode = archModeWrite;
1630 :
1631 428 : if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
1632 : {
1633 : /* This is used by pg_dumpall, and is not documented */
1634 98 : archiveFormat = archNull;
1635 98 : *mode = archModeAppend;
1636 : }
1637 330 : else if (pg_strcasecmp(format, "c") == 0)
1638 0 : archiveFormat = archCustom;
1639 330 : else if (pg_strcasecmp(format, "custom") == 0)
1640 92 : archiveFormat = archCustom;
1641 238 : else if (pg_strcasecmp(format, "d") == 0)
1642 0 : archiveFormat = archDirectory;
1643 238 : else if (pg_strcasecmp(format, "directory") == 0)
1644 20 : archiveFormat = archDirectory;
1645 218 : else if (pg_strcasecmp(format, "p") == 0)
1646 204 : archiveFormat = archNull;
1647 14 : else if (pg_strcasecmp(format, "plain") == 0)
1648 6 : archiveFormat = archNull;
1649 8 : else if (pg_strcasecmp(format, "t") == 0)
1650 0 : archiveFormat = archTar;
1651 8 : else if (pg_strcasecmp(format, "tar") == 0)
1652 6 : archiveFormat = archTar;
1653 : else
1654 2 : pg_fatal("invalid output format \"%s\" specified", format);
1655 426 : return archiveFormat;
1656 : }
1657 :
1658 : /*
1659 : * Find the OIDs of all schemas matching the given list of patterns,
1660 : * and append them to the given OID list.
1661 : */
1662 : static void
1663 432 : expand_schema_name_patterns(Archive *fout,
1664 : SimpleStringList *patterns,
1665 : SimpleOidList *oids,
1666 : bool strict_names)
1667 : {
1668 : PQExpBuffer query;
1669 : PGresult *res;
1670 : SimpleStringListCell *cell;
1671 : int i;
1672 :
1673 432 : if (patterns->head == NULL)
1674 390 : return; /* nothing to do */
1675 :
1676 42 : query = createPQExpBuffer();
1677 :
1678 : /*
1679 : * The loop below runs multiple SELECTs might sometimes result in
1680 : * duplicate entries in the OID list, but we don't care.
1681 : */
1682 :
1683 72 : for (cell = patterns->head; cell; cell = cell->next)
1684 : {
1685 : PQExpBufferData dbbuf;
1686 : int dotcnt;
1687 :
1688 42 : appendPQExpBufferStr(query,
1689 : "SELECT oid FROM pg_catalog.pg_namespace n\n");
1690 42 : initPQExpBuffer(&dbbuf);
1691 42 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1692 : false, NULL, "n.nspname", NULL, NULL, &dbbuf,
1693 : &dotcnt);
1694 42 : if (dotcnt > 1)
1695 4 : pg_fatal("improper qualified name (too many dotted names): %s",
1696 : cell->val);
1697 38 : else if (dotcnt == 1)
1698 6 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1699 32 : termPQExpBuffer(&dbbuf);
1700 :
1701 32 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1702 32 : if (strict_names && PQntuples(res) == 0)
1703 2 : pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
1704 :
1705 58 : for (i = 0; i < PQntuples(res); i++)
1706 : {
1707 28 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1708 : }
1709 :
1710 30 : PQclear(res);
1711 30 : resetPQExpBuffer(query);
1712 : }
1713 :
1714 30 : destroyPQExpBuffer(query);
1715 : }
1716 :
1717 : /*
1718 : * Find the OIDs of all extensions matching the given list of patterns,
1719 : * and append them to the given OID list.
1720 : */
1721 : static void
1722 388 : expand_extension_name_patterns(Archive *fout,
1723 : SimpleStringList *patterns,
1724 : SimpleOidList *oids,
1725 : bool strict_names)
1726 : {
1727 : PQExpBuffer query;
1728 : PGresult *res;
1729 : SimpleStringListCell *cell;
1730 : int i;
1731 :
1732 388 : if (patterns->head == NULL)
1733 374 : return; /* nothing to do */
1734 :
1735 14 : query = createPQExpBuffer();
1736 :
1737 : /*
1738 : * The loop below runs multiple SELECTs might sometimes result in
1739 : * duplicate entries in the OID list, but we don't care.
1740 : */
1741 28 : for (cell = patterns->head; cell; cell = cell->next)
1742 : {
1743 : int dotcnt;
1744 :
1745 14 : appendPQExpBufferStr(query,
1746 : "SELECT oid FROM pg_catalog.pg_extension e\n");
1747 14 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1748 : false, NULL, "e.extname", NULL, NULL, NULL,
1749 : &dotcnt);
1750 14 : if (dotcnt > 0)
1751 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1752 : cell->val);
1753 :
1754 14 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1755 14 : if (strict_names && PQntuples(res) == 0)
1756 0 : pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
1757 :
1758 26 : for (i = 0; i < PQntuples(res); i++)
1759 : {
1760 12 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1761 : }
1762 :
1763 14 : PQclear(res);
1764 14 : resetPQExpBuffer(query);
1765 : }
1766 :
1767 14 : destroyPQExpBuffer(query);
1768 : }
1769 :
1770 : /*
1771 : * Find the OIDs of all foreign servers matching the given list of patterns,
1772 : * and append them to the given OID list.
1773 : */
1774 : static void
1775 382 : expand_foreign_server_name_patterns(Archive *fout,
1776 : SimpleStringList *patterns,
1777 : SimpleOidList *oids)
1778 : {
1779 : PQExpBuffer query;
1780 : PGresult *res;
1781 : SimpleStringListCell *cell;
1782 : int i;
1783 :
1784 382 : if (patterns->head == NULL)
1785 376 : return; /* nothing to do */
1786 :
1787 6 : query = createPQExpBuffer();
1788 :
1789 : /*
1790 : * The loop below runs multiple SELECTs might sometimes result in
1791 : * duplicate entries in the OID list, but we don't care.
1792 : */
1793 :
1794 10 : for (cell = patterns->head; cell; cell = cell->next)
1795 : {
1796 : int dotcnt;
1797 :
1798 6 : appendPQExpBufferStr(query,
1799 : "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1800 6 : processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1801 : false, NULL, "s.srvname", NULL, NULL, NULL,
1802 : &dotcnt);
1803 6 : if (dotcnt > 0)
1804 0 : pg_fatal("improper qualified name (too many dotted names): %s",
1805 : cell->val);
1806 :
1807 6 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1808 6 : if (PQntuples(res) == 0)
1809 2 : pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1810 :
1811 8 : for (i = 0; i < PQntuples(res); i++)
1812 4 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1813 :
1814 4 : PQclear(res);
1815 4 : resetPQExpBuffer(query);
1816 : }
1817 :
1818 4 : destroyPQExpBuffer(query);
1819 : }
1820 :
1821 : /*
1822 : * Find the OIDs of all tables matching the given list of patterns,
1823 : * and append them to the given OID list. See also expand_dbname_patterns()
1824 : * in pg_dumpall.c
1825 : */
1826 : static void
1827 2310 : expand_table_name_patterns(Archive *fout,
1828 : SimpleStringList *patterns, SimpleOidList *oids,
1829 : bool strict_names, bool with_child_tables)
1830 : {
1831 : PQExpBuffer query;
1832 : PGresult *res;
1833 : SimpleStringListCell *cell;
1834 : int i;
1835 :
1836 2310 : if (patterns->head == NULL)
1837 2252 : return; /* nothing to do */
1838 :
1839 58 : query = createPQExpBuffer();
1840 :
1841 : /*
1842 : * this might sometimes result in duplicate entries in the OID list, but
1843 : * we don't care.
1844 : */
1845 :
1846 118 : for (cell = patterns->head; cell; cell = cell->next)
1847 : {
1848 : PQExpBufferData dbbuf;
1849 : int dotcnt;
1850 :
1851 : /*
1852 : * Query must remain ABSOLUTELY devoid of unqualified names. This
1853 : * would be unnecessary given a pg_table_is_visible() variant taking a
1854 : * search_path argument.
1855 : *
1856 : * For with_child_tables, we start with the basic query's results and
1857 : * recursively search the inheritance tree to add child tables.
1858 : */
1859 70 : if (with_child_tables)
1860 : {
1861 12 : appendPQExpBufferStr(query, "WITH RECURSIVE partition_tree (relid) AS (\n");
1862 : }
1863 :
1864 70 : appendPQExpBuffer(query,
1865 : "SELECT c.oid"
1866 : "\nFROM pg_catalog.pg_class c"
1867 : "\n LEFT JOIN pg_catalog.pg_namespace n"
1868 : "\n ON n.oid OPERATOR(pg_catalog.=) c.relnamespace"
1869 : "\nWHERE c.relkind OPERATOR(pg_catalog.=) ANY"
1870 : "\n (array['%c', '%c', '%c', '%c', '%c', '%c'])\n",
1871 : RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW,
1872 : RELKIND_MATVIEW, RELKIND_FOREIGN_TABLE,
1873 : RELKIND_PARTITIONED_TABLE);
1874 70 : initPQExpBuffer(&dbbuf);
1875 70 : processSQLNamePattern(GetConnection(fout), query, cell->val, true,
1876 : false, "n.nspname", "c.relname", NULL,
1877 : "pg_catalog.pg_table_is_visible(c.oid)", &dbbuf,
1878 : &dotcnt);
1879 70 : if (dotcnt > 2)
1880 2 : pg_fatal("improper relation name (too many dotted names): %s",
1881 : cell->val);
1882 68 : else if (dotcnt == 2)
1883 4 : prohibit_crossdb_refs(GetConnection(fout), dbbuf.data, cell->val);
1884 64 : termPQExpBuffer(&dbbuf);
1885 :
1886 64 : if (with_child_tables)
1887 : {
1888 12 : appendPQExpBufferStr(query, "UNION"
1889 : "\nSELECT i.inhrelid"
1890 : "\nFROM partition_tree p"
1891 : "\n JOIN pg_catalog.pg_inherits i"
1892 : "\n ON p.relid OPERATOR(pg_catalog.=) i.inhparent"
1893 : "\n)"
1894 : "\nSELECT relid FROM partition_tree");
1895 : }
1896 :
1897 64 : ExecuteSqlStatement(fout, "RESET search_path");
1898 64 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1899 64 : PQclear(ExecuteSqlQueryForSingleRow(fout,
1900 : ALWAYS_SECURE_SEARCH_PATH_SQL));
1901 64 : if (strict_names && PQntuples(res) == 0)
1902 4 : pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
1903 :
1904 148 : for (i = 0; i < PQntuples(res); i++)
1905 : {
1906 88 : simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1907 : }
1908 :
1909 60 : PQclear(res);
1910 60 : resetPQExpBuffer(query);
1911 : }
1912 :
1913 48 : destroyPQExpBuffer(query);
1914 : }
1915 :
1916 : /*
1917 : * Verifies that the connected database name matches the given database name,
1918 : * and if not, dies with an error about the given pattern.
1919 : *
1920 : * The 'dbname' argument should be a literal name parsed from 'pattern'.
1921 : */
1922 : static void
1923 10 : prohibit_crossdb_refs(PGconn *conn, const char *dbname, const char *pattern)
1924 : {
1925 : const char *db;
1926 :
1927 10 : db = PQdb(conn);
1928 10 : if (db == NULL)
1929 0 : pg_fatal("You are currently not connected to a database.");
1930 :
1931 10 : if (strcmp(db, dbname) != 0)
1932 10 : pg_fatal("cross-database references are not implemented: %s",
1933 : pattern);
1934 0 : }
1935 :
1936 : /*
1937 : * checkExtensionMembership
1938 : * Determine whether object is an extension member, and if so,
1939 : * record an appropriate dependency and set the object's dump flag.
1940 : *
1941 : * It's important to call this for each object that could be an extension
1942 : * member. Generally, we integrate this with determining the object's
1943 : * to-be-dumped-ness, since extension membership overrides other rules for that.
1944 : *
1945 : * Returns true if object is an extension member, else false.
1946 : */
1947 : static bool
1948 1185580 : checkExtensionMembership(DumpableObject *dobj, Archive *fout)
1949 : {
1950 1185580 : ExtensionInfo *ext = findOwningExtension(dobj->catId);
1951 :
1952 1185580 : if (ext == NULL)
1953 1183966 : return false;
1954 :
1955 1614 : dobj->ext_member = true;
1956 :
1957 : /* Record dependency so that getDependencies needn't deal with that */
1958 1614 : addObjectDependency(dobj, ext->dobj.dumpId);
1959 :
1960 : /*
1961 : * In 9.6 and above, mark the member object to have any non-initial ACLs
1962 : * dumped. (Any initial ACLs will be removed later, using data from
1963 : * pg_init_privs, so that we'll dump only the delta from the extension's
1964 : * initial setup.)
1965 : *
1966 : * Prior to 9.6, we do not include any extension member components.
1967 : *
1968 : * In binary upgrades, we still dump all components of the members
1969 : * individually, since the idea is to exactly reproduce the database
1970 : * contents rather than replace the extension contents with something
1971 : * different.
1972 : *
1973 : * Note: it might be interesting someday to implement storage and delta
1974 : * dumping of extension members' RLS policies and/or security labels.
1975 : * However there is a pitfall for RLS policies: trying to dump them
1976 : * requires getting a lock on their tables, and the calling user might not
1977 : * have privileges for that. We need no lock to examine a table's ACLs,
1978 : * so the current feature doesn't have a problem of that sort.
1979 : */
1980 1614 : if (fout->dopt->binary_upgrade)
1981 354 : dobj->dump = ext->dobj.dump;
1982 : else
1983 : {
1984 1260 : if (fout->remoteVersion < 90600)
1985 0 : dobj->dump = DUMP_COMPONENT_NONE;
1986 : else
1987 1260 : dobj->dump = ext->dobj.dump_contains & (DUMP_COMPONENT_ACL);
1988 : }
1989 :
1990 1614 : return true;
1991 : }
1992 :
1993 : /*
1994 : * selectDumpableNamespace: policy-setting subroutine
1995 : * Mark a namespace as to be dumped or not
1996 : */
1997 : static void
1998 2884 : selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
1999 : {
2000 : /*
2001 : * DUMP_COMPONENT_DEFINITION typically implies a CREATE SCHEMA statement
2002 : * and (for --clean) a DROP SCHEMA statement. (In the absence of
2003 : * DUMP_COMPONENT_DEFINITION, this value is irrelevant.)
2004 : */
2005 2884 : nsinfo->create = true;
2006 :
2007 : /*
2008 : * If specific tables are being dumped, do not dump any complete
2009 : * namespaces. If specific namespaces are being dumped, dump just those
2010 : * namespaces. Otherwise, dump all non-system namespaces.
2011 : */
2012 2884 : if (table_include_oids.head != NULL)
2013 100 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2014 2784 : else if (schema_include_oids.head != NULL)
2015 366 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump =
2016 366 : simple_oid_list_member(&schema_include_oids,
2017 : nsinfo->dobj.catId.oid) ?
2018 366 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2019 2418 : else if (fout->remoteVersion >= 90600 &&
2020 2418 : strcmp(nsinfo->dobj.name, "pg_catalog") == 0)
2021 : {
2022 : /*
2023 : * In 9.6 and above, we dump out any ACLs defined in pg_catalog, if
2024 : * they are interesting (and not the original ACLs which were set at
2025 : * initdb time, see pg_init_privs).
2026 : */
2027 334 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ACL;
2028 : }
2029 2084 : else if (strncmp(nsinfo->dobj.name, "pg_", 3) == 0 ||
2030 1014 : strcmp(nsinfo->dobj.name, "information_schema") == 0)
2031 : {
2032 : /* Other system schemas don't get dumped */
2033 1404 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2034 : }
2035 680 : else if (strcmp(nsinfo->dobj.name, "public") == 0)
2036 : {
2037 : /*
2038 : * The public schema is a strange beast that sits in a sort of
2039 : * no-mans-land between being a system object and a user object.
2040 : * CREATE SCHEMA would fail, so its DUMP_COMPONENT_DEFINITION is just
2041 : * a comment and an indication of ownership. If the owner is the
2042 : * default, omit that superfluous DUMP_COMPONENT_DEFINITION. Before
2043 : * v15, the default owner was BOOTSTRAP_SUPERUSERID.
2044 : */
2045 326 : nsinfo->create = false;
2046 326 : nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2047 326 : if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
2048 242 : nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
2049 326 : nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
2050 :
2051 : /*
2052 : * Also, make like it has a comment even if it doesn't; this is so
2053 : * that we'll emit a command to drop the comment, if appropriate.
2054 : * (Without this, we'd not call dumpCommentExtended for it.)
2055 : */
2056 326 : nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
2057 : }
2058 : else
2059 354 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
2060 :
2061 : /*
2062 : * In any case, a namespace can be excluded by an exclusion switch
2063 : */
2064 3920 : if (nsinfo->dobj.dump_contains &&
2065 1036 : simple_oid_list_member(&schema_exclude_oids,
2066 : nsinfo->dobj.catId.oid))
2067 6 : nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_NONE;
2068 :
2069 : /*
2070 : * If the schema belongs to an extension, allow extension membership to
2071 : * override the dump decision for the schema itself. However, this does
2072 : * not change dump_contains, so this won't change what we do with objects
2073 : * within the schema. (If they belong to the extension, they'll get
2074 : * suppressed by it, otherwise not.)
2075 : */
2076 2884 : (void) checkExtensionMembership(&nsinfo->dobj, fout);
2077 2884 : }
2078 :
2079 : /*
2080 : * selectDumpableTable: policy-setting subroutine
2081 : * Mark a table as to be dumped or not
2082 : */
2083 : static void
2084 99056 : selectDumpableTable(TableInfo *tbinfo, Archive *fout)
2085 : {
2086 99056 : if (checkExtensionMembership(&tbinfo->dobj, fout))
2087 450 : return; /* extension membership overrides all else */
2088 :
2089 : /*
2090 : * If specific tables are being dumped, dump just those tables; else, dump
2091 : * according to the parent namespace's dump flag.
2092 : */
2093 98606 : if (table_include_oids.head != NULL)
2094 10364 : tbinfo->dobj.dump = simple_oid_list_member(&table_include_oids,
2095 : tbinfo->dobj.catId.oid) ?
2096 5182 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2097 : else
2098 93424 : tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
2099 :
2100 : /*
2101 : * In any case, a table can be excluded by an exclusion switch
2102 : */
2103 161334 : if (tbinfo->dobj.dump &&
2104 62728 : simple_oid_list_member(&table_exclude_oids,
2105 : tbinfo->dobj.catId.oid))
2106 24 : tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
2107 : }
2108 :
2109 : /*
2110 : * selectDumpableType: policy-setting subroutine
2111 : * Mark a type as to be dumped or not
2112 : *
2113 : * If it's a table's rowtype or an autogenerated array type, we also apply a
2114 : * special type code to facilitate sorting into the desired order. (We don't
2115 : * want to consider those to be ordinary types because that would bring tables
2116 : * up into the datatype part of the dump order.) We still set the object's
2117 : * dump flag; that's not going to cause the dummy type to be dumped, but we
2118 : * need it so that casts involving such types will be dumped correctly -- see
2119 : * dumpCast. This means the flag should be set the same as for the underlying
2120 : * object (the table or base type).
2121 : */
2122 : static void
2123 272152 : selectDumpableType(TypeInfo *tyinfo, Archive *fout)
2124 : {
2125 : /* skip complex types, except for standalone composite types */
2126 272152 : if (OidIsValid(tyinfo->typrelid) &&
2127 97606 : tyinfo->typrelkind != RELKIND_COMPOSITE_TYPE)
2128 : {
2129 97242 : TableInfo *tytable = findTableByOid(tyinfo->typrelid);
2130 :
2131 97242 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2132 97242 : if (tytable != NULL)
2133 97242 : tyinfo->dobj.dump = tytable->dobj.dump;
2134 : else
2135 0 : tyinfo->dobj.dump = DUMP_COMPONENT_NONE;
2136 97242 : return;
2137 : }
2138 :
2139 : /* skip auto-generated array and multirange types */
2140 174910 : if (tyinfo->isArray || tyinfo->isMultirange)
2141 : {
2142 133092 : tyinfo->dobj.objType = DO_DUMMY_TYPE;
2143 :
2144 : /*
2145 : * Fall through to set the dump flag; we assume that the subsequent
2146 : * rules will do the same thing as they would for the array's base
2147 : * type or multirange's range type. (We cannot reliably look up the
2148 : * base type here, since getTypes may not have processed it yet.)
2149 : */
2150 : }
2151 :
2152 174910 : if (checkExtensionMembership(&tyinfo->dobj, fout))
2153 300 : return; /* extension membership overrides all else */
2154 :
2155 : /* Dump based on if the contents of the namespace are being dumped */
2156 174610 : tyinfo->dobj.dump = tyinfo->dobj.namespace->dobj.dump_contains;
2157 : }
2158 :
2159 : /*
2160 : * selectDumpableDefaultACL: policy-setting subroutine
2161 : * Mark a default ACL as to be dumped or not
2162 : *
2163 : * For per-schema default ACLs, dump if the schema is to be dumped.
2164 : * Otherwise dump if we are dumping "everything". Note that dumpSchema
2165 : * and aclsSkip are checked separately.
2166 : */
2167 : static void
2168 388 : selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
2169 : {
2170 : /* Default ACLs can't be extension members */
2171 :
2172 388 : if (dinfo->dobj.namespace)
2173 : /* default ACLs are considered part of the namespace */
2174 180 : dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump_contains;
2175 : else
2176 208 : dinfo->dobj.dump = dopt->include_everything ?
2177 208 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2178 388 : }
2179 :
2180 : /*
2181 : * selectDumpableCast: policy-setting subroutine
2182 : * Mark a cast as to be dumped or not
2183 : *
2184 : * Casts do not belong to any particular namespace (since they haven't got
2185 : * names), nor do they have identifiable owners. To distinguish user-defined
2186 : * casts from built-in ones, we must resort to checking whether the cast's
2187 : * OID is in the range reserved for initdb.
2188 : */
2189 : static void
2190 88910 : selectDumpableCast(CastInfo *cast, Archive *fout)
2191 : {
2192 88910 : if (checkExtensionMembership(&cast->dobj, fout))
2193 0 : return; /* extension membership overrides all else */
2194 :
2195 : /*
2196 : * This would be DUMP_COMPONENT_ACL for from-initdb casts, but they do not
2197 : * support ACLs currently.
2198 : */
2199 88910 : if (cast->dobj.catId.oid <= g_last_builtin_oid)
2200 88736 : cast->dobj.dump = DUMP_COMPONENT_NONE;
2201 : else
2202 174 : cast->dobj.dump = fout->dopt->include_everything ?
2203 174 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2204 : }
2205 :
2206 : /*
2207 : * selectDumpableProcLang: policy-setting subroutine
2208 : * Mark a procedural language as to be dumped or not
2209 : *
2210 : * Procedural languages do not belong to any particular namespace. To
2211 : * identify built-in languages, we must resort to checking whether the
2212 : * language's OID is in the range reserved for initdb.
2213 : */
2214 : static void
2215 466 : selectDumpableProcLang(ProcLangInfo *plang, Archive *fout)
2216 : {
2217 466 : if (checkExtensionMembership(&plang->dobj, fout))
2218 376 : return; /* extension membership overrides all else */
2219 :
2220 : /*
2221 : * Only include procedural languages when we are dumping everything.
2222 : *
2223 : * For from-initdb procedural languages, only include ACLs, as we do for
2224 : * the pg_catalog namespace. We need this because procedural languages do
2225 : * not live in any namespace.
2226 : */
2227 90 : if (!fout->dopt->include_everything)
2228 16 : plang->dobj.dump = DUMP_COMPONENT_NONE;
2229 : else
2230 : {
2231 74 : if (plang->dobj.catId.oid <= g_last_builtin_oid)
2232 0 : plang->dobj.dump = fout->remoteVersion < 90600 ?
2233 0 : DUMP_COMPONENT_NONE : DUMP_COMPONENT_ACL;
2234 : else
2235 74 : plang->dobj.dump = DUMP_COMPONENT_ALL;
2236 : }
2237 : }
2238 :
2239 : /*
2240 : * selectDumpableAccessMethod: policy-setting subroutine
2241 : * Mark an access method as to be dumped or not
2242 : *
2243 : * Access methods do not belong to any particular namespace. To identify
2244 : * built-in access methods, we must resort to checking whether the
2245 : * method's OID is in the range reserved for initdb.
2246 : */
2247 : static void
2248 2876 : selectDumpableAccessMethod(AccessMethodInfo *method, Archive *fout)
2249 : {
2250 : /* see getAccessMethods() comment about v9.6. */
2251 2876 : if (fout->remoteVersion < 90600)
2252 : {
2253 0 : method->dobj.dump = DUMP_COMPONENT_NONE;
2254 0 : return;
2255 : }
2256 :
2257 2876 : if (checkExtensionMembership(&method->dobj, fout))
2258 50 : return; /* extension membership overrides all else */
2259 :
2260 : /*
2261 : * This would be DUMP_COMPONENT_ACL for from-initdb access methods, but
2262 : * they do not support ACLs currently.
2263 : */
2264 2826 : if (method->dobj.catId.oid <= g_last_builtin_oid)
2265 2632 : method->dobj.dump = DUMP_COMPONENT_NONE;
2266 : else
2267 194 : method->dobj.dump = fout->dopt->include_everything ?
2268 194 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2269 : }
2270 :
2271 : /*
2272 : * selectDumpableExtension: policy-setting subroutine
2273 : * Mark an extension as to be dumped or not
2274 : *
2275 : * Built-in extensions should be skipped except for checking ACLs, since we
2276 : * assume those will already be installed in the target database. We identify
2277 : * such extensions by their having OIDs in the range reserved for initdb.
2278 : * We dump all user-added extensions by default. No extensions are dumped
2279 : * if include_everything is false (i.e., a --schema or --table switch was
2280 : * given), except if --extension specifies a list of extensions to dump.
2281 : */
2282 : static void
2283 438 : selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
2284 : {
2285 : /*
2286 : * Use DUMP_COMPONENT_ACL for built-in extensions, to allow users to
2287 : * change permissions on their member objects, if they wish to, and have
2288 : * those changes preserved.
2289 : */
2290 438 : if (extinfo->dobj.catId.oid <= g_last_builtin_oid)
2291 378 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_ACL;
2292 : else
2293 : {
2294 : /* check if there is a list of extensions to dump */
2295 60 : if (extension_include_oids.head != NULL)
2296 8 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2297 8 : simple_oid_list_member(&extension_include_oids,
2298 : extinfo->dobj.catId.oid) ?
2299 8 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2300 : else
2301 52 : extinfo->dobj.dump = extinfo->dobj.dump_contains =
2302 52 : dopt->include_everything ?
2303 52 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2304 :
2305 : /* check that the extension is not explicitly excluded */
2306 112 : if (extinfo->dobj.dump &&
2307 52 : simple_oid_list_member(&extension_exclude_oids,
2308 : extinfo->dobj.catId.oid))
2309 4 : extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
2310 : }
2311 438 : }
2312 :
2313 : /*
2314 : * selectDumpablePublicationObject: policy-setting subroutine
2315 : * Mark a publication object as to be dumped or not
2316 : *
2317 : * A publication can have schemas and tables which have schemas, but those are
2318 : * ignored in decision making, because publications are only dumped when we are
2319 : * dumping everything.
2320 : */
2321 : static void
2322 950 : selectDumpablePublicationObject(DumpableObject *dobj, Archive *fout)
2323 : {
2324 950 : if (checkExtensionMembership(dobj, fout))
2325 0 : return; /* extension membership overrides all else */
2326 :
2327 950 : dobj->dump = fout->dopt->include_everything ?
2328 950 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2329 : }
2330 :
2331 : /*
2332 : * selectDumpableStatisticsObject: policy-setting subroutine
2333 : * Mark an extended statistics object as to be dumped or not
2334 : *
2335 : * We dump an extended statistics object if the schema it's in and the table
2336 : * it's for are being dumped. (This'll need more thought if statistics
2337 : * objects ever support cross-table stats.)
2338 : */
2339 : static void
2340 326 : selectDumpableStatisticsObject(StatsExtInfo *sobj, Archive *fout)
2341 : {
2342 326 : if (checkExtensionMembership(&sobj->dobj, fout))
2343 0 : return; /* extension membership overrides all else */
2344 :
2345 326 : sobj->dobj.dump = sobj->dobj.namespace->dobj.dump_contains;
2346 326 : if (sobj->stattable == NULL ||
2347 326 : !(sobj->stattable->dobj.dump & DUMP_COMPONENT_DEFINITION))
2348 56 : sobj->dobj.dump = DUMP_COMPONENT_NONE;
2349 : }
2350 :
2351 : /*
2352 : * selectDumpableObject: policy-setting subroutine
2353 : * Mark a generic dumpable object as to be dumped or not
2354 : *
2355 : * Use this only for object types without a special-case routine above.
2356 : */
2357 : static void
2358 815202 : selectDumpableObject(DumpableObject *dobj, Archive *fout)
2359 : {
2360 815202 : if (checkExtensionMembership(dobj, fout))
2361 388 : return; /* extension membership overrides all else */
2362 :
2363 : /*
2364 : * Default policy is to dump if parent namespace is dumpable, or for
2365 : * non-namespace-associated items, dump if we're dumping "everything".
2366 : */
2367 814814 : if (dobj->namespace)
2368 813356 : dobj->dump = dobj->namespace->dobj.dump_contains;
2369 : else
2370 1458 : dobj->dump = fout->dopt->include_everything ?
2371 1458 : DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
2372 : }
2373 :
2374 : /*
2375 : * Dump a table's contents for loading using the COPY command
2376 : * - this routine is called by the Archiver when it wants the table
2377 : * to be dumped.
2378 : */
2379 : static int
2380 8130 : dumpTableData_copy(Archive *fout, const void *dcontext)
2381 : {
2382 8130 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2383 8130 : TableInfo *tbinfo = tdinfo->tdtable;
2384 8130 : const char *classname = tbinfo->dobj.name;
2385 8130 : PQExpBuffer q = createPQExpBuffer();
2386 :
2387 : /*
2388 : * Note: can't use getThreadLocalPQExpBuffer() here, we're calling fmtId
2389 : * which uses it already.
2390 : */
2391 8130 : PQExpBuffer clistBuf = createPQExpBuffer();
2392 8130 : PGconn *conn = GetConnection(fout);
2393 : PGresult *res;
2394 : int ret;
2395 : char *copybuf;
2396 : const char *column_list;
2397 :
2398 8130 : pg_log_info("dumping contents of table \"%s.%s\"",
2399 : tbinfo->dobj.namespace->dobj.name, classname);
2400 :
2401 : /*
2402 : * Specify the column list explicitly so that we have no possibility of
2403 : * retrieving data in the wrong column order. (The default column
2404 : * ordering of COPY will not be what we want in certain corner cases
2405 : * involving ADD COLUMN and inheritance.)
2406 : */
2407 8130 : column_list = fmtCopyColumnList(tbinfo, clistBuf);
2408 :
2409 : /*
2410 : * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
2411 : * a filter condition was specified. For other cases a simple COPY
2412 : * suffices.
2413 : */
2414 8130 : if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2415 : {
2416 : /* Temporary allows to access to foreign tables to dump data */
2417 154 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2418 2 : set_restrict_relation_kind(fout, "view");
2419 :
2420 154 : appendPQExpBufferStr(q, "COPY (SELECT ");
2421 : /* klugery to get rid of parens in column list */
2422 154 : if (strlen(column_list) > 2)
2423 : {
2424 154 : appendPQExpBufferStr(q, column_list + 1);
2425 154 : q->data[q->len - 1] = ' ';
2426 : }
2427 : else
2428 0 : appendPQExpBufferStr(q, "* ");
2429 :
2430 308 : appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
2431 154 : fmtQualifiedDumpable(tbinfo),
2432 154 : tdinfo->filtercond ? tdinfo->filtercond : "");
2433 : }
2434 : else
2435 : {
2436 7976 : appendPQExpBuffer(q, "COPY %s %s TO stdout;",
2437 7976 : fmtQualifiedDumpable(tbinfo),
2438 : column_list);
2439 : }
2440 8130 : res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
2441 8128 : PQclear(res);
2442 8128 : destroyPQExpBuffer(clistBuf);
2443 :
2444 : for (;;)
2445 : {
2446 3620602 : ret = PQgetCopyData(conn, ©buf, 0);
2447 :
2448 3620602 : if (ret < 0)
2449 8128 : break; /* done or error */
2450 :
2451 3612474 : if (copybuf)
2452 : {
2453 3612474 : WriteData(fout, copybuf, ret);
2454 3612474 : PQfreemem(copybuf);
2455 : }
2456 :
2457 : /* ----------
2458 : * THROTTLE:
2459 : *
2460 : * There was considerable discussion in late July, 2000 regarding
2461 : * slowing down pg_dump when backing up large tables. Users with both
2462 : * slow & fast (multi-processor) machines experienced performance
2463 : * degradation when doing a backup.
2464 : *
2465 : * Initial attempts based on sleeping for a number of ms for each ms
2466 : * of work were deemed too complex, then a simple 'sleep in each loop'
2467 : * implementation was suggested. The latter failed because the loop
2468 : * was too tight. Finally, the following was implemented:
2469 : *
2470 : * If throttle is non-zero, then
2471 : * See how long since the last sleep.
2472 : * Work out how long to sleep (based on ratio).
2473 : * If sleep is more than 100ms, then
2474 : * sleep
2475 : * reset timer
2476 : * EndIf
2477 : * EndIf
2478 : *
2479 : * where the throttle value was the number of ms to sleep per ms of
2480 : * work. The calculation was done in each loop.
2481 : *
2482 : * Most of the hard work is done in the backend, and this solution
2483 : * still did not work particularly well: on slow machines, the ratio
2484 : * was 50:1, and on medium paced machines, 1:1, and on fast
2485 : * multi-processor machines, it had little or no effect, for reasons
2486 : * that were unclear.
2487 : *
2488 : * Further discussion ensued, and the proposal was dropped.
2489 : *
2490 : * For those people who want this feature, it can be implemented using
2491 : * gettimeofday in each loop, calculating the time since last sleep,
2492 : * multiplying that by the sleep ratio, then if the result is more
2493 : * than a preset 'minimum sleep time' (say 100ms), call the 'select'
2494 : * function to sleep for a subsecond period ie.
2495 : *
2496 : * select(0, NULL, NULL, NULL, &tvi);
2497 : *
2498 : * This will return after the interval specified in the structure tvi.
2499 : * Finally, call gettimeofday again to save the 'last sleep time'.
2500 : * ----------
2501 : */
2502 : }
2503 8128 : archprintf(fout, "\\.\n\n\n");
2504 :
2505 8128 : if (ret == -2)
2506 : {
2507 : /* copy data transfer failed */
2508 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
2509 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2510 0 : pg_log_error_detail("Command was: %s", q->data);
2511 0 : exit_nicely(1);
2512 : }
2513 :
2514 : /* Check command status and return to normal libpq state */
2515 8128 : res = PQgetResult(conn);
2516 8128 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2517 : {
2518 0 : pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
2519 0 : pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
2520 0 : pg_log_error_detail("Command was: %s", q->data);
2521 0 : exit_nicely(1);
2522 : }
2523 8128 : PQclear(res);
2524 :
2525 : /* Do this to ensure we've pumped libpq back to idle state */
2526 8128 : if (PQgetResult(conn) != NULL)
2527 0 : pg_log_warning("unexpected extra results during COPY of table \"%s\"",
2528 : classname);
2529 :
2530 8128 : destroyPQExpBuffer(q);
2531 :
2532 : /* Revert back the setting */
2533 8128 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2534 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2535 :
2536 8128 : return 1;
2537 : }
2538 :
2539 : /*
2540 : * Dump table data using INSERT commands.
2541 : *
2542 : * Caution: when we restore from an archive file direct to database, the
2543 : * INSERT commands emitted by this function have to be parsed by
2544 : * pg_backup_db.c's ExecuteSimpleCommands(), which will not handle comments,
2545 : * E'' strings, or dollar-quoted strings. So don't emit anything like that.
2546 : */
2547 : static int
2548 162 : dumpTableData_insert(Archive *fout, const void *dcontext)
2549 : {
2550 162 : TableDataInfo *tdinfo = (TableDataInfo *) dcontext;
2551 162 : TableInfo *tbinfo = tdinfo->tdtable;
2552 162 : DumpOptions *dopt = fout->dopt;
2553 162 : PQExpBuffer q = createPQExpBuffer();
2554 162 : PQExpBuffer insertStmt = NULL;
2555 : char *attgenerated;
2556 : PGresult *res;
2557 : int nfields,
2558 : i;
2559 162 : int rows_per_statement = dopt->dump_inserts;
2560 162 : int rows_this_statement = 0;
2561 :
2562 : /* Temporary allows to access to foreign tables to dump data */
2563 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2564 0 : set_restrict_relation_kind(fout, "view");
2565 :
2566 : /*
2567 : * If we're going to emit INSERTs with column names, the most efficient
2568 : * way to deal with generated columns is to exclude them entirely. For
2569 : * INSERTs without column names, we have to emit DEFAULT rather than the
2570 : * actual column value --- but we can save a few cycles by fetching nulls
2571 : * rather than the uninteresting-to-us value.
2572 : */
2573 162 : attgenerated = (char *) pg_malloc(tbinfo->numatts * sizeof(char));
2574 162 : appendPQExpBufferStr(q, "DECLARE _pg_dump_cursor CURSOR FOR SELECT ");
2575 162 : nfields = 0;
2576 502 : for (i = 0; i < tbinfo->numatts; i++)
2577 : {
2578 340 : if (tbinfo->attisdropped[i])
2579 4 : continue;
2580 336 : if (tbinfo->attgenerated[i] && dopt->column_inserts)
2581 16 : continue;
2582 320 : if (nfields > 0)
2583 172 : appendPQExpBufferStr(q, ", ");
2584 320 : if (tbinfo->attgenerated[i])
2585 16 : appendPQExpBufferStr(q, "NULL");
2586 : else
2587 304 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[i]));
2588 320 : attgenerated[nfields] = tbinfo->attgenerated[i];
2589 320 : nfields++;
2590 : }
2591 : /* Servers before 9.4 will complain about zero-column SELECT */
2592 162 : if (nfields == 0)
2593 14 : appendPQExpBufferStr(q, "NULL");
2594 162 : appendPQExpBuffer(q, " FROM ONLY %s",
2595 162 : fmtQualifiedDumpable(tbinfo));
2596 162 : if (tdinfo->filtercond)
2597 0 : appendPQExpBuffer(q, " %s", tdinfo->filtercond);
2598 :
2599 162 : ExecuteSqlStatement(fout, q->data);
2600 :
2601 : while (1)
2602 : {
2603 262 : res = ExecuteSqlQuery(fout, "FETCH 100 FROM _pg_dump_cursor",
2604 : PGRES_TUPLES_OK);
2605 :
2606 : /* cross-check field count, allowing for dummy NULL if any */
2607 262 : if (nfields != PQnfields(res) &&
2608 20 : !(nfields == 0 && PQnfields(res) == 1))
2609 0 : pg_fatal("wrong number of fields retrieved from table \"%s\"",
2610 : tbinfo->dobj.name);
2611 :
2612 : /*
2613 : * First time through, we build as much of the INSERT statement as
2614 : * possible in "insertStmt", which we can then just print for each
2615 : * statement. If the table happens to have zero dumpable columns then
2616 : * this will be a complete statement, otherwise it will end in
2617 : * "VALUES" and be ready to have the row's column values printed.
2618 : */
2619 262 : if (insertStmt == NULL)
2620 : {
2621 : TableInfo *targettab;
2622 :
2623 162 : insertStmt = createPQExpBuffer();
2624 :
2625 : /*
2626 : * When load-via-partition-root is set or forced, get the root
2627 : * table name for the partition table, so that we can reload data
2628 : * through the root table.
2629 : */
2630 162 : if (tbinfo->ispartition &&
2631 96 : (dopt->load_via_partition_root ||
2632 48 : forcePartitionRootLoad(tbinfo)))
2633 14 : targettab = getRootTableInfo(tbinfo);
2634 : else
2635 148 : targettab = tbinfo;
2636 :
2637 162 : appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
2638 162 : fmtQualifiedDumpable(targettab));
2639 :
2640 : /* corner case for zero-column table */
2641 162 : if (nfields == 0)
2642 : {
2643 14 : appendPQExpBufferStr(insertStmt, "DEFAULT VALUES;\n");
2644 : }
2645 : else
2646 : {
2647 : /* append the list of column names if required */
2648 148 : if (dopt->column_inserts)
2649 : {
2650 66 : appendPQExpBufferChar(insertStmt, '(');
2651 200 : for (int field = 0; field < nfields; field++)
2652 : {
2653 134 : if (field > 0)
2654 68 : appendPQExpBufferStr(insertStmt, ", ");
2655 134 : appendPQExpBufferStr(insertStmt,
2656 134 : fmtId(PQfname(res, field)));
2657 : }
2658 66 : appendPQExpBufferStr(insertStmt, ") ");
2659 : }
2660 :
2661 148 : if (tbinfo->needs_override)
2662 4 : appendPQExpBufferStr(insertStmt, "OVERRIDING SYSTEM VALUE ");
2663 :
2664 148 : appendPQExpBufferStr(insertStmt, "VALUES");
2665 : }
2666 : }
2667 :
2668 6800 : for (int tuple = 0; tuple < PQntuples(res); tuple++)
2669 : {
2670 : /* Write the INSERT if not in the middle of a multi-row INSERT. */
2671 6538 : if (rows_this_statement == 0)
2672 6526 : archputs(insertStmt->data, fout);
2673 :
2674 : /*
2675 : * If it is zero-column table then we've already written the
2676 : * complete statement, which will mean we've disobeyed
2677 : * --rows-per-insert when it's set greater than 1. We do support
2678 : * a way to make this multi-row with: SELECT UNION ALL SELECT
2679 : * UNION ALL ... but that's non-standard so we should avoid it
2680 : * given that using INSERTs is mostly only ever needed for
2681 : * cross-database exports.
2682 : */
2683 6538 : if (nfields == 0)
2684 12 : continue;
2685 :
2686 : /* Emit a row heading */
2687 6526 : if (rows_per_statement == 1)
2688 6508 : archputs(" (", fout);
2689 18 : else if (rows_this_statement > 0)
2690 12 : archputs(",\n\t(", fout);
2691 : else
2692 6 : archputs("\n\t(", fout);
2693 :
2694 19690 : for (int field = 0; field < nfields; field++)
2695 : {
2696 13164 : if (field > 0)
2697 6638 : archputs(", ", fout);
2698 13164 : if (attgenerated[field])
2699 : {
2700 4 : archputs("DEFAULT", fout);
2701 4 : continue;
2702 : }
2703 13160 : if (PQgetisnull(res, tuple, field))
2704 : {
2705 166 : archputs("NULL", fout);
2706 166 : continue;
2707 : }
2708 :
2709 : /* XXX This code is partially duplicated in ruleutils.c */
2710 12994 : switch (PQftype(res, field))
2711 : {
2712 8938 : case INT2OID:
2713 : case INT4OID:
2714 : case INT8OID:
2715 : case OIDOID:
2716 : case FLOAT4OID:
2717 : case FLOAT8OID:
2718 : case NUMERICOID:
2719 : {
2720 : /*
2721 : * These types are printed without quotes unless
2722 : * they contain values that aren't accepted by the
2723 : * scanner unquoted (e.g., 'NaN'). Note that
2724 : * strtod() and friends might accept NaN, so we
2725 : * can't use that to test.
2726 : *
2727 : * In reality we only need to defend against
2728 : * infinity and NaN, so we need not get too crazy
2729 : * about pattern matching here.
2730 : */
2731 8938 : const char *s = PQgetvalue(res, tuple, field);
2732 :
2733 8938 : if (strspn(s, "0123456789 +-eE.") == strlen(s))
2734 8934 : archputs(s, fout);
2735 : else
2736 4 : archprintf(fout, "'%s'", s);
2737 : }
2738 8938 : break;
2739 :
2740 4 : case BITOID:
2741 : case VARBITOID:
2742 4 : archprintf(fout, "B'%s'",
2743 : PQgetvalue(res, tuple, field));
2744 4 : break;
2745 :
2746 8 : case BOOLOID:
2747 8 : if (strcmp(PQgetvalue(res, tuple, field), "t") == 0)
2748 4 : archputs("true", fout);
2749 : else
2750 4 : archputs("false", fout);
2751 8 : break;
2752 :
2753 4044 : default:
2754 : /* All other types are printed as string literals. */
2755 4044 : resetPQExpBuffer(q);
2756 4044 : appendStringLiteralAH(q,
2757 : PQgetvalue(res, tuple, field),
2758 : fout);
2759 4044 : archputs(q->data, fout);
2760 4044 : break;
2761 : }
2762 : }
2763 :
2764 : /* Terminate the row ... */
2765 6526 : archputs(")", fout);
2766 :
2767 : /* ... and the statement, if the target no. of rows is reached */
2768 6526 : if (++rows_this_statement >= rows_per_statement)
2769 : {
2770 6512 : if (dopt->do_nothing)
2771 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2772 : else
2773 6512 : archputs(";\n", fout);
2774 : /* Reset the row counter */
2775 6512 : rows_this_statement = 0;
2776 : }
2777 : }
2778 :
2779 262 : if (PQntuples(res) <= 0)
2780 : {
2781 162 : PQclear(res);
2782 162 : break;
2783 : }
2784 100 : PQclear(res);
2785 : }
2786 :
2787 : /* Terminate any statements that didn't make the row count. */
2788 162 : if (rows_this_statement > 0)
2789 : {
2790 2 : if (dopt->do_nothing)
2791 0 : archputs(" ON CONFLICT DO NOTHING;\n", fout);
2792 : else
2793 2 : archputs(";\n", fout);
2794 : }
2795 :
2796 162 : archputs("\n\n", fout);
2797 :
2798 162 : ExecuteSqlStatement(fout, "CLOSE _pg_dump_cursor");
2799 :
2800 162 : destroyPQExpBuffer(q);
2801 162 : if (insertStmt != NULL)
2802 162 : destroyPQExpBuffer(insertStmt);
2803 162 : free(attgenerated);
2804 :
2805 : /* Revert back the setting */
2806 162 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2807 0 : set_restrict_relation_kind(fout, "view, foreign-table");
2808 :
2809 162 : return 1;
2810 : }
2811 :
2812 : /*
2813 : * getRootTableInfo:
2814 : * get the root TableInfo for the given partition table.
2815 : */
2816 : static TableInfo *
2817 158 : getRootTableInfo(const TableInfo *tbinfo)
2818 : {
2819 : TableInfo *parentTbinfo;
2820 :
2821 : Assert(tbinfo->ispartition);
2822 : Assert(tbinfo->numParents == 1);
2823 :
2824 158 : parentTbinfo = tbinfo->parents[0];
2825 158 : while (parentTbinfo->ispartition)
2826 : {
2827 : Assert(parentTbinfo->numParents == 1);
2828 0 : parentTbinfo = parentTbinfo->parents[0];
2829 : }
2830 :
2831 158 : return parentTbinfo;
2832 : }
2833 :
2834 : /*
2835 : * forcePartitionRootLoad
2836 : * Check if we must force load_via_partition_root for this partition.
2837 : *
2838 : * This is required if any level of ancestral partitioned table has an
2839 : * unsafe partitioning scheme.
2840 : */
2841 : static bool
2842 2104 : forcePartitionRootLoad(const TableInfo *tbinfo)
2843 : {
2844 : TableInfo *parentTbinfo;
2845 :
2846 : Assert(tbinfo->ispartition);
2847 : Assert(tbinfo->numParents == 1);
2848 :
2849 2104 : parentTbinfo = tbinfo->parents[0];
2850 2104 : if (parentTbinfo->unsafe_partitions)
2851 158 : return true;
2852 2386 : while (parentTbinfo->ispartition)
2853 : {
2854 : Assert(parentTbinfo->numParents == 1);
2855 440 : parentTbinfo = parentTbinfo->parents[0];
2856 440 : if (parentTbinfo->unsafe_partitions)
2857 0 : return true;
2858 : }
2859 :
2860 1946 : return false;
2861 : }
2862 :
2863 : /*
2864 : * dumpTableData -
2865 : * dump the contents of a single table
2866 : *
2867 : * Actually, this just makes an ArchiveEntry for the table contents.
2868 : */
2869 : static void
2870 8452 : dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
2871 : {
2872 8452 : DumpOptions *dopt = fout->dopt;
2873 8452 : TableInfo *tbinfo = tdinfo->tdtable;
2874 8452 : PQExpBuffer copyBuf = createPQExpBuffer();
2875 8452 : PQExpBuffer clistBuf = createPQExpBuffer();
2876 : DataDumperPtr dumpFn;
2877 8452 : char *tdDefn = NULL;
2878 : char *copyStmt;
2879 : const char *copyFrom;
2880 :
2881 : /* We had better have loaded per-column details about this table */
2882 : Assert(tbinfo->interesting);
2883 :
2884 : /*
2885 : * When load-via-partition-root is set or forced, get the root table name
2886 : * for the partition table, so that we can reload data through the root
2887 : * table. Then construct a comment to be inserted into the TOC entry's
2888 : * defn field, so that such cases can be identified reliably.
2889 : */
2890 8452 : if (tbinfo->ispartition &&
2891 4112 : (dopt->load_via_partition_root ||
2892 2056 : forcePartitionRootLoad(tbinfo)))
2893 144 : {
2894 : TableInfo *parentTbinfo;
2895 : char *sanitized;
2896 :
2897 144 : parentTbinfo = getRootTableInfo(tbinfo);
2898 144 : copyFrom = fmtQualifiedDumpable(parentTbinfo);
2899 144 : sanitized = sanitize_line(copyFrom, true);
2900 144 : printfPQExpBuffer(copyBuf, "-- load via partition root %s",
2901 : sanitized);
2902 144 : free(sanitized);
2903 144 : tdDefn = pg_strdup(copyBuf->data);
2904 : }
2905 : else
2906 8308 : copyFrom = fmtQualifiedDumpable(tbinfo);
2907 :
2908 8452 : if (dopt->dump_inserts == 0)
2909 : {
2910 : /* Dump/restore using COPY */
2911 8290 : dumpFn = dumpTableData_copy;
2912 : /* must use 2 steps here 'cause fmtId is nonreentrant */
2913 8290 : printfPQExpBuffer(copyBuf, "COPY %s ",
2914 : copyFrom);
2915 8290 : appendPQExpBuffer(copyBuf, "%s FROM stdin;\n",
2916 : fmtCopyColumnList(tbinfo, clistBuf));
2917 8290 : copyStmt = copyBuf->data;
2918 : }
2919 : else
2920 : {
2921 : /* Restore using INSERT */
2922 162 : dumpFn = dumpTableData_insert;
2923 162 : copyStmt = NULL;
2924 : }
2925 :
2926 : /*
2927 : * Note: although the TableDataInfo is a full DumpableObject, we treat its
2928 : * dependency on its table as "special" and pass it to ArchiveEntry now.
2929 : * See comments for BuildArchiveDependencies.
2930 : */
2931 8452 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
2932 : {
2933 : TocEntry *te;
2934 :
2935 8452 : te = ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
2936 8452 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
2937 : .namespace = tbinfo->dobj.namespace->dobj.name,
2938 : .owner = tbinfo->rolname,
2939 : .description = "TABLE DATA",
2940 : .section = SECTION_DATA,
2941 : .createStmt = tdDefn,
2942 : .copyStmt = copyStmt,
2943 : .deps = &(tbinfo->dobj.dumpId),
2944 : .nDeps = 1,
2945 : .dumpFn = dumpFn,
2946 : .dumpArg = tdinfo));
2947 :
2948 : /*
2949 : * Set the TocEntry's dataLength in case we are doing a parallel dump
2950 : * and want to order dump jobs by table size. We choose to measure
2951 : * dataLength in table pages (including TOAST pages) during dump, so
2952 : * no scaling is needed.
2953 : *
2954 : * However, relpages is declared as "integer" in pg_class, and hence
2955 : * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
2956 : * Cast so that we get the right interpretation of table sizes
2957 : * exceeding INT_MAX pages.
2958 : */
2959 8452 : te->dataLength = (BlockNumber) tbinfo->relpages;
2960 8452 : te->dataLength += (BlockNumber) tbinfo->toastpages;
2961 :
2962 : /*
2963 : * If pgoff_t is only 32 bits wide, the above refinement is useless,
2964 : * and instead we'd better worry about integer overflow. Clamp to
2965 : * INT_MAX if the correct result exceeds that.
2966 : */
2967 : if (sizeof(te->dataLength) == 4 &&
2968 : (tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
2969 : te->dataLength < 0))
2970 : te->dataLength = INT_MAX;
2971 : }
2972 :
2973 8452 : destroyPQExpBuffer(copyBuf);
2974 8452 : destroyPQExpBuffer(clistBuf);
2975 8452 : }
2976 :
2977 : /*
2978 : * refreshMatViewData -
2979 : * load or refresh the contents of a single materialized view
2980 : *
2981 : * Actually, this just makes an ArchiveEntry for the REFRESH MATERIALIZED VIEW
2982 : * statement.
2983 : */
2984 : static void
2985 690 : refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo)
2986 : {
2987 690 : TableInfo *tbinfo = tdinfo->tdtable;
2988 : PQExpBuffer q;
2989 :
2990 : /* If the materialized view is not flagged as populated, skip this. */
2991 690 : if (!tbinfo->relispopulated)
2992 136 : return;
2993 :
2994 554 : q = createPQExpBuffer();
2995 :
2996 554 : appendPQExpBuffer(q, "REFRESH MATERIALIZED VIEW %s;\n",
2997 554 : fmtQualifiedDumpable(tbinfo));
2998 :
2999 554 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
3000 554 : ArchiveEntry(fout,
3001 : tdinfo->dobj.catId, /* catalog ID */
3002 554 : tdinfo->dobj.dumpId, /* dump ID */
3003 554 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
3004 : .namespace = tbinfo->dobj.namespace->dobj.name,
3005 : .owner = tbinfo->rolname,
3006 : .description = "MATERIALIZED VIEW DATA",
3007 : .section = SECTION_POST_DATA,
3008 : .createStmt = q->data,
3009 : .deps = tdinfo->dobj.dependencies,
3010 : .nDeps = tdinfo->dobj.nDeps));
3011 :
3012 554 : destroyPQExpBuffer(q);
3013 : }
3014 :
3015 : /*
3016 : * getTableData -
3017 : * set up dumpable objects representing the contents of tables
3018 : */
3019 : static void
3020 360 : getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
3021 : {
3022 : int i;
3023 :
3024 95438 : for (i = 0; i < numTables; i++)
3025 : {
3026 95078 : if (tblinfo[i].dobj.dump & DUMP_COMPONENT_DATA &&
3027 1864 : (!relkind || tblinfo[i].relkind == relkind))
3028 11784 : makeTableDataInfo(dopt, &(tblinfo[i]));
3029 : }
3030 360 : }
3031 :
3032 : /*
3033 : * Make a dumpable object for the data of this specific table
3034 : *
3035 : * Note: we make a TableDataInfo if and only if we are going to dump the
3036 : * table data; the "dump" field in such objects isn't very interesting.
3037 : */
3038 : static void
3039 12014 : makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
3040 : {
3041 : TableDataInfo *tdinfo;
3042 :
3043 : /*
3044 : * Nothing to do if we already decided to dump the table. This will
3045 : * happen for "config" tables.
3046 : */
3047 12014 : if (tbinfo->dataObj != NULL)
3048 2 : return;
3049 :
3050 : /* Skip VIEWs (no data to dump) */
3051 12012 : if (tbinfo->relkind == RELKIND_VIEW)
3052 968 : return;
3053 : /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
3054 11044 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
3055 76 : (foreign_servers_include_oids.head == NULL ||
3056 8 : !simple_oid_list_member(&foreign_servers_include_oids,
3057 : tbinfo->foreign_server)))
3058 74 : return;
3059 : /* Skip partitioned tables (data in partitions) */
3060 10970 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
3061 990 : return;
3062 :
3063 : /* Don't dump data in unlogged tables, if so requested */
3064 9980 : if (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
3065 82 : dopt->no_unlogged_table_data)
3066 36 : return;
3067 :
3068 : /* Check that the data is not explicitly excluded */
3069 9944 : if (simple_oid_list_member(&tabledata_exclude_oids,
3070 : tbinfo->dobj.catId.oid))
3071 16 : return;
3072 :
3073 : /* OK, let's dump it */
3074 9928 : tdinfo = (TableDataInfo *) pg_malloc(sizeof(TableDataInfo));
3075 :
3076 9928 : if (tbinfo->relkind == RELKIND_MATVIEW)
3077 690 : tdinfo->dobj.objType = DO_REFRESH_MATVIEW;
3078 9238 : else if (tbinfo->relkind == RELKIND_SEQUENCE)
3079 786 : tdinfo->dobj.objType = DO_SEQUENCE_SET;
3080 : else
3081 8452 : tdinfo->dobj.objType = DO_TABLE_DATA;
3082 :
3083 : /*
3084 : * Note: use tableoid 0 so that this object won't be mistaken for
3085 : * something that pg_depend entries apply to.
3086 : */
3087 9928 : tdinfo->dobj.catId.tableoid = 0;
3088 9928 : tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
3089 9928 : AssignDumpId(&tdinfo->dobj);
3090 9928 : tdinfo->dobj.name = tbinfo->dobj.name;
3091 9928 : tdinfo->dobj.namespace = tbinfo->dobj.namespace;
3092 9928 : tdinfo->tdtable = tbinfo;
3093 9928 : tdinfo->filtercond = NULL; /* might get set later */
3094 9928 : addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
3095 :
3096 : /* A TableDataInfo contains data, of course */
3097 9928 : tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
3098 :
3099 9928 : tbinfo->dataObj = tdinfo;
3100 :
3101 : /*
3102 : * Materialized view statistics must be restored after the data, because
3103 : * REFRESH MATERIALIZED VIEW replaces the storage and resets the stats.
3104 : *
3105 : * The dependency is added here because the statistics objects are created
3106 : * first.
3107 : */
3108 9928 : if (tbinfo->relkind == RELKIND_MATVIEW && tbinfo->stats != NULL)
3109 : {
3110 536 : tbinfo->stats->section = SECTION_POST_DATA;
3111 536 : addObjectDependency(&tbinfo->stats->dobj, tdinfo->dobj.dumpId);
3112 : }
3113 :
3114 : /* Make sure that we'll collect per-column info for this table. */
3115 9928 : tbinfo->interesting = true;
3116 : }
3117 :
3118 : /*
3119 : * The refresh for a materialized view must be dependent on the refresh for
3120 : * any materialized view that this one is dependent on.
3121 : *
3122 : * This must be called after all the objects are created, but before they are
3123 : * sorted.
3124 : */
3125 : static void
3126 292 : buildMatViewRefreshDependencies(Archive *fout)
3127 : {
3128 : PQExpBuffer query;
3129 : PGresult *res;
3130 : int ntups,
3131 : i;
3132 : int i_classid,
3133 : i_objid,
3134 : i_refobjid;
3135 :
3136 : /* No Mat Views before 9.3. */
3137 292 : if (fout->remoteVersion < 90300)
3138 0 : return;
3139 :
3140 292 : query = createPQExpBuffer();
3141 :
3142 292 : appendPQExpBufferStr(query, "WITH RECURSIVE w AS "
3143 : "( "
3144 : "SELECT d1.objid, d2.refobjid, c2.relkind AS refrelkind "
3145 : "FROM pg_depend d1 "
3146 : "JOIN pg_class c1 ON c1.oid = d1.objid "
3147 : "AND c1.relkind = " CppAsString2(RELKIND_MATVIEW)
3148 : " JOIN pg_rewrite r1 ON r1.ev_class = d1.objid "
3149 : "JOIN pg_depend d2 ON d2.classid = 'pg_rewrite'::regclass "
3150 : "AND d2.objid = r1.oid "
3151 : "AND d2.refobjid <> d1.objid "
3152 : "JOIN pg_class c2 ON c2.oid = d2.refobjid "
3153 : "AND c2.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3154 : CppAsString2(RELKIND_VIEW) ") "
3155 : "WHERE d1.classid = 'pg_class'::regclass "
3156 : "UNION "
3157 : "SELECT w.objid, d3.refobjid, c3.relkind "
3158 : "FROM w "
3159 : "JOIN pg_rewrite r3 ON r3.ev_class = w.refobjid "
3160 : "JOIN pg_depend d3 ON d3.classid = 'pg_rewrite'::regclass "
3161 : "AND d3.objid = r3.oid "
3162 : "AND d3.refobjid <> w.refobjid "
3163 : "JOIN pg_class c3 ON c3.oid = d3.refobjid "
3164 : "AND c3.relkind IN (" CppAsString2(RELKIND_MATVIEW) ","
3165 : CppAsString2(RELKIND_VIEW) ") "
3166 : ") "
3167 : "SELECT 'pg_class'::regclass::oid AS classid, objid, refobjid "
3168 : "FROM w "
3169 : "WHERE refrelkind = " CppAsString2(RELKIND_MATVIEW));
3170 :
3171 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
3172 :
3173 292 : ntups = PQntuples(res);
3174 :
3175 292 : i_classid = PQfnumber(res, "classid");
3176 292 : i_objid = PQfnumber(res, "objid");
3177 292 : i_refobjid = PQfnumber(res, "refobjid");
3178 :
3179 820 : for (i = 0; i < ntups; i++)
3180 : {
3181 : CatalogId objId;
3182 : CatalogId refobjId;
3183 : DumpableObject *dobj;
3184 : DumpableObject *refdobj;
3185 : TableInfo *tbinfo;
3186 : TableInfo *reftbinfo;
3187 :
3188 528 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
3189 528 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
3190 528 : refobjId.tableoid = objId.tableoid;
3191 528 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
3192 :
3193 528 : dobj = findObjectByCatalogId(objId);
3194 528 : if (dobj == NULL)
3195 96 : continue;
3196 :
3197 : Assert(dobj->objType == DO_TABLE);
3198 528 : tbinfo = (TableInfo *) dobj;
3199 : Assert(tbinfo->relkind == RELKIND_MATVIEW);
3200 528 : dobj = (DumpableObject *) tbinfo->dataObj;
3201 528 : if (dobj == NULL)
3202 96 : continue;
3203 : Assert(dobj->objType == DO_REFRESH_MATVIEW);
3204 :
3205 432 : refdobj = findObjectByCatalogId(refobjId);
3206 432 : if (refdobj == NULL)
3207 0 : continue;
3208 :
3209 : Assert(refdobj->objType == DO_TABLE);
3210 432 : reftbinfo = (TableInfo *) refdobj;
3211 : Assert(reftbinfo->relkind == RELKIND_MATVIEW);
3212 432 : refdobj = (DumpableObject *) reftbinfo->dataObj;
3213 432 : if (refdobj == NULL)
3214 0 : continue;
3215 : Assert(refdobj->objType == DO_REFRESH_MATVIEW);
3216 :
3217 432 : addObjectDependency(dobj, refdobj->dumpId);
3218 :
3219 432 : if (!reftbinfo->relispopulated)
3220 68 : tbinfo->relispopulated = false;
3221 : }
3222 :
3223 292 : PQclear(res);
3224 :
3225 292 : destroyPQExpBuffer(query);
3226 : }
3227 :
3228 : /*
3229 : * getTableDataFKConstraints -
3230 : * add dump-order dependencies reflecting foreign key constraints
3231 : *
3232 : * This code is executed only in a data-only dump --- in schema+data dumps
3233 : * we handle foreign key issues by not creating the FK constraints until
3234 : * after the data is loaded. In a data-only dump, however, we want to
3235 : * order the table data objects in such a way that a table's referenced
3236 : * tables are restored first. (In the presence of circular references or
3237 : * self-references this may be impossible; we'll detect and complain about
3238 : * that during the dependency sorting step.)
3239 : */
3240 : static void
3241 14 : getTableDataFKConstraints(void)
3242 : {
3243 : DumpableObject **dobjs;
3244 : int numObjs;
3245 : int i;
3246 :
3247 : /* Search through all the dumpable objects for FK constraints */
3248 14 : getDumpableObjects(&dobjs, &numObjs);
3249 51536 : for (i = 0; i < numObjs; i++)
3250 : {
3251 51522 : if (dobjs[i]->objType == DO_FK_CONSTRAINT)
3252 : {
3253 16 : ConstraintInfo *cinfo = (ConstraintInfo *) dobjs[i];
3254 : TableInfo *ftable;
3255 :
3256 : /* Not interesting unless both tables are to be dumped */
3257 16 : if (cinfo->contable == NULL ||
3258 16 : cinfo->contable->dataObj == NULL)
3259 8 : continue;
3260 8 : ftable = findTableByOid(cinfo->confrelid);
3261 8 : if (ftable == NULL ||
3262 8 : ftable->dataObj == NULL)
3263 0 : continue;
3264 :
3265 : /*
3266 : * Okay, make referencing table's TABLE_DATA object depend on the
3267 : * referenced table's TABLE_DATA object.
3268 : */
3269 8 : addObjectDependency(&cinfo->contable->dataObj->dobj,
3270 8 : ftable->dataObj->dobj.dumpId);
3271 : }
3272 : }
3273 14 : free(dobjs);
3274 14 : }
3275 :
3276 :
3277 : /*
3278 : * dumpDatabase:
3279 : * dump the database definition
3280 : */
3281 : static void
3282 174 : dumpDatabase(Archive *fout)
3283 : {
3284 174 : DumpOptions *dopt = fout->dopt;
3285 174 : PQExpBuffer dbQry = createPQExpBuffer();
3286 174 : PQExpBuffer delQry = createPQExpBuffer();
3287 174 : PQExpBuffer creaQry = createPQExpBuffer();
3288 174 : PQExpBuffer labelq = createPQExpBuffer();
3289 174 : PGconn *conn = GetConnection(fout);
3290 : PGresult *res;
3291 : int i_tableoid,
3292 : i_oid,
3293 : i_datname,
3294 : i_datdba,
3295 : i_encoding,
3296 : i_datlocprovider,
3297 : i_collate,
3298 : i_ctype,
3299 : i_datlocale,
3300 : i_daticurules,
3301 : i_frozenxid,
3302 : i_minmxid,
3303 : i_datacl,
3304 : i_acldefault,
3305 : i_datistemplate,
3306 : i_datconnlimit,
3307 : i_datcollversion,
3308 : i_tablespace;
3309 : CatalogId dbCatId;
3310 : DumpId dbDumpId;
3311 : DumpableAcl dbdacl;
3312 : const char *datname,
3313 : *dba,
3314 : *encoding,
3315 : *datlocprovider,
3316 : *collate,
3317 : *ctype,
3318 : *locale,
3319 : *icurules,
3320 : *datistemplate,
3321 : *datconnlimit,
3322 : *tablespace;
3323 : uint32 frozenxid,
3324 : minmxid;
3325 : char *qdatname;
3326 :
3327 174 : pg_log_info("saving database definition");
3328 :
3329 : /*
3330 : * Fetch the database-level properties for this database.
3331 : */
3332 174 : appendPQExpBufferStr(dbQry, "SELECT tableoid, oid, datname, "
3333 : "datdba, "
3334 : "pg_encoding_to_char(encoding) AS encoding, "
3335 : "datcollate, datctype, datfrozenxid, "
3336 : "datacl, acldefault('d', datdba) AS acldefault, "
3337 : "datistemplate, datconnlimit, ");
3338 174 : if (fout->remoteVersion >= 90300)
3339 174 : appendPQExpBufferStr(dbQry, "datminmxid, ");
3340 : else
3341 0 : appendPQExpBufferStr(dbQry, "0 AS datminmxid, ");
3342 174 : if (fout->remoteVersion >= 170000)
3343 174 : appendPQExpBufferStr(dbQry, "datlocprovider, datlocale, datcollversion, ");
3344 0 : else if (fout->remoteVersion >= 150000)
3345 0 : appendPQExpBufferStr(dbQry, "datlocprovider, daticulocale AS datlocale, datcollversion, ");
3346 : else
3347 0 : appendPQExpBufferStr(dbQry, "'c' AS datlocprovider, NULL AS datlocale, NULL AS datcollversion, ");
3348 174 : if (fout->remoteVersion >= 160000)
3349 174 : appendPQExpBufferStr(dbQry, "daticurules, ");
3350 : else
3351 0 : appendPQExpBufferStr(dbQry, "NULL AS daticurules, ");
3352 174 : appendPQExpBufferStr(dbQry,
3353 : "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
3354 : "shobj_description(oid, 'pg_database') AS description "
3355 : "FROM pg_database "
3356 : "WHERE datname = current_database()");
3357 :
3358 174 : res = ExecuteSqlQueryForSingleRow(fout, dbQry->data);
3359 :
3360 174 : i_tableoid = PQfnumber(res, "tableoid");
3361 174 : i_oid = PQfnumber(res, "oid");
3362 174 : i_datname = PQfnumber(res, "datname");
3363 174 : i_datdba = PQfnumber(res, "datdba");
3364 174 : i_encoding = PQfnumber(res, "encoding");
3365 174 : i_datlocprovider = PQfnumber(res, "datlocprovider");
3366 174 : i_collate = PQfnumber(res, "datcollate");
3367 174 : i_ctype = PQfnumber(res, "datctype");
3368 174 : i_datlocale = PQfnumber(res, "datlocale");
3369 174 : i_daticurules = PQfnumber(res, "daticurules");
3370 174 : i_frozenxid = PQfnumber(res, "datfrozenxid");
3371 174 : i_minmxid = PQfnumber(res, "datminmxid");
3372 174 : i_datacl = PQfnumber(res, "datacl");
3373 174 : i_acldefault = PQfnumber(res, "acldefault");
3374 174 : i_datistemplate = PQfnumber(res, "datistemplate");
3375 174 : i_datconnlimit = PQfnumber(res, "datconnlimit");
3376 174 : i_datcollversion = PQfnumber(res, "datcollversion");
3377 174 : i_tablespace = PQfnumber(res, "tablespace");
3378 :
3379 174 : dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
3380 174 : dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
3381 174 : datname = PQgetvalue(res, 0, i_datname);
3382 174 : dba = getRoleName(PQgetvalue(res, 0, i_datdba));
3383 174 : encoding = PQgetvalue(res, 0, i_encoding);
3384 174 : datlocprovider = PQgetvalue(res, 0, i_datlocprovider);
3385 174 : collate = PQgetvalue(res, 0, i_collate);
3386 174 : ctype = PQgetvalue(res, 0, i_ctype);
3387 174 : if (!PQgetisnull(res, 0, i_datlocale))
3388 28 : locale = PQgetvalue(res, 0, i_datlocale);
3389 : else
3390 146 : locale = NULL;
3391 174 : if (!PQgetisnull(res, 0, i_daticurules))
3392 0 : icurules = PQgetvalue(res, 0, i_daticurules);
3393 : else
3394 174 : icurules = NULL;
3395 174 : frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
3396 174 : minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
3397 174 : dbdacl.acl = PQgetvalue(res, 0, i_datacl);
3398 174 : dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
3399 174 : datistemplate = PQgetvalue(res, 0, i_datistemplate);
3400 174 : datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
3401 174 : tablespace = PQgetvalue(res, 0, i_tablespace);
3402 :
3403 174 : qdatname = pg_strdup(fmtId(datname));
3404 :
3405 : /*
3406 : * Prepare the CREATE DATABASE command. We must specify OID (if we want
3407 : * to preserve that), as well as the encoding, locale, and tablespace
3408 : * since those can't be altered later. Other DB properties are left to
3409 : * the DATABASE PROPERTIES entry, so that they can be applied after
3410 : * reconnecting to the target DB.
3411 : *
3412 : * For binary upgrade, we use the FILE_COPY strategy because testing has
3413 : * shown it to be faster. When the server is in binary upgrade mode, it
3414 : * will also skip the checkpoints this strategy ordinarily performs.
3415 : */
3416 174 : if (dopt->binary_upgrade)
3417 : {
3418 74 : appendPQExpBuffer(creaQry,
3419 : "CREATE DATABASE %s WITH TEMPLATE = template0 "
3420 : "OID = %u STRATEGY = FILE_COPY",
3421 : qdatname, dbCatId.oid);
3422 : }
3423 : else
3424 : {
3425 100 : appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
3426 : qdatname);
3427 : }
3428 174 : if (strlen(encoding) > 0)
3429 : {
3430 174 : appendPQExpBufferStr(creaQry, " ENCODING = ");
3431 174 : appendStringLiteralAH(creaQry, encoding, fout);
3432 : }
3433 :
3434 174 : appendPQExpBufferStr(creaQry, " LOCALE_PROVIDER = ");
3435 174 : if (datlocprovider[0] == 'b')
3436 28 : appendPQExpBufferStr(creaQry, "builtin");
3437 146 : else if (datlocprovider[0] == 'c')
3438 146 : appendPQExpBufferStr(creaQry, "libc");
3439 0 : else if (datlocprovider[0] == 'i')
3440 0 : appendPQExpBufferStr(creaQry, "icu");
3441 : else
3442 0 : pg_fatal("unrecognized locale provider: %s",
3443 : datlocprovider);
3444 :
3445 174 : if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
3446 : {
3447 174 : appendPQExpBufferStr(creaQry, " LOCALE = ");
3448 174 : appendStringLiteralAH(creaQry, collate, fout);
3449 : }
3450 : else
3451 : {
3452 0 : if (strlen(collate) > 0)
3453 : {
3454 0 : appendPQExpBufferStr(creaQry, " LC_COLLATE = ");
3455 0 : appendStringLiteralAH(creaQry, collate, fout);
3456 : }
3457 0 : if (strlen(ctype) > 0)
3458 : {
3459 0 : appendPQExpBufferStr(creaQry, " LC_CTYPE = ");
3460 0 : appendStringLiteralAH(creaQry, ctype, fout);
3461 : }
3462 : }
3463 174 : if (locale)
3464 : {
3465 28 : if (datlocprovider[0] == 'b')
3466 28 : appendPQExpBufferStr(creaQry, " BUILTIN_LOCALE = ");
3467 : else
3468 0 : appendPQExpBufferStr(creaQry, " ICU_LOCALE = ");
3469 :
3470 28 : appendStringLiteralAH(creaQry, locale, fout);
3471 : }
3472 :
3473 174 : if (icurules)
3474 : {
3475 0 : appendPQExpBufferStr(creaQry, " ICU_RULES = ");
3476 0 : appendStringLiteralAH(creaQry, icurules, fout);
3477 : }
3478 :
3479 : /*
3480 : * For binary upgrade, carry over the collation version. For normal
3481 : * dump/restore, omit the version, so that it is computed upon restore.
3482 : */
3483 174 : if (dopt->binary_upgrade)
3484 : {
3485 74 : if (!PQgetisnull(res, 0, i_datcollversion))
3486 : {
3487 74 : appendPQExpBufferStr(creaQry, " COLLATION_VERSION = ");
3488 74 : appendStringLiteralAH(creaQry,
3489 : PQgetvalue(res, 0, i_datcollversion),
3490 : fout);
3491 : }
3492 : }
3493 :
3494 : /*
3495 : * Note: looking at dopt->outputNoTablespaces here is completely the wrong
3496 : * thing; the decision whether to specify a tablespace should be left till
3497 : * pg_restore, so that pg_restore --no-tablespaces applies. Ideally we'd
3498 : * label the DATABASE entry with the tablespace and let the normal
3499 : * tablespace selection logic work ... but CREATE DATABASE doesn't pay
3500 : * attention to default_tablespace, so that won't work.
3501 : */
3502 174 : if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0 &&
3503 10 : !dopt->outputNoTablespaces)
3504 10 : appendPQExpBuffer(creaQry, " TABLESPACE = %s",
3505 : fmtId(tablespace));
3506 174 : appendPQExpBufferStr(creaQry, ";\n");
3507 :
3508 174 : appendPQExpBuffer(delQry, "DROP DATABASE %s;\n",
3509 : qdatname);
3510 :
3511 174 : dbDumpId = createDumpId();
3512 :
3513 174 : ArchiveEntry(fout,
3514 : dbCatId, /* catalog ID */
3515 : dbDumpId, /* dump ID */
3516 174 : ARCHIVE_OPTS(.tag = datname,
3517 : .owner = dba,
3518 : .description = "DATABASE",
3519 : .section = SECTION_PRE_DATA,
3520 : .createStmt = creaQry->data,
3521 : .dropStmt = delQry->data));
3522 :
3523 : /* Compute correct tag for archive entry */
3524 174 : appendPQExpBuffer(labelq, "DATABASE %s", qdatname);
3525 :
3526 : /* Dump DB comment if any */
3527 : {
3528 : /*
3529 : * 8.2 and up keep comments on shared objects in a shared table, so we
3530 : * cannot use the dumpComment() code used for other database objects.
3531 : * Be careful that the ArchiveEntry parameters match that function.
3532 : */
3533 174 : char *comment = PQgetvalue(res, 0, PQfnumber(res, "description"));
3534 :
3535 174 : if (comment && *comment && !dopt->no_comments)
3536 : {
3537 84 : resetPQExpBuffer(dbQry);
3538 :
3539 : /*
3540 : * Generates warning when loaded into a differently-named
3541 : * database.
3542 : */
3543 84 : appendPQExpBuffer(dbQry, "COMMENT ON DATABASE %s IS ", qdatname);
3544 84 : appendStringLiteralAH(dbQry, comment, fout);
3545 84 : appendPQExpBufferStr(dbQry, ";\n");
3546 :
3547 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3548 84 : ARCHIVE_OPTS(.tag = labelq->data,
3549 : .owner = dba,
3550 : .description = "COMMENT",
3551 : .section = SECTION_NONE,
3552 : .createStmt = dbQry->data,
3553 : .deps = &dbDumpId,
3554 : .nDeps = 1));
3555 : }
3556 : }
3557 :
3558 : /* Dump DB security label, if enabled */
3559 174 : if (!dopt->no_security_labels)
3560 : {
3561 : PGresult *shres;
3562 : PQExpBuffer seclabelQry;
3563 :
3564 174 : seclabelQry = createPQExpBuffer();
3565 :
3566 174 : buildShSecLabelQuery("pg_database", dbCatId.oid, seclabelQry);
3567 174 : shres = ExecuteSqlQuery(fout, seclabelQry->data, PGRES_TUPLES_OK);
3568 174 : resetPQExpBuffer(seclabelQry);
3569 174 : emitShSecLabels(conn, shres, seclabelQry, "DATABASE", datname);
3570 174 : if (seclabelQry->len > 0)
3571 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3572 0 : ARCHIVE_OPTS(.tag = labelq->data,
3573 : .owner = dba,
3574 : .description = "SECURITY LABEL",
3575 : .section = SECTION_NONE,
3576 : .createStmt = seclabelQry->data,
3577 : .deps = &dbDumpId,
3578 : .nDeps = 1));
3579 174 : destroyPQExpBuffer(seclabelQry);
3580 174 : PQclear(shres);
3581 : }
3582 :
3583 : /*
3584 : * Dump ACL if any. Note that we do not support initial privileges
3585 : * (pg_init_privs) on databases.
3586 : */
3587 174 : dbdacl.privtype = 0;
3588 174 : dbdacl.initprivs = NULL;
3589 :
3590 174 : dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
3591 : qdatname, NULL, NULL,
3592 : NULL, dba, &dbdacl);
3593 :
3594 : /*
3595 : * Now construct a DATABASE PROPERTIES archive entry to restore any
3596 : * non-default database-level properties. (The reason this must be
3597 : * separate is that we cannot put any additional commands into the TOC
3598 : * entry that has CREATE DATABASE. pg_restore would execute such a group
3599 : * in an implicit transaction block, and the backend won't allow CREATE
3600 : * DATABASE in that context.)
3601 : */
3602 174 : resetPQExpBuffer(creaQry);
3603 174 : resetPQExpBuffer(delQry);
3604 :
3605 174 : if (strlen(datconnlimit) > 0 && strcmp(datconnlimit, "-1") != 0)
3606 0 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s CONNECTION LIMIT = %s;\n",
3607 : qdatname, datconnlimit);
3608 :
3609 174 : if (strcmp(datistemplate, "t") == 0)
3610 : {
3611 22 : appendPQExpBuffer(creaQry, "ALTER DATABASE %s IS_TEMPLATE = true;\n",
3612 : qdatname);
3613 :
3614 : /*
3615 : * The backend won't accept DROP DATABASE on a template database. We
3616 : * can deal with that by removing the template marking before the DROP
3617 : * gets issued. We'd prefer to use ALTER DATABASE IF EXISTS here, but
3618 : * since no such command is currently supported, fake it with a direct
3619 : * UPDATE on pg_database.
3620 : */
3621 22 : appendPQExpBufferStr(delQry, "UPDATE pg_catalog.pg_database "
3622 : "SET datistemplate = false WHERE datname = ");
3623 22 : appendStringLiteralAH(delQry, datname, fout);
3624 22 : appendPQExpBufferStr(delQry, ";\n");
3625 : }
3626 :
3627 : /*
3628 : * We do not restore pg_database.dathasloginevt because it is set
3629 : * automatically on login event trigger creation.
3630 : */
3631 :
3632 : /* Add database-specific SET options */
3633 174 : dumpDatabaseConfig(fout, creaQry, datname, dbCatId.oid);
3634 :
3635 : /*
3636 : * We stick this binary-upgrade query into the DATABASE PROPERTIES archive
3637 : * entry, too, for lack of a better place.
3638 : */
3639 174 : if (dopt->binary_upgrade)
3640 : {
3641 74 : appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
3642 74 : appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
3643 : "SET datfrozenxid = '%u', datminmxid = '%u'\n"
3644 : "WHERE datname = ",
3645 : frozenxid, minmxid);
3646 74 : appendStringLiteralAH(creaQry, datname, fout);
3647 74 : appendPQExpBufferStr(creaQry, ";\n");
3648 : }
3649 :
3650 174 : if (creaQry->len > 0)
3651 82 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3652 82 : ARCHIVE_OPTS(.tag = datname,
3653 : .owner = dba,
3654 : .description = "DATABASE PROPERTIES",
3655 : .section = SECTION_PRE_DATA,
3656 : .createStmt = creaQry->data,
3657 : .dropStmt = delQry->data,
3658 : .deps = &dbDumpId));
3659 :
3660 : /*
3661 : * pg_largeobject comes from the old system intact, so set its
3662 : * relfrozenxids, relminmxids and relfilenode.
3663 : *
3664 : * pg_largeobject_metadata also comes from the old system intact for
3665 : * upgrades from v16 and newer, so set its relfrozenxids, relminmxids, and
3666 : * relfilenode, too. pg_upgrade can't copy/link the files from older
3667 : * versions because aclitem (needed by pg_largeobject_metadata.lomacl)
3668 : * changed its storage format in v16.
3669 : */
3670 174 : if (dopt->binary_upgrade)
3671 : {
3672 : PGresult *lo_res;
3673 74 : PQExpBuffer loFrozenQry = createPQExpBuffer();
3674 74 : PQExpBuffer loOutQry = createPQExpBuffer();
3675 74 : PQExpBuffer lomOutQry = createPQExpBuffer();
3676 74 : PQExpBuffer loHorizonQry = createPQExpBuffer();
3677 74 : PQExpBuffer lomHorizonQry = createPQExpBuffer();
3678 : int ii_relfrozenxid,
3679 : ii_relfilenode,
3680 : ii_oid,
3681 : ii_relminmxid;
3682 :
3683 74 : if (fout->remoteVersion >= 90300)
3684 74 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, relminmxid, relfilenode, oid\n"
3685 : "FROM pg_catalog.pg_class\n"
3686 : "WHERE oid IN (%u, %u, %u, %u);\n",
3687 : LargeObjectRelationId, LargeObjectLOidPNIndexId,
3688 : LargeObjectMetadataRelationId, LargeObjectMetadataOidIndexId);
3689 : else
3690 0 : appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid, 0 AS relminmxid, relfilenode, oid\n"
3691 : "FROM pg_catalog.pg_class\n"
3692 : "WHERE oid IN (%u, %u);\n",
3693 : LargeObjectRelationId, LargeObjectLOidPNIndexId);
3694 :
3695 74 : lo_res = ExecuteSqlQuery(fout, loFrozenQry->data, PGRES_TUPLES_OK);
3696 :
3697 74 : ii_relfrozenxid = PQfnumber(lo_res, "relfrozenxid");
3698 74 : ii_relminmxid = PQfnumber(lo_res, "relminmxid");
3699 74 : ii_relfilenode = PQfnumber(lo_res, "relfilenode");
3700 74 : ii_oid = PQfnumber(lo_res, "oid");
3701 :
3702 74 : appendPQExpBufferStr(loHorizonQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
3703 74 : appendPQExpBufferStr(lomHorizonQry, "\n-- For binary upgrade, set pg_largeobject_metadata relfrozenxid and relminmxid\n");
3704 74 : appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, preserve pg_largeobject and index relfilenodes\n");
3705 74 : appendPQExpBufferStr(lomOutQry, "\n-- For binary upgrade, preserve pg_largeobject_metadata and index relfilenodes\n");
3706 370 : for (int i = 0; i < PQntuples(lo_res); ++i)
3707 : {
3708 : Oid oid;
3709 : RelFileNumber relfilenumber;
3710 : PQExpBuffer horizonQry;
3711 : PQExpBuffer outQry;
3712 :
3713 296 : oid = atooid(PQgetvalue(lo_res, i, ii_oid));
3714 296 : relfilenumber = atooid(PQgetvalue(lo_res, i, ii_relfilenode));
3715 :
3716 296 : if (oid == LargeObjectRelationId ||
3717 : oid == LargeObjectLOidPNIndexId)
3718 : {
3719 148 : horizonQry = loHorizonQry;
3720 148 : outQry = loOutQry;
3721 : }
3722 : else
3723 : {
3724 148 : horizonQry = lomHorizonQry;
3725 148 : outQry = lomOutQry;
3726 : }
3727 :
3728 296 : appendPQExpBuffer(horizonQry, "UPDATE pg_catalog.pg_class\n"
3729 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
3730 : "WHERE oid = %u;\n",
3731 296 : atooid(PQgetvalue(lo_res, i, ii_relfrozenxid)),
3732 296 : atooid(PQgetvalue(lo_res, i, ii_relminmxid)),
3733 296 : atooid(PQgetvalue(lo_res, i, ii_oid)));
3734 :
3735 296 : if (oid == LargeObjectRelationId ||
3736 : oid == LargeObjectMetadataRelationId)
3737 148 : appendPQExpBuffer(outQry,
3738 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
3739 : relfilenumber);
3740 148 : else if (oid == LargeObjectLOidPNIndexId ||
3741 : oid == LargeObjectMetadataOidIndexId)
3742 148 : appendPQExpBuffer(outQry,
3743 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
3744 : relfilenumber);
3745 : }
3746 :
3747 74 : appendPQExpBufferStr(loOutQry,
3748 : "TRUNCATE pg_catalog.pg_largeobject;\n");
3749 74 : appendPQExpBufferStr(lomOutQry,
3750 : "TRUNCATE pg_catalog.pg_largeobject_metadata;\n");
3751 :
3752 74 : appendPQExpBufferStr(loOutQry, loHorizonQry->data);
3753 74 : appendPQExpBufferStr(lomOutQry, lomHorizonQry->data);
3754 :
3755 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3756 74 : ARCHIVE_OPTS(.tag = "pg_largeobject",
3757 : .description = "pg_largeobject",
3758 : .section = SECTION_PRE_DATA,
3759 : .createStmt = loOutQry->data));
3760 :
3761 74 : if (fout->remoteVersion >= 160000)
3762 74 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
3763 74 : ARCHIVE_OPTS(.tag = "pg_largeobject_metadata",
3764 : .description = "pg_largeobject_metadata",
3765 : .section = SECTION_PRE_DATA,
3766 : .createStmt = lomOutQry->data));
3767 :
3768 74 : PQclear(lo_res);
3769 :
3770 74 : destroyPQExpBuffer(loFrozenQry);
3771 74 : destroyPQExpBuffer(loHorizonQry);
3772 74 : destroyPQExpBuffer(lomHorizonQry);
3773 74 : destroyPQExpBuffer(loOutQry);
3774 74 : destroyPQExpBuffer(lomOutQry);
3775 : }
3776 :
3777 174 : PQclear(res);
3778 :
3779 174 : free(qdatname);
3780 174 : destroyPQExpBuffer(dbQry);
3781 174 : destroyPQExpBuffer(delQry);
3782 174 : destroyPQExpBuffer(creaQry);
3783 174 : destroyPQExpBuffer(labelq);
3784 174 : }
3785 :
3786 : /*
3787 : * Collect any database-specific or role-and-database-specific SET options
3788 : * for this database, and append them to outbuf.
3789 : */
3790 : static void
3791 174 : dumpDatabaseConfig(Archive *AH, PQExpBuffer outbuf,
3792 : const char *dbname, Oid dboid)
3793 : {
3794 174 : PGconn *conn = GetConnection(AH);
3795 174 : PQExpBuffer buf = createPQExpBuffer();
3796 : PGresult *res;
3797 :
3798 : /* First collect database-specific options */
3799 174 : printfPQExpBuffer(buf, "SELECT unnest(setconfig) FROM pg_db_role_setting "
3800 : "WHERE setrole = 0 AND setdatabase = '%u'::oid",
3801 : dboid);
3802 :
3803 174 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3804 :
3805 234 : for (int i = 0; i < PQntuples(res); i++)
3806 60 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 0),
3807 : "DATABASE", dbname, NULL, NULL,
3808 : outbuf);
3809 :
3810 174 : PQclear(res);
3811 :
3812 : /* Now look for role-and-database-specific options */
3813 174 : printfPQExpBuffer(buf, "SELECT rolname, unnest(setconfig) "
3814 : "FROM pg_db_role_setting s, pg_roles r "
3815 : "WHERE setrole = r.oid AND setdatabase = '%u'::oid",
3816 : dboid);
3817 :
3818 174 : res = ExecuteSqlQuery(AH, buf->data, PGRES_TUPLES_OK);
3819 :
3820 174 : for (int i = 0; i < PQntuples(res); i++)
3821 0 : makeAlterConfigCommand(conn, PQgetvalue(res, i, 1),
3822 0 : "ROLE", PQgetvalue(res, i, 0),
3823 : "DATABASE", dbname,
3824 : outbuf);
3825 :
3826 174 : PQclear(res);
3827 :
3828 174 : destroyPQExpBuffer(buf);
3829 174 : }
3830 :
3831 : /*
3832 : * dumpEncoding: put the correct encoding into the archive
3833 : */
3834 : static void
3835 376 : dumpEncoding(Archive *AH)
3836 : {
3837 376 : const char *encname = pg_encoding_to_char(AH->encoding);
3838 376 : PQExpBuffer qry = createPQExpBuffer();
3839 :
3840 376 : pg_log_info("saving encoding = %s", encname);
3841 :
3842 376 : appendPQExpBufferStr(qry, "SET client_encoding = ");
3843 376 : appendStringLiteralAH(qry, encname, AH);
3844 376 : appendPQExpBufferStr(qry, ";\n");
3845 :
3846 376 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3847 376 : ARCHIVE_OPTS(.tag = "ENCODING",
3848 : .description = "ENCODING",
3849 : .section = SECTION_PRE_DATA,
3850 : .createStmt = qry->data));
3851 :
3852 376 : destroyPQExpBuffer(qry);
3853 376 : }
3854 :
3855 :
3856 : /*
3857 : * dumpStdStrings: put the correct escape string behavior into the archive
3858 : */
3859 : static void
3860 376 : dumpStdStrings(Archive *AH)
3861 : {
3862 376 : const char *stdstrings = AH->std_strings ? "on" : "off";
3863 376 : PQExpBuffer qry = createPQExpBuffer();
3864 :
3865 376 : pg_log_info("saving \"standard_conforming_strings = %s\"",
3866 : stdstrings);
3867 :
3868 376 : appendPQExpBuffer(qry, "SET standard_conforming_strings = '%s';\n",
3869 : stdstrings);
3870 :
3871 376 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3872 376 : ARCHIVE_OPTS(.tag = "STDSTRINGS",
3873 : .description = "STDSTRINGS",
3874 : .section = SECTION_PRE_DATA,
3875 : .createStmt = qry->data));
3876 :
3877 376 : destroyPQExpBuffer(qry);
3878 376 : }
3879 :
3880 : /*
3881 : * dumpSearchPath: record the active search_path in the archive
3882 : */
3883 : static void
3884 376 : dumpSearchPath(Archive *AH)
3885 : {
3886 376 : PQExpBuffer qry = createPQExpBuffer();
3887 376 : PQExpBuffer path = createPQExpBuffer();
3888 : PGresult *res;
3889 376 : char **schemanames = NULL;
3890 376 : int nschemanames = 0;
3891 : int i;
3892 :
3893 : /*
3894 : * We use the result of current_schemas(), not the search_path GUC,
3895 : * because that might contain wildcards such as "$user", which won't
3896 : * necessarily have the same value during restore. Also, this way avoids
3897 : * listing schemas that may appear in search_path but not actually exist,
3898 : * which seems like a prudent exclusion.
3899 : */
3900 376 : res = ExecuteSqlQueryForSingleRow(AH,
3901 : "SELECT pg_catalog.current_schemas(false)");
3902 :
3903 376 : if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
3904 0 : pg_fatal("could not parse result of current_schemas()");
3905 :
3906 : /*
3907 : * We use set_config(), not a simple "SET search_path" command, because
3908 : * the latter has less-clean behavior if the search path is empty. While
3909 : * that's likely to get fixed at some point, it seems like a good idea to
3910 : * be as backwards-compatible as possible in what we put into archives.
3911 : */
3912 376 : for (i = 0; i < nschemanames; i++)
3913 : {
3914 0 : if (i > 0)
3915 0 : appendPQExpBufferStr(path, ", ");
3916 0 : appendPQExpBufferStr(path, fmtId(schemanames[i]));
3917 : }
3918 :
3919 376 : appendPQExpBufferStr(qry, "SELECT pg_catalog.set_config('search_path', ");
3920 376 : appendStringLiteralAH(qry, path->data, AH);
3921 376 : appendPQExpBufferStr(qry, ", false);\n");
3922 :
3923 376 : pg_log_info("saving \"search_path = %s\"", path->data);
3924 :
3925 376 : ArchiveEntry(AH, nilCatalogId, createDumpId(),
3926 376 : ARCHIVE_OPTS(.tag = "SEARCHPATH",
3927 : .description = "SEARCHPATH",
3928 : .section = SECTION_PRE_DATA,
3929 : .createStmt = qry->data));
3930 :
3931 : /* Also save it in AH->searchpath, in case we're doing plain text dump */
3932 376 : AH->searchpath = pg_strdup(qry->data);
3933 :
3934 376 : free(schemanames);
3935 376 : PQclear(res);
3936 376 : destroyPQExpBuffer(qry);
3937 376 : destroyPQExpBuffer(path);
3938 376 : }
3939 :
3940 :
3941 : /*
3942 : * getLOs:
3943 : * Collect schema-level data about large objects
3944 : */
3945 : static void
3946 320 : getLOs(Archive *fout)
3947 : {
3948 320 : DumpOptions *dopt = fout->dopt;
3949 320 : PQExpBuffer loQry = createPQExpBuffer();
3950 : PGresult *res;
3951 : int ntups;
3952 : int i;
3953 : int n;
3954 : int i_oid;
3955 : int i_lomowner;
3956 : int i_lomacl;
3957 : int i_acldefault;
3958 :
3959 320 : pg_log_info("reading large objects");
3960 :
3961 : /*
3962 : * Fetch LO OIDs and owner/ACL data. Order the data so that all the blobs
3963 : * with the same owner/ACL appear together.
3964 : */
3965 320 : appendPQExpBufferStr(loQry,
3966 : "SELECT oid, lomowner, lomacl, "
3967 : "acldefault('L', lomowner) AS acldefault "
3968 : "FROM pg_largeobject_metadata "
3969 : "ORDER BY lomowner, lomacl::pg_catalog.text, oid");
3970 :
3971 320 : res = ExecuteSqlQuery(fout, loQry->data, PGRES_TUPLES_OK);
3972 :
3973 320 : i_oid = PQfnumber(res, "oid");
3974 320 : i_lomowner = PQfnumber(res, "lomowner");
3975 320 : i_lomacl = PQfnumber(res, "lomacl");
3976 320 : i_acldefault = PQfnumber(res, "acldefault");
3977 :
3978 320 : ntups = PQntuples(res);
3979 :
3980 : /*
3981 : * Group the blobs into suitably-sized groups that have the same owner and
3982 : * ACL setting, and build a metadata and a data DumpableObject for each
3983 : * group. (If we supported initprivs for blobs, we'd have to insist that
3984 : * groups also share initprivs settings, since the DumpableObject only has
3985 : * room for one.) i is the index of the first tuple in the current group,
3986 : * and n is the number of tuples we include in the group.
3987 : */
3988 500 : for (i = 0; i < ntups; i += n)
3989 : {
3990 180 : Oid thisoid = atooid(PQgetvalue(res, i, i_oid));
3991 180 : char *thisowner = PQgetvalue(res, i, i_lomowner);
3992 180 : char *thisacl = PQgetvalue(res, i, i_lomacl);
3993 : LoInfo *loinfo;
3994 : DumpableObject *lodata;
3995 : char namebuf[64];
3996 :
3997 : /* Scan to find first tuple not to be included in group */
3998 180 : n = 1;
3999 210 : while (n < MAX_BLOBS_PER_ARCHIVE_ENTRY && i + n < ntups)
4000 : {
4001 108 : if (strcmp(thisowner, PQgetvalue(res, i + n, i_lomowner)) != 0 ||
4002 98 : strcmp(thisacl, PQgetvalue(res, i + n, i_lomacl)) != 0)
4003 : break;
4004 30 : n++;
4005 : }
4006 :
4007 : /* Build the metadata DumpableObject */
4008 180 : loinfo = (LoInfo *) pg_malloc(offsetof(LoInfo, looids) + n * sizeof(Oid));
4009 :
4010 180 : loinfo->dobj.objType = DO_LARGE_OBJECT;
4011 180 : loinfo->dobj.catId.tableoid = LargeObjectRelationId;
4012 180 : loinfo->dobj.catId.oid = thisoid;
4013 180 : AssignDumpId(&loinfo->dobj);
4014 :
4015 180 : if (n > 1)
4016 20 : snprintf(namebuf, sizeof(namebuf), "%u..%u", thisoid,
4017 20 : atooid(PQgetvalue(res, i + n - 1, i_oid)));
4018 : else
4019 160 : snprintf(namebuf, sizeof(namebuf), "%u", thisoid);
4020 180 : loinfo->dobj.name = pg_strdup(namebuf);
4021 180 : loinfo->dacl.acl = pg_strdup(thisacl);
4022 180 : loinfo->dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
4023 180 : loinfo->dacl.privtype = 0;
4024 180 : loinfo->dacl.initprivs = NULL;
4025 180 : loinfo->rolname = getRoleName(thisowner);
4026 180 : loinfo->numlos = n;
4027 180 : loinfo->looids[0] = thisoid;
4028 : /* Collect OIDs of the remaining blobs in this group */
4029 210 : for (int k = 1; k < n; k++)
4030 : {
4031 : CatalogId extraID;
4032 :
4033 30 : loinfo->looids[k] = atooid(PQgetvalue(res, i + k, i_oid));
4034 :
4035 : /* Make sure we can look up loinfo by any of the blobs' OIDs */
4036 30 : extraID.tableoid = LargeObjectRelationId;
4037 30 : extraID.oid = loinfo->looids[k];
4038 30 : recordAdditionalCatalogID(extraID, &loinfo->dobj);
4039 : }
4040 :
4041 : /* LOs have data */
4042 180 : loinfo->dobj.components |= DUMP_COMPONENT_DATA;
4043 :
4044 : /* Mark whether LO group has a non-empty ACL */
4045 180 : if (!PQgetisnull(res, i, i_lomacl))
4046 78 : loinfo->dobj.components |= DUMP_COMPONENT_ACL;
4047 :
4048 : /*
4049 : * In binary-upgrade mode for LOs, we do *not* dump out the LO data,
4050 : * as it will be copied by pg_upgrade, which simply copies the
4051 : * pg_largeobject table. We *do* however dump out anything but the
4052 : * data, as pg_upgrade copies just pg_largeobject, but not
4053 : * pg_largeobject_metadata, after the dump is restored. In versions
4054 : * before v12, this is done via proper large object commands. In
4055 : * newer versions, we dump the content of pg_largeobject_metadata and
4056 : * any associated pg_shdepend rows, which is faster to restore. (On
4057 : * <v12, pg_largeobject_metadata was created WITH OIDS, so the OID
4058 : * column is hidden and won't be dumped.)
4059 : */
4060 180 : if (dopt->binary_upgrade)
4061 : {
4062 26 : if (fout->remoteVersion >= 120000)
4063 : {
4064 : /*
4065 : * We should've saved pg_largeobject_metadata's dump ID before
4066 : * this point.
4067 : */
4068 : Assert(lo_metadata_dumpId);
4069 :
4070 26 : loinfo->dobj.dump &= ~(DUMP_COMPONENT_DATA | DUMP_COMPONENT_ACL | DUMP_COMPONENT_DEFINITION);
4071 :
4072 : /*
4073 : * Mark the large object as dependent on
4074 : * pg_largeobject_metadata so that any large object
4075 : * comments/seclables are dumped after it.
4076 : */
4077 26 : loinfo->dobj.dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4078 26 : loinfo->dobj.dependencies[0] = lo_metadata_dumpId;
4079 26 : loinfo->dobj.nDeps = loinfo->dobj.allocDeps = 1;
4080 : }
4081 : else
4082 0 : loinfo->dobj.dump &= ~DUMP_COMPONENT_DATA;
4083 : }
4084 :
4085 : /*
4086 : * Create a "BLOBS" data item for the group, too. This is just a
4087 : * placeholder for sorting; it carries no data now.
4088 : */
4089 180 : lodata = (DumpableObject *) pg_malloc(sizeof(DumpableObject));
4090 180 : lodata->objType = DO_LARGE_OBJECT_DATA;
4091 180 : lodata->catId = nilCatalogId;
4092 180 : AssignDumpId(lodata);
4093 180 : lodata->name = pg_strdup(namebuf);
4094 180 : lodata->components |= DUMP_COMPONENT_DATA;
4095 : /* Set up explicit dependency from data to metadata */
4096 180 : lodata->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
4097 180 : lodata->dependencies[0] = loinfo->dobj.dumpId;
4098 180 : lodata->nDeps = lodata->allocDeps = 1;
4099 : }
4100 :
4101 320 : PQclear(res);
4102 320 : destroyPQExpBuffer(loQry);
4103 320 : }
4104 :
4105 : /*
4106 : * dumpLO
4107 : *
4108 : * dump the definition (metadata) of the given large object group
4109 : */
4110 : static void
4111 168 : dumpLO(Archive *fout, const LoInfo *loinfo)
4112 : {
4113 168 : PQExpBuffer cquery = createPQExpBuffer();
4114 :
4115 : /*
4116 : * The "definition" is just a newline-separated list of OIDs. We need to
4117 : * put something into the dropStmt too, but it can just be a comment.
4118 : */
4119 366 : for (int i = 0; i < loinfo->numlos; i++)
4120 198 : appendPQExpBuffer(cquery, "%u\n", loinfo->looids[i]);
4121 :
4122 168 : if (loinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4123 154 : ArchiveEntry(fout, loinfo->dobj.catId, loinfo->dobj.dumpId,
4124 154 : ARCHIVE_OPTS(.tag = loinfo->dobj.name,
4125 : .owner = loinfo->rolname,
4126 : .description = "BLOB METADATA",
4127 : .section = SECTION_DATA,
4128 : .createStmt = cquery->data,
4129 : .dropStmt = "-- dummy"));
4130 :
4131 : /*
4132 : * Dump per-blob comments and seclabels if any. We assume these are rare
4133 : * enough that it's okay to generate retail TOC entries for them.
4134 : */
4135 168 : if (loinfo->dobj.dump & (DUMP_COMPONENT_COMMENT |
4136 : DUMP_COMPONENT_SECLABEL))
4137 : {
4138 206 : for (int i = 0; i < loinfo->numlos; i++)
4139 : {
4140 : CatalogId catId;
4141 : char namebuf[32];
4142 :
4143 : /* Build identifying info for this blob */
4144 118 : catId.tableoid = loinfo->dobj.catId.tableoid;
4145 118 : catId.oid = loinfo->looids[i];
4146 118 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[i]);
4147 :
4148 118 : if (loinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4149 118 : dumpComment(fout, "LARGE OBJECT", namebuf,
4150 118 : NULL, loinfo->rolname,
4151 118 : catId, 0, loinfo->dobj.dumpId);
4152 :
4153 118 : if (loinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4154 20 : dumpSecLabel(fout, "LARGE OBJECT", namebuf,
4155 20 : NULL, loinfo->rolname,
4156 20 : catId, 0, loinfo->dobj.dumpId);
4157 : }
4158 : }
4159 :
4160 : /*
4161 : * Dump the ACLs if any (remember that all blobs in the group will have
4162 : * the same ACL). If there's just one blob, dump a simple ACL entry; if
4163 : * there's more, make a "LARGE OBJECTS" entry that really contains only
4164 : * the ACL for the first blob. _printTocEntry() will be cued by the tag
4165 : * string to emit a mutated version for each blob.
4166 : */
4167 168 : if (loinfo->dobj.dump & DUMP_COMPONENT_ACL)
4168 : {
4169 : char namebuf[32];
4170 :
4171 : /* Build identifying info for the first blob */
4172 66 : snprintf(namebuf, sizeof(namebuf), "%u", loinfo->looids[0]);
4173 :
4174 66 : if (loinfo->numlos > 1)
4175 : {
4176 : char tagbuf[64];
4177 :
4178 0 : snprintf(tagbuf, sizeof(tagbuf), "LARGE OBJECTS %u..%u",
4179 0 : loinfo->looids[0], loinfo->looids[loinfo->numlos - 1]);
4180 :
4181 0 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4182 : "LARGE OBJECT", namebuf, NULL, NULL,
4183 0 : tagbuf, loinfo->rolname, &loinfo->dacl);
4184 : }
4185 : else
4186 : {
4187 66 : dumpACL(fout, loinfo->dobj.dumpId, InvalidDumpId,
4188 : "LARGE OBJECT", namebuf, NULL, NULL,
4189 66 : NULL, loinfo->rolname, &loinfo->dacl);
4190 : }
4191 : }
4192 :
4193 168 : destroyPQExpBuffer(cquery);
4194 168 : }
4195 :
4196 : /*
4197 : * dumpLOs:
4198 : * dump the data contents of the large objects in the given group
4199 : */
4200 : static int
4201 146 : dumpLOs(Archive *fout, const void *arg)
4202 : {
4203 146 : const LoInfo *loinfo = (const LoInfo *) arg;
4204 146 : PGconn *conn = GetConnection(fout);
4205 : char buf[LOBBUFSIZE];
4206 :
4207 146 : pg_log_info("saving large objects \"%s\"", loinfo->dobj.name);
4208 :
4209 308 : for (int i = 0; i < loinfo->numlos; i++)
4210 : {
4211 162 : Oid loOid = loinfo->looids[i];
4212 : int loFd;
4213 : int cnt;
4214 :
4215 : /* Open the LO */
4216 162 : loFd = lo_open(conn, loOid, INV_READ);
4217 162 : if (loFd == -1)
4218 0 : pg_fatal("could not open large object %u: %s",
4219 : loOid, PQerrorMessage(conn));
4220 :
4221 162 : StartLO(fout, loOid);
4222 :
4223 : /* Now read it in chunks, sending data to archive */
4224 : do
4225 : {
4226 254 : cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
4227 254 : if (cnt < 0)
4228 0 : pg_fatal("error reading large object %u: %s",
4229 : loOid, PQerrorMessage(conn));
4230 :
4231 254 : WriteData(fout, buf, cnt);
4232 254 : } while (cnt > 0);
4233 :
4234 162 : lo_close(conn, loFd);
4235 :
4236 162 : EndLO(fout, loOid);
4237 : }
4238 :
4239 146 : return 1;
4240 : }
4241 :
4242 : /*
4243 : * getPolicies
4244 : * get information about all RLS policies on dumpable tables.
4245 : */
4246 : void
4247 376 : getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
4248 : {
4249 376 : DumpOptions *dopt = fout->dopt;
4250 : PQExpBuffer query;
4251 : PQExpBuffer tbloids;
4252 : PGresult *res;
4253 : PolicyInfo *polinfo;
4254 : int i_oid;
4255 : int i_tableoid;
4256 : int i_polrelid;
4257 : int i_polname;
4258 : int i_polcmd;
4259 : int i_polpermissive;
4260 : int i_polroles;
4261 : int i_polqual;
4262 : int i_polwithcheck;
4263 : int i,
4264 : j,
4265 : ntups;
4266 :
4267 : /* No policies before 9.5 */
4268 376 : if (fout->remoteVersion < 90500)
4269 0 : return;
4270 :
4271 : /* Skip if --no-policies was specified */
4272 376 : if (dopt->no_policies)
4273 2 : return;
4274 :
4275 374 : query = createPQExpBuffer();
4276 374 : tbloids = createPQExpBuffer();
4277 :
4278 : /*
4279 : * Identify tables of interest, and check which ones have RLS enabled.
4280 : */
4281 374 : appendPQExpBufferChar(tbloids, '{');
4282 98716 : for (i = 0; i < numTables; i++)
4283 : {
4284 98342 : TableInfo *tbinfo = &tblinfo[i];
4285 :
4286 : /* Ignore row security on tables not to be dumped */
4287 98342 : if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
4288 84594 : continue;
4289 :
4290 : /* It can't have RLS or policies if it's not a table */
4291 13748 : if (tbinfo->relkind != RELKIND_RELATION &&
4292 3892 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE)
4293 2700 : continue;
4294 :
4295 : /* Add it to the list of table OIDs to be probed below */
4296 11048 : if (tbloids->len > 1) /* do we have more than the '{'? */
4297 10804 : appendPQExpBufferChar(tbloids, ',');
4298 11048 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
4299 :
4300 : /* Is RLS enabled? (That's separate from whether it has policies) */
4301 11048 : if (tbinfo->rowsec)
4302 : {
4303 106 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4304 :
4305 : /*
4306 : * We represent RLS being enabled on a table by creating a
4307 : * PolicyInfo object with null polname.
4308 : *
4309 : * Note: use tableoid 0 so that this object won't be mistaken for
4310 : * something that pg_depend entries apply to.
4311 : */
4312 106 : polinfo = pg_malloc(sizeof(PolicyInfo));
4313 106 : polinfo->dobj.objType = DO_POLICY;
4314 106 : polinfo->dobj.catId.tableoid = 0;
4315 106 : polinfo->dobj.catId.oid = tbinfo->dobj.catId.oid;
4316 106 : AssignDumpId(&polinfo->dobj);
4317 106 : polinfo->dobj.namespace = tbinfo->dobj.namespace;
4318 106 : polinfo->dobj.name = pg_strdup(tbinfo->dobj.name);
4319 106 : polinfo->poltable = tbinfo;
4320 106 : polinfo->polname = NULL;
4321 106 : polinfo->polcmd = '\0';
4322 106 : polinfo->polpermissive = 0;
4323 106 : polinfo->polroles = NULL;
4324 106 : polinfo->polqual = NULL;
4325 106 : polinfo->polwithcheck = NULL;
4326 : }
4327 : }
4328 374 : appendPQExpBufferChar(tbloids, '}');
4329 :
4330 : /*
4331 : * Now, read all RLS policies belonging to the tables of interest, and
4332 : * create PolicyInfo objects for them. (Note that we must filter the
4333 : * results server-side not locally, because we dare not apply pg_get_expr
4334 : * to tables we don't have lock on.)
4335 : */
4336 374 : pg_log_info("reading row-level security policies");
4337 :
4338 374 : printfPQExpBuffer(query,
4339 : "SELECT pol.oid, pol.tableoid, pol.polrelid, pol.polname, pol.polcmd, ");
4340 374 : if (fout->remoteVersion >= 100000)
4341 374 : appendPQExpBufferStr(query, "pol.polpermissive, ");
4342 : else
4343 0 : appendPQExpBufferStr(query, "'t' as polpermissive, ");
4344 374 : appendPQExpBuffer(query,
4345 : "CASE WHEN pol.polroles = '{0}' THEN NULL ELSE "
4346 : " 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, "
4347 : "pg_catalog.pg_get_expr(pol.polqual, pol.polrelid) AS polqual, "
4348 : "pg_catalog.pg_get_expr(pol.polwithcheck, pol.polrelid) AS polwithcheck "
4349 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
4350 : "JOIN pg_catalog.pg_policy pol ON (src.tbloid = pol.polrelid)",
4351 : tbloids->data);
4352 :
4353 374 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4354 :
4355 374 : ntups = PQntuples(res);
4356 374 : if (ntups > 0)
4357 : {
4358 86 : i_oid = PQfnumber(res, "oid");
4359 86 : i_tableoid = PQfnumber(res, "tableoid");
4360 86 : i_polrelid = PQfnumber(res, "polrelid");
4361 86 : i_polname = PQfnumber(res, "polname");
4362 86 : i_polcmd = PQfnumber(res, "polcmd");
4363 86 : i_polpermissive = PQfnumber(res, "polpermissive");
4364 86 : i_polroles = PQfnumber(res, "polroles");
4365 86 : i_polqual = PQfnumber(res, "polqual");
4366 86 : i_polwithcheck = PQfnumber(res, "polwithcheck");
4367 :
4368 86 : polinfo = pg_malloc(ntups * sizeof(PolicyInfo));
4369 :
4370 632 : for (j = 0; j < ntups; j++)
4371 : {
4372 546 : Oid polrelid = atooid(PQgetvalue(res, j, i_polrelid));
4373 546 : TableInfo *tbinfo = findTableByOid(polrelid);
4374 :
4375 546 : tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
4376 :
4377 546 : polinfo[j].dobj.objType = DO_POLICY;
4378 546 : polinfo[j].dobj.catId.tableoid =
4379 546 : atooid(PQgetvalue(res, j, i_tableoid));
4380 546 : polinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
4381 546 : AssignDumpId(&polinfo[j].dobj);
4382 546 : polinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4383 546 : polinfo[j].poltable = tbinfo;
4384 546 : polinfo[j].polname = pg_strdup(PQgetvalue(res, j, i_polname));
4385 546 : polinfo[j].dobj.name = pg_strdup(polinfo[j].polname);
4386 :
4387 546 : polinfo[j].polcmd = *(PQgetvalue(res, j, i_polcmd));
4388 546 : polinfo[j].polpermissive = *(PQgetvalue(res, j, i_polpermissive)) == 't';
4389 :
4390 546 : if (PQgetisnull(res, j, i_polroles))
4391 242 : polinfo[j].polroles = NULL;
4392 : else
4393 304 : polinfo[j].polroles = pg_strdup(PQgetvalue(res, j, i_polroles));
4394 :
4395 546 : if (PQgetisnull(res, j, i_polqual))
4396 76 : polinfo[j].polqual = NULL;
4397 : else
4398 470 : polinfo[j].polqual = pg_strdup(PQgetvalue(res, j, i_polqual));
4399 :
4400 546 : if (PQgetisnull(res, j, i_polwithcheck))
4401 288 : polinfo[j].polwithcheck = NULL;
4402 : else
4403 258 : polinfo[j].polwithcheck
4404 258 : = pg_strdup(PQgetvalue(res, j, i_polwithcheck));
4405 : }
4406 : }
4407 :
4408 374 : PQclear(res);
4409 :
4410 374 : destroyPQExpBuffer(query);
4411 374 : destroyPQExpBuffer(tbloids);
4412 : }
4413 :
4414 : /*
4415 : * dumpPolicy
4416 : * dump the definition of the given policy
4417 : */
4418 : static void
4419 652 : dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
4420 : {
4421 652 : DumpOptions *dopt = fout->dopt;
4422 652 : TableInfo *tbinfo = polinfo->poltable;
4423 : PQExpBuffer query;
4424 : PQExpBuffer delqry;
4425 : PQExpBuffer polprefix;
4426 : char *qtabname;
4427 : const char *cmd;
4428 : char *tag;
4429 :
4430 : /* Do nothing if not dumping schema */
4431 652 : if (!dopt->dumpSchema)
4432 98 : return;
4433 :
4434 : /*
4435 : * If polname is NULL, then this record is just indicating that ROW LEVEL
4436 : * SECURITY is enabled for the table. Dump as ALTER TABLE <table> ENABLE
4437 : * ROW LEVEL SECURITY.
4438 : */
4439 554 : if (polinfo->polname == NULL)
4440 : {
4441 92 : query = createPQExpBuffer();
4442 :
4443 92 : appendPQExpBuffer(query, "ALTER TABLE %s ENABLE ROW LEVEL SECURITY;",
4444 92 : fmtQualifiedDumpable(tbinfo));
4445 :
4446 : /*
4447 : * We must emit the ROW SECURITY object's dependency on its table
4448 : * explicitly, because it will not match anything in pg_depend (unlike
4449 : * the case for other PolicyInfo objects).
4450 : */
4451 92 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4452 92 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4453 92 : ARCHIVE_OPTS(.tag = polinfo->dobj.name,
4454 : .namespace = polinfo->dobj.namespace->dobj.name,
4455 : .owner = tbinfo->rolname,
4456 : .description = "ROW SECURITY",
4457 : .section = SECTION_POST_DATA,
4458 : .createStmt = query->data,
4459 : .deps = &(tbinfo->dobj.dumpId),
4460 : .nDeps = 1));
4461 :
4462 92 : destroyPQExpBuffer(query);
4463 92 : return;
4464 : }
4465 :
4466 462 : if (polinfo->polcmd == '*')
4467 154 : cmd = "";
4468 308 : else if (polinfo->polcmd == 'r')
4469 82 : cmd = " FOR SELECT";
4470 226 : else if (polinfo->polcmd == 'a')
4471 62 : cmd = " FOR INSERT";
4472 164 : else if (polinfo->polcmd == 'w')
4473 82 : cmd = " FOR UPDATE";
4474 82 : else if (polinfo->polcmd == 'd')
4475 82 : cmd = " FOR DELETE";
4476 : else
4477 0 : pg_fatal("unexpected policy command type: %c",
4478 : polinfo->polcmd);
4479 :
4480 462 : query = createPQExpBuffer();
4481 462 : delqry = createPQExpBuffer();
4482 462 : polprefix = createPQExpBuffer();
4483 :
4484 462 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
4485 :
4486 462 : appendPQExpBuffer(query, "CREATE POLICY %s", fmtId(polinfo->polname));
4487 :
4488 462 : appendPQExpBuffer(query, " ON %s%s%s", fmtQualifiedDumpable(tbinfo),
4489 462 : !polinfo->polpermissive ? " AS RESTRICTIVE" : "", cmd);
4490 :
4491 462 : if (polinfo->polroles != NULL)
4492 248 : appendPQExpBuffer(query, " TO %s", polinfo->polroles);
4493 :
4494 462 : if (polinfo->polqual != NULL)
4495 400 : appendPQExpBuffer(query, " USING (%s)", polinfo->polqual);
4496 :
4497 462 : if (polinfo->polwithcheck != NULL)
4498 216 : appendPQExpBuffer(query, " WITH CHECK (%s)", polinfo->polwithcheck);
4499 :
4500 462 : appendPQExpBufferStr(query, ";\n");
4501 :
4502 462 : appendPQExpBuffer(delqry, "DROP POLICY %s", fmtId(polinfo->polname));
4503 462 : appendPQExpBuffer(delqry, " ON %s;\n", fmtQualifiedDumpable(tbinfo));
4504 :
4505 462 : appendPQExpBuffer(polprefix, "POLICY %s ON",
4506 462 : fmtId(polinfo->polname));
4507 :
4508 462 : tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);
4509 :
4510 462 : if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4511 462 : ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
4512 462 : ARCHIVE_OPTS(.tag = tag,
4513 : .namespace = polinfo->dobj.namespace->dobj.name,
4514 : .owner = tbinfo->rolname,
4515 : .description = "POLICY",
4516 : .section = SECTION_POST_DATA,
4517 : .createStmt = query->data,
4518 : .dropStmt = delqry->data));
4519 :
4520 462 : if (polinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4521 62 : dumpComment(fout, polprefix->data, qtabname,
4522 62 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
4523 62 : polinfo->dobj.catId, 0, polinfo->dobj.dumpId);
4524 :
4525 462 : free(tag);
4526 462 : destroyPQExpBuffer(query);
4527 462 : destroyPQExpBuffer(delqry);
4528 462 : destroyPQExpBuffer(polprefix);
4529 462 : free(qtabname);
4530 : }
4531 :
4532 : /*
4533 : * getPublications
4534 : * get information about publications
4535 : */
4536 : void
4537 376 : getPublications(Archive *fout)
4538 : {
4539 376 : DumpOptions *dopt = fout->dopt;
4540 : PQExpBuffer query;
4541 : PGresult *res;
4542 : PublicationInfo *pubinfo;
4543 : int i_tableoid;
4544 : int i_oid;
4545 : int i_pubname;
4546 : int i_pubowner;
4547 : int i_puballtables;
4548 : int i_puballsequences;
4549 : int i_pubinsert;
4550 : int i_pubupdate;
4551 : int i_pubdelete;
4552 : int i_pubtruncate;
4553 : int i_pubviaroot;
4554 : int i_pubgencols;
4555 : int i,
4556 : ntups;
4557 :
4558 376 : if (dopt->no_publications || fout->remoteVersion < 100000)
4559 0 : return;
4560 :
4561 376 : query = createPQExpBuffer();
4562 :
4563 : /* Get the publications. */
4564 376 : appendPQExpBufferStr(query, "SELECT p.tableoid, p.oid, p.pubname, "
4565 : "p.pubowner, p.puballtables, p.pubinsert, "
4566 : "p.pubupdate, p.pubdelete, ");
4567 :
4568 376 : if (fout->remoteVersion >= 110000)
4569 376 : appendPQExpBufferStr(query, "p.pubtruncate, ");
4570 : else
4571 0 : appendPQExpBufferStr(query, "false AS pubtruncate, ");
4572 :
4573 376 : if (fout->remoteVersion >= 130000)
4574 376 : appendPQExpBufferStr(query, "p.pubviaroot, ");
4575 : else
4576 0 : appendPQExpBufferStr(query, "false AS pubviaroot, ");
4577 :
4578 376 : if (fout->remoteVersion >= 180000)
4579 376 : appendPQExpBufferStr(query, "p.pubgencols, ");
4580 : else
4581 0 : appendPQExpBuffer(query, "'%c' AS pubgencols, ", PUBLISH_GENCOLS_NONE);
4582 :
4583 376 : if (fout->remoteVersion >= 190000)
4584 376 : appendPQExpBufferStr(query, "p.puballsequences ");
4585 : else
4586 0 : appendPQExpBufferStr(query, "false AS puballsequences ");
4587 :
4588 376 : appendPQExpBufferStr(query, "FROM pg_publication p");
4589 :
4590 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4591 :
4592 376 : ntups = PQntuples(res);
4593 :
4594 376 : if (ntups == 0)
4595 270 : goto cleanup;
4596 :
4597 106 : i_tableoid = PQfnumber(res, "tableoid");
4598 106 : i_oid = PQfnumber(res, "oid");
4599 106 : i_pubname = PQfnumber(res, "pubname");
4600 106 : i_pubowner = PQfnumber(res, "pubowner");
4601 106 : i_puballtables = PQfnumber(res, "puballtables");
4602 106 : i_puballsequences = PQfnumber(res, "puballsequences");
4603 106 : i_pubinsert = PQfnumber(res, "pubinsert");
4604 106 : i_pubupdate = PQfnumber(res, "pubupdate");
4605 106 : i_pubdelete = PQfnumber(res, "pubdelete");
4606 106 : i_pubtruncate = PQfnumber(res, "pubtruncate");
4607 106 : i_pubviaroot = PQfnumber(res, "pubviaroot");
4608 106 : i_pubgencols = PQfnumber(res, "pubgencols");
4609 :
4610 106 : pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
4611 :
4612 808 : for (i = 0; i < ntups; i++)
4613 : {
4614 702 : pubinfo[i].dobj.objType = DO_PUBLICATION;
4615 702 : pubinfo[i].dobj.catId.tableoid =
4616 702 : atooid(PQgetvalue(res, i, i_tableoid));
4617 702 : pubinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4618 702 : AssignDumpId(&pubinfo[i].dobj);
4619 702 : pubinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_pubname));
4620 702 : pubinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_pubowner));
4621 702 : pubinfo[i].puballtables =
4622 702 : (strcmp(PQgetvalue(res, i, i_puballtables), "t") == 0);
4623 702 : pubinfo[i].puballsequences =
4624 702 : (strcmp(PQgetvalue(res, i, i_puballsequences), "t") == 0);
4625 702 : pubinfo[i].pubinsert =
4626 702 : (strcmp(PQgetvalue(res, i, i_pubinsert), "t") == 0);
4627 702 : pubinfo[i].pubupdate =
4628 702 : (strcmp(PQgetvalue(res, i, i_pubupdate), "t") == 0);
4629 702 : pubinfo[i].pubdelete =
4630 702 : (strcmp(PQgetvalue(res, i, i_pubdelete), "t") == 0);
4631 702 : pubinfo[i].pubtruncate =
4632 702 : (strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
4633 702 : pubinfo[i].pubviaroot =
4634 702 : (strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
4635 702 : pubinfo[i].pubgencols_type =
4636 702 : *(PQgetvalue(res, i, i_pubgencols));
4637 :
4638 : /* Decide whether we want to dump it */
4639 702 : selectDumpableObject(&(pubinfo[i].dobj), fout);
4640 : }
4641 :
4642 106 : cleanup:
4643 376 : PQclear(res);
4644 :
4645 376 : destroyPQExpBuffer(query);
4646 : }
4647 :
4648 : /*
4649 : * dumpPublication
4650 : * dump the definition of the given publication
4651 : */
4652 : static void
4653 570 : dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
4654 : {
4655 570 : DumpOptions *dopt = fout->dopt;
4656 : PQExpBuffer delq;
4657 : PQExpBuffer query;
4658 : char *qpubname;
4659 570 : bool first = true;
4660 :
4661 : /* Do nothing if not dumping schema */
4662 570 : if (!dopt->dumpSchema)
4663 84 : return;
4664 :
4665 486 : delq = createPQExpBuffer();
4666 486 : query = createPQExpBuffer();
4667 :
4668 486 : qpubname = pg_strdup(fmtId(pubinfo->dobj.name));
4669 :
4670 486 : appendPQExpBuffer(delq, "DROP PUBLICATION %s;\n",
4671 : qpubname);
4672 :
4673 486 : appendPQExpBuffer(query, "CREATE PUBLICATION %s",
4674 : qpubname);
4675 :
4676 486 : if (pubinfo->puballtables && pubinfo->puballsequences)
4677 62 : appendPQExpBufferStr(query, " FOR ALL TABLES, ALL SEQUENCES");
4678 424 : else if (pubinfo->puballtables)
4679 64 : appendPQExpBufferStr(query, " FOR ALL TABLES");
4680 360 : else if (pubinfo->puballsequences)
4681 62 : appendPQExpBufferStr(query, " FOR ALL SEQUENCES");
4682 :
4683 486 : appendPQExpBufferStr(query, " WITH (publish = '");
4684 486 : if (pubinfo->pubinsert)
4685 : {
4686 362 : appendPQExpBufferStr(query, "insert");
4687 362 : first = false;
4688 : }
4689 :
4690 486 : if (pubinfo->pubupdate)
4691 : {
4692 362 : if (!first)
4693 362 : appendPQExpBufferStr(query, ", ");
4694 :
4695 362 : appendPQExpBufferStr(query, "update");
4696 362 : first = false;
4697 : }
4698 :
4699 486 : if (pubinfo->pubdelete)
4700 : {
4701 362 : if (!first)
4702 362 : appendPQExpBufferStr(query, ", ");
4703 :
4704 362 : appendPQExpBufferStr(query, "delete");
4705 362 : first = false;
4706 : }
4707 :
4708 486 : if (pubinfo->pubtruncate)
4709 : {
4710 362 : if (!first)
4711 362 : appendPQExpBufferStr(query, ", ");
4712 :
4713 362 : appendPQExpBufferStr(query, "truncate");
4714 362 : first = false;
4715 : }
4716 :
4717 486 : appendPQExpBufferChar(query, '\'');
4718 :
4719 486 : if (pubinfo->pubviaroot)
4720 10 : appendPQExpBufferStr(query, ", publish_via_partition_root = true");
4721 :
4722 486 : if (pubinfo->pubgencols_type == PUBLISH_GENCOLS_STORED)
4723 62 : appendPQExpBufferStr(query, ", publish_generated_columns = stored");
4724 :
4725 486 : appendPQExpBufferStr(query, ");\n");
4726 :
4727 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4728 486 : ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
4729 486 : ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
4730 : .owner = pubinfo->rolname,
4731 : .description = "PUBLICATION",
4732 : .section = SECTION_POST_DATA,
4733 : .createStmt = query->data,
4734 : .dropStmt = delq->data));
4735 :
4736 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
4737 62 : dumpComment(fout, "PUBLICATION", qpubname,
4738 62 : NULL, pubinfo->rolname,
4739 62 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4740 :
4741 486 : if (pubinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
4742 0 : dumpSecLabel(fout, "PUBLICATION", qpubname,
4743 0 : NULL, pubinfo->rolname,
4744 0 : pubinfo->dobj.catId, 0, pubinfo->dobj.dumpId);
4745 :
4746 486 : destroyPQExpBuffer(delq);
4747 486 : destroyPQExpBuffer(query);
4748 486 : free(qpubname);
4749 : }
4750 :
4751 : /*
4752 : * getPublicationNamespaces
4753 : * get information about publication membership for dumpable schemas.
4754 : */
4755 : void
4756 376 : getPublicationNamespaces(Archive *fout)
4757 : {
4758 : PQExpBuffer query;
4759 : PGresult *res;
4760 : PublicationSchemaInfo *pubsinfo;
4761 376 : DumpOptions *dopt = fout->dopt;
4762 : int i_tableoid;
4763 : int i_oid;
4764 : int i_pnpubid;
4765 : int i_pnnspid;
4766 : int i,
4767 : j,
4768 : ntups;
4769 :
4770 376 : if (dopt->no_publications || fout->remoteVersion < 150000)
4771 0 : return;
4772 :
4773 376 : query = createPQExpBuffer();
4774 :
4775 : /* Collect all publication membership info. */
4776 376 : appendPQExpBufferStr(query,
4777 : "SELECT tableoid, oid, pnpubid, pnnspid "
4778 : "FROM pg_catalog.pg_publication_namespace");
4779 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4780 :
4781 376 : ntups = PQntuples(res);
4782 :
4783 376 : i_tableoid = PQfnumber(res, "tableoid");
4784 376 : i_oid = PQfnumber(res, "oid");
4785 376 : i_pnpubid = PQfnumber(res, "pnpubid");
4786 376 : i_pnnspid = PQfnumber(res, "pnnspid");
4787 :
4788 : /* this allocation may be more than we need */
4789 376 : pubsinfo = pg_malloc(ntups * sizeof(PublicationSchemaInfo));
4790 376 : j = 0;
4791 :
4792 626 : for (i = 0; i < ntups; i++)
4793 : {
4794 250 : Oid pnpubid = atooid(PQgetvalue(res, i, i_pnpubid));
4795 250 : Oid pnnspid = atooid(PQgetvalue(res, i, i_pnnspid));
4796 : PublicationInfo *pubinfo;
4797 : NamespaceInfo *nspinfo;
4798 :
4799 : /*
4800 : * Ignore any entries for which we aren't interested in either the
4801 : * publication or the rel.
4802 : */
4803 250 : pubinfo = findPublicationByOid(pnpubid);
4804 250 : if (pubinfo == NULL)
4805 0 : continue;
4806 250 : nspinfo = findNamespaceByOid(pnnspid);
4807 250 : if (nspinfo == NULL)
4808 0 : continue;
4809 :
4810 : /* OK, make a DumpableObject for this relationship */
4811 250 : pubsinfo[j].dobj.objType = DO_PUBLICATION_TABLE_IN_SCHEMA;
4812 250 : pubsinfo[j].dobj.catId.tableoid =
4813 250 : atooid(PQgetvalue(res, i, i_tableoid));
4814 250 : pubsinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4815 250 : AssignDumpId(&pubsinfo[j].dobj);
4816 250 : pubsinfo[j].dobj.namespace = nspinfo->dobj.namespace;
4817 250 : pubsinfo[j].dobj.name = nspinfo->dobj.name;
4818 250 : pubsinfo[j].publication = pubinfo;
4819 250 : pubsinfo[j].pubschema = nspinfo;
4820 :
4821 : /* Decide whether we want to dump it */
4822 250 : selectDumpablePublicationObject(&(pubsinfo[j].dobj), fout);
4823 :
4824 250 : j++;
4825 : }
4826 :
4827 376 : PQclear(res);
4828 376 : destroyPQExpBuffer(query);
4829 : }
4830 :
4831 : /*
4832 : * getPublicationTables
4833 : * get information about publication membership for dumpable tables.
4834 : */
4835 : void
4836 376 : getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
4837 : {
4838 : PQExpBuffer query;
4839 : PGresult *res;
4840 : PublicationRelInfo *pubrinfo;
4841 376 : DumpOptions *dopt = fout->dopt;
4842 : int i_tableoid;
4843 : int i_oid;
4844 : int i_prpubid;
4845 : int i_prrelid;
4846 : int i_prrelqual;
4847 : int i_prattrs;
4848 : int i,
4849 : j,
4850 : ntups;
4851 :
4852 376 : if (dopt->no_publications || fout->remoteVersion < 100000)
4853 0 : return;
4854 :
4855 376 : query = createPQExpBuffer();
4856 :
4857 : /* Collect all publication membership info. */
4858 376 : if (fout->remoteVersion >= 150000)
4859 376 : appendPQExpBufferStr(query,
4860 : "SELECT tableoid, oid, prpubid, prrelid, "
4861 : "pg_catalog.pg_get_expr(prqual, prrelid) AS prrelqual, "
4862 : "(CASE\n"
4863 : " WHEN pr.prattrs IS NOT NULL THEN\n"
4864 : " (SELECT array_agg(attname)\n"
4865 : " FROM\n"
4866 : " pg_catalog.generate_series(0, pg_catalog.array_upper(pr.prattrs::pg_catalog.int2[], 1)) s,\n"
4867 : " pg_catalog.pg_attribute\n"
4868 : " WHERE attrelid = pr.prrelid AND attnum = prattrs[s])\n"
4869 : " ELSE NULL END) prattrs "
4870 : "FROM pg_catalog.pg_publication_rel pr");
4871 : else
4872 0 : appendPQExpBufferStr(query,
4873 : "SELECT tableoid, oid, prpubid, prrelid, "
4874 : "NULL AS prrelqual, NULL AS prattrs "
4875 : "FROM pg_catalog.pg_publication_rel");
4876 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
4877 :
4878 376 : ntups = PQntuples(res);
4879 :
4880 376 : i_tableoid = PQfnumber(res, "tableoid");
4881 376 : i_oid = PQfnumber(res, "oid");
4882 376 : i_prpubid = PQfnumber(res, "prpubid");
4883 376 : i_prrelid = PQfnumber(res, "prrelid");
4884 376 : i_prrelqual = PQfnumber(res, "prrelqual");
4885 376 : i_prattrs = PQfnumber(res, "prattrs");
4886 :
4887 : /* this allocation may be more than we need */
4888 376 : pubrinfo = pg_malloc(ntups * sizeof(PublicationRelInfo));
4889 376 : j = 0;
4890 :
4891 1076 : for (i = 0; i < ntups; i++)
4892 : {
4893 700 : Oid prpubid = atooid(PQgetvalue(res, i, i_prpubid));
4894 700 : Oid prrelid = atooid(PQgetvalue(res, i, i_prrelid));
4895 : PublicationInfo *pubinfo;
4896 : TableInfo *tbinfo;
4897 :
4898 : /*
4899 : * Ignore any entries for which we aren't interested in either the
4900 : * publication or the rel.
4901 : */
4902 700 : pubinfo = findPublicationByOid(prpubid);
4903 700 : if (pubinfo == NULL)
4904 0 : continue;
4905 700 : tbinfo = findTableByOid(prrelid);
4906 700 : if (tbinfo == NULL)
4907 0 : continue;
4908 :
4909 : /* OK, make a DumpableObject for this relationship */
4910 700 : pubrinfo[j].dobj.objType = DO_PUBLICATION_REL;
4911 700 : pubrinfo[j].dobj.catId.tableoid =
4912 700 : atooid(PQgetvalue(res, i, i_tableoid));
4913 700 : pubrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
4914 700 : AssignDumpId(&pubrinfo[j].dobj);
4915 700 : pubrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
4916 700 : pubrinfo[j].dobj.name = tbinfo->dobj.name;
4917 700 : pubrinfo[j].publication = pubinfo;
4918 700 : pubrinfo[j].pubtable = tbinfo;
4919 700 : if (PQgetisnull(res, i, i_prrelqual))
4920 388 : pubrinfo[j].pubrelqual = NULL;
4921 : else
4922 312 : pubrinfo[j].pubrelqual = pg_strdup(PQgetvalue(res, i, i_prrelqual));
4923 :
4924 700 : if (!PQgetisnull(res, i, i_prattrs))
4925 : {
4926 : char **attnames;
4927 : int nattnames;
4928 : PQExpBuffer attribs;
4929 :
4930 222 : if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
4931 : &attnames, &nattnames))
4932 0 : pg_fatal("could not parse %s array", "prattrs");
4933 222 : attribs = createPQExpBuffer();
4934 638 : for (int k = 0; k < nattnames; k++)
4935 : {
4936 416 : if (k > 0)
4937 194 : appendPQExpBufferStr(attribs, ", ");
4938 :
4939 416 : appendPQExpBufferStr(attribs, fmtId(attnames[k]));
4940 : }
4941 222 : pubrinfo[j].pubrattrs = attribs->data;
4942 222 : free(attribs); /* but not attribs->data */
4943 222 : free(attnames);
4944 : }
4945 : else
4946 478 : pubrinfo[j].pubrattrs = NULL;
4947 :
4948 : /* Decide whether we want to dump it */
4949 700 : selectDumpablePublicationObject(&(pubrinfo[j].dobj), fout);
4950 :
4951 700 : j++;
4952 : }
4953 :
4954 376 : PQclear(res);
4955 376 : destroyPQExpBuffer(query);
4956 : }
4957 :
4958 : /*
4959 : * dumpPublicationNamespace
4960 : * dump the definition of the given publication schema mapping.
4961 : */
4962 : static void
4963 198 : dumpPublicationNamespace(Archive *fout, const PublicationSchemaInfo *pubsinfo)
4964 : {
4965 198 : DumpOptions *dopt = fout->dopt;
4966 198 : NamespaceInfo *schemainfo = pubsinfo->pubschema;
4967 198 : PublicationInfo *pubinfo = pubsinfo->publication;
4968 : PQExpBuffer query;
4969 : char *tag;
4970 :
4971 : /* Do nothing if not dumping schema */
4972 198 : if (!dopt->dumpSchema)
4973 24 : return;
4974 :
4975 174 : tag = psprintf("%s %s", pubinfo->dobj.name, schemainfo->dobj.name);
4976 :
4977 174 : query = createPQExpBuffer();
4978 :
4979 174 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ", fmtId(pubinfo->dobj.name));
4980 174 : appendPQExpBuffer(query, "ADD TABLES IN SCHEMA %s;\n", fmtId(schemainfo->dobj.name));
4981 :
4982 : /*
4983 : * There is no point in creating drop query as the drop is done by schema
4984 : * drop.
4985 : */
4986 174 : if (pubsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
4987 174 : ArchiveEntry(fout, pubsinfo->dobj.catId, pubsinfo->dobj.dumpId,
4988 174 : ARCHIVE_OPTS(.tag = tag,
4989 : .namespace = schemainfo->dobj.name,
4990 : .owner = pubinfo->rolname,
4991 : .description = "PUBLICATION TABLES IN SCHEMA",
4992 : .section = SECTION_POST_DATA,
4993 : .createStmt = query->data));
4994 :
4995 : /* These objects can't currently have comments or seclabels */
4996 :
4997 174 : free(tag);
4998 174 : destroyPQExpBuffer(query);
4999 : }
5000 :
5001 : /*
5002 : * dumpPublicationTable
5003 : * dump the definition of the given publication table mapping
5004 : */
5005 : static void
5006 568 : dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
5007 : {
5008 568 : DumpOptions *dopt = fout->dopt;
5009 568 : PublicationInfo *pubinfo = pubrinfo->publication;
5010 568 : TableInfo *tbinfo = pubrinfo->pubtable;
5011 : PQExpBuffer query;
5012 : char *tag;
5013 :
5014 : /* Do nothing if not dumping schema */
5015 568 : if (!dopt->dumpSchema)
5016 84 : return;
5017 :
5018 484 : tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);
5019 :
5020 484 : query = createPQExpBuffer();
5021 :
5022 484 : appendPQExpBuffer(query, "ALTER PUBLICATION %s ADD TABLE ONLY",
5023 484 : fmtId(pubinfo->dobj.name));
5024 484 : appendPQExpBuffer(query, " %s",
5025 484 : fmtQualifiedDumpable(tbinfo));
5026 :
5027 484 : if (pubrinfo->pubrattrs)
5028 154 : appendPQExpBuffer(query, " (%s)", pubrinfo->pubrattrs);
5029 :
5030 484 : if (pubrinfo->pubrelqual)
5031 : {
5032 : /*
5033 : * It's necessary to add parentheses around the expression because
5034 : * pg_get_expr won't supply the parentheses for things like WHERE
5035 : * TRUE.
5036 : */
5037 216 : appendPQExpBuffer(query, " WHERE (%s)", pubrinfo->pubrelqual);
5038 : }
5039 484 : appendPQExpBufferStr(query, ";\n");
5040 :
5041 : /*
5042 : * There is no point in creating a drop query as the drop is done by table
5043 : * drop. (If you think to change this, see also _printTocEntry().)
5044 : * Although this object doesn't really have ownership as such, set the
5045 : * owner field anyway to ensure that the command is run by the correct
5046 : * role at restore time.
5047 : */
5048 484 : if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5049 484 : ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
5050 484 : ARCHIVE_OPTS(.tag = tag,
5051 : .namespace = tbinfo->dobj.namespace->dobj.name,
5052 : .owner = pubinfo->rolname,
5053 : .description = "PUBLICATION TABLE",
5054 : .section = SECTION_POST_DATA,
5055 : .createStmt = query->data));
5056 :
5057 : /* These objects can't currently have comments or seclabels */
5058 :
5059 484 : free(tag);
5060 484 : destroyPQExpBuffer(query);
5061 : }
5062 :
5063 : /*
5064 : * Is the currently connected user a superuser?
5065 : */
5066 : static bool
5067 374 : is_superuser(Archive *fout)
5068 : {
5069 374 : ArchiveHandle *AH = (ArchiveHandle *) fout;
5070 : const char *val;
5071 :
5072 374 : val = PQparameterStatus(AH->connection, "is_superuser");
5073 :
5074 374 : if (val && strcmp(val, "on") == 0)
5075 368 : return true;
5076 :
5077 6 : return false;
5078 : }
5079 :
5080 : /*
5081 : * Set the given value to restrict_nonsystem_relation_kind value. Since
5082 : * restrict_nonsystem_relation_kind is introduced in minor version releases,
5083 : * the setting query is effective only where available.
5084 : */
5085 : static void
5086 444 : set_restrict_relation_kind(Archive *AH, const char *value)
5087 : {
5088 444 : PQExpBuffer query = createPQExpBuffer();
5089 : PGresult *res;
5090 :
5091 444 : appendPQExpBuffer(query,
5092 : "SELECT set_config(name, '%s', false) "
5093 : "FROM pg_settings "
5094 : "WHERE name = 'restrict_nonsystem_relation_kind'",
5095 : value);
5096 444 : res = ExecuteSqlQuery(AH, query->data, PGRES_TUPLES_OK);
5097 :
5098 444 : PQclear(res);
5099 444 : destroyPQExpBuffer(query);
5100 444 : }
5101 :
5102 : /*
5103 : * getSubscriptions
5104 : * get information about subscriptions
5105 : */
5106 : void
5107 376 : getSubscriptions(Archive *fout)
5108 : {
5109 376 : DumpOptions *dopt = fout->dopt;
5110 : PQExpBuffer query;
5111 : PGresult *res;
5112 : SubscriptionInfo *subinfo;
5113 : int i_tableoid;
5114 : int i_oid;
5115 : int i_subname;
5116 : int i_subowner;
5117 : int i_subbinary;
5118 : int i_substream;
5119 : int i_subtwophasestate;
5120 : int i_subdisableonerr;
5121 : int i_subpasswordrequired;
5122 : int i_subrunasowner;
5123 : int i_subconninfo;
5124 : int i_subslotname;
5125 : int i_subsynccommit;
5126 : int i_subpublications;
5127 : int i_suborigin;
5128 : int i_suboriginremotelsn;
5129 : int i_subenabled;
5130 : int i_subfailover;
5131 : int i_subretaindeadtuples;
5132 : int i_submaxretention;
5133 : int i,
5134 : ntups;
5135 :
5136 376 : if (dopt->no_subscriptions || fout->remoteVersion < 100000)
5137 2 : return;
5138 :
5139 374 : if (!is_superuser(fout))
5140 : {
5141 : int n;
5142 :
5143 6 : res = ExecuteSqlQuery(fout,
5144 : "SELECT count(*) FROM pg_subscription "
5145 : "WHERE subdbid = (SELECT oid FROM pg_database"
5146 : " WHERE datname = current_database())",
5147 : PGRES_TUPLES_OK);
5148 6 : n = atoi(PQgetvalue(res, 0, 0));
5149 6 : if (n > 0)
5150 4 : pg_log_warning("subscriptions not dumped because current user is not a superuser");
5151 6 : PQclear(res);
5152 6 : return;
5153 : }
5154 :
5155 368 : query = createPQExpBuffer();
5156 :
5157 : /* Get the subscriptions in current database. */
5158 368 : appendPQExpBufferStr(query,
5159 : "SELECT s.tableoid, s.oid, s.subname,\n"
5160 : " s.subowner,\n"
5161 : " s.subconninfo, s.subslotname, s.subsynccommit,\n"
5162 : " s.subpublications,\n");
5163 :
5164 368 : if (fout->remoteVersion >= 140000)
5165 368 : appendPQExpBufferStr(query, " s.subbinary,\n");
5166 : else
5167 0 : appendPQExpBufferStr(query, " false AS subbinary,\n");
5168 :
5169 368 : if (fout->remoteVersion >= 140000)
5170 368 : appendPQExpBufferStr(query, " s.substream,\n");
5171 : else
5172 0 : appendPQExpBufferStr(query, " 'f' AS substream,\n");
5173 :
5174 368 : if (fout->remoteVersion >= 150000)
5175 368 : appendPQExpBufferStr(query,
5176 : " s.subtwophasestate,\n"
5177 : " s.subdisableonerr,\n");
5178 : else
5179 0 : appendPQExpBuffer(query,
5180 : " '%c' AS subtwophasestate,\n"
5181 : " false AS subdisableonerr,\n",
5182 : LOGICALREP_TWOPHASE_STATE_DISABLED);
5183 :
5184 368 : if (fout->remoteVersion >= 160000)
5185 368 : appendPQExpBufferStr(query,
5186 : " s.subpasswordrequired,\n"
5187 : " s.subrunasowner,\n"
5188 : " s.suborigin,\n");
5189 : else
5190 0 : appendPQExpBuffer(query,
5191 : " 't' AS subpasswordrequired,\n"
5192 : " 't' AS subrunasowner,\n"
5193 : " '%s' AS suborigin,\n",
5194 : LOGICALREP_ORIGIN_ANY);
5195 :
5196 368 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5197 76 : appendPQExpBufferStr(query, " o.remote_lsn AS suboriginremotelsn,\n"
5198 : " s.subenabled,\n");
5199 : else
5200 292 : appendPQExpBufferStr(query, " NULL AS suboriginremotelsn,\n"
5201 : " false AS subenabled,\n");
5202 :
5203 368 : if (fout->remoteVersion >= 170000)
5204 368 : appendPQExpBufferStr(query,
5205 : " s.subfailover,\n");
5206 : else
5207 0 : appendPQExpBufferStr(query,
5208 : " false AS subfailover,\n");
5209 :
5210 368 : if (fout->remoteVersion >= 190000)
5211 368 : appendPQExpBufferStr(query,
5212 : " s.subretaindeadtuples,\n");
5213 : else
5214 0 : appendPQExpBufferStr(query,
5215 : " false AS subretaindeadtuples,\n");
5216 :
5217 368 : if (fout->remoteVersion >= 190000)
5218 368 : appendPQExpBufferStr(query,
5219 : " s.submaxretention\n");
5220 : else
5221 0 : appendPQExpBuffer(query,
5222 : " 0 AS submaxretention\n");
5223 :
5224 368 : appendPQExpBufferStr(query,
5225 : "FROM pg_subscription s\n");
5226 :
5227 368 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5228 76 : appendPQExpBufferStr(query,
5229 : "LEFT JOIN pg_catalog.pg_replication_origin_status o \n"
5230 : " ON o.external_id = 'pg_' || s.oid::text \n");
5231 :
5232 368 : appendPQExpBufferStr(query,
5233 : "WHERE s.subdbid = (SELECT oid FROM pg_database\n"
5234 : " WHERE datname = current_database())");
5235 :
5236 368 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5237 :
5238 368 : ntups = PQntuples(res);
5239 :
5240 : /*
5241 : * Get subscription fields. We don't include subskiplsn in the dump as
5242 : * after restoring the dump this value may no longer be relevant.
5243 : */
5244 368 : i_tableoid = PQfnumber(res, "tableoid");
5245 368 : i_oid = PQfnumber(res, "oid");
5246 368 : i_subname = PQfnumber(res, "subname");
5247 368 : i_subowner = PQfnumber(res, "subowner");
5248 368 : i_subenabled = PQfnumber(res, "subenabled");
5249 368 : i_subbinary = PQfnumber(res, "subbinary");
5250 368 : i_substream = PQfnumber(res, "substream");
5251 368 : i_subtwophasestate = PQfnumber(res, "subtwophasestate");
5252 368 : i_subdisableonerr = PQfnumber(res, "subdisableonerr");
5253 368 : i_subpasswordrequired = PQfnumber(res, "subpasswordrequired");
5254 368 : i_subrunasowner = PQfnumber(res, "subrunasowner");
5255 368 : i_subfailover = PQfnumber(res, "subfailover");
5256 368 : i_subretaindeadtuples = PQfnumber(res, "subretaindeadtuples");
5257 368 : i_submaxretention = PQfnumber(res, "submaxretention");
5258 368 : i_subconninfo = PQfnumber(res, "subconninfo");
5259 368 : i_subslotname = PQfnumber(res, "subslotname");
5260 368 : i_subsynccommit = PQfnumber(res, "subsynccommit");
5261 368 : i_subpublications = PQfnumber(res, "subpublications");
5262 368 : i_suborigin = PQfnumber(res, "suborigin");
5263 368 : i_suboriginremotelsn = PQfnumber(res, "suboriginremotelsn");
5264 :
5265 368 : subinfo = pg_malloc(ntups * sizeof(SubscriptionInfo));
5266 :
5267 624 : for (i = 0; i < ntups; i++)
5268 : {
5269 256 : subinfo[i].dobj.objType = DO_SUBSCRIPTION;
5270 256 : subinfo[i].dobj.catId.tableoid =
5271 256 : atooid(PQgetvalue(res, i, i_tableoid));
5272 256 : subinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
5273 256 : AssignDumpId(&subinfo[i].dobj);
5274 256 : subinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_subname));
5275 256 : subinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_subowner));
5276 :
5277 256 : subinfo[i].subenabled =
5278 256 : (strcmp(PQgetvalue(res, i, i_subenabled), "t") == 0);
5279 256 : subinfo[i].subbinary =
5280 256 : (strcmp(PQgetvalue(res, i, i_subbinary), "t") == 0);
5281 256 : subinfo[i].substream = *(PQgetvalue(res, i, i_substream));
5282 256 : subinfo[i].subtwophasestate = *(PQgetvalue(res, i, i_subtwophasestate));
5283 256 : subinfo[i].subdisableonerr =
5284 256 : (strcmp(PQgetvalue(res, i, i_subdisableonerr), "t") == 0);
5285 256 : subinfo[i].subpasswordrequired =
5286 256 : (strcmp(PQgetvalue(res, i, i_subpasswordrequired), "t") == 0);
5287 256 : subinfo[i].subrunasowner =
5288 256 : (strcmp(PQgetvalue(res, i, i_subrunasowner), "t") == 0);
5289 256 : subinfo[i].subfailover =
5290 256 : (strcmp(PQgetvalue(res, i, i_subfailover), "t") == 0);
5291 256 : subinfo[i].subretaindeadtuples =
5292 256 : (strcmp(PQgetvalue(res, i, i_subretaindeadtuples), "t") == 0);
5293 256 : subinfo[i].submaxretention =
5294 256 : atoi(PQgetvalue(res, i, i_submaxretention));
5295 512 : subinfo[i].subconninfo =
5296 256 : pg_strdup(PQgetvalue(res, i, i_subconninfo));
5297 256 : if (PQgetisnull(res, i, i_subslotname))
5298 0 : subinfo[i].subslotname = NULL;
5299 : else
5300 256 : subinfo[i].subslotname =
5301 256 : pg_strdup(PQgetvalue(res, i, i_subslotname));
5302 512 : subinfo[i].subsynccommit =
5303 256 : pg_strdup(PQgetvalue(res, i, i_subsynccommit));
5304 512 : subinfo[i].subpublications =
5305 256 : pg_strdup(PQgetvalue(res, i, i_subpublications));
5306 256 : subinfo[i].suborigin = pg_strdup(PQgetvalue(res, i, i_suborigin));
5307 256 : if (PQgetisnull(res, i, i_suboriginremotelsn))
5308 254 : subinfo[i].suboriginremotelsn = NULL;
5309 : else
5310 2 : subinfo[i].suboriginremotelsn =
5311 2 : pg_strdup(PQgetvalue(res, i, i_suboriginremotelsn));
5312 :
5313 : /* Decide whether we want to dump it */
5314 256 : selectDumpableObject(&(subinfo[i].dobj), fout);
5315 : }
5316 368 : PQclear(res);
5317 :
5318 368 : destroyPQExpBuffer(query);
5319 : }
5320 :
5321 : /*
5322 : * getSubscriptionRelations
5323 : * Get information about subscription membership for dumpable relations. This
5324 : * will be used only in binary-upgrade mode for PG17 or later versions.
5325 : */
5326 : void
5327 376 : getSubscriptionRelations(Archive *fout)
5328 : {
5329 376 : DumpOptions *dopt = fout->dopt;
5330 376 : SubscriptionInfo *subinfo = NULL;
5331 : SubRelInfo *subrinfo;
5332 : PGresult *res;
5333 : int i_srsubid;
5334 : int i_srrelid;
5335 : int i_srsubstate;
5336 : int i_srsublsn;
5337 : int ntups;
5338 376 : Oid last_srsubid = InvalidOid;
5339 :
5340 376 : if (dopt->no_subscriptions || !dopt->binary_upgrade ||
5341 76 : fout->remoteVersion < 170000)
5342 300 : return;
5343 :
5344 76 : res = ExecuteSqlQuery(fout,
5345 : "SELECT srsubid, srrelid, srsubstate, srsublsn "
5346 : "FROM pg_catalog.pg_subscription_rel "
5347 : "ORDER BY srsubid",
5348 : PGRES_TUPLES_OK);
5349 76 : ntups = PQntuples(res);
5350 76 : if (ntups == 0)
5351 74 : goto cleanup;
5352 :
5353 : /* Get pg_subscription_rel attributes */
5354 2 : i_srsubid = PQfnumber(res, "srsubid");
5355 2 : i_srrelid = PQfnumber(res, "srrelid");
5356 2 : i_srsubstate = PQfnumber(res, "srsubstate");
5357 2 : i_srsublsn = PQfnumber(res, "srsublsn");
5358 :
5359 2 : subrinfo = pg_malloc(ntups * sizeof(SubRelInfo));
5360 8 : for (int i = 0; i < ntups; i++)
5361 : {
5362 6 : Oid cur_srsubid = atooid(PQgetvalue(res, i, i_srsubid));
5363 6 : Oid relid = atooid(PQgetvalue(res, i, i_srrelid));
5364 : TableInfo *tblinfo;
5365 :
5366 : /*
5367 : * If we switched to a new subscription, check if the subscription
5368 : * exists.
5369 : */
5370 6 : if (cur_srsubid != last_srsubid)
5371 : {
5372 4 : subinfo = findSubscriptionByOid(cur_srsubid);
5373 4 : if (subinfo == NULL)
5374 0 : pg_fatal("subscription with OID %u does not exist", cur_srsubid);
5375 :
5376 4 : last_srsubid = cur_srsubid;
5377 : }
5378 :
5379 6 : tblinfo = findTableByOid(relid);
5380 6 : if (tblinfo == NULL)
5381 0 : pg_fatal("failed sanity check, relation with OID %u not found",
5382 : relid);
5383 :
5384 : /* OK, make a DumpableObject for this relationship */
5385 6 : subrinfo[i].dobj.objType = DO_SUBSCRIPTION_REL;
5386 6 : subrinfo[i].dobj.catId.tableoid = relid;
5387 6 : subrinfo[i].dobj.catId.oid = cur_srsubid;
5388 6 : AssignDumpId(&subrinfo[i].dobj);
5389 6 : subrinfo[i].dobj.namespace = tblinfo->dobj.namespace;
5390 6 : subrinfo[i].dobj.name = tblinfo->dobj.name;
5391 6 : subrinfo[i].subinfo = subinfo;
5392 6 : subrinfo[i].tblinfo = tblinfo;
5393 6 : subrinfo[i].srsubstate = PQgetvalue(res, i, i_srsubstate)[0];
5394 6 : if (PQgetisnull(res, i, i_srsublsn))
5395 2 : subrinfo[i].srsublsn = NULL;
5396 : else
5397 4 : subrinfo[i].srsublsn = pg_strdup(PQgetvalue(res, i, i_srsublsn));
5398 :
5399 : /* Decide whether we want to dump it */
5400 6 : selectDumpableObject(&(subrinfo[i].dobj), fout);
5401 : }
5402 :
5403 2 : cleanup:
5404 76 : PQclear(res);
5405 : }
5406 :
5407 : /*
5408 : * dumpSubscriptionTable
5409 : * Dump the definition of the given subscription table mapping. This will be
5410 : * used only in binary-upgrade mode for PG17 or later versions.
5411 : */
5412 : static void
5413 6 : dumpSubscriptionTable(Archive *fout, const SubRelInfo *subrinfo)
5414 : {
5415 6 : DumpOptions *dopt = fout->dopt;
5416 6 : SubscriptionInfo *subinfo = subrinfo->subinfo;
5417 : PQExpBuffer query;
5418 : char *tag;
5419 :
5420 : /* Do nothing if not dumping schema */
5421 6 : if (!dopt->dumpSchema)
5422 0 : return;
5423 :
5424 : Assert(fout->dopt->binary_upgrade && fout->remoteVersion >= 170000);
5425 :
5426 6 : tag = psprintf("%s %s", subinfo->dobj.name, subrinfo->tblinfo->dobj.name);
5427 :
5428 6 : query = createPQExpBuffer();
5429 :
5430 6 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5431 : {
5432 : /*
5433 : * binary_upgrade_add_sub_rel_state will add the subscription relation
5434 : * to pg_subscription_rel table. This will be used only in
5435 : * binary-upgrade mode.
5436 : */
5437 6 : appendPQExpBufferStr(query,
5438 : "\n-- For binary upgrade, must preserve the subscriber table.\n");
5439 6 : appendPQExpBufferStr(query,
5440 : "SELECT pg_catalog.binary_upgrade_add_sub_rel_state(");
5441 6 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5442 6 : appendPQExpBuffer(query,
5443 : ", %u, '%c'",
5444 6 : subrinfo->tblinfo->dobj.catId.oid,
5445 6 : subrinfo->srsubstate);
5446 :
5447 6 : if (subrinfo->srsublsn && subrinfo->srsublsn[0] != '\0')
5448 4 : appendPQExpBuffer(query, ", '%s'", subrinfo->srsublsn);
5449 : else
5450 2 : appendPQExpBufferStr(query, ", NULL");
5451 :
5452 6 : appendPQExpBufferStr(query, ");\n");
5453 : }
5454 :
5455 : /*
5456 : * There is no point in creating a drop query as the drop is done by table
5457 : * drop. (If you think to change this, see also _printTocEntry().)
5458 : * Although this object doesn't really have ownership as such, set the
5459 : * owner field anyway to ensure that the command is run by the correct
5460 : * role at restore time.
5461 : */
5462 6 : if (subrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5463 6 : ArchiveEntry(fout, subrinfo->dobj.catId, subrinfo->dobj.dumpId,
5464 6 : ARCHIVE_OPTS(.tag = tag,
5465 : .namespace = subrinfo->tblinfo->dobj.namespace->dobj.name,
5466 : .owner = subinfo->rolname,
5467 : .description = "SUBSCRIPTION TABLE",
5468 : .section = SECTION_POST_DATA,
5469 : .createStmt = query->data));
5470 :
5471 : /* These objects can't currently have comments or seclabels */
5472 :
5473 6 : free(tag);
5474 6 : destroyPQExpBuffer(query);
5475 : }
5476 :
5477 : /*
5478 : * dumpSubscription
5479 : * dump the definition of the given subscription
5480 : */
5481 : static void
5482 220 : dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
5483 : {
5484 220 : DumpOptions *dopt = fout->dopt;
5485 : PQExpBuffer delq;
5486 : PQExpBuffer query;
5487 : PQExpBuffer publications;
5488 : char *qsubname;
5489 220 : char **pubnames = NULL;
5490 220 : int npubnames = 0;
5491 : int i;
5492 :
5493 : /* Do nothing if not dumping schema */
5494 220 : if (!dopt->dumpSchema)
5495 36 : return;
5496 :
5497 184 : delq = createPQExpBuffer();
5498 184 : query = createPQExpBuffer();
5499 :
5500 184 : qsubname = pg_strdup(fmtId(subinfo->dobj.name));
5501 :
5502 184 : appendPQExpBuffer(delq, "DROP SUBSCRIPTION %s;\n",
5503 : qsubname);
5504 :
5505 184 : appendPQExpBuffer(query, "CREATE SUBSCRIPTION %s CONNECTION ",
5506 : qsubname);
5507 184 : appendStringLiteralAH(query, subinfo->subconninfo, fout);
5508 :
5509 : /* Build list of quoted publications and append them to query. */
5510 184 : if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
5511 0 : pg_fatal("could not parse %s array", "subpublications");
5512 :
5513 184 : publications = createPQExpBuffer();
5514 368 : for (i = 0; i < npubnames; i++)
5515 : {
5516 184 : if (i > 0)
5517 0 : appendPQExpBufferStr(publications, ", ");
5518 :
5519 184 : appendPQExpBufferStr(publications, fmtId(pubnames[i]));
5520 : }
5521 :
5522 184 : appendPQExpBuffer(query, " PUBLICATION %s WITH (connect = false, slot_name = ", publications->data);
5523 184 : if (subinfo->subslotname)
5524 184 : appendStringLiteralAH(query, subinfo->subslotname, fout);
5525 : else
5526 0 : appendPQExpBufferStr(query, "NONE");
5527 :
5528 184 : if (subinfo->subbinary)
5529 0 : appendPQExpBufferStr(query, ", binary = true");
5530 :
5531 184 : if (subinfo->substream == LOGICALREP_STREAM_ON)
5532 60 : appendPQExpBufferStr(query, ", streaming = on");
5533 124 : else if (subinfo->substream == LOGICALREP_STREAM_PARALLEL)
5534 64 : appendPQExpBufferStr(query, ", streaming = parallel");
5535 : else
5536 60 : appendPQExpBufferStr(query, ", streaming = off");
5537 :
5538 184 : if (subinfo->subtwophasestate != LOGICALREP_TWOPHASE_STATE_DISABLED)
5539 0 : appendPQExpBufferStr(query, ", two_phase = on");
5540 :
5541 184 : if (subinfo->subdisableonerr)
5542 0 : appendPQExpBufferStr(query, ", disable_on_error = true");
5543 :
5544 184 : if (!subinfo->subpasswordrequired)
5545 0 : appendPQExpBufferStr(query, ", password_required = false");
5546 :
5547 184 : if (subinfo->subrunasowner)
5548 0 : appendPQExpBufferStr(query, ", run_as_owner = true");
5549 :
5550 184 : if (subinfo->subfailover)
5551 2 : appendPQExpBufferStr(query, ", failover = true");
5552 :
5553 184 : if (subinfo->subretaindeadtuples)
5554 2 : appendPQExpBufferStr(query, ", retain_dead_tuples = true");
5555 :
5556 184 : if (subinfo->submaxretention)
5557 0 : appendPQExpBuffer(query, ", max_retention_duration = %d", subinfo->submaxretention);
5558 :
5559 184 : if (strcmp(subinfo->subsynccommit, "off") != 0)
5560 0 : appendPQExpBuffer(query, ", synchronous_commit = %s", fmtId(subinfo->subsynccommit));
5561 :
5562 184 : if (pg_strcasecmp(subinfo->suborigin, LOGICALREP_ORIGIN_ANY) != 0)
5563 60 : appendPQExpBuffer(query, ", origin = %s", subinfo->suborigin);
5564 :
5565 184 : appendPQExpBufferStr(query, ");\n");
5566 :
5567 : /*
5568 : * In binary-upgrade mode, we allow the replication to continue after the
5569 : * upgrade.
5570 : */
5571 184 : if (dopt->binary_upgrade && fout->remoteVersion >= 170000)
5572 : {
5573 10 : if (subinfo->suboriginremotelsn)
5574 : {
5575 : /*
5576 : * Preserve the remote_lsn for the subscriber's replication
5577 : * origin. This value is required to start the replication from
5578 : * the position before the upgrade. This value will be stale if
5579 : * the publisher gets upgraded before the subscriber node.
5580 : * However, this shouldn't be a problem as the upgrade of the
5581 : * publisher ensures that all the transactions were replicated
5582 : * before upgrading it.
5583 : */
5584 2 : appendPQExpBufferStr(query,
5585 : "\n-- For binary upgrade, must preserve the remote_lsn for the subscriber's replication origin.\n");
5586 2 : appendPQExpBufferStr(query,
5587 : "SELECT pg_catalog.binary_upgrade_replorigin_advance(");
5588 2 : appendStringLiteralAH(query, subinfo->dobj.name, fout);
5589 2 : appendPQExpBuffer(query, ", '%s');\n", subinfo->suboriginremotelsn);
5590 : }
5591 :
5592 10 : if (subinfo->subenabled)
5593 : {
5594 : /*
5595 : * Enable the subscription to allow the replication to continue
5596 : * after the upgrade.
5597 : */
5598 2 : appendPQExpBufferStr(query,
5599 : "\n-- For binary upgrade, must preserve the subscriber's running state.\n");
5600 2 : appendPQExpBuffer(query, "ALTER SUBSCRIPTION %s ENABLE;\n", qsubname);
5601 : }
5602 : }
5603 :
5604 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
5605 184 : ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
5606 184 : ARCHIVE_OPTS(.tag = subinfo->dobj.name,
5607 : .owner = subinfo->rolname,
5608 : .description = "SUBSCRIPTION",
5609 : .section = SECTION_POST_DATA,
5610 : .createStmt = query->data,
5611 : .dropStmt = delq->data));
5612 :
5613 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
5614 60 : dumpComment(fout, "SUBSCRIPTION", qsubname,
5615 60 : NULL, subinfo->rolname,
5616 60 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5617 :
5618 184 : if (subinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
5619 0 : dumpSecLabel(fout, "SUBSCRIPTION", qsubname,
5620 0 : NULL, subinfo->rolname,
5621 0 : subinfo->dobj.catId, 0, subinfo->dobj.dumpId);
5622 :
5623 184 : destroyPQExpBuffer(publications);
5624 184 : free(pubnames);
5625 :
5626 184 : destroyPQExpBuffer(delq);
5627 184 : destroyPQExpBuffer(query);
5628 184 : free(qsubname);
5629 : }
5630 :
5631 : /*
5632 : * Given a "create query", append as many ALTER ... DEPENDS ON EXTENSION as
5633 : * the object needs.
5634 : */
5635 : static void
5636 10152 : append_depends_on_extension(Archive *fout,
5637 : PQExpBuffer create,
5638 : const DumpableObject *dobj,
5639 : const char *catalog,
5640 : const char *keyword,
5641 : const char *objname)
5642 : {
5643 10152 : if (dobj->depends_on_ext)
5644 : {
5645 : char *nm;
5646 : PGresult *res;
5647 : PQExpBuffer query;
5648 : int ntups;
5649 : int i_extname;
5650 : int i;
5651 :
5652 : /* dodge fmtId() non-reentrancy */
5653 84 : nm = pg_strdup(objname);
5654 :
5655 84 : query = createPQExpBuffer();
5656 84 : appendPQExpBuffer(query,
5657 : "SELECT e.extname "
5658 : "FROM pg_catalog.pg_depend d, pg_catalog.pg_extension e "
5659 : "WHERE d.refobjid = e.oid AND classid = '%s'::pg_catalog.regclass "
5660 : "AND objid = '%u'::pg_catalog.oid AND deptype = 'x' "
5661 : "AND refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass",
5662 : catalog,
5663 84 : dobj->catId.oid);
5664 84 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
5665 84 : ntups = PQntuples(res);
5666 84 : i_extname = PQfnumber(res, "extname");
5667 168 : for (i = 0; i < ntups; i++)
5668 : {
5669 84 : appendPQExpBuffer(create, "\nALTER %s %s DEPENDS ON EXTENSION %s;",
5670 : keyword, nm,
5671 84 : fmtId(PQgetvalue(res, i, i_extname)));
5672 : }
5673 :
5674 84 : PQclear(res);
5675 84 : destroyPQExpBuffer(query);
5676 84 : pg_free(nm);
5677 : }
5678 10152 : }
5679 :
5680 : static Oid
5681 0 : get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
5682 : {
5683 : /*
5684 : * If the old version didn't assign an array type, but the new version
5685 : * does, we must select an unused type OID to assign. This currently only
5686 : * happens for domains, when upgrading pre-v11 to v11 and up.
5687 : *
5688 : * Note: local state here is kind of ugly, but we must have some, since we
5689 : * mustn't choose the same unused OID more than once.
5690 : */
5691 : static Oid next_possible_free_oid = FirstNormalObjectId;
5692 : PGresult *res;
5693 : bool is_dup;
5694 :
5695 : do
5696 : {
5697 0 : ++next_possible_free_oid;
5698 0 : printfPQExpBuffer(upgrade_query,
5699 : "SELECT EXISTS(SELECT 1 "
5700 : "FROM pg_catalog.pg_type "
5701 : "WHERE oid = '%u'::pg_catalog.oid);",
5702 : next_possible_free_oid);
5703 0 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5704 0 : is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
5705 0 : PQclear(res);
5706 0 : } while (is_dup);
5707 :
5708 0 : return next_possible_free_oid;
5709 : }
5710 :
5711 : static void
5712 1892 : binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
5713 : PQExpBuffer upgrade_buffer,
5714 : Oid pg_type_oid,
5715 : bool force_array_type,
5716 : bool include_multirange_type)
5717 : {
5718 1892 : PQExpBuffer upgrade_query = createPQExpBuffer();
5719 : PGresult *res;
5720 : Oid pg_type_array_oid;
5721 : Oid pg_type_multirange_oid;
5722 : Oid pg_type_multirange_array_oid;
5723 : TypeInfo *tinfo;
5724 :
5725 1892 : appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
5726 1892 : appendPQExpBuffer(upgrade_buffer,
5727 : "SELECT pg_catalog.binary_upgrade_set_next_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5728 : pg_type_oid);
5729 :
5730 1892 : tinfo = findTypeByOid(pg_type_oid);
5731 1892 : if (tinfo)
5732 1892 : pg_type_array_oid = tinfo->typarray;
5733 : else
5734 0 : pg_type_array_oid = InvalidOid;
5735 :
5736 1892 : if (!OidIsValid(pg_type_array_oid) && force_array_type)
5737 0 : pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5738 :
5739 1892 : if (OidIsValid(pg_type_array_oid))
5740 : {
5741 1888 : appendPQExpBufferStr(upgrade_buffer,
5742 : "\n-- For binary upgrade, must preserve pg_type array oid\n");
5743 1888 : appendPQExpBuffer(upgrade_buffer,
5744 : "SELECT pg_catalog.binary_upgrade_set_next_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5745 : pg_type_array_oid);
5746 : }
5747 :
5748 : /*
5749 : * Pre-set the multirange type oid and its own array type oid.
5750 : */
5751 1892 : if (include_multirange_type)
5752 : {
5753 16 : if (fout->remoteVersion >= 140000)
5754 : {
5755 16 : printfPQExpBuffer(upgrade_query,
5756 : "SELECT t.oid, t.typarray "
5757 : "FROM pg_catalog.pg_type t "
5758 : "JOIN pg_catalog.pg_range r "
5759 : "ON t.oid = r.rngmultitypid "
5760 : "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
5761 : pg_type_oid);
5762 :
5763 16 : res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
5764 :
5765 16 : pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
5766 16 : pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
5767 :
5768 16 : PQclear(res);
5769 : }
5770 : else
5771 : {
5772 0 : pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5773 0 : pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
5774 : }
5775 :
5776 16 : appendPQExpBufferStr(upgrade_buffer,
5777 : "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
5778 16 : appendPQExpBuffer(upgrade_buffer,
5779 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5780 : pg_type_multirange_oid);
5781 16 : appendPQExpBufferStr(upgrade_buffer,
5782 : "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
5783 16 : appendPQExpBuffer(upgrade_buffer,
5784 : "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
5785 : pg_type_multirange_array_oid);
5786 : }
5787 :
5788 1892 : destroyPQExpBuffer(upgrade_query);
5789 1892 : }
5790 :
5791 : static void
5792 1742 : binary_upgrade_set_type_oids_by_rel(Archive *fout,
5793 : PQExpBuffer upgrade_buffer,
5794 : const TableInfo *tbinfo)
5795 : {
5796 1742 : Oid pg_type_oid = tbinfo->reltype;
5797 :
5798 1742 : if (OidIsValid(pg_type_oid))
5799 1742 : binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
5800 : pg_type_oid, false, false);
5801 1742 : }
5802 :
5803 : /*
5804 : * bsearch() comparator for BinaryUpgradeClassOidItem
5805 : */
5806 : static int
5807 24950 : BinaryUpgradeClassOidItemCmp(const void *p1, const void *p2)
5808 : {
5809 24950 : BinaryUpgradeClassOidItem v1 = *((const BinaryUpgradeClassOidItem *) p1);
5810 24950 : BinaryUpgradeClassOidItem v2 = *((const BinaryUpgradeClassOidItem *) p2);
5811 :
5812 24950 : return pg_cmp_u32(v1.oid, v2.oid);
5813 : }
5814 :
5815 : /*
5816 : * collectBinaryUpgradeClassOids
5817 : *
5818 : * Construct a table of pg_class information required for
5819 : * binary_upgrade_set_pg_class_oids(). The table is sorted by OID for speed in
5820 : * lookup.
5821 : */
5822 : static void
5823 76 : collectBinaryUpgradeClassOids(Archive *fout)
5824 : {
5825 : PGresult *res;
5826 : const char *query;
5827 :
5828 76 : query = "SELECT c.oid, c.relkind, c.relfilenode, c.reltoastrelid, "
5829 : "ct.relfilenode, i.indexrelid, cti.relfilenode "
5830 : "FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_index i "
5831 : "ON (c.reltoastrelid = i.indrelid AND i.indisvalid) "
5832 : "LEFT JOIN pg_catalog.pg_class ct ON (c.reltoastrelid = ct.oid) "
5833 : "LEFT JOIN pg_catalog.pg_class AS cti ON (i.indexrelid = cti.oid) "
5834 : "ORDER BY c.oid;";
5835 :
5836 76 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
5837 :
5838 76 : nbinaryUpgradeClassOids = PQntuples(res);
5839 76 : binaryUpgradeClassOids = (BinaryUpgradeClassOidItem *)
5840 76 : pg_malloc(nbinaryUpgradeClassOids * sizeof(BinaryUpgradeClassOidItem));
5841 :
5842 35402 : for (int i = 0; i < nbinaryUpgradeClassOids; i++)
5843 : {
5844 35326 : binaryUpgradeClassOids[i].oid = atooid(PQgetvalue(res, i, 0));
5845 35326 : binaryUpgradeClassOids[i].relkind = *PQgetvalue(res, i, 1);
5846 35326 : binaryUpgradeClassOids[i].relfilenumber = atooid(PQgetvalue(res, i, 2));
5847 35326 : binaryUpgradeClassOids[i].toast_oid = atooid(PQgetvalue(res, i, 3));
5848 35326 : binaryUpgradeClassOids[i].toast_relfilenumber = atooid(PQgetvalue(res, i, 4));
5849 35326 : binaryUpgradeClassOids[i].toast_index_oid = atooid(PQgetvalue(res, i, 5));
5850 35326 : binaryUpgradeClassOids[i].toast_index_relfilenumber = atooid(PQgetvalue(res, i, 6));
5851 : }
5852 :
5853 76 : PQclear(res);
5854 76 : }
5855 :
5856 : static void
5857 2526 : binary_upgrade_set_pg_class_oids(Archive *fout,
5858 : PQExpBuffer upgrade_buffer, Oid pg_class_oid)
5859 : {
5860 2526 : BinaryUpgradeClassOidItem key = {0};
5861 : BinaryUpgradeClassOidItem *entry;
5862 :
5863 : Assert(binaryUpgradeClassOids);
5864 :
5865 : /*
5866 : * Preserve the OID and relfilenumber of the table, table's index, table's
5867 : * toast table and toast table's index if any.
5868 : *
5869 : * One complexity is that the current table definition might not require
5870 : * the creation of a TOAST table, but the old database might have a TOAST
5871 : * table that was created earlier, before some wide columns were dropped.
5872 : * By setting the TOAST oid we force creation of the TOAST heap and index
5873 : * by the new backend, so we can copy the files during binary upgrade
5874 : * without worrying about this case.
5875 : */
5876 2526 : key.oid = pg_class_oid;
5877 2526 : entry = bsearch(&key, binaryUpgradeClassOids, nbinaryUpgradeClassOids,
5878 : sizeof(BinaryUpgradeClassOidItem),
5879 : BinaryUpgradeClassOidItemCmp);
5880 :
5881 2526 : appendPQExpBufferStr(upgrade_buffer,
5882 : "\n-- For binary upgrade, must preserve pg_class oids and relfilenodes\n");
5883 :
5884 2526 : if (entry->relkind != RELKIND_INDEX &&
5885 1966 : entry->relkind != RELKIND_PARTITIONED_INDEX)
5886 : {
5887 1910 : appendPQExpBuffer(upgrade_buffer,
5888 : "SELECT pg_catalog.binary_upgrade_set_next_heap_pg_class_oid('%u'::pg_catalog.oid);\n",
5889 : pg_class_oid);
5890 :
5891 : /*
5892 : * Not every relation has storage. Also, in a pre-v12 database,
5893 : * partitioned tables have a relfilenumber, which should not be
5894 : * preserved when upgrading.
5895 : */
5896 1910 : if (RelFileNumberIsValid(entry->relfilenumber) &&
5897 1582 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5898 1582 : appendPQExpBuffer(upgrade_buffer,
5899 : "SELECT pg_catalog.binary_upgrade_set_next_heap_relfilenode('%u'::pg_catalog.oid);\n",
5900 : entry->relfilenumber);
5901 :
5902 : /*
5903 : * In a pre-v12 database, partitioned tables might be marked as having
5904 : * toast tables, but we should ignore them if so.
5905 : */
5906 1910 : if (OidIsValid(entry->toast_oid) &&
5907 554 : entry->relkind != RELKIND_PARTITIONED_TABLE)
5908 : {
5909 554 : appendPQExpBuffer(upgrade_buffer,
5910 : "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_class_oid('%u'::pg_catalog.oid);\n",
5911 : entry->toast_oid);
5912 554 : appendPQExpBuffer(upgrade_buffer,
5913 : "SELECT pg_catalog.binary_upgrade_set_next_toast_relfilenode('%u'::pg_catalog.oid);\n",
5914 : entry->toast_relfilenumber);
5915 :
5916 : /* every toast table has an index */
5917 554 : appendPQExpBuffer(upgrade_buffer,
5918 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5919 : entry->toast_index_oid);
5920 554 : appendPQExpBuffer(upgrade_buffer,
5921 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5922 : entry->toast_index_relfilenumber);
5923 : }
5924 : }
5925 : else
5926 : {
5927 : /* Preserve the OID and relfilenumber of the index */
5928 616 : appendPQExpBuffer(upgrade_buffer,
5929 : "SELECT pg_catalog.binary_upgrade_set_next_index_pg_class_oid('%u'::pg_catalog.oid);\n",
5930 : pg_class_oid);
5931 616 : appendPQExpBuffer(upgrade_buffer,
5932 : "SELECT pg_catalog.binary_upgrade_set_next_index_relfilenode('%u'::pg_catalog.oid);\n",
5933 : entry->relfilenumber);
5934 : }
5935 :
5936 2526 : appendPQExpBufferChar(upgrade_buffer, '\n');
5937 2526 : }
5938 :
5939 : /*
5940 : * If the DumpableObject is a member of an extension, add a suitable
5941 : * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer.
5942 : *
5943 : * For somewhat historical reasons, objname should already be quoted,
5944 : * but not objnamespace (if any).
5945 : */
5946 : static void
5947 3026 : binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
5948 : const DumpableObject *dobj,
5949 : const char *objtype,
5950 : const char *objname,
5951 : const char *objnamespace)
5952 : {
5953 3026 : DumpableObject *extobj = NULL;
5954 : int i;
5955 :
5956 3026 : if (!dobj->ext_member)
5957 2984 : return;
5958 :
5959 : /*
5960 : * Find the parent extension. We could avoid this search if we wanted to
5961 : * add a link field to DumpableObject, but the space costs of that would
5962 : * be considerable. We assume that member objects could only have a
5963 : * direct dependency on their own extension, not any others.
5964 : */
5965 42 : for (i = 0; i < dobj->nDeps; i++)
5966 : {
5967 42 : extobj = findObjectByDumpId(dobj->dependencies[i]);
5968 42 : if (extobj && extobj->objType == DO_EXTENSION)
5969 42 : break;
5970 0 : extobj = NULL;
5971 : }
5972 42 : if (extobj == NULL)
5973 0 : pg_fatal("could not find parent extension for %s %s",
5974 : objtype, objname);
5975 :
5976 42 : appendPQExpBufferStr(upgrade_buffer,
5977 : "\n-- For binary upgrade, handle extension membership the hard way\n");
5978 42 : appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s ",
5979 42 : fmtId(extobj->name),
5980 : objtype);
5981 42 : if (objnamespace && *objnamespace)
5982 36 : appendPQExpBuffer(upgrade_buffer, "%s.", fmtId(objnamespace));
5983 42 : appendPQExpBuffer(upgrade_buffer, "%s;\n", objname);
5984 : }
5985 :
5986 : /*
5987 : * getNamespaces:
5988 : * get information about all namespaces in the system catalogs
5989 : */
5990 : void
5991 378 : getNamespaces(Archive *fout)
5992 : {
5993 : PGresult *res;
5994 : int ntups;
5995 : int i;
5996 : PQExpBuffer query;
5997 : NamespaceInfo *nsinfo;
5998 : int i_tableoid;
5999 : int i_oid;
6000 : int i_nspname;
6001 : int i_nspowner;
6002 : int i_nspacl;
6003 : int i_acldefault;
6004 :
6005 378 : query = createPQExpBuffer();
6006 :
6007 : /*
6008 : * we fetch all namespaces including system ones, so that every object we
6009 : * read in can be linked to a containing namespace.
6010 : */
6011 378 : appendPQExpBufferStr(query, "SELECT n.tableoid, n.oid, n.nspname, "
6012 : "n.nspowner, "
6013 : "n.nspacl, "
6014 : "acldefault('n', n.nspowner) AS acldefault "
6015 : "FROM pg_namespace n");
6016 :
6017 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6018 :
6019 378 : ntups = PQntuples(res);
6020 :
6021 378 : nsinfo = (NamespaceInfo *) pg_malloc(ntups * sizeof(NamespaceInfo));
6022 :
6023 378 : i_tableoid = PQfnumber(res, "tableoid");
6024 378 : i_oid = PQfnumber(res, "oid");
6025 378 : i_nspname = PQfnumber(res, "nspname");
6026 378 : i_nspowner = PQfnumber(res, "nspowner");
6027 378 : i_nspacl = PQfnumber(res, "nspacl");
6028 378 : i_acldefault = PQfnumber(res, "acldefault");
6029 :
6030 3262 : for (i = 0; i < ntups; i++)
6031 : {
6032 : const char *nspowner;
6033 :
6034 2884 : nsinfo[i].dobj.objType = DO_NAMESPACE;
6035 2884 : nsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6036 2884 : nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6037 2884 : AssignDumpId(&nsinfo[i].dobj);
6038 2884 : nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
6039 2884 : nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
6040 2884 : nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6041 2884 : nsinfo[i].dacl.privtype = 0;
6042 2884 : nsinfo[i].dacl.initprivs = NULL;
6043 2884 : nspowner = PQgetvalue(res, i, i_nspowner);
6044 2884 : nsinfo[i].nspowner = atooid(nspowner);
6045 2884 : nsinfo[i].rolname = getRoleName(nspowner);
6046 :
6047 : /* Decide whether to dump this namespace */
6048 2884 : selectDumpableNamespace(&nsinfo[i], fout);
6049 :
6050 : /* Mark whether namespace has an ACL */
6051 2884 : if (!PQgetisnull(res, i, i_nspacl))
6052 1258 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6053 :
6054 : /*
6055 : * We ignore any pg_init_privs.initprivs entry for the public schema
6056 : * and assume a predetermined default, for several reasons. First,
6057 : * dropping and recreating the schema removes its pg_init_privs entry,
6058 : * but an empty destination database starts with this ACL nonetheless.
6059 : * Second, we support dump/reload of public schema ownership changes.
6060 : * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
6061 : * initprivs continues to reflect the initial owner. Hence,
6062 : * synthesize the value that nspacl will have after the restore's
6063 : * ALTER SCHEMA OWNER. Third, this makes the destination database
6064 : * match the source's ACL, even if the latter was an initdb-default
6065 : * ACL, which changed in v15. An upgrade pulls in changes to most
6066 : * system object ACLs that the DBA had not customized. We've made the
6067 : * public schema depart from that, because changing its ACL so easily
6068 : * breaks applications.
6069 : */
6070 2884 : if (strcmp(nsinfo[i].dobj.name, "public") == 0)
6071 : {
6072 370 : PQExpBuffer aclarray = createPQExpBuffer();
6073 370 : PQExpBuffer aclitem = createPQExpBuffer();
6074 :
6075 : /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
6076 370 : appendPQExpBufferChar(aclarray, '{');
6077 370 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6078 370 : appendPQExpBufferStr(aclitem, "=UC/");
6079 370 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6080 370 : appendPGArray(aclarray, aclitem->data);
6081 370 : resetPQExpBuffer(aclitem);
6082 370 : appendPQExpBufferStr(aclitem, "=U/");
6083 370 : quoteAclUserName(aclitem, nsinfo[i].rolname);
6084 370 : appendPGArray(aclarray, aclitem->data);
6085 370 : appendPQExpBufferChar(aclarray, '}');
6086 :
6087 370 : nsinfo[i].dacl.privtype = 'i';
6088 370 : nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
6089 370 : nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6090 :
6091 370 : destroyPQExpBuffer(aclarray);
6092 370 : destroyPQExpBuffer(aclitem);
6093 : }
6094 : }
6095 :
6096 378 : PQclear(res);
6097 378 : destroyPQExpBuffer(query);
6098 378 : }
6099 :
6100 : /*
6101 : * findNamespace:
6102 : * given a namespace OID, look up the info read by getNamespaces
6103 : */
6104 : static NamespaceInfo *
6105 1185818 : findNamespace(Oid nsoid)
6106 : {
6107 : NamespaceInfo *nsinfo;
6108 :
6109 1185818 : nsinfo = findNamespaceByOid(nsoid);
6110 1185818 : if (nsinfo == NULL)
6111 0 : pg_fatal("schema with OID %u does not exist", nsoid);
6112 1185818 : return nsinfo;
6113 : }
6114 :
6115 : /*
6116 : * getExtensions:
6117 : * read all extensions in the system catalogs and return them in the
6118 : * ExtensionInfo* structure
6119 : *
6120 : * numExtensions is set to the number of extensions read in
6121 : */
6122 : ExtensionInfo *
6123 378 : getExtensions(Archive *fout, int *numExtensions)
6124 : {
6125 378 : DumpOptions *dopt = fout->dopt;
6126 : PGresult *res;
6127 : int ntups;
6128 : int i;
6129 : PQExpBuffer query;
6130 378 : ExtensionInfo *extinfo = NULL;
6131 : int i_tableoid;
6132 : int i_oid;
6133 : int i_extname;
6134 : int i_nspname;
6135 : int i_extrelocatable;
6136 : int i_extversion;
6137 : int i_extconfig;
6138 : int i_extcondition;
6139 :
6140 378 : query = createPQExpBuffer();
6141 :
6142 378 : appendPQExpBufferStr(query, "SELECT x.tableoid, x.oid, "
6143 : "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition "
6144 : "FROM pg_extension x "
6145 : "JOIN pg_namespace n ON n.oid = x.extnamespace");
6146 :
6147 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6148 :
6149 378 : ntups = PQntuples(res);
6150 378 : if (ntups == 0)
6151 0 : goto cleanup;
6152 :
6153 378 : extinfo = (ExtensionInfo *) pg_malloc(ntups * sizeof(ExtensionInfo));
6154 :
6155 378 : i_tableoid = PQfnumber(res, "tableoid");
6156 378 : i_oid = PQfnumber(res, "oid");
6157 378 : i_extname = PQfnumber(res, "extname");
6158 378 : i_nspname = PQfnumber(res, "nspname");
6159 378 : i_extrelocatable = PQfnumber(res, "extrelocatable");
6160 378 : i_extversion = PQfnumber(res, "extversion");
6161 378 : i_extconfig = PQfnumber(res, "extconfig");
6162 378 : i_extcondition = PQfnumber(res, "extcondition");
6163 :
6164 816 : for (i = 0; i < ntups; i++)
6165 : {
6166 438 : extinfo[i].dobj.objType = DO_EXTENSION;
6167 438 : extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6168 438 : extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6169 438 : AssignDumpId(&extinfo[i].dobj);
6170 438 : extinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_extname));
6171 438 : extinfo[i].namespace = pg_strdup(PQgetvalue(res, i, i_nspname));
6172 438 : extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't';
6173 438 : extinfo[i].extversion = pg_strdup(PQgetvalue(res, i, i_extversion));
6174 438 : extinfo[i].extconfig = pg_strdup(PQgetvalue(res, i, i_extconfig));
6175 438 : extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
6176 :
6177 : /* Decide whether we want to dump it */
6178 438 : selectDumpableExtension(&(extinfo[i]), dopt);
6179 : }
6180 :
6181 378 : cleanup:
6182 378 : PQclear(res);
6183 378 : destroyPQExpBuffer(query);
6184 :
6185 378 : *numExtensions = ntups;
6186 :
6187 378 : return extinfo;
6188 : }
6189 :
6190 : /*
6191 : * getTypes:
6192 : * get information about all types in the system catalogs
6193 : *
6194 : * NB: this must run after getFuncs() because we assume we can do
6195 : * findFuncByOid().
6196 : */
6197 : void
6198 376 : getTypes(Archive *fout)
6199 : {
6200 : PGresult *res;
6201 : int ntups;
6202 : int i;
6203 376 : PQExpBuffer query = createPQExpBuffer();
6204 : TypeInfo *tyinfo;
6205 : ShellTypeInfo *stinfo;
6206 : int i_tableoid;
6207 : int i_oid;
6208 : int i_typname;
6209 : int i_typnamespace;
6210 : int i_typacl;
6211 : int i_acldefault;
6212 : int i_typowner;
6213 : int i_typelem;
6214 : int i_typrelid;
6215 : int i_typrelkind;
6216 : int i_typtype;
6217 : int i_typisdefined;
6218 : int i_isarray;
6219 : int i_typarray;
6220 :
6221 : /*
6222 : * we include even the built-in types because those may be used as array
6223 : * elements by user-defined types
6224 : *
6225 : * we filter out the built-in types when we dump out the types
6226 : *
6227 : * same approach for undefined (shell) types and array types
6228 : *
6229 : * Note: as of 8.3 we can reliably detect whether a type is an
6230 : * auto-generated array type by checking the element type's typarray.
6231 : * (Before that the test is capable of generating false positives.) We
6232 : * still check for name beginning with '_', though, so as to avoid the
6233 : * cost of the subselect probe for all standard types. This would have to
6234 : * be revisited if the backend ever allows renaming of array types.
6235 : */
6236 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, typname, "
6237 : "typnamespace, typacl, "
6238 : "acldefault('T', typowner) AS acldefault, "
6239 : "typowner, "
6240 : "typelem, typrelid, typarray, "
6241 : "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
6242 : "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
6243 : "typtype, typisdefined, "
6244 : "typname[0] = '_' AND typelem != 0 AND "
6245 : "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
6246 : "FROM pg_type");
6247 :
6248 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6249 :
6250 376 : ntups = PQntuples(res);
6251 :
6252 376 : tyinfo = (TypeInfo *) pg_malloc(ntups * sizeof(TypeInfo));
6253 :
6254 376 : i_tableoid = PQfnumber(res, "tableoid");
6255 376 : i_oid = PQfnumber(res, "oid");
6256 376 : i_typname = PQfnumber(res, "typname");
6257 376 : i_typnamespace = PQfnumber(res, "typnamespace");
6258 376 : i_typacl = PQfnumber(res, "typacl");
6259 376 : i_acldefault = PQfnumber(res, "acldefault");
6260 376 : i_typowner = PQfnumber(res, "typowner");
6261 376 : i_typelem = PQfnumber(res, "typelem");
6262 376 : i_typrelid = PQfnumber(res, "typrelid");
6263 376 : i_typrelkind = PQfnumber(res, "typrelkind");
6264 376 : i_typtype = PQfnumber(res, "typtype");
6265 376 : i_typisdefined = PQfnumber(res, "typisdefined");
6266 376 : i_isarray = PQfnumber(res, "isarray");
6267 376 : i_typarray = PQfnumber(res, "typarray");
6268 :
6269 272528 : for (i = 0; i < ntups; i++)
6270 : {
6271 272152 : tyinfo[i].dobj.objType = DO_TYPE;
6272 272152 : tyinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6273 272152 : tyinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6274 272152 : AssignDumpId(&tyinfo[i].dobj);
6275 272152 : tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
6276 544304 : tyinfo[i].dobj.namespace =
6277 272152 : findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
6278 272152 : tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
6279 272152 : tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6280 272152 : tyinfo[i].dacl.privtype = 0;
6281 272152 : tyinfo[i].dacl.initprivs = NULL;
6282 272152 : tyinfo[i].ftypname = NULL; /* may get filled later */
6283 272152 : tyinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_typowner));
6284 272152 : tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
6285 272152 : tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
6286 272152 : tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
6287 272152 : tyinfo[i].typtype = *PQgetvalue(res, i, i_typtype);
6288 272152 : tyinfo[i].shellType = NULL;
6289 :
6290 272152 : if (strcmp(PQgetvalue(res, i, i_typisdefined), "t") == 0)
6291 272048 : tyinfo[i].isDefined = true;
6292 : else
6293 104 : tyinfo[i].isDefined = false;
6294 :
6295 272152 : if (strcmp(PQgetvalue(res, i, i_isarray), "t") == 0)
6296 130572 : tyinfo[i].isArray = true;
6297 : else
6298 141580 : tyinfo[i].isArray = false;
6299 :
6300 272152 : tyinfo[i].typarray = atooid(PQgetvalue(res, i, i_typarray));
6301 :
6302 272152 : if (tyinfo[i].typtype == TYPTYPE_MULTIRANGE)
6303 2520 : tyinfo[i].isMultirange = true;
6304 : else
6305 269632 : tyinfo[i].isMultirange = false;
6306 :
6307 : /* Decide whether we want to dump it */
6308 272152 : selectDumpableType(&tyinfo[i], fout);
6309 :
6310 : /* Mark whether type has an ACL */
6311 272152 : if (!PQgetisnull(res, i, i_typacl))
6312 410 : tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
6313 :
6314 : /*
6315 : * If it's a domain, fetch info about its constraints, if any
6316 : */
6317 272152 : tyinfo[i].nDomChecks = 0;
6318 272152 : tyinfo[i].domChecks = NULL;
6319 272152 : tyinfo[i].notnull = NULL;
6320 272152 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6321 29962 : tyinfo[i].typtype == TYPTYPE_DOMAIN)
6322 316 : getDomainConstraints(fout, &(tyinfo[i]));
6323 :
6324 : /*
6325 : * If it's a base type, make a DumpableObject representing a shell
6326 : * definition of the type. We will need to dump that ahead of the I/O
6327 : * functions for the type. Similarly, range types need a shell
6328 : * definition in case they have a canonicalize function.
6329 : *
6330 : * Note: the shell type doesn't have a catId. You might think it
6331 : * should copy the base type's catId, but then it might capture the
6332 : * pg_depend entries for the type, which we don't want.
6333 : */
6334 272152 : if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
6335 29962 : (tyinfo[i].typtype == TYPTYPE_BASE ||
6336 14554 : tyinfo[i].typtype == TYPTYPE_RANGE))
6337 : {
6338 15656 : stinfo = (ShellTypeInfo *) pg_malloc(sizeof(ShellTypeInfo));
6339 15656 : stinfo->dobj.objType = DO_SHELL_TYPE;
6340 15656 : stinfo->dobj.catId = nilCatalogId;
6341 15656 : AssignDumpId(&stinfo->dobj);
6342 15656 : stinfo->dobj.name = pg_strdup(tyinfo[i].dobj.name);
6343 15656 : stinfo->dobj.namespace = tyinfo[i].dobj.namespace;
6344 15656 : stinfo->baseType = &(tyinfo[i]);
6345 15656 : tyinfo[i].shellType = stinfo;
6346 :
6347 : /*
6348 : * Initially mark the shell type as not to be dumped. We'll only
6349 : * dump it if the I/O or canonicalize functions need to be dumped;
6350 : * this is taken care of while sorting dependencies.
6351 : */
6352 15656 : stinfo->dobj.dump = DUMP_COMPONENT_NONE;
6353 : }
6354 : }
6355 :
6356 376 : PQclear(res);
6357 :
6358 376 : destroyPQExpBuffer(query);
6359 376 : }
6360 :
6361 : /*
6362 : * getOperators:
6363 : * get information about all operators in the system catalogs
6364 : */
6365 : void
6366 376 : getOperators(Archive *fout)
6367 : {
6368 : PGresult *res;
6369 : int ntups;
6370 : int i;
6371 376 : PQExpBuffer query = createPQExpBuffer();
6372 : OprInfo *oprinfo;
6373 : int i_tableoid;
6374 : int i_oid;
6375 : int i_oprname;
6376 : int i_oprnamespace;
6377 : int i_oprowner;
6378 : int i_oprkind;
6379 : int i_oprleft;
6380 : int i_oprright;
6381 : int i_oprcode;
6382 :
6383 : /*
6384 : * find all operators, including builtin operators; we filter out
6385 : * system-defined operators at dump-out time.
6386 : */
6387 :
6388 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, oprname, "
6389 : "oprnamespace, "
6390 : "oprowner, "
6391 : "oprkind, "
6392 : "oprleft, "
6393 : "oprright, "
6394 : "oprcode::oid AS oprcode "
6395 : "FROM pg_operator");
6396 :
6397 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6398 :
6399 376 : ntups = PQntuples(res);
6400 :
6401 376 : oprinfo = (OprInfo *) pg_malloc(ntups * sizeof(OprInfo));
6402 :
6403 376 : i_tableoid = PQfnumber(res, "tableoid");
6404 376 : i_oid = PQfnumber(res, "oid");
6405 376 : i_oprname = PQfnumber(res, "oprname");
6406 376 : i_oprnamespace = PQfnumber(res, "oprnamespace");
6407 376 : i_oprowner = PQfnumber(res, "oprowner");
6408 376 : i_oprkind = PQfnumber(res, "oprkind");
6409 376 : i_oprleft = PQfnumber(res, "oprleft");
6410 376 : i_oprright = PQfnumber(res, "oprright");
6411 376 : i_oprcode = PQfnumber(res, "oprcode");
6412 :
6413 301084 : for (i = 0; i < ntups; i++)
6414 : {
6415 300708 : oprinfo[i].dobj.objType = DO_OPERATOR;
6416 300708 : oprinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6417 300708 : oprinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6418 300708 : AssignDumpId(&oprinfo[i].dobj);
6419 300708 : oprinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oprname));
6420 601416 : oprinfo[i].dobj.namespace =
6421 300708 : findNamespace(atooid(PQgetvalue(res, i, i_oprnamespace)));
6422 300708 : oprinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_oprowner));
6423 300708 : oprinfo[i].oprkind = (PQgetvalue(res, i, i_oprkind))[0];
6424 300708 : oprinfo[i].oprleft = atooid(PQgetvalue(res, i, i_oprleft));
6425 300708 : oprinfo[i].oprright = atooid(PQgetvalue(res, i, i_oprright));
6426 300708 : oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
6427 :
6428 : /* Decide whether we want to dump it */
6429 300708 : selectDumpableObject(&(oprinfo[i].dobj), fout);
6430 : }
6431 :
6432 376 : PQclear(res);
6433 :
6434 376 : destroyPQExpBuffer(query);
6435 376 : }
6436 :
6437 : /*
6438 : * getCollations:
6439 : * get information about all collations in the system catalogs
6440 : */
6441 : void
6442 376 : getCollations(Archive *fout)
6443 : {
6444 : PGresult *res;
6445 : int ntups;
6446 : int i;
6447 : PQExpBuffer query;
6448 : CollInfo *collinfo;
6449 : int i_tableoid;
6450 : int i_oid;
6451 : int i_collname;
6452 : int i_collnamespace;
6453 : int i_collowner;
6454 : int i_collencoding;
6455 :
6456 376 : query = createPQExpBuffer();
6457 :
6458 : /*
6459 : * find all collations, including builtin collations; we filter out
6460 : * system-defined collations at dump-out time.
6461 : */
6462 :
6463 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, collname, "
6464 : "collnamespace, "
6465 : "collowner, "
6466 : "collencoding "
6467 : "FROM pg_collation");
6468 :
6469 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6470 :
6471 376 : ntups = PQntuples(res);
6472 :
6473 376 : collinfo = (CollInfo *) pg_malloc(ntups * sizeof(CollInfo));
6474 :
6475 376 : i_tableoid = PQfnumber(res, "tableoid");
6476 376 : i_oid = PQfnumber(res, "oid");
6477 376 : i_collname = PQfnumber(res, "collname");
6478 376 : i_collnamespace = PQfnumber(res, "collnamespace");
6479 376 : i_collowner = PQfnumber(res, "collowner");
6480 376 : i_collencoding = PQfnumber(res, "collencoding");
6481 :
6482 307414 : for (i = 0; i < ntups; i++)
6483 : {
6484 307038 : collinfo[i].dobj.objType = DO_COLLATION;
6485 307038 : collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6486 307038 : collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6487 307038 : AssignDumpId(&collinfo[i].dobj);
6488 307038 : collinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_collname));
6489 614076 : collinfo[i].dobj.namespace =
6490 307038 : findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)));
6491 307038 : collinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_collowner));
6492 307038 : collinfo[i].collencoding = atoi(PQgetvalue(res, i, i_collencoding));
6493 :
6494 : /* Decide whether we want to dump it */
6495 307038 : selectDumpableObject(&(collinfo[i].dobj), fout);
6496 : }
6497 :
6498 376 : PQclear(res);
6499 :
6500 376 : destroyPQExpBuffer(query);
6501 376 : }
6502 :
6503 : /*
6504 : * getConversions:
6505 : * get information about all conversions in the system catalogs
6506 : */
6507 : void
6508 376 : getConversions(Archive *fout)
6509 : {
6510 : PGresult *res;
6511 : int ntups;
6512 : int i;
6513 : PQExpBuffer query;
6514 : ConvInfo *convinfo;
6515 : int i_tableoid;
6516 : int i_oid;
6517 : int i_conname;
6518 : int i_connamespace;
6519 : int i_conowner;
6520 :
6521 376 : query = createPQExpBuffer();
6522 :
6523 : /*
6524 : * find all conversions, including builtin conversions; we filter out
6525 : * system-defined conversions at dump-out time.
6526 : */
6527 :
6528 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
6529 : "connamespace, "
6530 : "conowner "
6531 : "FROM pg_conversion");
6532 :
6533 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6534 :
6535 376 : ntups = PQntuples(res);
6536 :
6537 376 : convinfo = (ConvInfo *) pg_malloc(ntups * sizeof(ConvInfo));
6538 :
6539 376 : i_tableoid = PQfnumber(res, "tableoid");
6540 376 : i_oid = PQfnumber(res, "oid");
6541 376 : i_conname = PQfnumber(res, "conname");
6542 376 : i_connamespace = PQfnumber(res, "connamespace");
6543 376 : i_conowner = PQfnumber(res, "conowner");
6544 :
6545 48594 : for (i = 0; i < ntups; i++)
6546 : {
6547 48218 : convinfo[i].dobj.objType = DO_CONVERSION;
6548 48218 : convinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6549 48218 : convinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6550 48218 : AssignDumpId(&convinfo[i].dobj);
6551 48218 : convinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
6552 96436 : convinfo[i].dobj.namespace =
6553 48218 : findNamespace(atooid(PQgetvalue(res, i, i_connamespace)));
6554 48218 : convinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_conowner));
6555 :
6556 : /* Decide whether we want to dump it */
6557 48218 : selectDumpableObject(&(convinfo[i].dobj), fout);
6558 : }
6559 :
6560 376 : PQclear(res);
6561 :
6562 376 : destroyPQExpBuffer(query);
6563 376 : }
6564 :
6565 : /*
6566 : * getAccessMethods:
6567 : * get information about all user-defined access methods
6568 : */
6569 : void
6570 376 : getAccessMethods(Archive *fout)
6571 : {
6572 : PGresult *res;
6573 : int ntups;
6574 : int i;
6575 : PQExpBuffer query;
6576 : AccessMethodInfo *aminfo;
6577 : int i_tableoid;
6578 : int i_oid;
6579 : int i_amname;
6580 : int i_amhandler;
6581 : int i_amtype;
6582 :
6583 376 : query = createPQExpBuffer();
6584 :
6585 : /*
6586 : * Select all access methods from pg_am table. v9.6 introduced CREATE
6587 : * ACCESS METHOD, so earlier versions usually have only built-in access
6588 : * methods. v9.6 also changed the access method API, replacing dozens of
6589 : * pg_am columns with amhandler. Even if a user created an access method
6590 : * by "INSERT INTO pg_am", we have no way to translate pre-v9.6 pg_am
6591 : * columns to a v9.6+ CREATE ACCESS METHOD. Hence, before v9.6, read
6592 : * pg_am just to facilitate findAccessMethodByOid() providing the
6593 : * OID-to-name mapping.
6594 : */
6595 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, amname, ");
6596 376 : if (fout->remoteVersion >= 90600)
6597 376 : appendPQExpBufferStr(query,
6598 : "amtype, "
6599 : "amhandler::pg_catalog.regproc AS amhandler ");
6600 : else
6601 0 : appendPQExpBufferStr(query,
6602 : "'i'::pg_catalog.\"char\" AS amtype, "
6603 : "'-'::pg_catalog.regproc AS amhandler ");
6604 376 : appendPQExpBufferStr(query, "FROM pg_am");
6605 :
6606 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6607 :
6608 376 : ntups = PQntuples(res);
6609 :
6610 376 : aminfo = (AccessMethodInfo *) pg_malloc(ntups * sizeof(AccessMethodInfo));
6611 :
6612 376 : i_tableoid = PQfnumber(res, "tableoid");
6613 376 : i_oid = PQfnumber(res, "oid");
6614 376 : i_amname = PQfnumber(res, "amname");
6615 376 : i_amhandler = PQfnumber(res, "amhandler");
6616 376 : i_amtype = PQfnumber(res, "amtype");
6617 :
6618 3252 : for (i = 0; i < ntups; i++)
6619 : {
6620 2876 : aminfo[i].dobj.objType = DO_ACCESS_METHOD;
6621 2876 : aminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6622 2876 : aminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6623 2876 : AssignDumpId(&aminfo[i].dobj);
6624 2876 : aminfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_amname));
6625 2876 : aminfo[i].dobj.namespace = NULL;
6626 2876 : aminfo[i].amhandler = pg_strdup(PQgetvalue(res, i, i_amhandler));
6627 2876 : aminfo[i].amtype = *(PQgetvalue(res, i, i_amtype));
6628 :
6629 : /* Decide whether we want to dump it */
6630 2876 : selectDumpableAccessMethod(&(aminfo[i]), fout);
6631 : }
6632 :
6633 376 : PQclear(res);
6634 :
6635 376 : destroyPQExpBuffer(query);
6636 376 : }
6637 :
6638 :
6639 : /*
6640 : * getOpclasses:
6641 : * get information about all opclasses in the system catalogs
6642 : */
6643 : void
6644 376 : getOpclasses(Archive *fout)
6645 : {
6646 : PGresult *res;
6647 : int ntups;
6648 : int i;
6649 376 : PQExpBuffer query = createPQExpBuffer();
6650 : OpclassInfo *opcinfo;
6651 : int i_tableoid;
6652 : int i_oid;
6653 : int i_opcmethod;
6654 : int i_opcname;
6655 : int i_opcnamespace;
6656 : int i_opcowner;
6657 :
6658 : /*
6659 : * find all opclasses, including builtin opclasses; we filter out
6660 : * system-defined opclasses at dump-out time.
6661 : */
6662 :
6663 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opcmethod, opcname, "
6664 : "opcnamespace, "
6665 : "opcowner "
6666 : "FROM pg_opclass");
6667 :
6668 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6669 :
6670 376 : ntups = PQntuples(res);
6671 :
6672 376 : opcinfo = (OpclassInfo *) pg_malloc(ntups * sizeof(OpclassInfo));
6673 :
6674 376 : i_tableoid = PQfnumber(res, "tableoid");
6675 376 : i_oid = PQfnumber(res, "oid");
6676 376 : i_opcmethod = PQfnumber(res, "opcmethod");
6677 376 : i_opcname = PQfnumber(res, "opcname");
6678 376 : i_opcnamespace = PQfnumber(res, "opcnamespace");
6679 376 : i_opcowner = PQfnumber(res, "opcowner");
6680 :
6681 67240 : for (i = 0; i < ntups; i++)
6682 : {
6683 66864 : opcinfo[i].dobj.objType = DO_OPCLASS;
6684 66864 : opcinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6685 66864 : opcinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6686 66864 : AssignDumpId(&opcinfo[i].dobj);
6687 66864 : opcinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opcname));
6688 133728 : opcinfo[i].dobj.namespace =
6689 66864 : findNamespace(atooid(PQgetvalue(res, i, i_opcnamespace)));
6690 66864 : opcinfo[i].opcmethod = atooid(PQgetvalue(res, i, i_opcmethod));
6691 66864 : opcinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opcowner));
6692 :
6693 : /* Decide whether we want to dump it */
6694 66864 : selectDumpableObject(&(opcinfo[i].dobj), fout);
6695 : }
6696 :
6697 376 : PQclear(res);
6698 :
6699 376 : destroyPQExpBuffer(query);
6700 376 : }
6701 :
6702 : /*
6703 : * getOpfamilies:
6704 : * get information about all opfamilies in the system catalogs
6705 : */
6706 : void
6707 376 : getOpfamilies(Archive *fout)
6708 : {
6709 : PGresult *res;
6710 : int ntups;
6711 : int i;
6712 : PQExpBuffer query;
6713 : OpfamilyInfo *opfinfo;
6714 : int i_tableoid;
6715 : int i_oid;
6716 : int i_opfmethod;
6717 : int i_opfname;
6718 : int i_opfnamespace;
6719 : int i_opfowner;
6720 :
6721 376 : query = createPQExpBuffer();
6722 :
6723 : /*
6724 : * find all opfamilies, including builtin opfamilies; we filter out
6725 : * system-defined opfamilies at dump-out time.
6726 : */
6727 :
6728 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, opfmethod, opfname, "
6729 : "opfnamespace, "
6730 : "opfowner "
6731 : "FROM pg_opfamily");
6732 :
6733 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6734 :
6735 376 : ntups = PQntuples(res);
6736 :
6737 376 : opfinfo = (OpfamilyInfo *) pg_malloc(ntups * sizeof(OpfamilyInfo));
6738 :
6739 376 : i_tableoid = PQfnumber(res, "tableoid");
6740 376 : i_oid = PQfnumber(res, "oid");
6741 376 : i_opfname = PQfnumber(res, "opfname");
6742 376 : i_opfmethod = PQfnumber(res, "opfmethod");
6743 376 : i_opfnamespace = PQfnumber(res, "opfnamespace");
6744 376 : i_opfowner = PQfnumber(res, "opfowner");
6745 :
6746 55550 : for (i = 0; i < ntups; i++)
6747 : {
6748 55174 : opfinfo[i].dobj.objType = DO_OPFAMILY;
6749 55174 : opfinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6750 55174 : opfinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6751 55174 : AssignDumpId(&opfinfo[i].dobj);
6752 55174 : opfinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_opfname));
6753 110348 : opfinfo[i].dobj.namespace =
6754 55174 : findNamespace(atooid(PQgetvalue(res, i, i_opfnamespace)));
6755 55174 : opfinfo[i].opfmethod = atooid(PQgetvalue(res, i, i_opfmethod));
6756 55174 : opfinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_opfowner));
6757 :
6758 : /* Decide whether we want to dump it */
6759 55174 : selectDumpableObject(&(opfinfo[i].dobj), fout);
6760 : }
6761 :
6762 376 : PQclear(res);
6763 :
6764 376 : destroyPQExpBuffer(query);
6765 376 : }
6766 :
6767 : /*
6768 : * getAggregates:
6769 : * get information about all user-defined aggregates in the system catalogs
6770 : */
6771 : void
6772 376 : getAggregates(Archive *fout)
6773 : {
6774 376 : DumpOptions *dopt = fout->dopt;
6775 : PGresult *res;
6776 : int ntups;
6777 : int i;
6778 376 : PQExpBuffer query = createPQExpBuffer();
6779 : AggInfo *agginfo;
6780 : int i_tableoid;
6781 : int i_oid;
6782 : int i_aggname;
6783 : int i_aggnamespace;
6784 : int i_pronargs;
6785 : int i_proargtypes;
6786 : int i_proowner;
6787 : int i_aggacl;
6788 : int i_acldefault;
6789 :
6790 : /*
6791 : * Find all interesting aggregates. See comment in getFuncs() for the
6792 : * rationale behind the filtering logic.
6793 : */
6794 376 : if (fout->remoteVersion >= 90600)
6795 : {
6796 : const char *agg_check;
6797 :
6798 752 : agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
6799 376 : : "p.proisagg");
6800 :
6801 376 : appendPQExpBuffer(query, "SELECT p.tableoid, p.oid, "
6802 : "p.proname AS aggname, "
6803 : "p.pronamespace AS aggnamespace, "
6804 : "p.pronargs, p.proargtypes, "
6805 : "p.proowner, "
6806 : "p.proacl AS aggacl, "
6807 : "acldefault('f', p.proowner) AS acldefault "
6808 : "FROM pg_proc p "
6809 : "LEFT JOIN pg_init_privs pip ON "
6810 : "(p.oid = pip.objoid "
6811 : "AND pip.classoid = 'pg_proc'::regclass "
6812 : "AND pip.objsubid = 0) "
6813 : "WHERE %s AND ("
6814 : "p.pronamespace != "
6815 : "(SELECT oid FROM pg_namespace "
6816 : "WHERE nspname = 'pg_catalog') OR "
6817 : "p.proacl IS DISTINCT FROM pip.initprivs",
6818 : agg_check);
6819 376 : if (dopt->binary_upgrade)
6820 76 : appendPQExpBufferStr(query,
6821 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6822 : "classid = 'pg_proc'::regclass AND "
6823 : "objid = p.oid AND "
6824 : "refclassid = 'pg_extension'::regclass AND "
6825 : "deptype = 'e')");
6826 376 : appendPQExpBufferChar(query, ')');
6827 : }
6828 : else
6829 : {
6830 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, proname AS aggname, "
6831 : "pronamespace AS aggnamespace, "
6832 : "pronargs, proargtypes, "
6833 : "proowner, "
6834 : "proacl AS aggacl, "
6835 : "acldefault('f', proowner) AS acldefault "
6836 : "FROM pg_proc p "
6837 : "WHERE proisagg AND ("
6838 : "pronamespace != "
6839 : "(SELECT oid FROM pg_namespace "
6840 : "WHERE nspname = 'pg_catalog')");
6841 0 : if (dopt->binary_upgrade)
6842 0 : appendPQExpBufferStr(query,
6843 : " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6844 : "classid = 'pg_proc'::regclass AND "
6845 : "objid = p.oid AND "
6846 : "refclassid = 'pg_extension'::regclass AND "
6847 : "deptype = 'e')");
6848 0 : appendPQExpBufferChar(query, ')');
6849 : }
6850 :
6851 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
6852 :
6853 376 : ntups = PQntuples(res);
6854 :
6855 376 : agginfo = (AggInfo *) pg_malloc(ntups * sizeof(AggInfo));
6856 :
6857 376 : i_tableoid = PQfnumber(res, "tableoid");
6858 376 : i_oid = PQfnumber(res, "oid");
6859 376 : i_aggname = PQfnumber(res, "aggname");
6860 376 : i_aggnamespace = PQfnumber(res, "aggnamespace");
6861 376 : i_pronargs = PQfnumber(res, "pronargs");
6862 376 : i_proargtypes = PQfnumber(res, "proargtypes");
6863 376 : i_proowner = PQfnumber(res, "proowner");
6864 376 : i_aggacl = PQfnumber(res, "aggacl");
6865 376 : i_acldefault = PQfnumber(res, "acldefault");
6866 :
6867 1174 : for (i = 0; i < ntups; i++)
6868 : {
6869 798 : agginfo[i].aggfn.dobj.objType = DO_AGG;
6870 798 : agginfo[i].aggfn.dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
6871 798 : agginfo[i].aggfn.dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
6872 798 : AssignDumpId(&agginfo[i].aggfn.dobj);
6873 798 : agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
6874 1596 : agginfo[i].aggfn.dobj.namespace =
6875 798 : findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
6876 798 : agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
6877 798 : agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
6878 798 : agginfo[i].aggfn.dacl.privtype = 0;
6879 798 : agginfo[i].aggfn.dacl.initprivs = NULL;
6880 798 : agginfo[i].aggfn.rolname = getRoleName(PQgetvalue(res, i, i_proowner));
6881 798 : agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
6882 798 : agginfo[i].aggfn.prorettype = InvalidOid; /* not saved */
6883 798 : agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
6884 798 : if (agginfo[i].aggfn.nargs == 0)
6885 112 : agginfo[i].aggfn.argtypes = NULL;
6886 : else
6887 : {
6888 686 : agginfo[i].aggfn.argtypes = (Oid *) pg_malloc(agginfo[i].aggfn.nargs * sizeof(Oid));
6889 686 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
6890 686 : agginfo[i].aggfn.argtypes,
6891 686 : agginfo[i].aggfn.nargs);
6892 : }
6893 798 : agginfo[i].aggfn.postponed_def = false; /* might get set during sort */
6894 :
6895 : /* Decide whether we want to dump it */
6896 798 : selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
6897 :
6898 : /* Mark whether aggregate has an ACL */
6899 798 : if (!PQgetisnull(res, i, i_aggacl))
6900 50 : agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
6901 : }
6902 :
6903 376 : PQclear(res);
6904 :
6905 376 : destroyPQExpBuffer(query);
6906 376 : }
6907 :
6908 : /*
6909 : * getFuncs:
6910 : * get information about all user-defined functions in the system catalogs
6911 : */
6912 : void
6913 376 : getFuncs(Archive *fout)
6914 : {
6915 376 : DumpOptions *dopt = fout->dopt;
6916 : PGresult *res;
6917 : int ntups;
6918 : int i;
6919 376 : PQExpBuffer query = createPQExpBuffer();
6920 : FuncInfo *finfo;
6921 : int i_tableoid;
6922 : int i_oid;
6923 : int i_proname;
6924 : int i_pronamespace;
6925 : int i_proowner;
6926 : int i_prolang;
6927 : int i_pronargs;
6928 : int i_proargtypes;
6929 : int i_prorettype;
6930 : int i_proacl;
6931 : int i_acldefault;
6932 :
6933 : /*
6934 : * Find all interesting functions. This is a bit complicated:
6935 : *
6936 : * 1. Always exclude aggregates; those are handled elsewhere.
6937 : *
6938 : * 2. Always exclude functions that are internally dependent on something
6939 : * else, since presumably those will be created as a result of creating
6940 : * the something else. This currently acts only to suppress constructor
6941 : * functions for range types. Note this is OK only because the
6942 : * constructors don't have any dependencies the range type doesn't have;
6943 : * otherwise we might not get creation ordering correct.
6944 : *
6945 : * 3. Otherwise, we normally exclude functions in pg_catalog. However, if
6946 : * they're members of extensions and we are in binary-upgrade mode then
6947 : * include them, since we want to dump extension members individually in
6948 : * that mode. Also, if they are used by casts or transforms then we need
6949 : * to gather the information about them, though they won't be dumped if
6950 : * they are built-in. Also, in 9.6 and up, include functions in
6951 : * pg_catalog if they have an ACL different from what's shown in
6952 : * pg_init_privs (so we have to join to pg_init_privs; annoying).
6953 : */
6954 376 : if (fout->remoteVersion >= 90600)
6955 : {
6956 : const char *not_agg_check;
6957 :
6958 752 : not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
6959 376 : : "NOT p.proisagg");
6960 :
6961 376 : appendPQExpBuffer(query,
6962 : "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
6963 : "p.pronargs, p.proargtypes, p.prorettype, "
6964 : "p.proacl, "
6965 : "acldefault('f', p.proowner) AS acldefault, "
6966 : "p.pronamespace, "
6967 : "p.proowner "
6968 : "FROM pg_proc p "
6969 : "LEFT JOIN pg_init_privs pip ON "
6970 : "(p.oid = pip.objoid "
6971 : "AND pip.classoid = 'pg_proc'::regclass "
6972 : "AND pip.objsubid = 0) "
6973 : "WHERE %s"
6974 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
6975 : "WHERE classid = 'pg_proc'::regclass AND "
6976 : "objid = p.oid AND deptype = 'i')"
6977 : "\n AND ("
6978 : "\n pronamespace != "
6979 : "(SELECT oid FROM pg_namespace "
6980 : "WHERE nspname = 'pg_catalog')"
6981 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
6982 : "\n WHERE pg_cast.oid > %u "
6983 : "\n AND p.oid = pg_cast.castfunc)"
6984 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
6985 : "\n WHERE pg_transform.oid > %u AND "
6986 : "\n (p.oid = pg_transform.trffromsql"
6987 : "\n OR p.oid = pg_transform.trftosql))",
6988 : not_agg_check,
6989 : g_last_builtin_oid,
6990 : g_last_builtin_oid);
6991 376 : if (dopt->binary_upgrade)
6992 76 : appendPQExpBufferStr(query,
6993 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
6994 : "classid = 'pg_proc'::regclass AND "
6995 : "objid = p.oid AND "
6996 : "refclassid = 'pg_extension'::regclass AND "
6997 : "deptype = 'e')");
6998 376 : appendPQExpBufferStr(query,
6999 : "\n OR p.proacl IS DISTINCT FROM pip.initprivs");
7000 376 : appendPQExpBufferChar(query, ')');
7001 : }
7002 : else
7003 : {
7004 0 : appendPQExpBuffer(query,
7005 : "SELECT tableoid, oid, proname, prolang, "
7006 : "pronargs, proargtypes, prorettype, proacl, "
7007 : "acldefault('f', proowner) AS acldefault, "
7008 : "pronamespace, "
7009 : "proowner "
7010 : "FROM pg_proc p "
7011 : "WHERE NOT proisagg"
7012 : "\n AND NOT EXISTS (SELECT 1 FROM pg_depend "
7013 : "WHERE classid = 'pg_proc'::regclass AND "
7014 : "objid = p.oid AND deptype = 'i')"
7015 : "\n AND ("
7016 : "\n pronamespace != "
7017 : "(SELECT oid FROM pg_namespace "
7018 : "WHERE nspname = 'pg_catalog')"
7019 : "\n OR EXISTS (SELECT 1 FROM pg_cast"
7020 : "\n WHERE pg_cast.oid > '%u'::oid"
7021 : "\n AND p.oid = pg_cast.castfunc)",
7022 : g_last_builtin_oid);
7023 :
7024 0 : if (fout->remoteVersion >= 90500)
7025 0 : appendPQExpBuffer(query,
7026 : "\n OR EXISTS (SELECT 1 FROM pg_transform"
7027 : "\n WHERE pg_transform.oid > '%u'::oid"
7028 : "\n AND (p.oid = pg_transform.trffromsql"
7029 : "\n OR p.oid = pg_transform.trftosql))",
7030 : g_last_builtin_oid);
7031 :
7032 0 : if (dopt->binary_upgrade)
7033 0 : appendPQExpBufferStr(query,
7034 : "\n OR EXISTS(SELECT 1 FROM pg_depend WHERE "
7035 : "classid = 'pg_proc'::regclass AND "
7036 : "objid = p.oid AND "
7037 : "refclassid = 'pg_extension'::regclass AND "
7038 : "deptype = 'e')");
7039 0 : appendPQExpBufferChar(query, ')');
7040 : }
7041 :
7042 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7043 :
7044 376 : ntups = PQntuples(res);
7045 :
7046 376 : finfo = (FuncInfo *) pg_malloc0(ntups * sizeof(FuncInfo));
7047 :
7048 376 : i_tableoid = PQfnumber(res, "tableoid");
7049 376 : i_oid = PQfnumber(res, "oid");
7050 376 : i_proname = PQfnumber(res, "proname");
7051 376 : i_pronamespace = PQfnumber(res, "pronamespace");
7052 376 : i_proowner = PQfnumber(res, "proowner");
7053 376 : i_prolang = PQfnumber(res, "prolang");
7054 376 : i_pronargs = PQfnumber(res, "pronargs");
7055 376 : i_proargtypes = PQfnumber(res, "proargtypes");
7056 376 : i_prorettype = PQfnumber(res, "prorettype");
7057 376 : i_proacl = PQfnumber(res, "proacl");
7058 376 : i_acldefault = PQfnumber(res, "acldefault");
7059 :
7060 9956 : for (i = 0; i < ntups; i++)
7061 : {
7062 9580 : finfo[i].dobj.objType = DO_FUNC;
7063 9580 : finfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
7064 9580 : finfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
7065 9580 : AssignDumpId(&finfo[i].dobj);
7066 9580 : finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
7067 19160 : finfo[i].dobj.namespace =
7068 9580 : findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
7069 9580 : finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
7070 9580 : finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7071 9580 : finfo[i].dacl.privtype = 0;
7072 9580 : finfo[i].dacl.initprivs = NULL;
7073 9580 : finfo[i].rolname = getRoleName(PQgetvalue(res, i, i_proowner));
7074 9580 : finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
7075 9580 : finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
7076 9580 : finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
7077 9580 : if (finfo[i].nargs == 0)
7078 2144 : finfo[i].argtypes = NULL;
7079 : else
7080 : {
7081 7436 : finfo[i].argtypes = (Oid *) pg_malloc(finfo[i].nargs * sizeof(Oid));
7082 7436 : parseOidArray(PQgetvalue(res, i, i_proargtypes),
7083 7436 : finfo[i].argtypes, finfo[i].nargs);
7084 : }
7085 9580 : finfo[i].postponed_def = false; /* might get set during sort */
7086 :
7087 : /* Decide whether we want to dump it */
7088 9580 : selectDumpableObject(&(finfo[i].dobj), fout);
7089 :
7090 : /* Mark whether function has an ACL */
7091 9580 : if (!PQgetisnull(res, i, i_proacl))
7092 280 : finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7093 : }
7094 :
7095 376 : PQclear(res);
7096 :
7097 376 : destroyPQExpBuffer(query);
7098 376 : }
7099 :
7100 : /*
7101 : * getRelationStatistics
7102 : * register the statistics object as a dependent of the relation.
7103 : *
7104 : * reltuples is passed as a string to avoid complexities in converting from/to
7105 : * floating point.
7106 : */
7107 : static RelStatsInfo *
7108 19176 : getRelationStatistics(Archive *fout, DumpableObject *rel, int32 relpages,
7109 : char *reltuples, int32 relallvisible,
7110 : int32 relallfrozen, char relkind,
7111 : char **indAttNames, int nindAttNames)
7112 : {
7113 19176 : if (!fout->dopt->dumpStatistics)
7114 12108 : return NULL;
7115 :
7116 7068 : if ((relkind == RELKIND_RELATION) ||
7117 2968 : (relkind == RELKIND_PARTITIONED_TABLE) ||
7118 1772 : (relkind == RELKIND_INDEX) ||
7119 1144 : (relkind == RELKIND_PARTITIONED_INDEX) ||
7120 538 : (relkind == RELKIND_MATVIEW ||
7121 : relkind == RELKIND_FOREIGN_TABLE))
7122 : {
7123 6594 : RelStatsInfo *info = pg_malloc0(sizeof(RelStatsInfo));
7124 6594 : DumpableObject *dobj = &info->dobj;
7125 :
7126 6594 : dobj->objType = DO_REL_STATS;
7127 6594 : dobj->catId.tableoid = 0;
7128 6594 : dobj->catId.oid = 0;
7129 6594 : AssignDumpId(dobj);
7130 6594 : dobj->dependencies = (DumpId *) pg_malloc(sizeof(DumpId));
7131 6594 : dobj->dependencies[0] = rel->dumpId;
7132 6594 : dobj->nDeps = 1;
7133 6594 : dobj->allocDeps = 1;
7134 6594 : dobj->components |= DUMP_COMPONENT_STATISTICS;
7135 6594 : dobj->name = pg_strdup(rel->name);
7136 6594 : dobj->namespace = rel->namespace;
7137 6594 : info->relpages = relpages;
7138 6594 : info->reltuples = pstrdup(reltuples);
7139 6594 : info->relallvisible = relallvisible;
7140 6594 : info->relallfrozen = relallfrozen;
7141 6594 : info->relkind = relkind;
7142 6594 : info->indAttNames = indAttNames;
7143 6594 : info->nindAttNames = nindAttNames;
7144 :
7145 : /*
7146 : * Ordinarily, stats go in SECTION_DATA for tables and
7147 : * SECTION_POST_DATA for indexes.
7148 : *
7149 : * However, the section may be updated later for materialized view
7150 : * stats. REFRESH MATERIALIZED VIEW replaces the storage and resets
7151 : * the stats, so the stats must be restored after the data. Also, the
7152 : * materialized view definition may be postponed to SECTION_POST_DATA
7153 : * (see repairMatViewBoundaryMultiLoop()).
7154 : */
7155 6594 : switch (info->relkind)
7156 : {
7157 4770 : case RELKIND_RELATION:
7158 : case RELKIND_PARTITIONED_TABLE:
7159 : case RELKIND_MATVIEW:
7160 : case RELKIND_FOREIGN_TABLE:
7161 4770 : info->section = SECTION_DATA;
7162 4770 : break;
7163 1824 : case RELKIND_INDEX:
7164 : case RELKIND_PARTITIONED_INDEX:
7165 1824 : info->section = SECTION_POST_DATA;
7166 1824 : break;
7167 0 : default:
7168 0 : pg_fatal("cannot dump statistics for relation kind \"%c\"",
7169 : info->relkind);
7170 : }
7171 :
7172 6594 : return info;
7173 : }
7174 474 : return NULL;
7175 : }
7176 :
7177 : /*
7178 : * getTables
7179 : * read all the tables (no indexes) in the system catalogs,
7180 : * and return them as an array of TableInfo structures
7181 : *
7182 : * *numTables is set to the number of tables read in
7183 : */
7184 : TableInfo *
7185 378 : getTables(Archive *fout, int *numTables)
7186 : {
7187 378 : DumpOptions *dopt = fout->dopt;
7188 : PGresult *res;
7189 : int ntups;
7190 : int i;
7191 378 : PQExpBuffer query = createPQExpBuffer();
7192 : TableInfo *tblinfo;
7193 : int i_reltableoid;
7194 : int i_reloid;
7195 : int i_relname;
7196 : int i_relnamespace;
7197 : int i_relkind;
7198 : int i_reltype;
7199 : int i_relowner;
7200 : int i_relchecks;
7201 : int i_relhasindex;
7202 : int i_relhasrules;
7203 : int i_relpages;
7204 : int i_reltuples;
7205 : int i_relallvisible;
7206 : int i_relallfrozen;
7207 : int i_toastpages;
7208 : int i_owning_tab;
7209 : int i_owning_col;
7210 : int i_reltablespace;
7211 : int i_relhasoids;
7212 : int i_relhastriggers;
7213 : int i_relpersistence;
7214 : int i_relispopulated;
7215 : int i_relreplident;
7216 : int i_relrowsec;
7217 : int i_relforcerowsec;
7218 : int i_relfrozenxid;
7219 : int i_toastfrozenxid;
7220 : int i_toastoid;
7221 : int i_relminmxid;
7222 : int i_toastminmxid;
7223 : int i_reloptions;
7224 : int i_checkoption;
7225 : int i_toastreloptions;
7226 : int i_reloftype;
7227 : int i_foreignserver;
7228 : int i_amname;
7229 : int i_is_identity_sequence;
7230 : int i_relacl;
7231 : int i_acldefault;
7232 : int i_ispartition;
7233 :
7234 : /*
7235 : * Find all the tables and table-like objects.
7236 : *
7237 : * We must fetch all tables in this phase because otherwise we cannot
7238 : * correctly identify inherited columns, owned sequences, etc.
7239 : *
7240 : * We include system catalogs, so that we can work if a user table is
7241 : * defined to inherit from a system catalog (pretty weird, but...)
7242 : *
7243 : * Note: in this phase we should collect only a minimal amount of
7244 : * information about each table, basically just enough to decide if it is
7245 : * interesting. In particular, since we do not yet have lock on any user
7246 : * table, we MUST NOT invoke any server-side data collection functions
7247 : * (for instance, pg_get_partkeydef()). Those are likely to fail or give
7248 : * wrong answers if any concurrent DDL is happening.
7249 : */
7250 :
7251 378 : appendPQExpBufferStr(query,
7252 : "SELECT c.tableoid, c.oid, c.relname, "
7253 : "c.relnamespace, c.relkind, c.reltype, "
7254 : "c.relowner, "
7255 : "c.relchecks, "
7256 : "c.relhasindex, c.relhasrules, c.relpages, "
7257 : "c.reltuples, c.relallvisible, ");
7258 :
7259 378 : if (fout->remoteVersion >= 180000)
7260 378 : appendPQExpBufferStr(query, "c.relallfrozen, ");
7261 : else
7262 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7263 :
7264 378 : appendPQExpBufferStr(query,
7265 : "c.relhastriggers, c.relpersistence, "
7266 : "c.reloftype, "
7267 : "c.relacl, "
7268 : "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
7269 : " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, "
7270 : "CASE WHEN c.relkind = " CppAsString2(RELKIND_FOREIGN_TABLE) " THEN "
7271 : "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
7272 : "ELSE 0 END AS foreignserver, "
7273 : "c.relfrozenxid, tc.relfrozenxid AS tfrozenxid, "
7274 : "tc.oid AS toid, "
7275 : "tc.relpages AS toastpages, "
7276 : "tc.reloptions AS toast_reloptions, "
7277 : "d.refobjid AS owning_tab, "
7278 : "d.refobjsubid AS owning_col, "
7279 : "tsp.spcname AS reltablespace, ");
7280 :
7281 378 : if (fout->remoteVersion >= 120000)
7282 378 : appendPQExpBufferStr(query,
7283 : "false AS relhasoids, ");
7284 : else
7285 0 : appendPQExpBufferStr(query,
7286 : "c.relhasoids, ");
7287 :
7288 378 : if (fout->remoteVersion >= 90300)
7289 378 : appendPQExpBufferStr(query,
7290 : "c.relispopulated, ");
7291 : else
7292 0 : appendPQExpBufferStr(query,
7293 : "'t' as relispopulated, ");
7294 :
7295 378 : if (fout->remoteVersion >= 90400)
7296 378 : appendPQExpBufferStr(query,
7297 : "c.relreplident, ");
7298 : else
7299 0 : appendPQExpBufferStr(query,
7300 : "'d' AS relreplident, ");
7301 :
7302 378 : if (fout->remoteVersion >= 90500)
7303 378 : appendPQExpBufferStr(query,
7304 : "c.relrowsecurity, c.relforcerowsecurity, ");
7305 : else
7306 0 : appendPQExpBufferStr(query,
7307 : "false AS relrowsecurity, "
7308 : "false AS relforcerowsecurity, ");
7309 :
7310 378 : if (fout->remoteVersion >= 90300)
7311 378 : appendPQExpBufferStr(query,
7312 : "c.relminmxid, tc.relminmxid AS tminmxid, ");
7313 : else
7314 0 : appendPQExpBufferStr(query,
7315 : "0 AS relminmxid, 0 AS tminmxid, ");
7316 :
7317 378 : if (fout->remoteVersion >= 90300)
7318 378 : appendPQExpBufferStr(query,
7319 : "array_remove(array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, "
7320 : "CASE WHEN 'check_option=local' = ANY (c.reloptions) THEN 'LOCAL'::text "
7321 : "WHEN 'check_option=cascaded' = ANY (c.reloptions) THEN 'CASCADED'::text ELSE NULL END AS checkoption, ");
7322 : else
7323 0 : appendPQExpBufferStr(query,
7324 : "c.reloptions, NULL AS checkoption, ");
7325 :
7326 378 : if (fout->remoteVersion >= 90600)
7327 378 : appendPQExpBufferStr(query,
7328 : "am.amname, ");
7329 : else
7330 0 : appendPQExpBufferStr(query,
7331 : "NULL AS amname, ");
7332 :
7333 378 : if (fout->remoteVersion >= 90600)
7334 378 : appendPQExpBufferStr(query,
7335 : "(d.deptype = 'i') IS TRUE AS is_identity_sequence, ");
7336 : else
7337 0 : appendPQExpBufferStr(query,
7338 : "false AS is_identity_sequence, ");
7339 :
7340 378 : if (fout->remoteVersion >= 100000)
7341 378 : appendPQExpBufferStr(query,
7342 : "c.relispartition AS ispartition ");
7343 : else
7344 0 : appendPQExpBufferStr(query,
7345 : "false AS ispartition ");
7346 :
7347 : /*
7348 : * Left join to pg_depend to pick up dependency info linking sequences to
7349 : * their owning column, if any (note this dependency is AUTO except for
7350 : * identity sequences, where it's INTERNAL). Also join to pg_tablespace to
7351 : * collect the spcname.
7352 : */
7353 378 : appendPQExpBufferStr(query,
7354 : "\nFROM pg_class c\n"
7355 : "LEFT JOIN pg_depend d ON "
7356 : "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
7357 : "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
7358 : "d.objsubid = 0 AND "
7359 : "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
7360 : "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
7361 :
7362 : /*
7363 : * In 9.6 and up, left join to pg_am to pick up the amname.
7364 : */
7365 378 : if (fout->remoteVersion >= 90600)
7366 378 : appendPQExpBufferStr(query,
7367 : "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
7368 :
7369 : /*
7370 : * We purposefully ignore toast OIDs for partitioned tables; the reason is
7371 : * that versions 10 and 11 have them, but later versions do not, so
7372 : * emitting them causes the upgrade to fail.
7373 : */
7374 378 : appendPQExpBufferStr(query,
7375 : "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
7376 : " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
7377 : " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
7378 :
7379 : /*
7380 : * Restrict to interesting relkinds (in particular, not indexes). Not all
7381 : * relkinds are possible in older servers, but it's not worth the trouble
7382 : * to emit a version-dependent list.
7383 : *
7384 : * Composite-type table entries won't be dumped as such, but we have to
7385 : * make a DumpableObject for them so that we can track dependencies of the
7386 : * composite type (pg_depend entries for columns of the composite type
7387 : * link to the pg_class entry not the pg_type entry).
7388 : */
7389 378 : appendPQExpBufferStr(query,
7390 : "WHERE c.relkind IN ("
7391 : CppAsString2(RELKIND_RELATION) ", "
7392 : CppAsString2(RELKIND_SEQUENCE) ", "
7393 : CppAsString2(RELKIND_VIEW) ", "
7394 : CppAsString2(RELKIND_COMPOSITE_TYPE) ", "
7395 : CppAsString2(RELKIND_MATVIEW) ", "
7396 : CppAsString2(RELKIND_FOREIGN_TABLE) ", "
7397 : CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n"
7398 : "ORDER BY c.oid");
7399 :
7400 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7401 :
7402 378 : ntups = PQntuples(res);
7403 :
7404 378 : *numTables = ntups;
7405 :
7406 : /*
7407 : * Extract data from result and lock dumpable tables. We do the locking
7408 : * before anything else, to minimize the window wherein a table could
7409 : * disappear under us.
7410 : *
7411 : * Note that we have to save info about all tables here, even when dumping
7412 : * only one, because we don't yet know which tables might be inheritance
7413 : * ancestors of the target table.
7414 : */
7415 378 : tblinfo = (TableInfo *) pg_malloc0(ntups * sizeof(TableInfo));
7416 :
7417 378 : i_reltableoid = PQfnumber(res, "tableoid");
7418 378 : i_reloid = PQfnumber(res, "oid");
7419 378 : i_relname = PQfnumber(res, "relname");
7420 378 : i_relnamespace = PQfnumber(res, "relnamespace");
7421 378 : i_relkind = PQfnumber(res, "relkind");
7422 378 : i_reltype = PQfnumber(res, "reltype");
7423 378 : i_relowner = PQfnumber(res, "relowner");
7424 378 : i_relchecks = PQfnumber(res, "relchecks");
7425 378 : i_relhasindex = PQfnumber(res, "relhasindex");
7426 378 : i_relhasrules = PQfnumber(res, "relhasrules");
7427 378 : i_relpages = PQfnumber(res, "relpages");
7428 378 : i_reltuples = PQfnumber(res, "reltuples");
7429 378 : i_relallvisible = PQfnumber(res, "relallvisible");
7430 378 : i_relallfrozen = PQfnumber(res, "relallfrozen");
7431 378 : i_toastpages = PQfnumber(res, "toastpages");
7432 378 : i_owning_tab = PQfnumber(res, "owning_tab");
7433 378 : i_owning_col = PQfnumber(res, "owning_col");
7434 378 : i_reltablespace = PQfnumber(res, "reltablespace");
7435 378 : i_relhasoids = PQfnumber(res, "relhasoids");
7436 378 : i_relhastriggers = PQfnumber(res, "relhastriggers");
7437 378 : i_relpersistence = PQfnumber(res, "relpersistence");
7438 378 : i_relispopulated = PQfnumber(res, "relispopulated");
7439 378 : i_relreplident = PQfnumber(res, "relreplident");
7440 378 : i_relrowsec = PQfnumber(res, "relrowsecurity");
7441 378 : i_relforcerowsec = PQfnumber(res, "relforcerowsecurity");
7442 378 : i_relfrozenxid = PQfnumber(res, "relfrozenxid");
7443 378 : i_toastfrozenxid = PQfnumber(res, "tfrozenxid");
7444 378 : i_toastoid = PQfnumber(res, "toid");
7445 378 : i_relminmxid = PQfnumber(res, "relminmxid");
7446 378 : i_toastminmxid = PQfnumber(res, "tminmxid");
7447 378 : i_reloptions = PQfnumber(res, "reloptions");
7448 378 : i_checkoption = PQfnumber(res, "checkoption");
7449 378 : i_toastreloptions = PQfnumber(res, "toast_reloptions");
7450 378 : i_reloftype = PQfnumber(res, "reloftype");
7451 378 : i_foreignserver = PQfnumber(res, "foreignserver");
7452 378 : i_amname = PQfnumber(res, "amname");
7453 378 : i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
7454 378 : i_relacl = PQfnumber(res, "relacl");
7455 378 : i_acldefault = PQfnumber(res, "acldefault");
7456 378 : i_ispartition = PQfnumber(res, "ispartition");
7457 :
7458 378 : if (dopt->lockWaitTimeout)
7459 : {
7460 : /*
7461 : * Arrange to fail instead of waiting forever for a table lock.
7462 : *
7463 : * NB: this coding assumes that the only queries issued within the
7464 : * following loop are LOCK TABLEs; else the timeout may be undesirably
7465 : * applied to other things too.
7466 : */
7467 4 : resetPQExpBuffer(query);
7468 4 : appendPQExpBufferStr(query, "SET statement_timeout = ");
7469 4 : appendStringLiteralConn(query, dopt->lockWaitTimeout, GetConnection(fout));
7470 4 : ExecuteSqlStatement(fout, query->data);
7471 : }
7472 :
7473 378 : resetPQExpBuffer(query);
7474 :
7475 99800 : for (i = 0; i < ntups; i++)
7476 : {
7477 99422 : int32 relallvisible = atoi(PQgetvalue(res, i, i_relallvisible));
7478 99422 : int32 relallfrozen = atoi(PQgetvalue(res, i, i_relallfrozen));
7479 :
7480 99422 : tblinfo[i].dobj.objType = DO_TABLE;
7481 99422 : tblinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_reltableoid));
7482 99422 : tblinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_reloid));
7483 99422 : AssignDumpId(&tblinfo[i].dobj);
7484 99422 : tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
7485 198844 : tblinfo[i].dobj.namespace =
7486 99422 : findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
7487 99422 : tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
7488 99422 : tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
7489 99422 : tblinfo[i].dacl.privtype = 0;
7490 99422 : tblinfo[i].dacl.initprivs = NULL;
7491 99422 : tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
7492 99422 : tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
7493 99422 : tblinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_relowner));
7494 99422 : tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
7495 99422 : tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
7496 99422 : tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
7497 99422 : tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
7498 99422 : if (PQgetisnull(res, i, i_toastpages))
7499 79940 : tblinfo[i].toastpages = 0;
7500 : else
7501 19482 : tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
7502 99422 : if (PQgetisnull(res, i, i_owning_tab))
7503 : {
7504 98592 : tblinfo[i].owning_tab = InvalidOid;
7505 98592 : tblinfo[i].owning_col = 0;
7506 : }
7507 : else
7508 : {
7509 830 : tblinfo[i].owning_tab = atooid(PQgetvalue(res, i, i_owning_tab));
7510 830 : tblinfo[i].owning_col = atoi(PQgetvalue(res, i, i_owning_col));
7511 : }
7512 99422 : tblinfo[i].reltablespace = pg_strdup(PQgetvalue(res, i, i_reltablespace));
7513 99422 : tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
7514 99422 : tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
7515 99422 : tblinfo[i].relpersistence = *(PQgetvalue(res, i, i_relpersistence));
7516 99422 : tblinfo[i].relispopulated = (strcmp(PQgetvalue(res, i, i_relispopulated), "t") == 0);
7517 99422 : tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
7518 99422 : tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
7519 99422 : tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
7520 99422 : tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
7521 99422 : tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
7522 99422 : tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
7523 99422 : tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
7524 99422 : tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
7525 99422 : tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
7526 99422 : if (PQgetisnull(res, i, i_checkoption))
7527 99330 : tblinfo[i].checkoption = NULL;
7528 : else
7529 92 : tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
7530 99422 : tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
7531 99422 : tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
7532 99422 : tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
7533 99422 : if (PQgetisnull(res, i, i_amname))
7534 59650 : tblinfo[i].amname = NULL;
7535 : else
7536 39772 : tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
7537 99422 : tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
7538 99422 : tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
7539 :
7540 : /* other fields were zeroed above */
7541 :
7542 : /*
7543 : * Decide whether we want to dump this table.
7544 : */
7545 99422 : if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
7546 366 : tblinfo[i].dobj.dump = DUMP_COMPONENT_NONE;
7547 : else
7548 99056 : selectDumpableTable(&tblinfo[i], fout);
7549 :
7550 : /*
7551 : * Now, consider the table "interesting" if we need to dump its
7552 : * definition, data or its statistics. Later on, we'll skip a lot of
7553 : * data collection for uninteresting tables.
7554 : *
7555 : * Note: the "interesting" flag will also be set by flagInhTables for
7556 : * parents of interesting tables, so that we collect necessary
7557 : * inheritance info even when the parents are not themselves being
7558 : * dumped. This is the main reason why we need an "interesting" flag
7559 : * that's separate from the components-to-dump bitmask.
7560 : */
7561 99422 : tblinfo[i].interesting = (tblinfo[i].dobj.dump &
7562 : (DUMP_COMPONENT_DEFINITION |
7563 : DUMP_COMPONENT_DATA |
7564 99422 : DUMP_COMPONENT_STATISTICS)) != 0;
7565 :
7566 99422 : tblinfo[i].dummy_view = false; /* might get set during sort */
7567 99422 : tblinfo[i].postponed_def = false; /* might get set during sort */
7568 :
7569 : /* Tables have data */
7570 99422 : tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
7571 :
7572 : /* Mark whether table has an ACL */
7573 99422 : if (!PQgetisnull(res, i, i_relacl))
7574 79760 : tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
7575 99422 : tblinfo[i].hascolumnACLs = false; /* may get set later */
7576 :
7577 : /* Add statistics */
7578 99422 : if (tblinfo[i].interesting)
7579 : {
7580 : RelStatsInfo *stats;
7581 :
7582 27916 : stats = getRelationStatistics(fout, &tblinfo[i].dobj,
7583 13958 : tblinfo[i].relpages,
7584 : PQgetvalue(res, i, i_reltuples),
7585 : relallvisible, relallfrozen,
7586 13958 : tblinfo[i].relkind, NULL, 0);
7587 13958 : if (tblinfo[i].relkind == RELKIND_MATVIEW)
7588 796 : tblinfo[i].stats = stats;
7589 : }
7590 :
7591 : /*
7592 : * Read-lock target tables to make sure they aren't DROPPED or altered
7593 : * in schema before we get around to dumping them.
7594 : *
7595 : * Note that we don't explicitly lock parents of the target tables; we
7596 : * assume our lock on the child is enough to prevent schema
7597 : * alterations to parent tables.
7598 : *
7599 : * NOTE: it'd be kinda nice to lock other relations too, not only
7600 : * plain or partitioned tables, but the backend doesn't presently
7601 : * allow that.
7602 : *
7603 : * We only need to lock the table for certain components; see
7604 : * pg_dump.h
7605 : */
7606 99422 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
7607 13958 : (tblinfo[i].relkind == RELKIND_RELATION ||
7608 3958 : tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
7609 : {
7610 : /*
7611 : * Tables are locked in batches. When dumping from a remote
7612 : * server this can save a significant amount of time by reducing
7613 : * the number of round trips.
7614 : */
7615 11208 : if (query->len == 0)
7616 248 : appendPQExpBuffer(query, "LOCK TABLE %s",
7617 248 : fmtQualifiedDumpable(&tblinfo[i]));
7618 : else
7619 : {
7620 10960 : appendPQExpBuffer(query, ", %s",
7621 10960 : fmtQualifiedDumpable(&tblinfo[i]));
7622 :
7623 : /* Arbitrarily end a batch when query length reaches 100K. */
7624 10960 : if (query->len >= 100000)
7625 : {
7626 : /* Lock another batch of tables. */
7627 0 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7628 0 : ExecuteSqlStatement(fout, query->data);
7629 0 : resetPQExpBuffer(query);
7630 : }
7631 : }
7632 : }
7633 : }
7634 :
7635 378 : if (query->len != 0)
7636 : {
7637 : /* Lock the tables in the last batch. */
7638 248 : appendPQExpBufferStr(query, " IN ACCESS SHARE MODE");
7639 248 : ExecuteSqlStatement(fout, query->data);
7640 : }
7641 :
7642 376 : if (dopt->lockWaitTimeout)
7643 : {
7644 4 : ExecuteSqlStatement(fout, "SET statement_timeout = 0");
7645 : }
7646 :
7647 376 : PQclear(res);
7648 :
7649 376 : destroyPQExpBuffer(query);
7650 :
7651 376 : return tblinfo;
7652 : }
7653 :
7654 : /*
7655 : * getOwnedSeqs
7656 : * identify owned sequences and mark them as dumpable if owning table is
7657 : *
7658 : * We used to do this in getTables(), but it's better to do it after the
7659 : * index used by findTableByOid() has been set up.
7660 : */
7661 : void
7662 376 : getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
7663 : {
7664 : int i;
7665 :
7666 : /*
7667 : * Force sequences that are "owned" by table columns to be dumped whenever
7668 : * their owning table is being dumped.
7669 : */
7670 99258 : for (i = 0; i < numTables; i++)
7671 : {
7672 98882 : TableInfo *seqinfo = &tblinfo[i];
7673 : TableInfo *owning_tab;
7674 :
7675 98882 : if (!OidIsValid(seqinfo->owning_tab))
7676 98058 : continue; /* not an owned sequence */
7677 :
7678 824 : owning_tab = findTableByOid(seqinfo->owning_tab);
7679 824 : if (owning_tab == NULL)
7680 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
7681 : seqinfo->owning_tab, seqinfo->dobj.catId.oid);
7682 :
7683 : /*
7684 : * For an identity sequence, dump exactly the same components for the
7685 : * sequence as for the owning table. This is important because we
7686 : * treat the identity sequence as an integral part of the table. For
7687 : * example, there is not any DDL command that allows creation of such
7688 : * a sequence independently of the table.
7689 : *
7690 : * For other owned sequences such as serial sequences, we need to dump
7691 : * the components that are being dumped for the table and any
7692 : * components that the sequence is explicitly marked with.
7693 : *
7694 : * We can't simply use the set of components which are being dumped
7695 : * for the table as the table might be in an extension (and only the
7696 : * non-extension components, eg: ACLs if changed, security labels, and
7697 : * policies, are being dumped) while the sequence is not (and
7698 : * therefore the definition and other components should also be
7699 : * dumped).
7700 : *
7701 : * If the sequence is part of the extension then it should be properly
7702 : * marked by checkExtensionMembership() and this will be a no-op as
7703 : * the table will be equivalently marked.
7704 : */
7705 824 : if (seqinfo->is_identity_sequence)
7706 398 : seqinfo->dobj.dump = owning_tab->dobj.dump;
7707 : else
7708 426 : seqinfo->dobj.dump |= owning_tab->dobj.dump;
7709 :
7710 : /* Make sure that necessary data is available if we're dumping it */
7711 824 : if (seqinfo->dobj.dump != DUMP_COMPONENT_NONE)
7712 : {
7713 632 : seqinfo->interesting = true;
7714 632 : owning_tab->interesting = true;
7715 : }
7716 : }
7717 376 : }
7718 :
7719 : /*
7720 : * getInherits
7721 : * read all the inheritance information
7722 : * from the system catalogs return them in the InhInfo* structure
7723 : *
7724 : * numInherits is set to the number of pairs read in
7725 : */
7726 : InhInfo *
7727 376 : getInherits(Archive *fout, int *numInherits)
7728 : {
7729 : PGresult *res;
7730 : int ntups;
7731 : int i;
7732 376 : PQExpBuffer query = createPQExpBuffer();
7733 : InhInfo *inhinfo;
7734 :
7735 : int i_inhrelid;
7736 : int i_inhparent;
7737 :
7738 : /* find all the inheritance information */
7739 376 : appendPQExpBufferStr(query, "SELECT inhrelid, inhparent FROM pg_inherits");
7740 :
7741 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7742 :
7743 376 : ntups = PQntuples(res);
7744 :
7745 376 : *numInherits = ntups;
7746 :
7747 376 : inhinfo = (InhInfo *) pg_malloc(ntups * sizeof(InhInfo));
7748 :
7749 376 : i_inhrelid = PQfnumber(res, "inhrelid");
7750 376 : i_inhparent = PQfnumber(res, "inhparent");
7751 :
7752 7168 : for (i = 0; i < ntups; i++)
7753 : {
7754 6792 : inhinfo[i].inhrelid = atooid(PQgetvalue(res, i, i_inhrelid));
7755 6792 : inhinfo[i].inhparent = atooid(PQgetvalue(res, i, i_inhparent));
7756 : }
7757 :
7758 376 : PQclear(res);
7759 :
7760 376 : destroyPQExpBuffer(query);
7761 :
7762 376 : return inhinfo;
7763 : }
7764 :
7765 : /*
7766 : * getPartitioningInfo
7767 : * get information about partitioning
7768 : *
7769 : * For the most part, we only collect partitioning info about tables we
7770 : * intend to dump. However, this function has to consider all partitioned
7771 : * tables in the database, because we need to know about parents of partitions
7772 : * we are going to dump even if the parents themselves won't be dumped.
7773 : *
7774 : * Specifically, what we need to know is whether each partitioned table
7775 : * has an "unsafe" partitioning scheme that requires us to force
7776 : * load-via-partition-root mode for its children. Currently the only case
7777 : * for which we force that is hash partitioning on enum columns, since the
7778 : * hash codes depend on enum value OIDs which won't be replicated across
7779 : * dump-and-reload. There are other cases in which load-via-partition-root
7780 : * might be necessary, but we expect users to cope with them.
7781 : */
7782 : void
7783 376 : getPartitioningInfo(Archive *fout)
7784 : {
7785 : PQExpBuffer query;
7786 : PGresult *res;
7787 : int ntups;
7788 :
7789 : /* hash partitioning didn't exist before v11 */
7790 376 : if (fout->remoteVersion < 110000)
7791 0 : return;
7792 : /* needn't bother if not dumping data */
7793 376 : if (!fout->dopt->dumpData)
7794 84 : return;
7795 :
7796 292 : query = createPQExpBuffer();
7797 :
7798 : /*
7799 : * Unsafe partitioning schemes are exactly those for which hash enum_ops
7800 : * appears among the partition opclasses. We needn't check partstrat.
7801 : *
7802 : * Note that this query may well retrieve info about tables we aren't
7803 : * going to dump and hence have no lock on. That's okay since we need not
7804 : * invoke any unsafe server-side functions.
7805 : */
7806 292 : appendPQExpBufferStr(query,
7807 : "SELECT partrelid FROM pg_partitioned_table WHERE\n"
7808 : "(SELECT c.oid FROM pg_opclass c JOIN pg_am a "
7809 : "ON c.opcmethod = a.oid\n"
7810 : "WHERE opcname = 'enum_ops' "
7811 : "AND opcnamespace = 'pg_catalog'::regnamespace "
7812 : "AND amname = 'hash') = ANY(partclass)");
7813 :
7814 292 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
7815 :
7816 292 : ntups = PQntuples(res);
7817 :
7818 378 : for (int i = 0; i < ntups; i++)
7819 : {
7820 86 : Oid tabrelid = atooid(PQgetvalue(res, i, 0));
7821 : TableInfo *tbinfo;
7822 :
7823 86 : tbinfo = findTableByOid(tabrelid);
7824 86 : if (tbinfo == NULL)
7825 0 : pg_fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found",
7826 : tabrelid);
7827 86 : tbinfo->unsafe_partitions = true;
7828 : }
7829 :
7830 292 : PQclear(res);
7831 :
7832 292 : destroyPQExpBuffer(query);
7833 : }
7834 :
7835 : /*
7836 : * getIndexes
7837 : * get information about every index on a dumpable table
7838 : *
7839 : * Note: index data is not returned directly to the caller, but it
7840 : * does get entered into the DumpableObject tables.
7841 : */
7842 : void
7843 376 : getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
7844 : {
7845 376 : PQExpBuffer query = createPQExpBuffer();
7846 376 : PQExpBuffer tbloids = createPQExpBuffer();
7847 : PGresult *res;
7848 : int ntups;
7849 : int curtblindx;
7850 : IndxInfo *indxinfo;
7851 : int i_tableoid,
7852 : i_oid,
7853 : i_indrelid,
7854 : i_indexname,
7855 : i_relpages,
7856 : i_reltuples,
7857 : i_relallvisible,
7858 : i_relallfrozen,
7859 : i_parentidx,
7860 : i_indexdef,
7861 : i_indnkeyatts,
7862 : i_indnatts,
7863 : i_indkey,
7864 : i_indisclustered,
7865 : i_indisreplident,
7866 : i_indnullsnotdistinct,
7867 : i_contype,
7868 : i_conname,
7869 : i_condeferrable,
7870 : i_condeferred,
7871 : i_conperiod,
7872 : i_contableoid,
7873 : i_conoid,
7874 : i_condef,
7875 : i_indattnames,
7876 : i_tablespace,
7877 : i_indreloptions,
7878 : i_indstatcols,
7879 : i_indstatvals;
7880 :
7881 : /*
7882 : * We want to perform just one query against pg_index. However, we
7883 : * mustn't try to select every row of the catalog and then sort it out on
7884 : * the client side, because some of the server-side functions we need
7885 : * would be unsafe to apply to tables we don't have lock on. Hence, we
7886 : * build an array of the OIDs of tables we care about (and now have lock
7887 : * on!), and use a WHERE clause to constrain which rows are selected.
7888 : */
7889 376 : appendPQExpBufferChar(tbloids, '{');
7890 99258 : for (int i = 0; i < numTables; i++)
7891 : {
7892 98882 : TableInfo *tbinfo = &tblinfo[i];
7893 :
7894 98882 : if (!tbinfo->hasindex)
7895 70026 : continue;
7896 :
7897 : /*
7898 : * We can ignore indexes of uninteresting tables.
7899 : */
7900 28856 : if (!tbinfo->interesting)
7901 24842 : continue;
7902 :
7903 : /* OK, we need info for this table */
7904 4014 : if (tbloids->len > 1) /* do we have more than the '{'? */
7905 3856 : appendPQExpBufferChar(tbloids, ',');
7906 4014 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
7907 : }
7908 376 : appendPQExpBufferChar(tbloids, '}');
7909 :
7910 376 : appendPQExpBufferStr(query,
7911 : "SELECT t.tableoid, t.oid, i.indrelid, "
7912 : "t.relname AS indexname, "
7913 : "t.relpages, t.reltuples, t.relallvisible, ");
7914 :
7915 376 : if (fout->remoteVersion >= 180000)
7916 376 : appendPQExpBufferStr(query, "t.relallfrozen, ");
7917 : else
7918 0 : appendPQExpBufferStr(query, "0 AS relallfrozen, ");
7919 :
7920 376 : appendPQExpBufferStr(query,
7921 : "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
7922 : "i.indkey, i.indisclustered, "
7923 : "c.contype, c.conname, "
7924 : "c.condeferrable, c.condeferred, "
7925 : "c.tableoid AS contableoid, "
7926 : "c.oid AS conoid, "
7927 : "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
7928 : "CASE WHEN i.indexprs IS NOT NULL THEN "
7929 : "(SELECT pg_catalog.array_agg(attname ORDER BY attnum)"
7930 : " FROM pg_catalog.pg_attribute "
7931 : " WHERE attrelid = i.indexrelid) "
7932 : "ELSE NULL END AS indattnames, "
7933 : "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
7934 : "t.reloptions AS indreloptions, ");
7935 :
7936 :
7937 376 : if (fout->remoteVersion >= 90400)
7938 376 : appendPQExpBufferStr(query,
7939 : "i.indisreplident, ");
7940 : else
7941 0 : appendPQExpBufferStr(query,
7942 : "false AS indisreplident, ");
7943 :
7944 376 : if (fout->remoteVersion >= 110000)
7945 376 : appendPQExpBufferStr(query,
7946 : "inh.inhparent AS parentidx, "
7947 : "i.indnkeyatts AS indnkeyatts, "
7948 : "i.indnatts AS indnatts, "
7949 : "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
7950 : " FROM pg_catalog.pg_attribute "
7951 : " WHERE attrelid = i.indexrelid AND "
7952 : " attstattarget >= 0) AS indstatcols, "
7953 : "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
7954 : " FROM pg_catalog.pg_attribute "
7955 : " WHERE attrelid = i.indexrelid AND "
7956 : " attstattarget >= 0) AS indstatvals, ");
7957 : else
7958 0 : appendPQExpBufferStr(query,
7959 : "0 AS parentidx, "
7960 : "i.indnatts AS indnkeyatts, "
7961 : "i.indnatts AS indnatts, "
7962 : "'' AS indstatcols, "
7963 : "'' AS indstatvals, ");
7964 :
7965 376 : if (fout->remoteVersion >= 150000)
7966 376 : appendPQExpBufferStr(query,
7967 : "i.indnullsnotdistinct, ");
7968 : else
7969 0 : appendPQExpBufferStr(query,
7970 : "false AS indnullsnotdistinct, ");
7971 :
7972 376 : if (fout->remoteVersion >= 180000)
7973 376 : appendPQExpBufferStr(query,
7974 : "c.conperiod ");
7975 : else
7976 0 : appendPQExpBufferStr(query,
7977 : "NULL AS conperiod ");
7978 :
7979 : /*
7980 : * The point of the messy-looking outer join is to find a constraint that
7981 : * is related by an internal dependency link to the index. If we find one,
7982 : * create a CONSTRAINT entry linked to the INDEX entry. We assume an
7983 : * index won't have more than one internal dependency.
7984 : *
7985 : * Note: the check on conrelid is redundant, but useful because that
7986 : * column is indexed while conindid is not.
7987 : */
7988 376 : if (fout->remoteVersion >= 110000)
7989 : {
7990 376 : appendPQExpBuffer(query,
7991 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
7992 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
7993 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
7994 : "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
7995 : "LEFT JOIN pg_catalog.pg_constraint c "
7996 : "ON (i.indrelid = c.conrelid AND "
7997 : "i.indexrelid = c.conindid AND "
7998 : "c.contype IN ('p','u','x')) "
7999 : "LEFT JOIN pg_catalog.pg_inherits inh "
8000 : "ON (inh.inhrelid = indexrelid) "
8001 : "WHERE (i.indisvalid OR t2.relkind = 'p') "
8002 : "AND i.indisready "
8003 : "ORDER BY i.indrelid, indexname",
8004 : tbloids->data);
8005 : }
8006 : else
8007 : {
8008 : /*
8009 : * the test on indisready is necessary in 9.2, and harmless in
8010 : * earlier/later versions
8011 : */
8012 0 : appendPQExpBuffer(query,
8013 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8014 : "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
8015 : "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
8016 : "LEFT JOIN pg_catalog.pg_constraint c "
8017 : "ON (i.indrelid = c.conrelid AND "
8018 : "i.indexrelid = c.conindid AND "
8019 : "c.contype IN ('p','u','x')) "
8020 : "WHERE i.indisvalid AND i.indisready "
8021 : "ORDER BY i.indrelid, indexname",
8022 : tbloids->data);
8023 : }
8024 :
8025 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8026 :
8027 376 : ntups = PQntuples(res);
8028 :
8029 376 : i_tableoid = PQfnumber(res, "tableoid");
8030 376 : i_oid = PQfnumber(res, "oid");
8031 376 : i_indrelid = PQfnumber(res, "indrelid");
8032 376 : i_indexname = PQfnumber(res, "indexname");
8033 376 : i_relpages = PQfnumber(res, "relpages");
8034 376 : i_reltuples = PQfnumber(res, "reltuples");
8035 376 : i_relallvisible = PQfnumber(res, "relallvisible");
8036 376 : i_relallfrozen = PQfnumber(res, "relallfrozen");
8037 376 : i_parentidx = PQfnumber(res, "parentidx");
8038 376 : i_indexdef = PQfnumber(res, "indexdef");
8039 376 : i_indnkeyatts = PQfnumber(res, "indnkeyatts");
8040 376 : i_indnatts = PQfnumber(res, "indnatts");
8041 376 : i_indkey = PQfnumber(res, "indkey");
8042 376 : i_indisclustered = PQfnumber(res, "indisclustered");
8043 376 : i_indisreplident = PQfnumber(res, "indisreplident");
8044 376 : i_indnullsnotdistinct = PQfnumber(res, "indnullsnotdistinct");
8045 376 : i_contype = PQfnumber(res, "contype");
8046 376 : i_conname = PQfnumber(res, "conname");
8047 376 : i_condeferrable = PQfnumber(res, "condeferrable");
8048 376 : i_condeferred = PQfnumber(res, "condeferred");
8049 376 : i_conperiod = PQfnumber(res, "conperiod");
8050 376 : i_contableoid = PQfnumber(res, "contableoid");
8051 376 : i_conoid = PQfnumber(res, "conoid");
8052 376 : i_condef = PQfnumber(res, "condef");
8053 376 : i_indattnames = PQfnumber(res, "indattnames");
8054 376 : i_tablespace = PQfnumber(res, "tablespace");
8055 376 : i_indreloptions = PQfnumber(res, "indreloptions");
8056 376 : i_indstatcols = PQfnumber(res, "indstatcols");
8057 376 : i_indstatvals = PQfnumber(res, "indstatvals");
8058 :
8059 376 : indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
8060 :
8061 : /*
8062 : * Outer loop iterates once per table, not once per row. Incrementing of
8063 : * j is handled by the inner loop.
8064 : */
8065 376 : curtblindx = -1;
8066 4350 : for (int j = 0; j < ntups;)
8067 : {
8068 3974 : Oid indrelid = atooid(PQgetvalue(res, j, i_indrelid));
8069 3974 : TableInfo *tbinfo = NULL;
8070 3974 : char **indAttNames = NULL;
8071 3974 : int nindAttNames = 0;
8072 : int numinds;
8073 :
8074 : /* Count rows for this table */
8075 5218 : for (numinds = 1; numinds < ntups - j; numinds++)
8076 5060 : if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
8077 3816 : break;
8078 :
8079 : /*
8080 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8081 : * order.
8082 : */
8083 45954 : while (++curtblindx < numTables)
8084 : {
8085 45954 : tbinfo = &tblinfo[curtblindx];
8086 45954 : if (tbinfo->dobj.catId.oid == indrelid)
8087 3974 : break;
8088 : }
8089 3974 : if (curtblindx >= numTables)
8090 0 : pg_fatal("unrecognized table OID %u", indrelid);
8091 : /* cross-check that we only got requested tables */
8092 3974 : if (!tbinfo->hasindex ||
8093 3974 : !tbinfo->interesting)
8094 0 : pg_fatal("unexpected index data for table \"%s\"",
8095 : tbinfo->dobj.name);
8096 :
8097 : /* Save data for this table */
8098 3974 : tbinfo->indexes = indxinfo + j;
8099 3974 : tbinfo->numIndexes = numinds;
8100 :
8101 9192 : for (int c = 0; c < numinds; c++, j++)
8102 : {
8103 : char contype;
8104 : char indexkind;
8105 : RelStatsInfo *relstats;
8106 5218 : int32 relpages = atoi(PQgetvalue(res, j, i_relpages));
8107 5218 : int32 relallvisible = atoi(PQgetvalue(res, j, i_relallvisible));
8108 5218 : int32 relallfrozen = atoi(PQgetvalue(res, j, i_relallfrozen));
8109 :
8110 5218 : indxinfo[j].dobj.objType = DO_INDEX;
8111 5218 : indxinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8112 5218 : indxinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8113 5218 : AssignDumpId(&indxinfo[j].dobj);
8114 5218 : indxinfo[j].dobj.dump = tbinfo->dobj.dump;
8115 5218 : indxinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_indexname));
8116 5218 : indxinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8117 5218 : indxinfo[j].indextable = tbinfo;
8118 5218 : indxinfo[j].indexdef = pg_strdup(PQgetvalue(res, j, i_indexdef));
8119 5218 : indxinfo[j].indnkeyattrs = atoi(PQgetvalue(res, j, i_indnkeyatts));
8120 5218 : indxinfo[j].indnattrs = atoi(PQgetvalue(res, j, i_indnatts));
8121 5218 : indxinfo[j].tablespace = pg_strdup(PQgetvalue(res, j, i_tablespace));
8122 5218 : indxinfo[j].indreloptions = pg_strdup(PQgetvalue(res, j, i_indreloptions));
8123 5218 : indxinfo[j].indstatcols = pg_strdup(PQgetvalue(res, j, i_indstatcols));
8124 5218 : indxinfo[j].indstatvals = pg_strdup(PQgetvalue(res, j, i_indstatvals));
8125 5218 : indxinfo[j].indkeys = (Oid *) pg_malloc(indxinfo[j].indnattrs * sizeof(Oid));
8126 5218 : parseOidArray(PQgetvalue(res, j, i_indkey),
8127 5218 : indxinfo[j].indkeys, indxinfo[j].indnattrs);
8128 5218 : indxinfo[j].indisclustered = (PQgetvalue(res, j, i_indisclustered)[0] == 't');
8129 5218 : indxinfo[j].indisreplident = (PQgetvalue(res, j, i_indisreplident)[0] == 't');
8130 5218 : indxinfo[j].indnullsnotdistinct = (PQgetvalue(res, j, i_indnullsnotdistinct)[0] == 't');
8131 5218 : indxinfo[j].parentidx = atooid(PQgetvalue(res, j, i_parentidx));
8132 5218 : indxinfo[j].partattaches = (SimplePtrList)
8133 : {
8134 : NULL, NULL
8135 : };
8136 :
8137 5218 : if (indxinfo[j].parentidx == 0)
8138 4070 : indexkind = RELKIND_INDEX;
8139 : else
8140 1148 : indexkind = RELKIND_PARTITIONED_INDEX;
8141 :
8142 5218 : if (!PQgetisnull(res, j, i_indattnames))
8143 : {
8144 292 : if (!parsePGArray(PQgetvalue(res, j, i_indattnames),
8145 : &indAttNames, &nindAttNames))
8146 0 : pg_fatal("could not parse %s array", "indattnames");
8147 : }
8148 :
8149 5218 : relstats = getRelationStatistics(fout, &indxinfo[j].dobj, relpages,
8150 : PQgetvalue(res, j, i_reltuples),
8151 : relallvisible, relallfrozen, indexkind,
8152 : indAttNames, nindAttNames);
8153 :
8154 5218 : contype = *(PQgetvalue(res, j, i_contype));
8155 5218 : if (contype == 'p' || contype == 'u' || contype == 'x')
8156 3034 : {
8157 : /*
8158 : * If we found a constraint matching the index, create an
8159 : * entry for it.
8160 : */
8161 : ConstraintInfo *constrinfo;
8162 :
8163 3034 : constrinfo = (ConstraintInfo *) pg_malloc(sizeof(ConstraintInfo));
8164 3034 : constrinfo->dobj.objType = DO_CONSTRAINT;
8165 3034 : constrinfo->dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8166 3034 : constrinfo->dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8167 3034 : AssignDumpId(&constrinfo->dobj);
8168 3034 : constrinfo->dobj.dump = tbinfo->dobj.dump;
8169 3034 : constrinfo->dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8170 3034 : constrinfo->dobj.namespace = tbinfo->dobj.namespace;
8171 3034 : constrinfo->contable = tbinfo;
8172 3034 : constrinfo->condomain = NULL;
8173 3034 : constrinfo->contype = contype;
8174 3034 : if (contype == 'x')
8175 20 : constrinfo->condef = pg_strdup(PQgetvalue(res, j, i_condef));
8176 : else
8177 3014 : constrinfo->condef = NULL;
8178 3034 : constrinfo->confrelid = InvalidOid;
8179 3034 : constrinfo->conindex = indxinfo[j].dobj.dumpId;
8180 3034 : constrinfo->condeferrable = *(PQgetvalue(res, j, i_condeferrable)) == 't';
8181 3034 : constrinfo->condeferred = *(PQgetvalue(res, j, i_condeferred)) == 't';
8182 3034 : constrinfo->conperiod = *(PQgetvalue(res, j, i_conperiod)) == 't';
8183 3034 : constrinfo->conislocal = true;
8184 3034 : constrinfo->separate = true;
8185 :
8186 3034 : indxinfo[j].indexconstraint = constrinfo->dobj.dumpId;
8187 3034 : if (relstats != NULL)
8188 1060 : addObjectDependency(&relstats->dobj, constrinfo->dobj.dumpId);
8189 : }
8190 : else
8191 : {
8192 : /* Plain secondary index */
8193 2184 : indxinfo[j].indexconstraint = 0;
8194 : }
8195 : }
8196 : }
8197 :
8198 376 : PQclear(res);
8199 :
8200 376 : destroyPQExpBuffer(query);
8201 376 : destroyPQExpBuffer(tbloids);
8202 376 : }
8203 :
8204 : /*
8205 : * getExtendedStatistics
8206 : * get information about extended-statistics objects.
8207 : *
8208 : * Note: extended statistics data is not returned directly to the caller, but
8209 : * it does get entered into the DumpableObject tables.
8210 : */
8211 : void
8212 376 : getExtendedStatistics(Archive *fout)
8213 : {
8214 : PQExpBuffer query;
8215 : PGresult *res;
8216 : StatsExtInfo *statsextinfo;
8217 : int ntups;
8218 : int i_tableoid;
8219 : int i_oid;
8220 : int i_stxname;
8221 : int i_stxnamespace;
8222 : int i_stxowner;
8223 : int i_stxrelid;
8224 : int i_stattarget;
8225 : int i;
8226 :
8227 : /* Extended statistics were new in v10 */
8228 376 : if (fout->remoteVersion < 100000)
8229 0 : return;
8230 :
8231 376 : query = createPQExpBuffer();
8232 :
8233 376 : if (fout->remoteVersion < 130000)
8234 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8235 : "stxnamespace, stxowner, stxrelid, NULL AS stxstattarget "
8236 : "FROM pg_catalog.pg_statistic_ext");
8237 : else
8238 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, stxname, "
8239 : "stxnamespace, stxowner, stxrelid, stxstattarget "
8240 : "FROM pg_catalog.pg_statistic_ext");
8241 :
8242 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8243 :
8244 376 : ntups = PQntuples(res);
8245 :
8246 376 : i_tableoid = PQfnumber(res, "tableoid");
8247 376 : i_oid = PQfnumber(res, "oid");
8248 376 : i_stxname = PQfnumber(res, "stxname");
8249 376 : i_stxnamespace = PQfnumber(res, "stxnamespace");
8250 376 : i_stxowner = PQfnumber(res, "stxowner");
8251 376 : i_stxrelid = PQfnumber(res, "stxrelid");
8252 376 : i_stattarget = PQfnumber(res, "stxstattarget");
8253 :
8254 376 : statsextinfo = (StatsExtInfo *) pg_malloc(ntups * sizeof(StatsExtInfo));
8255 :
8256 702 : for (i = 0; i < ntups; i++)
8257 : {
8258 326 : statsextinfo[i].dobj.objType = DO_STATSEXT;
8259 326 : statsextinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8260 326 : statsextinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8261 326 : AssignDumpId(&statsextinfo[i].dobj);
8262 326 : statsextinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_stxname));
8263 652 : statsextinfo[i].dobj.namespace =
8264 326 : findNamespace(atooid(PQgetvalue(res, i, i_stxnamespace)));
8265 326 : statsextinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_stxowner));
8266 652 : statsextinfo[i].stattable =
8267 326 : findTableByOid(atooid(PQgetvalue(res, i, i_stxrelid)));
8268 326 : if (PQgetisnull(res, i, i_stattarget))
8269 236 : statsextinfo[i].stattarget = -1;
8270 : else
8271 90 : statsextinfo[i].stattarget = atoi(PQgetvalue(res, i, i_stattarget));
8272 :
8273 : /* Decide whether we want to dump it */
8274 326 : selectDumpableStatisticsObject(&(statsextinfo[i]), fout);
8275 : }
8276 :
8277 376 : PQclear(res);
8278 376 : destroyPQExpBuffer(query);
8279 : }
8280 :
8281 : /*
8282 : * getConstraints
8283 : *
8284 : * Get info about constraints on dumpable tables.
8285 : *
8286 : * Currently handles foreign keys only.
8287 : * Unique and primary key constraints are handled with indexes,
8288 : * while check constraints are processed in getTableAttrs().
8289 : */
8290 : void
8291 376 : getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
8292 : {
8293 376 : PQExpBuffer query = createPQExpBuffer();
8294 376 : PQExpBuffer tbloids = createPQExpBuffer();
8295 : PGresult *res;
8296 : int ntups;
8297 : int curtblindx;
8298 376 : TableInfo *tbinfo = NULL;
8299 : ConstraintInfo *constrinfo;
8300 : int i_contableoid,
8301 : i_conoid,
8302 : i_conrelid,
8303 : i_conname,
8304 : i_confrelid,
8305 : i_conindid,
8306 : i_condef;
8307 :
8308 : /*
8309 : * We want to perform just one query against pg_constraint. However, we
8310 : * mustn't try to select every row of the catalog and then sort it out on
8311 : * the client side, because some of the server-side functions we need
8312 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8313 : * build an array of the OIDs of tables we care about (and now have lock
8314 : * on!), and use a WHERE clause to constrain which rows are selected.
8315 : */
8316 376 : appendPQExpBufferChar(tbloids, '{');
8317 99258 : for (int i = 0; i < numTables; i++)
8318 : {
8319 98882 : TableInfo *tinfo = &tblinfo[i];
8320 :
8321 98882 : if (!(tinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8322 85026 : continue;
8323 :
8324 : /* OK, we need info for this table */
8325 13856 : if (tbloids->len > 1) /* do we have more than the '{'? */
8326 13606 : appendPQExpBufferChar(tbloids, ',');
8327 13856 : appendPQExpBuffer(tbloids, "%u", tinfo->dobj.catId.oid);
8328 : }
8329 376 : appendPQExpBufferChar(tbloids, '}');
8330 :
8331 376 : appendPQExpBufferStr(query,
8332 : "SELECT c.tableoid, c.oid, "
8333 : "conrelid, conname, confrelid, ");
8334 376 : if (fout->remoteVersion >= 110000)
8335 376 : appendPQExpBufferStr(query, "conindid, ");
8336 : else
8337 0 : appendPQExpBufferStr(query, "0 AS conindid, ");
8338 376 : appendPQExpBuffer(query,
8339 : "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
8340 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8341 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
8342 : "WHERE contype = 'f' ",
8343 : tbloids->data);
8344 376 : if (fout->remoteVersion >= 110000)
8345 376 : appendPQExpBufferStr(query,
8346 : "AND conparentid = 0 ");
8347 376 : appendPQExpBufferStr(query,
8348 : "ORDER BY conrelid, conname");
8349 :
8350 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8351 :
8352 376 : ntups = PQntuples(res);
8353 :
8354 376 : i_contableoid = PQfnumber(res, "tableoid");
8355 376 : i_conoid = PQfnumber(res, "oid");
8356 376 : i_conrelid = PQfnumber(res, "conrelid");
8357 376 : i_conname = PQfnumber(res, "conname");
8358 376 : i_confrelid = PQfnumber(res, "confrelid");
8359 376 : i_conindid = PQfnumber(res, "conindid");
8360 376 : i_condef = PQfnumber(res, "condef");
8361 :
8362 376 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8363 :
8364 376 : curtblindx = -1;
8365 718 : for (int j = 0; j < ntups; j++)
8366 : {
8367 342 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
8368 : TableInfo *reftable;
8369 :
8370 : /*
8371 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8372 : * order.
8373 : */
8374 342 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
8375 : {
8376 26990 : while (++curtblindx < numTables)
8377 : {
8378 26990 : tbinfo = &tblinfo[curtblindx];
8379 26990 : if (tbinfo->dobj.catId.oid == conrelid)
8380 322 : break;
8381 : }
8382 322 : if (curtblindx >= numTables)
8383 0 : pg_fatal("unrecognized table OID %u", conrelid);
8384 : }
8385 :
8386 342 : constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
8387 342 : constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
8388 342 : constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
8389 342 : AssignDumpId(&constrinfo[j].dobj);
8390 342 : constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
8391 342 : constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
8392 342 : constrinfo[j].contable = tbinfo;
8393 342 : constrinfo[j].condomain = NULL;
8394 342 : constrinfo[j].contype = 'f';
8395 342 : constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
8396 342 : constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
8397 342 : constrinfo[j].conindex = 0;
8398 342 : constrinfo[j].condeferrable = false;
8399 342 : constrinfo[j].condeferred = false;
8400 342 : constrinfo[j].conislocal = true;
8401 342 : constrinfo[j].separate = true;
8402 :
8403 : /*
8404 : * Restoring an FK that points to a partitioned table requires that
8405 : * all partition indexes have been attached beforehand. Ensure that
8406 : * happens by making the constraint depend on each index partition
8407 : * attach object.
8408 : */
8409 342 : reftable = findTableByOid(constrinfo[j].confrelid);
8410 342 : if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
8411 : {
8412 40 : Oid indexOid = atooid(PQgetvalue(res, j, i_conindid));
8413 :
8414 40 : if (indexOid != InvalidOid)
8415 : {
8416 40 : for (int k = 0; k < reftable->numIndexes; k++)
8417 : {
8418 : IndxInfo *refidx;
8419 :
8420 : /* not our index? */
8421 40 : if (reftable->indexes[k].dobj.catId.oid != indexOid)
8422 0 : continue;
8423 :
8424 40 : refidx = &reftable->indexes[k];
8425 40 : addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
8426 40 : break;
8427 : }
8428 : }
8429 : }
8430 : }
8431 :
8432 376 : PQclear(res);
8433 :
8434 376 : destroyPQExpBuffer(query);
8435 376 : destroyPQExpBuffer(tbloids);
8436 376 : }
8437 :
8438 : /*
8439 : * addConstrChildIdxDeps
8440 : *
8441 : * Recursive subroutine for getConstraints
8442 : *
8443 : * Given an object representing a foreign key constraint and an index on the
8444 : * partitioned table it references, mark the constraint object as dependent
8445 : * on the DO_INDEX_ATTACH object of each index partition, recursively
8446 : * drilling down to their partitions if any. This ensures that the FK is not
8447 : * restored until the index is fully marked valid.
8448 : */
8449 : static void
8450 90 : addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
8451 : {
8452 : SimplePtrListCell *cell;
8453 :
8454 : Assert(dobj->objType == DO_FK_CONSTRAINT);
8455 :
8456 310 : for (cell = refidx->partattaches.head; cell; cell = cell->next)
8457 : {
8458 220 : IndexAttachInfo *attach = (IndexAttachInfo *) cell->ptr;
8459 :
8460 220 : addObjectDependency(dobj, attach->dobj.dumpId);
8461 :
8462 220 : if (attach->partitionIdx->partattaches.head != NULL)
8463 50 : addConstrChildIdxDeps(dobj, attach->partitionIdx);
8464 : }
8465 90 : }
8466 :
8467 : /*
8468 : * getDomainConstraints
8469 : *
8470 : * Get info about constraints on a domain.
8471 : */
8472 : static void
8473 316 : getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
8474 : {
8475 : ConstraintInfo *constrinfo;
8476 316 : PQExpBuffer query = createPQExpBuffer();
8477 : PGresult *res;
8478 : int i_tableoid,
8479 : i_oid,
8480 : i_conname,
8481 : i_consrc,
8482 : i_convalidated,
8483 : i_contype;
8484 : int ntups;
8485 :
8486 316 : if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
8487 : {
8488 : /*
8489 : * Set up query for constraint-specific details. For servers 17 and
8490 : * up, domains have constraints of type 'n' as well as 'c', otherwise
8491 : * just the latter.
8492 : */
8493 86 : appendPQExpBuffer(query,
8494 : "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
8495 : "SELECT tableoid, oid, conname, "
8496 : "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8497 : "convalidated, contype "
8498 : "FROM pg_catalog.pg_constraint "
8499 : "WHERE contypid = $1 AND contype IN (%s) "
8500 : "ORDER BY conname",
8501 86 : fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
8502 :
8503 86 : ExecuteSqlStatement(fout, query->data);
8504 :
8505 86 : fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
8506 : }
8507 :
8508 316 : printfPQExpBuffer(query,
8509 : "EXECUTE getDomainConstraints('%u')",
8510 : tyinfo->dobj.catId.oid);
8511 :
8512 316 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8513 :
8514 316 : ntups = PQntuples(res);
8515 :
8516 316 : i_tableoid = PQfnumber(res, "tableoid");
8517 316 : i_oid = PQfnumber(res, "oid");
8518 316 : i_conname = PQfnumber(res, "conname");
8519 316 : i_consrc = PQfnumber(res, "consrc");
8520 316 : i_convalidated = PQfnumber(res, "convalidated");
8521 316 : i_contype = PQfnumber(res, "contype");
8522 :
8523 316 : constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
8524 316 : tyinfo->domChecks = constrinfo;
8525 :
8526 : /* 'i' tracks result rows; 'j' counts CHECK constraints */
8527 648 : for (int i = 0, j = 0; i < ntups; i++)
8528 : {
8529 332 : bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
8530 332 : char contype = (PQgetvalue(res, i, i_contype))[0];
8531 : ConstraintInfo *constraint;
8532 :
8533 332 : if (contype == CONSTRAINT_CHECK)
8534 : {
8535 226 : constraint = &constrinfo[j++];
8536 226 : tyinfo->nDomChecks++;
8537 : }
8538 : else
8539 : {
8540 : Assert(contype == CONSTRAINT_NOTNULL);
8541 : Assert(tyinfo->notnull == NULL);
8542 : /* use last item in array for the not-null constraint */
8543 106 : tyinfo->notnull = &(constrinfo[ntups - 1]);
8544 106 : constraint = tyinfo->notnull;
8545 : }
8546 :
8547 332 : constraint->dobj.objType = DO_CONSTRAINT;
8548 332 : constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8549 332 : constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8550 332 : AssignDumpId(&(constraint->dobj));
8551 332 : constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
8552 332 : constraint->dobj.namespace = tyinfo->dobj.namespace;
8553 332 : constraint->contable = NULL;
8554 332 : constraint->condomain = tyinfo;
8555 332 : constraint->contype = contype;
8556 332 : constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
8557 332 : constraint->confrelid = InvalidOid;
8558 332 : constraint->conindex = 0;
8559 332 : constraint->condeferrable = false;
8560 332 : constraint->condeferred = false;
8561 332 : constraint->conislocal = true;
8562 :
8563 332 : constraint->separate = !validated;
8564 :
8565 : /*
8566 : * Make the domain depend on the constraint, ensuring it won't be
8567 : * output till any constraint dependencies are OK. If the constraint
8568 : * has not been validated, it's going to be dumped after the domain
8569 : * anyway, so this doesn't matter.
8570 : */
8571 332 : if (validated)
8572 322 : addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
8573 : }
8574 :
8575 316 : PQclear(res);
8576 :
8577 316 : destroyPQExpBuffer(query);
8578 316 : }
8579 :
8580 : /*
8581 : * getRules
8582 : * get basic information about every rule in the system
8583 : */
8584 : void
8585 376 : getRules(Archive *fout)
8586 : {
8587 : PGresult *res;
8588 : int ntups;
8589 : int i;
8590 376 : PQExpBuffer query = createPQExpBuffer();
8591 : RuleInfo *ruleinfo;
8592 : int i_tableoid;
8593 : int i_oid;
8594 : int i_rulename;
8595 : int i_ruletable;
8596 : int i_ev_type;
8597 : int i_is_instead;
8598 : int i_ev_enabled;
8599 :
8600 376 : appendPQExpBufferStr(query, "SELECT "
8601 : "tableoid, oid, rulename, "
8602 : "ev_class AS ruletable, ev_type, is_instead, "
8603 : "ev_enabled "
8604 : "FROM pg_rewrite "
8605 : "ORDER BY oid");
8606 :
8607 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8608 :
8609 376 : ntups = PQntuples(res);
8610 :
8611 376 : ruleinfo = (RuleInfo *) pg_malloc(ntups * sizeof(RuleInfo));
8612 :
8613 376 : i_tableoid = PQfnumber(res, "tableoid");
8614 376 : i_oid = PQfnumber(res, "oid");
8615 376 : i_rulename = PQfnumber(res, "rulename");
8616 376 : i_ruletable = PQfnumber(res, "ruletable");
8617 376 : i_ev_type = PQfnumber(res, "ev_type");
8618 376 : i_is_instead = PQfnumber(res, "is_instead");
8619 376 : i_ev_enabled = PQfnumber(res, "ev_enabled");
8620 :
8621 58686 : for (i = 0; i < ntups; i++)
8622 : {
8623 : Oid ruletableoid;
8624 :
8625 58310 : ruleinfo[i].dobj.objType = DO_RULE;
8626 58310 : ruleinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8627 58310 : ruleinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8628 58310 : AssignDumpId(&ruleinfo[i].dobj);
8629 58310 : ruleinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_rulename));
8630 58310 : ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
8631 58310 : ruleinfo[i].ruletable = findTableByOid(ruletableoid);
8632 58310 : if (ruleinfo[i].ruletable == NULL)
8633 0 : pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
8634 : ruletableoid, ruleinfo[i].dobj.catId.oid);
8635 58310 : ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
8636 58310 : ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
8637 58310 : ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
8638 58310 : ruleinfo[i].is_instead = *(PQgetvalue(res, i, i_is_instead)) == 't';
8639 58310 : ruleinfo[i].ev_enabled = *(PQgetvalue(res, i, i_ev_enabled));
8640 58310 : if (ruleinfo[i].ruletable)
8641 : {
8642 : /*
8643 : * If the table is a view or materialized view, force its ON
8644 : * SELECT rule to be sorted before the view itself --- this
8645 : * ensures that any dependencies for the rule affect the table's
8646 : * positioning. Other rules are forced to appear after their
8647 : * table.
8648 : */
8649 58310 : if ((ruleinfo[i].ruletable->relkind == RELKIND_VIEW ||
8650 1398 : ruleinfo[i].ruletable->relkind == RELKIND_MATVIEW) &&
8651 57848 : ruleinfo[i].ev_type == '1' && ruleinfo[i].is_instead)
8652 : {
8653 57012 : addObjectDependency(&ruleinfo[i].ruletable->dobj,
8654 57012 : ruleinfo[i].dobj.dumpId);
8655 : /* We'll merge the rule into CREATE VIEW, if possible */
8656 57012 : ruleinfo[i].separate = false;
8657 : }
8658 : else
8659 : {
8660 1298 : addObjectDependency(&ruleinfo[i].dobj,
8661 1298 : ruleinfo[i].ruletable->dobj.dumpId);
8662 1298 : ruleinfo[i].separate = true;
8663 : }
8664 : }
8665 : else
8666 0 : ruleinfo[i].separate = true;
8667 : }
8668 :
8669 376 : PQclear(res);
8670 :
8671 376 : destroyPQExpBuffer(query);
8672 376 : }
8673 :
8674 : /*
8675 : * getTriggers
8676 : * get information about every trigger on a dumpable table
8677 : *
8678 : * Note: trigger data is not returned directly to the caller, but it
8679 : * does get entered into the DumpableObject tables.
8680 : */
8681 : void
8682 376 : getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
8683 : {
8684 376 : PQExpBuffer query = createPQExpBuffer();
8685 376 : PQExpBuffer tbloids = createPQExpBuffer();
8686 : PGresult *res;
8687 : int ntups;
8688 : int curtblindx;
8689 : TriggerInfo *tginfo;
8690 : int i_tableoid,
8691 : i_oid,
8692 : i_tgrelid,
8693 : i_tgname,
8694 : i_tgenabled,
8695 : i_tgispartition,
8696 : i_tgdef;
8697 :
8698 : /*
8699 : * We want to perform just one query against pg_trigger. However, we
8700 : * mustn't try to select every row of the catalog and then sort it out on
8701 : * the client side, because some of the server-side functions we need
8702 : * would be unsafe to apply to tables we don't have lock on. Hence, we
8703 : * build an array of the OIDs of tables we care about (and now have lock
8704 : * on!), and use a WHERE clause to constrain which rows are selected.
8705 : */
8706 376 : appendPQExpBufferChar(tbloids, '{');
8707 99258 : for (int i = 0; i < numTables; i++)
8708 : {
8709 98882 : TableInfo *tbinfo = &tblinfo[i];
8710 :
8711 98882 : if (!tbinfo->hastriggers ||
8712 2228 : !(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
8713 97178 : continue;
8714 :
8715 : /* OK, we need info for this table */
8716 1704 : if (tbloids->len > 1) /* do we have more than the '{'? */
8717 1602 : appendPQExpBufferChar(tbloids, ',');
8718 1704 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
8719 : }
8720 376 : appendPQExpBufferChar(tbloids, '}');
8721 :
8722 376 : if (fout->remoteVersion >= 150000)
8723 : {
8724 : /*
8725 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8726 : * result in non-forward-compatible dumps of WHEN clauses due to
8727 : * under-parenthesization.
8728 : *
8729 : * NB: We need to see partition triggers in case the tgenabled flag
8730 : * has been changed from the parent.
8731 : */
8732 376 : appendPQExpBuffer(query,
8733 : "SELECT t.tgrelid, t.tgname, "
8734 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8735 : "t.tgenabled, t.tableoid, t.oid, "
8736 : "t.tgparentid <> 0 AS tgispartition\n"
8737 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8738 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8739 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8740 : "WHERE ((NOT t.tgisinternal AND t.tgparentid = 0) "
8741 : "OR t.tgenabled != u.tgenabled) "
8742 : "ORDER BY t.tgrelid, t.tgname",
8743 : tbloids->data);
8744 : }
8745 0 : else if (fout->remoteVersion >= 130000)
8746 : {
8747 : /*
8748 : * NB: think not to use pretty=true in pg_get_triggerdef. It could
8749 : * result in non-forward-compatible dumps of WHEN clauses due to
8750 : * under-parenthesization.
8751 : *
8752 : * NB: We need to see tgisinternal triggers in partitions, in case the
8753 : * tgenabled flag has been changed from the parent.
8754 : */
8755 0 : appendPQExpBuffer(query,
8756 : "SELECT t.tgrelid, t.tgname, "
8757 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8758 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition\n"
8759 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8760 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8761 : "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
8762 : "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
8763 : "ORDER BY t.tgrelid, t.tgname",
8764 : tbloids->data);
8765 : }
8766 0 : else if (fout->remoteVersion >= 110000)
8767 : {
8768 : /*
8769 : * NB: We need to see tgisinternal triggers in partitions, in case the
8770 : * tgenabled flag has been changed from the parent. No tgparentid in
8771 : * version 11-12, so we have to match them via pg_depend.
8772 : *
8773 : * See above about pretty=true in pg_get_triggerdef.
8774 : */
8775 0 : appendPQExpBuffer(query,
8776 : "SELECT t.tgrelid, t.tgname, "
8777 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8778 : "t.tgenabled, t.tableoid, t.oid, t.tgisinternal as tgispartition "
8779 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8780 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8781 : "LEFT JOIN pg_catalog.pg_depend AS d ON "
8782 : " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8783 : " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
8784 : " d.objid = t.oid "
8785 : "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
8786 : "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
8787 : "ORDER BY t.tgrelid, t.tgname",
8788 : tbloids->data);
8789 : }
8790 : else
8791 : {
8792 : /* See above about pretty=true in pg_get_triggerdef */
8793 0 : appendPQExpBuffer(query,
8794 : "SELECT t.tgrelid, t.tgname, "
8795 : "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
8796 : "t.tgenabled, false as tgispartition, "
8797 : "t.tableoid, t.oid "
8798 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
8799 : "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
8800 : "WHERE NOT tgisinternal "
8801 : "ORDER BY t.tgrelid, t.tgname",
8802 : tbloids->data);
8803 : }
8804 :
8805 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8806 :
8807 376 : ntups = PQntuples(res);
8808 :
8809 376 : i_tableoid = PQfnumber(res, "tableoid");
8810 376 : i_oid = PQfnumber(res, "oid");
8811 376 : i_tgrelid = PQfnumber(res, "tgrelid");
8812 376 : i_tgname = PQfnumber(res, "tgname");
8813 376 : i_tgenabled = PQfnumber(res, "tgenabled");
8814 376 : i_tgispartition = PQfnumber(res, "tgispartition");
8815 376 : i_tgdef = PQfnumber(res, "tgdef");
8816 :
8817 376 : tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
8818 :
8819 : /*
8820 : * Outer loop iterates once per table, not once per row. Incrementing of
8821 : * j is handled by the inner loop.
8822 : */
8823 376 : curtblindx = -1;
8824 988 : for (int j = 0; j < ntups;)
8825 : {
8826 612 : Oid tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
8827 612 : TableInfo *tbinfo = NULL;
8828 : int numtrigs;
8829 :
8830 : /* Count rows for this table */
8831 1046 : for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
8832 944 : if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
8833 510 : break;
8834 :
8835 : /*
8836 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
8837 : * order.
8838 : */
8839 32090 : while (++curtblindx < numTables)
8840 : {
8841 32090 : tbinfo = &tblinfo[curtblindx];
8842 32090 : if (tbinfo->dobj.catId.oid == tgrelid)
8843 612 : break;
8844 : }
8845 612 : if (curtblindx >= numTables)
8846 0 : pg_fatal("unrecognized table OID %u", tgrelid);
8847 :
8848 : /* Save data for this table */
8849 612 : tbinfo->triggers = tginfo + j;
8850 612 : tbinfo->numTriggers = numtrigs;
8851 :
8852 1658 : for (int c = 0; c < numtrigs; c++, j++)
8853 : {
8854 1046 : tginfo[j].dobj.objType = DO_TRIGGER;
8855 1046 : tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
8856 1046 : tginfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
8857 1046 : AssignDumpId(&tginfo[j].dobj);
8858 1046 : tginfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_tgname));
8859 1046 : tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
8860 1046 : tginfo[j].tgtable = tbinfo;
8861 1046 : tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
8862 1046 : tginfo[j].tgispartition = *(PQgetvalue(res, j, i_tgispartition)) == 't';
8863 1046 : tginfo[j].tgdef = pg_strdup(PQgetvalue(res, j, i_tgdef));
8864 : }
8865 : }
8866 :
8867 376 : PQclear(res);
8868 :
8869 376 : destroyPQExpBuffer(query);
8870 376 : destroyPQExpBuffer(tbloids);
8871 376 : }
8872 :
8873 : /*
8874 : * getEventTriggers
8875 : * get information about event triggers
8876 : */
8877 : void
8878 376 : getEventTriggers(Archive *fout)
8879 : {
8880 : int i;
8881 : PQExpBuffer query;
8882 : PGresult *res;
8883 : EventTriggerInfo *evtinfo;
8884 : int i_tableoid,
8885 : i_oid,
8886 : i_evtname,
8887 : i_evtevent,
8888 : i_evtowner,
8889 : i_evttags,
8890 : i_evtfname,
8891 : i_evtenabled;
8892 : int ntups;
8893 :
8894 : /* Before 9.3, there are no event triggers */
8895 376 : if (fout->remoteVersion < 90300)
8896 0 : return;
8897 :
8898 376 : query = createPQExpBuffer();
8899 :
8900 376 : appendPQExpBufferStr(query,
8901 : "SELECT e.tableoid, e.oid, evtname, evtenabled, "
8902 : "evtevent, evtowner, "
8903 : "array_to_string(array("
8904 : "select quote_literal(x) "
8905 : " from unnest(evttags) as t(x)), ', ') as evttags, "
8906 : "e.evtfoid::regproc as evtfname "
8907 : "FROM pg_event_trigger e "
8908 : "ORDER BY e.oid");
8909 :
8910 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8911 :
8912 376 : ntups = PQntuples(res);
8913 :
8914 376 : evtinfo = (EventTriggerInfo *) pg_malloc(ntups * sizeof(EventTriggerInfo));
8915 :
8916 376 : i_tableoid = PQfnumber(res, "tableoid");
8917 376 : i_oid = PQfnumber(res, "oid");
8918 376 : i_evtname = PQfnumber(res, "evtname");
8919 376 : i_evtevent = PQfnumber(res, "evtevent");
8920 376 : i_evtowner = PQfnumber(res, "evtowner");
8921 376 : i_evttags = PQfnumber(res, "evttags");
8922 376 : i_evtfname = PQfnumber(res, "evtfname");
8923 376 : i_evtenabled = PQfnumber(res, "evtenabled");
8924 :
8925 480 : for (i = 0; i < ntups; i++)
8926 : {
8927 104 : evtinfo[i].dobj.objType = DO_EVENT_TRIGGER;
8928 104 : evtinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
8929 104 : evtinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
8930 104 : AssignDumpId(&evtinfo[i].dobj);
8931 104 : evtinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_evtname));
8932 104 : evtinfo[i].evtname = pg_strdup(PQgetvalue(res, i, i_evtname));
8933 104 : evtinfo[i].evtevent = pg_strdup(PQgetvalue(res, i, i_evtevent));
8934 104 : evtinfo[i].evtowner = getRoleName(PQgetvalue(res, i, i_evtowner));
8935 104 : evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
8936 104 : evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
8937 104 : evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
8938 :
8939 : /* Decide whether we want to dump it */
8940 104 : selectDumpableObject(&(evtinfo[i].dobj), fout);
8941 : }
8942 :
8943 376 : PQclear(res);
8944 :
8945 376 : destroyPQExpBuffer(query);
8946 : }
8947 :
8948 : /*
8949 : * getProcLangs
8950 : * get basic information about every procedural language in the system
8951 : *
8952 : * NB: this must run after getFuncs() because we assume we can do
8953 : * findFuncByOid().
8954 : */
8955 : void
8956 376 : getProcLangs(Archive *fout)
8957 : {
8958 : PGresult *res;
8959 : int ntups;
8960 : int i;
8961 376 : PQExpBuffer query = createPQExpBuffer();
8962 : ProcLangInfo *planginfo;
8963 : int i_tableoid;
8964 : int i_oid;
8965 : int i_lanname;
8966 : int i_lanpltrusted;
8967 : int i_lanplcallfoid;
8968 : int i_laninline;
8969 : int i_lanvalidator;
8970 : int i_lanacl;
8971 : int i_acldefault;
8972 : int i_lanowner;
8973 :
8974 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
8975 : "lanname, lanpltrusted, lanplcallfoid, "
8976 : "laninline, lanvalidator, "
8977 : "lanacl, "
8978 : "acldefault('l', lanowner) AS acldefault, "
8979 : "lanowner "
8980 : "FROM pg_language "
8981 : "WHERE lanispl "
8982 : "ORDER BY oid");
8983 :
8984 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
8985 :
8986 376 : ntups = PQntuples(res);
8987 :
8988 376 : planginfo = (ProcLangInfo *) pg_malloc(ntups * sizeof(ProcLangInfo));
8989 :
8990 376 : i_tableoid = PQfnumber(res, "tableoid");
8991 376 : i_oid = PQfnumber(res, "oid");
8992 376 : i_lanname = PQfnumber(res, "lanname");
8993 376 : i_lanpltrusted = PQfnumber(res, "lanpltrusted");
8994 376 : i_lanplcallfoid = PQfnumber(res, "lanplcallfoid");
8995 376 : i_laninline = PQfnumber(res, "laninline");
8996 376 : i_lanvalidator = PQfnumber(res, "lanvalidator");
8997 376 : i_lanacl = PQfnumber(res, "lanacl");
8998 376 : i_acldefault = PQfnumber(res, "acldefault");
8999 376 : i_lanowner = PQfnumber(res, "lanowner");
9000 :
9001 842 : for (i = 0; i < ntups; i++)
9002 : {
9003 466 : planginfo[i].dobj.objType = DO_PROCLANG;
9004 466 : planginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9005 466 : planginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9006 466 : AssignDumpId(&planginfo[i].dobj);
9007 :
9008 466 : planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
9009 466 : planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
9010 466 : planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
9011 466 : planginfo[i].dacl.privtype = 0;
9012 466 : planginfo[i].dacl.initprivs = NULL;
9013 466 : planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
9014 466 : planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
9015 466 : planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
9016 466 : planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
9017 466 : planginfo[i].lanowner = getRoleName(PQgetvalue(res, i, i_lanowner));
9018 :
9019 : /* Decide whether we want to dump it */
9020 466 : selectDumpableProcLang(&(planginfo[i]), fout);
9021 :
9022 : /* Mark whether language has an ACL */
9023 466 : if (!PQgetisnull(res, i, i_lanacl))
9024 90 : planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
9025 : }
9026 :
9027 376 : PQclear(res);
9028 :
9029 376 : destroyPQExpBuffer(query);
9030 376 : }
9031 :
9032 : /*
9033 : * getCasts
9034 : * get basic information about most casts in the system
9035 : *
9036 : * Skip casts from a range to its multirange, since we'll create those
9037 : * automatically.
9038 : */
9039 : void
9040 376 : getCasts(Archive *fout)
9041 : {
9042 : PGresult *res;
9043 : int ntups;
9044 : int i;
9045 376 : PQExpBuffer query = createPQExpBuffer();
9046 : CastInfo *castinfo;
9047 : int i_tableoid;
9048 : int i_oid;
9049 : int i_castsource;
9050 : int i_casttarget;
9051 : int i_castfunc;
9052 : int i_castcontext;
9053 : int i_castmethod;
9054 :
9055 376 : if (fout->remoteVersion >= 140000)
9056 : {
9057 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9058 : "castsource, casttarget, castfunc, castcontext, "
9059 : "castmethod "
9060 : "FROM pg_cast c "
9061 : "WHERE NOT EXISTS ( "
9062 : "SELECT 1 FROM pg_range r "
9063 : "WHERE c.castsource = r.rngtypid "
9064 : "AND c.casttarget = r.rngmultitypid "
9065 : ") "
9066 : "ORDER BY 3,4");
9067 : }
9068 : else
9069 : {
9070 0 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9071 : "castsource, casttarget, castfunc, castcontext, "
9072 : "castmethod "
9073 : "FROM pg_cast ORDER BY 3,4");
9074 : }
9075 :
9076 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9077 :
9078 376 : ntups = PQntuples(res);
9079 :
9080 376 : castinfo = (CastInfo *) pg_malloc(ntups * sizeof(CastInfo));
9081 :
9082 376 : i_tableoid = PQfnumber(res, "tableoid");
9083 376 : i_oid = PQfnumber(res, "oid");
9084 376 : i_castsource = PQfnumber(res, "castsource");
9085 376 : i_casttarget = PQfnumber(res, "casttarget");
9086 376 : i_castfunc = PQfnumber(res, "castfunc");
9087 376 : i_castcontext = PQfnumber(res, "castcontext");
9088 376 : i_castmethod = PQfnumber(res, "castmethod");
9089 :
9090 89286 : for (i = 0; i < ntups; i++)
9091 : {
9092 : PQExpBufferData namebuf;
9093 : TypeInfo *sTypeInfo;
9094 : TypeInfo *tTypeInfo;
9095 :
9096 88910 : castinfo[i].dobj.objType = DO_CAST;
9097 88910 : castinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9098 88910 : castinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9099 88910 : AssignDumpId(&castinfo[i].dobj);
9100 88910 : castinfo[i].castsource = atooid(PQgetvalue(res, i, i_castsource));
9101 88910 : castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
9102 88910 : castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
9103 88910 : castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
9104 88910 : castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
9105 :
9106 : /*
9107 : * Try to name cast as concatenation of typnames. This is only used
9108 : * for purposes of sorting. If we fail to find either type, the name
9109 : * will be an empty string.
9110 : */
9111 88910 : initPQExpBuffer(&namebuf);
9112 88910 : sTypeInfo = findTypeByOid(castinfo[i].castsource);
9113 88910 : tTypeInfo = findTypeByOid(castinfo[i].casttarget);
9114 88910 : if (sTypeInfo && tTypeInfo)
9115 88910 : appendPQExpBuffer(&namebuf, "%s %s",
9116 : sTypeInfo->dobj.name, tTypeInfo->dobj.name);
9117 88910 : castinfo[i].dobj.name = namebuf.data;
9118 :
9119 : /* Decide whether we want to dump it */
9120 88910 : selectDumpableCast(&(castinfo[i]), fout);
9121 : }
9122 :
9123 376 : PQclear(res);
9124 :
9125 376 : destroyPQExpBuffer(query);
9126 376 : }
9127 :
9128 : static char *
9129 176 : get_language_name(Archive *fout, Oid langid)
9130 : {
9131 : PQExpBuffer query;
9132 : PGresult *res;
9133 : char *lanname;
9134 :
9135 176 : query = createPQExpBuffer();
9136 176 : appendPQExpBuffer(query, "SELECT lanname FROM pg_language WHERE oid = %u", langid);
9137 176 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
9138 176 : lanname = pg_strdup(fmtId(PQgetvalue(res, 0, 0)));
9139 176 : destroyPQExpBuffer(query);
9140 176 : PQclear(res);
9141 :
9142 176 : return lanname;
9143 : }
9144 :
9145 : /*
9146 : * getTransforms
9147 : * get basic information about every transform in the system
9148 : */
9149 : void
9150 376 : getTransforms(Archive *fout)
9151 : {
9152 : PGresult *res;
9153 : int ntups;
9154 : int i;
9155 : PQExpBuffer query;
9156 : TransformInfo *transforminfo;
9157 : int i_tableoid;
9158 : int i_oid;
9159 : int i_trftype;
9160 : int i_trflang;
9161 : int i_trffromsql;
9162 : int i_trftosql;
9163 :
9164 : /* Transforms didn't exist pre-9.5 */
9165 376 : if (fout->remoteVersion < 90500)
9166 0 : return;
9167 :
9168 376 : query = createPQExpBuffer();
9169 :
9170 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, "
9171 : "trftype, trflang, trffromsql::oid, trftosql::oid "
9172 : "FROM pg_transform "
9173 : "ORDER BY 3,4");
9174 :
9175 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
9176 :
9177 376 : ntups = PQntuples(res);
9178 :
9179 376 : transforminfo = (TransformInfo *) pg_malloc(ntups * sizeof(TransformInfo));
9180 :
9181 376 : i_tableoid = PQfnumber(res, "tableoid");
9182 376 : i_oid = PQfnumber(res, "oid");
9183 376 : i_trftype = PQfnumber(res, "trftype");
9184 376 : i_trflang = PQfnumber(res, "trflang");
9185 376 : i_trffromsql = PQfnumber(res, "trffromsql");
9186 376 : i_trftosql = PQfnumber(res, "trftosql");
9187 :
9188 480 : for (i = 0; i < ntups; i++)
9189 : {
9190 : PQExpBufferData namebuf;
9191 : TypeInfo *typeInfo;
9192 : char *lanname;
9193 :
9194 104 : transforminfo[i].dobj.objType = DO_TRANSFORM;
9195 104 : transforminfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
9196 104 : transforminfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
9197 104 : AssignDumpId(&transforminfo[i].dobj);
9198 104 : transforminfo[i].trftype = atooid(PQgetvalue(res, i, i_trftype));
9199 104 : transforminfo[i].trflang = atooid(PQgetvalue(res, i, i_trflang));
9200 104 : transforminfo[i].trffromsql = atooid(PQgetvalue(res, i, i_trffromsql));
9201 104 : transforminfo[i].trftosql = atooid(PQgetvalue(res, i, i_trftosql));
9202 :
9203 : /*
9204 : * Try to name transform as concatenation of type and language name.
9205 : * This is only used for purposes of sorting. If we fail to find
9206 : * either, the name will be an empty string.
9207 : */
9208 104 : initPQExpBuffer(&namebuf);
9209 104 : typeInfo = findTypeByOid(transforminfo[i].trftype);
9210 104 : lanname = get_language_name(fout, transforminfo[i].trflang);
9211 104 : if (typeInfo && lanname)
9212 104 : appendPQExpBuffer(&namebuf, "%s %s",
9213 : typeInfo->dobj.name, lanname);
9214 104 : transforminfo[i].dobj.name = namebuf.data;
9215 104 : free(lanname);
9216 :
9217 : /* Decide whether we want to dump it */
9218 104 : selectDumpableObject(&(transforminfo[i].dobj), fout);
9219 : }
9220 :
9221 376 : PQclear(res);
9222 :
9223 376 : destroyPQExpBuffer(query);
9224 : }
9225 :
9226 : /*
9227 : * getTableAttrs -
9228 : * for each interesting table, read info about its attributes
9229 : * (names, types, default values, CHECK constraints, etc)
9230 : *
9231 : * modifies tblinfo
9232 : */
9233 : void
9234 376 : getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
9235 : {
9236 376 : DumpOptions *dopt = fout->dopt;
9237 376 : PQExpBuffer q = createPQExpBuffer();
9238 376 : PQExpBuffer tbloids = createPQExpBuffer();
9239 376 : PQExpBuffer checkoids = createPQExpBuffer();
9240 376 : PQExpBuffer invalidnotnulloids = NULL;
9241 : PGresult *res;
9242 : int ntups;
9243 : int curtblindx;
9244 : int i_attrelid;
9245 : int i_attnum;
9246 : int i_attname;
9247 : int i_atttypname;
9248 : int i_attstattarget;
9249 : int i_attstorage;
9250 : int i_typstorage;
9251 : int i_attidentity;
9252 : int i_attgenerated;
9253 : int i_attisdropped;
9254 : int i_attlen;
9255 : int i_attalign;
9256 : int i_attislocal;
9257 : int i_notnull_name;
9258 : int i_notnull_comment;
9259 : int i_notnull_noinherit;
9260 : int i_notnull_islocal;
9261 : int i_notnull_invalidoid;
9262 : int i_attoptions;
9263 : int i_attcollation;
9264 : int i_attcompression;
9265 : int i_attfdwoptions;
9266 : int i_attmissingval;
9267 : int i_atthasdef;
9268 :
9269 : /*
9270 : * We want to perform just one query against pg_attribute, and then just
9271 : * one against pg_attrdef (for DEFAULTs) and two against pg_constraint
9272 : * (for CHECK constraints and for NOT NULL constraints). However, we
9273 : * mustn't try to select every row of those catalogs and then sort it out
9274 : * on the client side, because some of the server-side functions we need
9275 : * would be unsafe to apply to tables we don't have lock on. Hence, we
9276 : * build an array of the OIDs of tables we care about (and now have lock
9277 : * on!), and use a WHERE clause to constrain which rows are selected.
9278 : */
9279 376 : appendPQExpBufferChar(tbloids, '{');
9280 376 : appendPQExpBufferChar(checkoids, '{');
9281 99258 : for (int i = 0; i < numTables; i++)
9282 : {
9283 98882 : TableInfo *tbinfo = &tblinfo[i];
9284 :
9285 : /* Don't bother to collect info for sequences */
9286 98882 : if (tbinfo->relkind == RELKIND_SEQUENCE)
9287 1276 : continue;
9288 :
9289 : /*
9290 : * Don't bother with uninteresting tables, either. For binary
9291 : * upgrades, this is bypassed for pg_largeobject_metadata and
9292 : * pg_shdepend so that the columns names are collected for the
9293 : * corresponding COPY commands. Restoring the data for those catalogs
9294 : * is faster than restoring the equivalent set of large object
9295 : * commands. We can only do this for upgrades from v12 and newer; in
9296 : * older versions, pg_largeobject_metadata was created WITH OIDS, so
9297 : * the OID column is hidden and won't be dumped.
9298 : */
9299 97606 : if (!tbinfo->interesting &&
9300 84456 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9301 16376 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9302 16300 : tbinfo->dobj.catId.oid == SharedDependRelationId)))
9303 84304 : continue;
9304 :
9305 : /* OK, we need info for this table */
9306 13302 : if (tbloids->len > 1) /* do we have more than the '{'? */
9307 13014 : appendPQExpBufferChar(tbloids, ',');
9308 13302 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9309 :
9310 13302 : if (tbinfo->ncheck > 0)
9311 : {
9312 : /* Also make a list of the ones with check constraints */
9313 1056 : if (checkoids->len > 1) /* do we have more than the '{'? */
9314 918 : appendPQExpBufferChar(checkoids, ',');
9315 1056 : appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
9316 : }
9317 : }
9318 376 : appendPQExpBufferChar(tbloids, '}');
9319 376 : appendPQExpBufferChar(checkoids, '}');
9320 :
9321 : /*
9322 : * Find all the user attributes and their types.
9323 : *
9324 : * Since we only want to dump COLLATE clauses for attributes whose
9325 : * collation is different from their type's default, we use a CASE here to
9326 : * suppress uninteresting attcollations cheaply.
9327 : */
9328 376 : appendPQExpBufferStr(q,
9329 : "SELECT\n"
9330 : "a.attrelid,\n"
9331 : "a.attnum,\n"
9332 : "a.attname,\n"
9333 : "a.attstattarget,\n"
9334 : "a.attstorage,\n"
9335 : "t.typstorage,\n"
9336 : "a.atthasdef,\n"
9337 : "a.attisdropped,\n"
9338 : "a.attlen,\n"
9339 : "a.attalign,\n"
9340 : "a.attislocal,\n"
9341 : "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n"
9342 : "array_to_string(a.attoptions, ', ') AS attoptions,\n"
9343 : "CASE WHEN a.attcollation <> t.typcollation "
9344 : "THEN a.attcollation ELSE 0 END AS attcollation,\n"
9345 : "pg_catalog.array_to_string(ARRAY("
9346 : "SELECT pg_catalog.quote_ident(option_name) || "
9347 : "' ' || pg_catalog.quote_literal(option_value) "
9348 : "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
9349 : "ORDER BY option_name"
9350 : "), E',\n ') AS attfdwoptions,\n");
9351 :
9352 : /*
9353 : * Find out any NOT NULL markings for each column. In 18 and up we read
9354 : * pg_constraint to obtain the constraint name, and for valid constraints
9355 : * also pg_description to obtain its comment. notnull_noinherit is set
9356 : * according to the NO INHERIT property. For versions prior to 18, we
9357 : * store an empty string as the name when a constraint is marked as
9358 : * attnotnull (this cues dumpTableSchema to print the NOT NULL clause
9359 : * without a name); also, such cases are never NO INHERIT.
9360 : *
9361 : * For invalid constraints, we need to store their OIDs for processing
9362 : * elsewhere, so we bring the pg_constraint.oid value when the constraint
9363 : * is invalid, and NULL otherwise. Their comments are handled not here
9364 : * but by collectComments, because they're their own dumpable object.
9365 : *
9366 : * We track in notnull_islocal whether the constraint was defined directly
9367 : * in this table or via an ancestor, for binary upgrade. flagInhAttrs
9368 : * might modify this later.
9369 : */
9370 376 : if (fout->remoteVersion >= 180000)
9371 376 : appendPQExpBufferStr(q,
9372 : "co.conname AS notnull_name,\n"
9373 : "CASE WHEN co.convalidated THEN pt.description"
9374 : " ELSE NULL END AS notnull_comment,\n"
9375 : "CASE WHEN NOT co.convalidated THEN co.oid "
9376 : "ELSE NULL END AS notnull_invalidoid,\n"
9377 : "co.connoinherit AS notnull_noinherit,\n"
9378 : "co.conislocal AS notnull_islocal,\n");
9379 : else
9380 0 : appendPQExpBufferStr(q,
9381 : "CASE WHEN a.attnotnull THEN '' ELSE NULL END AS notnull_name,\n"
9382 : "NULL AS notnull_comment,\n"
9383 : "NULL AS notnull_invalidoid,\n"
9384 : "false AS notnull_noinherit,\n"
9385 : "CASE WHEN a.attislocal THEN true\n"
9386 : " WHEN a.attnotnull AND NOT a.attislocal THEN true\n"
9387 : " ELSE false\n"
9388 : "END AS notnull_islocal,\n");
9389 :
9390 376 : if (fout->remoteVersion >= 140000)
9391 376 : appendPQExpBufferStr(q,
9392 : "a.attcompression AS attcompression,\n");
9393 : else
9394 0 : appendPQExpBufferStr(q,
9395 : "'' AS attcompression,\n");
9396 :
9397 376 : if (fout->remoteVersion >= 100000)
9398 376 : appendPQExpBufferStr(q,
9399 : "a.attidentity,\n");
9400 : else
9401 0 : appendPQExpBufferStr(q,
9402 : "'' AS attidentity,\n");
9403 :
9404 376 : if (fout->remoteVersion >= 110000)
9405 376 : appendPQExpBufferStr(q,
9406 : "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
9407 : "THEN a.attmissingval ELSE null END AS attmissingval,\n");
9408 : else
9409 0 : appendPQExpBufferStr(q,
9410 : "NULL AS attmissingval,\n");
9411 :
9412 376 : if (fout->remoteVersion >= 120000)
9413 376 : appendPQExpBufferStr(q,
9414 : "a.attgenerated\n");
9415 : else
9416 0 : appendPQExpBufferStr(q,
9417 : "'' AS attgenerated\n");
9418 :
9419 : /* need left join to pg_type to not fail on dropped columns ... */
9420 376 : appendPQExpBuffer(q,
9421 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9422 : "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
9423 : "LEFT JOIN pg_catalog.pg_type t "
9424 : "ON (a.atttypid = t.oid)\n",
9425 : tbloids->data);
9426 :
9427 : /*
9428 : * In versions 18 and up, we need pg_constraint for explicit NOT NULL
9429 : * entries and pg_description to get their comments.
9430 : */
9431 376 : if (fout->remoteVersion >= 180000)
9432 376 : appendPQExpBufferStr(q,
9433 : " LEFT JOIN pg_catalog.pg_constraint co ON "
9434 : "(a.attrelid = co.conrelid\n"
9435 : " AND co.contype = 'n' AND "
9436 : "co.conkey = array[a.attnum])\n"
9437 : " LEFT JOIN pg_catalog.pg_description pt ON "
9438 : "(pt.classoid = co.tableoid AND pt.objoid = co.oid)\n");
9439 :
9440 376 : appendPQExpBufferStr(q,
9441 : "WHERE a.attnum > 0::pg_catalog.int2\n"
9442 : "ORDER BY a.attrelid, a.attnum");
9443 :
9444 376 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9445 :
9446 376 : ntups = PQntuples(res);
9447 :
9448 376 : i_attrelid = PQfnumber(res, "attrelid");
9449 376 : i_attnum = PQfnumber(res, "attnum");
9450 376 : i_attname = PQfnumber(res, "attname");
9451 376 : i_atttypname = PQfnumber(res, "atttypname");
9452 376 : i_attstattarget = PQfnumber(res, "attstattarget");
9453 376 : i_attstorage = PQfnumber(res, "attstorage");
9454 376 : i_typstorage = PQfnumber(res, "typstorage");
9455 376 : i_attidentity = PQfnumber(res, "attidentity");
9456 376 : i_attgenerated = PQfnumber(res, "attgenerated");
9457 376 : i_attisdropped = PQfnumber(res, "attisdropped");
9458 376 : i_attlen = PQfnumber(res, "attlen");
9459 376 : i_attalign = PQfnumber(res, "attalign");
9460 376 : i_attislocal = PQfnumber(res, "attislocal");
9461 376 : i_notnull_name = PQfnumber(res, "notnull_name");
9462 376 : i_notnull_comment = PQfnumber(res, "notnull_comment");
9463 376 : i_notnull_invalidoid = PQfnumber(res, "notnull_invalidoid");
9464 376 : i_notnull_noinherit = PQfnumber(res, "notnull_noinherit");
9465 376 : i_notnull_islocal = PQfnumber(res, "notnull_islocal");
9466 376 : i_attoptions = PQfnumber(res, "attoptions");
9467 376 : i_attcollation = PQfnumber(res, "attcollation");
9468 376 : i_attcompression = PQfnumber(res, "attcompression");
9469 376 : i_attfdwoptions = PQfnumber(res, "attfdwoptions");
9470 376 : i_attmissingval = PQfnumber(res, "attmissingval");
9471 376 : i_atthasdef = PQfnumber(res, "atthasdef");
9472 :
9473 : /* Within the next loop, we'll accumulate OIDs of tables with defaults */
9474 376 : resetPQExpBuffer(tbloids);
9475 376 : appendPQExpBufferChar(tbloids, '{');
9476 :
9477 : /*
9478 : * Outer loop iterates once per table, not once per row. Incrementing of
9479 : * r is handled by the inner loop.
9480 : */
9481 376 : curtblindx = -1;
9482 13402 : for (int r = 0; r < ntups;)
9483 : {
9484 13026 : Oid attrelid = atooid(PQgetvalue(res, r, i_attrelid));
9485 13026 : TableInfo *tbinfo = NULL;
9486 : int numatts;
9487 : bool hasdefaults;
9488 :
9489 : /* Count rows for this table */
9490 49350 : for (numatts = 1; numatts < ntups - r; numatts++)
9491 49068 : if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
9492 12744 : break;
9493 :
9494 : /*
9495 : * Locate the associated TableInfo; we rely on tblinfo[] being in OID
9496 : * order.
9497 : */
9498 68258 : while (++curtblindx < numTables)
9499 : {
9500 68258 : tbinfo = &tblinfo[curtblindx];
9501 68258 : if (tbinfo->dobj.catId.oid == attrelid)
9502 13026 : break;
9503 : }
9504 13026 : if (curtblindx >= numTables)
9505 0 : pg_fatal("unrecognized table OID %u", attrelid);
9506 : /* cross-check that we only got requested tables */
9507 13026 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
9508 13026 : (!tbinfo->interesting &&
9509 152 : !(fout->dopt->binary_upgrade && fout->remoteVersion >= 120000 &&
9510 152 : (tbinfo->dobj.catId.oid == LargeObjectMetadataRelationId ||
9511 76 : tbinfo->dobj.catId.oid == SharedDependRelationId))))
9512 0 : pg_fatal("unexpected column data for table \"%s\"",
9513 : tbinfo->dobj.name);
9514 :
9515 : /* Save data for this table */
9516 13026 : tbinfo->numatts = numatts;
9517 13026 : tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
9518 13026 : tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
9519 13026 : tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
9520 13026 : tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
9521 13026 : tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
9522 13026 : tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
9523 13026 : tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
9524 13026 : tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
9525 13026 : tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
9526 13026 : tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
9527 13026 : tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
9528 13026 : tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
9529 13026 : tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
9530 13026 : tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
9531 13026 : tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
9532 13026 : tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
9533 13026 : tbinfo->notnull_constrs = (char **) pg_malloc(numatts * sizeof(char *));
9534 13026 : tbinfo->notnull_comment = (char **) pg_malloc(numatts * sizeof(char *));
9535 13026 : tbinfo->notnull_invalid = (bool *) pg_malloc(numatts * sizeof(bool));
9536 13026 : tbinfo->notnull_noinh = (bool *) pg_malloc(numatts * sizeof(bool));
9537 13026 : tbinfo->notnull_islocal = (bool *) pg_malloc(numatts * sizeof(bool));
9538 13026 : tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
9539 13026 : hasdefaults = false;
9540 :
9541 62376 : for (int j = 0; j < numatts; j++, r++)
9542 : {
9543 49350 : if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
9544 0 : pg_fatal("invalid column numbering in table \"%s\"",
9545 : tbinfo->dobj.name);
9546 49350 : tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
9547 49350 : tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
9548 49350 : if (PQgetisnull(res, r, i_attstattarget))
9549 49270 : tbinfo->attstattarget[j] = -1;
9550 : else
9551 80 : tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
9552 49350 : tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
9553 49350 : tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
9554 49350 : tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
9555 49350 : tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
9556 49350 : tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
9557 49350 : tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
9558 49350 : tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
9559 49350 : tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
9560 49350 : tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
9561 :
9562 : /* Handle not-null constraint name and flags */
9563 49350 : determineNotNullFlags(fout, res, r,
9564 : tbinfo, j,
9565 : i_notnull_name,
9566 : i_notnull_comment,
9567 : i_notnull_invalidoid,
9568 : i_notnull_noinherit,
9569 : i_notnull_islocal,
9570 : &invalidnotnulloids);
9571 :
9572 49350 : tbinfo->notnull_comment[j] = PQgetisnull(res, r, i_notnull_comment) ?
9573 49350 : NULL : pg_strdup(PQgetvalue(res, r, i_notnull_comment));
9574 49350 : tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
9575 49350 : tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
9576 49350 : tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
9577 49350 : tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
9578 49350 : tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
9579 49350 : tbinfo->attrdefs[j] = NULL; /* fix below */
9580 49350 : if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
9581 2536 : hasdefaults = true;
9582 : }
9583 :
9584 13026 : if (hasdefaults)
9585 : {
9586 : /* Collect OIDs of interesting tables that have defaults */
9587 1904 : if (tbloids->len > 1) /* do we have more than the '{'? */
9588 1768 : appendPQExpBufferChar(tbloids, ',');
9589 1904 : appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
9590 : }
9591 : }
9592 :
9593 : /* If invalidnotnulloids has any data, finalize it */
9594 376 : if (invalidnotnulloids != NULL)
9595 86 : appendPQExpBufferChar(invalidnotnulloids, '}');
9596 :
9597 376 : PQclear(res);
9598 :
9599 : /*
9600 : * Now get info about column defaults. This is skipped for a data-only
9601 : * dump, as it is only needed for table schemas.
9602 : */
9603 376 : if (dopt->dumpSchema && tbloids->len > 1)
9604 : {
9605 : AttrDefInfo *attrdefs;
9606 : int numDefaults;
9607 120 : TableInfo *tbinfo = NULL;
9608 :
9609 120 : pg_log_info("finding table default expressions");
9610 :
9611 120 : appendPQExpBufferChar(tbloids, '}');
9612 :
9613 120 : printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
9614 : "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
9615 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9616 : "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
9617 : "ORDER BY a.adrelid, a.adnum",
9618 : tbloids->data);
9619 :
9620 120 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9621 :
9622 120 : numDefaults = PQntuples(res);
9623 120 : attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
9624 :
9625 120 : curtblindx = -1;
9626 2460 : for (int j = 0; j < numDefaults; j++)
9627 : {
9628 2340 : Oid adtableoid = atooid(PQgetvalue(res, j, 0));
9629 2340 : Oid adoid = atooid(PQgetvalue(res, j, 1));
9630 2340 : Oid adrelid = atooid(PQgetvalue(res, j, 2));
9631 2340 : int adnum = atoi(PQgetvalue(res, j, 3));
9632 2340 : char *adsrc = PQgetvalue(res, j, 4);
9633 :
9634 : /*
9635 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9636 : * OID order.
9637 : */
9638 2340 : if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
9639 : {
9640 37456 : while (++curtblindx < numTables)
9641 : {
9642 37456 : tbinfo = &tblinfo[curtblindx];
9643 37456 : if (tbinfo->dobj.catId.oid == adrelid)
9644 1768 : break;
9645 : }
9646 1768 : if (curtblindx >= numTables)
9647 0 : pg_fatal("unrecognized table OID %u", adrelid);
9648 : }
9649 :
9650 2340 : if (adnum <= 0 || adnum > tbinfo->numatts)
9651 0 : pg_fatal("invalid adnum value %d for table \"%s\"",
9652 : adnum, tbinfo->dobj.name);
9653 :
9654 : /*
9655 : * dropped columns shouldn't have defaults, but just in case,
9656 : * ignore 'em
9657 : */
9658 2340 : if (tbinfo->attisdropped[adnum - 1])
9659 0 : continue;
9660 :
9661 2340 : attrdefs[j].dobj.objType = DO_ATTRDEF;
9662 2340 : attrdefs[j].dobj.catId.tableoid = adtableoid;
9663 2340 : attrdefs[j].dobj.catId.oid = adoid;
9664 2340 : AssignDumpId(&attrdefs[j].dobj);
9665 2340 : attrdefs[j].adtable = tbinfo;
9666 2340 : attrdefs[j].adnum = adnum;
9667 2340 : attrdefs[j].adef_expr = pg_strdup(adsrc);
9668 :
9669 2340 : attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
9670 2340 : attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
9671 :
9672 2340 : attrdefs[j].dobj.dump = tbinfo->dobj.dump;
9673 :
9674 : /*
9675 : * Figure out whether the default/generation expression should be
9676 : * dumped as part of the main CREATE TABLE (or similar) command or
9677 : * as a separate ALTER TABLE (or similar) command. The preference
9678 : * is to put it into the CREATE command, but in some cases that's
9679 : * not possible.
9680 : */
9681 2340 : if (tbinfo->attgenerated[adnum - 1])
9682 : {
9683 : /*
9684 : * Column generation expressions cannot be dumped separately,
9685 : * because there is no syntax for it. By setting separate to
9686 : * false here we prevent the "default" from being processed as
9687 : * its own dumpable object. Later, flagInhAttrs() will mark
9688 : * it as not to be dumped at all, if possible (that is, if it
9689 : * can be inherited from a parent).
9690 : */
9691 1312 : attrdefs[j].separate = false;
9692 : }
9693 1028 : else if (tbinfo->relkind == RELKIND_VIEW)
9694 : {
9695 : /*
9696 : * Defaults on a VIEW must always be dumped as separate ALTER
9697 : * TABLE commands.
9698 : */
9699 64 : attrdefs[j].separate = true;
9700 : }
9701 964 : else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
9702 : {
9703 : /* column will be suppressed, print default separately */
9704 8 : attrdefs[j].separate = true;
9705 : }
9706 : else
9707 : {
9708 956 : attrdefs[j].separate = false;
9709 : }
9710 :
9711 2340 : if (!attrdefs[j].separate)
9712 : {
9713 : /*
9714 : * Mark the default as needing to appear before the table, so
9715 : * that any dependencies it has must be emitted before the
9716 : * CREATE TABLE. If this is not possible, we'll change to
9717 : * "separate" mode while sorting dependencies.
9718 : */
9719 2268 : addObjectDependency(&tbinfo->dobj,
9720 2268 : attrdefs[j].dobj.dumpId);
9721 : }
9722 :
9723 2340 : tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
9724 : }
9725 :
9726 120 : PQclear(res);
9727 : }
9728 :
9729 : /*
9730 : * Get info about NOT NULL NOT VALID constraints. This is skipped for a
9731 : * data-only dump, as it is only needed for table schemas.
9732 : */
9733 376 : if (dopt->dumpSchema && invalidnotnulloids)
9734 : {
9735 : ConstraintInfo *constrs;
9736 : int numConstrs;
9737 : int i_tableoid;
9738 : int i_oid;
9739 : int i_conrelid;
9740 : int i_conname;
9741 : int i_consrc;
9742 : int i_conislocal;
9743 :
9744 74 : pg_log_info("finding invalid not-null constraints");
9745 :
9746 74 : resetPQExpBuffer(q);
9747 74 : appendPQExpBuffer(q,
9748 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9749 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9750 : "conislocal, convalidated "
9751 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(conoid)\n"
9752 : "JOIN pg_catalog.pg_constraint c ON (src.conoid = c.oid)\n"
9753 : "ORDER BY c.conrelid, c.conname",
9754 74 : invalidnotnulloids->data);
9755 :
9756 74 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9757 :
9758 74 : numConstrs = PQntuples(res);
9759 74 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9760 :
9761 74 : i_tableoid = PQfnumber(res, "tableoid");
9762 74 : i_oid = PQfnumber(res, "oid");
9763 74 : i_conrelid = PQfnumber(res, "conrelid");
9764 74 : i_conname = PQfnumber(res, "conname");
9765 74 : i_consrc = PQfnumber(res, "consrc");
9766 74 : i_conislocal = PQfnumber(res, "conislocal");
9767 :
9768 : /* As above, this loop iterates once per table, not once per row */
9769 74 : curtblindx = -1;
9770 208 : for (int j = 0; j < numConstrs;)
9771 : {
9772 134 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9773 134 : TableInfo *tbinfo = NULL;
9774 : int numcons;
9775 :
9776 : /* Count rows for this table */
9777 134 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9778 60 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9779 60 : break;
9780 :
9781 : /*
9782 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9783 : * OID order.
9784 : */
9785 25168 : while (++curtblindx < numTables)
9786 : {
9787 25168 : tbinfo = &tblinfo[curtblindx];
9788 25168 : if (tbinfo->dobj.catId.oid == conrelid)
9789 134 : break;
9790 : }
9791 134 : if (curtblindx >= numTables)
9792 0 : pg_fatal("unrecognized table OID %u", conrelid);
9793 :
9794 268 : for (int c = 0; c < numcons; c++, j++)
9795 : {
9796 134 : constrs[j].dobj.objType = DO_CONSTRAINT;
9797 134 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9798 134 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9799 134 : AssignDumpId(&constrs[j].dobj);
9800 134 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9801 134 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9802 134 : constrs[j].contable = tbinfo;
9803 134 : constrs[j].condomain = NULL;
9804 134 : constrs[j].contype = 'n';
9805 134 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9806 134 : constrs[j].confrelid = InvalidOid;
9807 134 : constrs[j].conindex = 0;
9808 134 : constrs[j].condeferrable = false;
9809 134 : constrs[j].condeferred = false;
9810 134 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9811 :
9812 : /*
9813 : * All invalid not-null constraints must be dumped separately,
9814 : * because CREATE TABLE would not create them as invalid, and
9815 : * also because they must be created after potentially
9816 : * violating data has been loaded.
9817 : */
9818 134 : constrs[j].separate = true;
9819 :
9820 134 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9821 : }
9822 : }
9823 74 : PQclear(res);
9824 : }
9825 :
9826 : /*
9827 : * Get info about table CHECK constraints. This is skipped for a
9828 : * data-only dump, as it is only needed for table schemas.
9829 : */
9830 376 : if (dopt->dumpSchema && checkoids->len > 2)
9831 : {
9832 : ConstraintInfo *constrs;
9833 : int numConstrs;
9834 : int i_tableoid;
9835 : int i_oid;
9836 : int i_conrelid;
9837 : int i_conname;
9838 : int i_consrc;
9839 : int i_conislocal;
9840 : int i_convalidated;
9841 :
9842 122 : pg_log_info("finding table check constraints");
9843 :
9844 122 : resetPQExpBuffer(q);
9845 122 : appendPQExpBuffer(q,
9846 : "SELECT c.tableoid, c.oid, conrelid, conname, "
9847 : "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, "
9848 : "conislocal, convalidated "
9849 : "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
9850 : "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
9851 : "WHERE contype = 'c' "
9852 : "ORDER BY c.conrelid, c.conname",
9853 : checkoids->data);
9854 :
9855 122 : res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
9856 :
9857 122 : numConstrs = PQntuples(res);
9858 122 : constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
9859 :
9860 122 : i_tableoid = PQfnumber(res, "tableoid");
9861 122 : i_oid = PQfnumber(res, "oid");
9862 122 : i_conrelid = PQfnumber(res, "conrelid");
9863 122 : i_conname = PQfnumber(res, "conname");
9864 122 : i_consrc = PQfnumber(res, "consrc");
9865 122 : i_conislocal = PQfnumber(res, "conislocal");
9866 122 : i_convalidated = PQfnumber(res, "convalidated");
9867 :
9868 : /* As above, this loop iterates once per table, not once per row */
9869 122 : curtblindx = -1;
9870 1076 : for (int j = 0; j < numConstrs;)
9871 : {
9872 954 : Oid conrelid = atooid(PQgetvalue(res, j, i_conrelid));
9873 954 : TableInfo *tbinfo = NULL;
9874 : int numcons;
9875 :
9876 : /* Count rows for this table */
9877 1224 : for (numcons = 1; numcons < numConstrs - j; numcons++)
9878 1102 : if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
9879 832 : break;
9880 :
9881 : /*
9882 : * Locate the associated TableInfo; we rely on tblinfo[] being in
9883 : * OID order.
9884 : */
9885 36068 : while (++curtblindx < numTables)
9886 : {
9887 36068 : tbinfo = &tblinfo[curtblindx];
9888 36068 : if (tbinfo->dobj.catId.oid == conrelid)
9889 954 : break;
9890 : }
9891 954 : if (curtblindx >= numTables)
9892 0 : pg_fatal("unrecognized table OID %u", conrelid);
9893 :
9894 954 : if (numcons != tbinfo->ncheck)
9895 : {
9896 0 : pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
9897 : "expected %d check constraints on table \"%s\" but found %d",
9898 : tbinfo->ncheck),
9899 : tbinfo->ncheck, tbinfo->dobj.name, numcons);
9900 0 : pg_log_error_hint("The system catalogs might be corrupted.");
9901 0 : exit_nicely(1);
9902 : }
9903 :
9904 954 : tbinfo->checkexprs = constrs + j;
9905 :
9906 2178 : for (int c = 0; c < numcons; c++, j++)
9907 : {
9908 1224 : bool validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
9909 :
9910 1224 : constrs[j].dobj.objType = DO_CONSTRAINT;
9911 1224 : constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
9912 1224 : constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
9913 1224 : AssignDumpId(&constrs[j].dobj);
9914 1224 : constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
9915 1224 : constrs[j].dobj.namespace = tbinfo->dobj.namespace;
9916 1224 : constrs[j].contable = tbinfo;
9917 1224 : constrs[j].condomain = NULL;
9918 1224 : constrs[j].contype = 'c';
9919 1224 : constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
9920 1224 : constrs[j].confrelid = InvalidOid;
9921 1224 : constrs[j].conindex = 0;
9922 1224 : constrs[j].condeferrable = false;
9923 1224 : constrs[j].condeferred = false;
9924 1224 : constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
9925 :
9926 : /*
9927 : * An unvalidated constraint needs to be dumped separately, so
9928 : * that potentially-violating existing data is loaded before
9929 : * the constraint.
9930 : */
9931 1224 : constrs[j].separate = !validated;
9932 :
9933 1224 : constrs[j].dobj.dump = tbinfo->dobj.dump;
9934 :
9935 : /*
9936 : * Mark the constraint as needing to appear before the table
9937 : * --- this is so that any other dependencies of the
9938 : * constraint will be emitted before we try to create the
9939 : * table. If the constraint is to be dumped separately, it
9940 : * will be dumped after data is loaded anyway, so don't do it.
9941 : * (There's an automatic dependency in the opposite direction
9942 : * anyway, so don't need to add one manually here.)
9943 : */
9944 1224 : if (!constrs[j].separate)
9945 1094 : addObjectDependency(&tbinfo->dobj,
9946 1094 : constrs[j].dobj.dumpId);
9947 :
9948 : /*
9949 : * We will detect later whether the constraint must be split
9950 : * out from the table definition.
9951 : */
9952 : }
9953 : }
9954 :
9955 122 : PQclear(res);
9956 : }
9957 :
9958 376 : destroyPQExpBuffer(q);
9959 376 : destroyPQExpBuffer(tbloids);
9960 376 : destroyPQExpBuffer(checkoids);
9961 376 : }
9962 :
9963 : /*
9964 : * Based on the getTableAttrs query's row corresponding to one column, set
9965 : * the name and flags to handle a not-null constraint for that column in
9966 : * the tbinfo struct.
9967 : *
9968 : * Result row 'r' is for tbinfo's attribute 'j'.
9969 : *
9970 : * There are four possibilities:
9971 : * 1) the column has no not-null constraints. In that case, ->notnull_constrs
9972 : * (the constraint name) remains NULL.
9973 : * 2) The column has a constraint with no name (this is the case when
9974 : * constraints come from pre-18 servers). In this case, ->notnull_constrs
9975 : * is set to the empty string; dumpTableSchema will print just "NOT NULL".
9976 : * 3) The column has an invalid not-null constraint. This must be treated
9977 : * as a separate object (because it must be created after the table data
9978 : * is loaded). So we add its OID to invalidnotnulloids for processing
9979 : * elsewhere and do nothing further with it here. We distinguish this
9980 : * case because the "notnull_invalidoid" column has been set to a non-NULL
9981 : * value, which is the constraint OID. Valid constraints have a null OID.
9982 : * 4) The column has a constraint with a known name; in that case
9983 : * notnull_constrs carries that name and dumpTableSchema will print
9984 : * "CONSTRAINT the_name NOT NULL". However, if the name is the default
9985 : * (table_column_not_null) and there's no comment on the constraint,
9986 : * there's no need to print that name in the dump, so notnull_constrs
9987 : * is set to the empty string and it behaves as case 2.
9988 : *
9989 : * In a child table that inherits from a parent already containing NOT NULL
9990 : * constraints and the columns in the child don't have their own NOT NULL
9991 : * declarations, we suppress printing constraints in the child: the
9992 : * constraints are acquired at the point where the child is attached to the
9993 : * parent. This is tracked in ->notnull_islocal; for servers pre-18 this is
9994 : * set not here but in flagInhAttrs. That flag is also used when the
9995 : * constraint was validated in a child but all its parent have it as NOT
9996 : * VALID.
9997 : *
9998 : * Any of these constraints might have the NO INHERIT bit. If so we set
9999 : * ->notnull_noinh and NO INHERIT will be printed by dumpTableSchema.
10000 : *
10001 : * In case 4 above, the name comparison is a bit of a hack; it actually fails
10002 : * to do the right thing in all but the trivial case. However, the downside
10003 : * of getting it wrong is simply that the name is printed rather than
10004 : * suppressed, so it's not a big deal.
10005 : *
10006 : * invalidnotnulloids is expected to be given as NULL; if any invalid not-null
10007 : * constraints are found, it is initialized and filled with the array of
10008 : * OIDs of such constraints, for later processing.
10009 : */
10010 : static void
10011 49350 : determineNotNullFlags(Archive *fout, PGresult *res, int r,
10012 : TableInfo *tbinfo, int j,
10013 : int i_notnull_name,
10014 : int i_notnull_comment,
10015 : int i_notnull_invalidoid,
10016 : int i_notnull_noinherit,
10017 : int i_notnull_islocal,
10018 : PQExpBuffer *invalidnotnulloids)
10019 : {
10020 49350 : DumpOptions *dopt = fout->dopt;
10021 :
10022 : /*
10023 : * If this not-null constraint is not valid, list its OID in
10024 : * invalidnotnulloids and do nothing further. It'll be processed
10025 : * elsewhere later.
10026 : *
10027 : * Because invalid not-null constraints are rare, we don't want to malloc
10028 : * invalidnotnulloids until we're sure we're going it need it, which
10029 : * happens here.
10030 : */
10031 49350 : if (!PQgetisnull(res, r, i_notnull_invalidoid))
10032 : {
10033 146 : char *constroid = PQgetvalue(res, r, i_notnull_invalidoid);
10034 :
10035 146 : if (*invalidnotnulloids == NULL)
10036 : {
10037 86 : *invalidnotnulloids = createPQExpBuffer();
10038 86 : appendPQExpBufferChar(*invalidnotnulloids, '{');
10039 86 : appendPQExpBufferStr(*invalidnotnulloids, constroid);
10040 : }
10041 : else
10042 60 : appendPQExpBuffer(*invalidnotnulloids, ",%s", constroid);
10043 :
10044 : /*
10045 : * Track when a parent constraint is invalid for the cases where a
10046 : * child constraint has been validated independenly.
10047 : */
10048 146 : tbinfo->notnull_invalid[j] = true;
10049 :
10050 : /* nothing else to do */
10051 146 : tbinfo->notnull_constrs[j] = NULL;
10052 146 : return;
10053 : }
10054 :
10055 : /*
10056 : * notnull_noinh is straight from the query result. notnull_islocal also,
10057 : * though flagInhAttrs may change that one later.
10058 : */
10059 49204 : tbinfo->notnull_noinh[j] = PQgetvalue(res, r, i_notnull_noinherit)[0] == 't';
10060 49204 : tbinfo->notnull_islocal[j] = PQgetvalue(res, r, i_notnull_islocal)[0] == 't';
10061 49204 : tbinfo->notnull_invalid[j] = false;
10062 :
10063 : /*
10064 : * Determine a constraint name to use. If the column is not marked not-
10065 : * null, we set NULL which cues ... to do nothing. An empty string says
10066 : * to print an unnamed NOT NULL, and anything else is a constraint name to
10067 : * use.
10068 : */
10069 49204 : if (fout->remoteVersion < 180000)
10070 : {
10071 : /*
10072 : * < 18 doesn't have not-null names, so an unnamed constraint is
10073 : * sufficient.
10074 : */
10075 0 : if (PQgetisnull(res, r, i_notnull_name))
10076 0 : tbinfo->notnull_constrs[j] = NULL;
10077 : else
10078 0 : tbinfo->notnull_constrs[j] = "";
10079 : }
10080 : else
10081 : {
10082 49204 : if (PQgetisnull(res, r, i_notnull_name))
10083 43954 : tbinfo->notnull_constrs[j] = NULL;
10084 : else
10085 : {
10086 : /*
10087 : * In binary upgrade of inheritance child tables, must have a
10088 : * constraint name that we can UPDATE later; same if there's a
10089 : * comment on the constraint.
10090 : */
10091 5250 : if ((dopt->binary_upgrade &&
10092 666 : !tbinfo->ispartition &&
10093 5756 : !tbinfo->notnull_islocal) ||
10094 5250 : !PQgetisnull(res, r, i_notnull_comment))
10095 : {
10096 96 : tbinfo->notnull_constrs[j] =
10097 96 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10098 : }
10099 : else
10100 : {
10101 : char *default_name;
10102 :
10103 : /* XXX should match ChooseConstraintName better */
10104 5154 : default_name = psprintf("%s_%s_not_null", tbinfo->dobj.name,
10105 5154 : tbinfo->attnames[j]);
10106 5154 : if (strcmp(default_name,
10107 5154 : PQgetvalue(res, r, i_notnull_name)) == 0)
10108 3406 : tbinfo->notnull_constrs[j] = "";
10109 : else
10110 : {
10111 1748 : tbinfo->notnull_constrs[j] =
10112 1748 : pstrdup(PQgetvalue(res, r, i_notnull_name));
10113 : }
10114 5154 : free(default_name);
10115 : }
10116 : }
10117 : }
10118 : }
10119 :
10120 : /*
10121 : * Test whether a column should be printed as part of table's CREATE TABLE.
10122 : * Column number is zero-based.
10123 : *
10124 : * Normally this is always true, but it's false for dropped columns, as well
10125 : * as those that were inherited without any local definition. (If we print
10126 : * such a column it will mistakenly get pg_attribute.attislocal set to true.)
10127 : * For partitions, it's always true, because we want the partitions to be
10128 : * created independently and ATTACH PARTITION used afterwards.
10129 : *
10130 : * In binary_upgrade mode, we must print all columns and fix the attislocal/
10131 : * attisdropped state later, so as to keep control of the physical column
10132 : * order.
10133 : *
10134 : * This function exists because there are scattered nonobvious places that
10135 : * must be kept in sync with this decision.
10136 : */
10137 : bool
10138 80044 : shouldPrintColumn(const DumpOptions *dopt, const TableInfo *tbinfo, int colno)
10139 : {
10140 80044 : if (dopt->binary_upgrade)
10141 12452 : return true;
10142 67592 : if (tbinfo->attisdropped[colno])
10143 1452 : return false;
10144 66140 : return (tbinfo->attislocal[colno] || tbinfo->ispartition);
10145 : }
10146 :
10147 :
10148 : /*
10149 : * getTSParsers:
10150 : * get information about all text search parsers in the system catalogs
10151 : */
10152 : void
10153 376 : getTSParsers(Archive *fout)
10154 : {
10155 : PGresult *res;
10156 : int ntups;
10157 : int i;
10158 : PQExpBuffer query;
10159 : TSParserInfo *prsinfo;
10160 : int i_tableoid;
10161 : int i_oid;
10162 : int i_prsname;
10163 : int i_prsnamespace;
10164 : int i_prsstart;
10165 : int i_prstoken;
10166 : int i_prsend;
10167 : int i_prsheadline;
10168 : int i_prslextype;
10169 :
10170 376 : query = createPQExpBuffer();
10171 :
10172 : /*
10173 : * find all text search objects, including builtin ones; we filter out
10174 : * system-defined objects at dump-out time.
10175 : */
10176 :
10177 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, prsname, prsnamespace, "
10178 : "prsstart::oid, prstoken::oid, "
10179 : "prsend::oid, prsheadline::oid, prslextype::oid "
10180 : "FROM pg_ts_parser");
10181 :
10182 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10183 :
10184 376 : ntups = PQntuples(res);
10185 :
10186 376 : prsinfo = (TSParserInfo *) pg_malloc(ntups * sizeof(TSParserInfo));
10187 :
10188 376 : i_tableoid = PQfnumber(res, "tableoid");
10189 376 : i_oid = PQfnumber(res, "oid");
10190 376 : i_prsname = PQfnumber(res, "prsname");
10191 376 : i_prsnamespace = PQfnumber(res, "prsnamespace");
10192 376 : i_prsstart = PQfnumber(res, "prsstart");
10193 376 : i_prstoken = PQfnumber(res, "prstoken");
10194 376 : i_prsend = PQfnumber(res, "prsend");
10195 376 : i_prsheadline = PQfnumber(res, "prsheadline");
10196 376 : i_prslextype = PQfnumber(res, "prslextype");
10197 :
10198 842 : for (i = 0; i < ntups; i++)
10199 : {
10200 466 : prsinfo[i].dobj.objType = DO_TSPARSER;
10201 466 : prsinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10202 466 : prsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10203 466 : AssignDumpId(&prsinfo[i].dobj);
10204 466 : prsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_prsname));
10205 932 : prsinfo[i].dobj.namespace =
10206 466 : findNamespace(atooid(PQgetvalue(res, i, i_prsnamespace)));
10207 466 : prsinfo[i].prsstart = atooid(PQgetvalue(res, i, i_prsstart));
10208 466 : prsinfo[i].prstoken = atooid(PQgetvalue(res, i, i_prstoken));
10209 466 : prsinfo[i].prsend = atooid(PQgetvalue(res, i, i_prsend));
10210 466 : prsinfo[i].prsheadline = atooid(PQgetvalue(res, i, i_prsheadline));
10211 466 : prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
10212 :
10213 : /* Decide whether we want to dump it */
10214 466 : selectDumpableObject(&(prsinfo[i].dobj), fout);
10215 : }
10216 :
10217 376 : PQclear(res);
10218 :
10219 376 : destroyPQExpBuffer(query);
10220 376 : }
10221 :
10222 : /*
10223 : * getTSDictionaries:
10224 : * get information about all text search dictionaries in the system catalogs
10225 : */
10226 : void
10227 376 : getTSDictionaries(Archive *fout)
10228 : {
10229 : PGresult *res;
10230 : int ntups;
10231 : int i;
10232 : PQExpBuffer query;
10233 : TSDictInfo *dictinfo;
10234 : int i_tableoid;
10235 : int i_oid;
10236 : int i_dictname;
10237 : int i_dictnamespace;
10238 : int i_dictowner;
10239 : int i_dicttemplate;
10240 : int i_dictinitoption;
10241 :
10242 376 : query = createPQExpBuffer();
10243 :
10244 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, dictname, "
10245 : "dictnamespace, dictowner, "
10246 : "dicttemplate, dictinitoption "
10247 : "FROM pg_ts_dict");
10248 :
10249 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10250 :
10251 376 : ntups = PQntuples(res);
10252 :
10253 376 : dictinfo = (TSDictInfo *) pg_malloc(ntups * sizeof(TSDictInfo));
10254 :
10255 376 : i_tableoid = PQfnumber(res, "tableoid");
10256 376 : i_oid = PQfnumber(res, "oid");
10257 376 : i_dictname = PQfnumber(res, "dictname");
10258 376 : i_dictnamespace = PQfnumber(res, "dictnamespace");
10259 376 : i_dictowner = PQfnumber(res, "dictowner");
10260 376 : i_dictinitoption = PQfnumber(res, "dictinitoption");
10261 376 : i_dicttemplate = PQfnumber(res, "dicttemplate");
10262 :
10263 11872 : for (i = 0; i < ntups; i++)
10264 : {
10265 11496 : dictinfo[i].dobj.objType = DO_TSDICT;
10266 11496 : dictinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10267 11496 : dictinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10268 11496 : AssignDumpId(&dictinfo[i].dobj);
10269 11496 : dictinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_dictname));
10270 22992 : dictinfo[i].dobj.namespace =
10271 11496 : findNamespace(atooid(PQgetvalue(res, i, i_dictnamespace)));
10272 11496 : dictinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_dictowner));
10273 11496 : dictinfo[i].dicttemplate = atooid(PQgetvalue(res, i, i_dicttemplate));
10274 11496 : if (PQgetisnull(res, i, i_dictinitoption))
10275 466 : dictinfo[i].dictinitoption = NULL;
10276 : else
10277 11030 : dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
10278 :
10279 : /* Decide whether we want to dump it */
10280 11496 : selectDumpableObject(&(dictinfo[i].dobj), fout);
10281 : }
10282 :
10283 376 : PQclear(res);
10284 :
10285 376 : destroyPQExpBuffer(query);
10286 376 : }
10287 :
10288 : /*
10289 : * getTSTemplates:
10290 : * get information about all text search templates in the system catalogs
10291 : */
10292 : void
10293 376 : getTSTemplates(Archive *fout)
10294 : {
10295 : PGresult *res;
10296 : int ntups;
10297 : int i;
10298 : PQExpBuffer query;
10299 : TSTemplateInfo *tmplinfo;
10300 : int i_tableoid;
10301 : int i_oid;
10302 : int i_tmplname;
10303 : int i_tmplnamespace;
10304 : int i_tmplinit;
10305 : int i_tmpllexize;
10306 :
10307 376 : query = createPQExpBuffer();
10308 :
10309 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, tmplname, "
10310 : "tmplnamespace, tmplinit::oid, tmpllexize::oid "
10311 : "FROM pg_ts_template");
10312 :
10313 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10314 :
10315 376 : ntups = PQntuples(res);
10316 :
10317 376 : tmplinfo = (TSTemplateInfo *) pg_malloc(ntups * sizeof(TSTemplateInfo));
10318 :
10319 376 : i_tableoid = PQfnumber(res, "tableoid");
10320 376 : i_oid = PQfnumber(res, "oid");
10321 376 : i_tmplname = PQfnumber(res, "tmplname");
10322 376 : i_tmplnamespace = PQfnumber(res, "tmplnamespace");
10323 376 : i_tmplinit = PQfnumber(res, "tmplinit");
10324 376 : i_tmpllexize = PQfnumber(res, "tmpllexize");
10325 :
10326 2346 : for (i = 0; i < ntups; i++)
10327 : {
10328 1970 : tmplinfo[i].dobj.objType = DO_TSTEMPLATE;
10329 1970 : tmplinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10330 1970 : tmplinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10331 1970 : AssignDumpId(&tmplinfo[i].dobj);
10332 1970 : tmplinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_tmplname));
10333 3940 : tmplinfo[i].dobj.namespace =
10334 1970 : findNamespace(atooid(PQgetvalue(res, i, i_tmplnamespace)));
10335 1970 : tmplinfo[i].tmplinit = atooid(PQgetvalue(res, i, i_tmplinit));
10336 1970 : tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
10337 :
10338 : /* Decide whether we want to dump it */
10339 1970 : selectDumpableObject(&(tmplinfo[i].dobj), fout);
10340 : }
10341 :
10342 376 : PQclear(res);
10343 :
10344 376 : destroyPQExpBuffer(query);
10345 376 : }
10346 :
10347 : /*
10348 : * getTSConfigurations:
10349 : * get information about all text search configurations
10350 : */
10351 : void
10352 376 : getTSConfigurations(Archive *fout)
10353 : {
10354 : PGresult *res;
10355 : int ntups;
10356 : int i;
10357 : PQExpBuffer query;
10358 : TSConfigInfo *cfginfo;
10359 : int i_tableoid;
10360 : int i_oid;
10361 : int i_cfgname;
10362 : int i_cfgnamespace;
10363 : int i_cfgowner;
10364 : int i_cfgparser;
10365 :
10366 376 : query = createPQExpBuffer();
10367 :
10368 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, cfgname, "
10369 : "cfgnamespace, cfgowner, cfgparser "
10370 : "FROM pg_ts_config");
10371 :
10372 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10373 :
10374 376 : ntups = PQntuples(res);
10375 :
10376 376 : cfginfo = (TSConfigInfo *) pg_malloc(ntups * sizeof(TSConfigInfo));
10377 :
10378 376 : i_tableoid = PQfnumber(res, "tableoid");
10379 376 : i_oid = PQfnumber(res, "oid");
10380 376 : i_cfgname = PQfnumber(res, "cfgname");
10381 376 : i_cfgnamespace = PQfnumber(res, "cfgnamespace");
10382 376 : i_cfgowner = PQfnumber(res, "cfgowner");
10383 376 : i_cfgparser = PQfnumber(res, "cfgparser");
10384 :
10385 11802 : for (i = 0; i < ntups; i++)
10386 : {
10387 11426 : cfginfo[i].dobj.objType = DO_TSCONFIG;
10388 11426 : cfginfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10389 11426 : cfginfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10390 11426 : AssignDumpId(&cfginfo[i].dobj);
10391 11426 : cfginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_cfgname));
10392 22852 : cfginfo[i].dobj.namespace =
10393 11426 : findNamespace(atooid(PQgetvalue(res, i, i_cfgnamespace)));
10394 11426 : cfginfo[i].rolname = getRoleName(PQgetvalue(res, i, i_cfgowner));
10395 11426 : cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
10396 :
10397 : /* Decide whether we want to dump it */
10398 11426 : selectDumpableObject(&(cfginfo[i].dobj), fout);
10399 : }
10400 :
10401 376 : PQclear(res);
10402 :
10403 376 : destroyPQExpBuffer(query);
10404 376 : }
10405 :
10406 : /*
10407 : * getForeignDataWrappers:
10408 : * get information about all foreign-data wrappers in the system catalogs
10409 : */
10410 : void
10411 376 : getForeignDataWrappers(Archive *fout)
10412 : {
10413 : PGresult *res;
10414 : int ntups;
10415 : int i;
10416 : PQExpBuffer query;
10417 : FdwInfo *fdwinfo;
10418 : int i_tableoid;
10419 : int i_oid;
10420 : int i_fdwname;
10421 : int i_fdwowner;
10422 : int i_fdwhandler;
10423 : int i_fdwvalidator;
10424 : int i_fdwacl;
10425 : int i_acldefault;
10426 : int i_fdwoptions;
10427 :
10428 376 : query = createPQExpBuffer();
10429 :
10430 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, fdwname, "
10431 : "fdwowner, "
10432 : "fdwhandler::pg_catalog.regproc, "
10433 : "fdwvalidator::pg_catalog.regproc, "
10434 : "fdwacl, "
10435 : "acldefault('F', fdwowner) AS acldefault, "
10436 : "array_to_string(ARRAY("
10437 : "SELECT quote_ident(option_name) || ' ' || "
10438 : "quote_literal(option_value) "
10439 : "FROM pg_options_to_table(fdwoptions) "
10440 : "ORDER BY option_name"
10441 : "), E',\n ') AS fdwoptions "
10442 : "FROM pg_foreign_data_wrapper");
10443 :
10444 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10445 :
10446 376 : ntups = PQntuples(res);
10447 :
10448 376 : fdwinfo = (FdwInfo *) pg_malloc(ntups * sizeof(FdwInfo));
10449 :
10450 376 : i_tableoid = PQfnumber(res, "tableoid");
10451 376 : i_oid = PQfnumber(res, "oid");
10452 376 : i_fdwname = PQfnumber(res, "fdwname");
10453 376 : i_fdwowner = PQfnumber(res, "fdwowner");
10454 376 : i_fdwhandler = PQfnumber(res, "fdwhandler");
10455 376 : i_fdwvalidator = PQfnumber(res, "fdwvalidator");
10456 376 : i_fdwacl = PQfnumber(res, "fdwacl");
10457 376 : i_acldefault = PQfnumber(res, "acldefault");
10458 376 : i_fdwoptions = PQfnumber(res, "fdwoptions");
10459 :
10460 518 : for (i = 0; i < ntups; i++)
10461 : {
10462 142 : fdwinfo[i].dobj.objType = DO_FDW;
10463 142 : fdwinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10464 142 : fdwinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10465 142 : AssignDumpId(&fdwinfo[i].dobj);
10466 142 : fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
10467 142 : fdwinfo[i].dobj.namespace = NULL;
10468 142 : fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
10469 142 : fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10470 142 : fdwinfo[i].dacl.privtype = 0;
10471 142 : fdwinfo[i].dacl.initprivs = NULL;
10472 142 : fdwinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_fdwowner));
10473 142 : fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
10474 142 : fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
10475 142 : fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
10476 :
10477 : /* Decide whether we want to dump it */
10478 142 : selectDumpableObject(&(fdwinfo[i].dobj), fout);
10479 :
10480 : /* Mark whether FDW has an ACL */
10481 142 : if (!PQgetisnull(res, i, i_fdwacl))
10482 90 : fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10483 : }
10484 :
10485 376 : PQclear(res);
10486 :
10487 376 : destroyPQExpBuffer(query);
10488 376 : }
10489 :
10490 : /*
10491 : * getForeignServers:
10492 : * get information about all foreign servers in the system catalogs
10493 : */
10494 : void
10495 376 : getForeignServers(Archive *fout)
10496 : {
10497 : PGresult *res;
10498 : int ntups;
10499 : int i;
10500 : PQExpBuffer query;
10501 : ForeignServerInfo *srvinfo;
10502 : int i_tableoid;
10503 : int i_oid;
10504 : int i_srvname;
10505 : int i_srvowner;
10506 : int i_srvfdw;
10507 : int i_srvtype;
10508 : int i_srvversion;
10509 : int i_srvacl;
10510 : int i_acldefault;
10511 : int i_srvoptions;
10512 :
10513 376 : query = createPQExpBuffer();
10514 :
10515 376 : appendPQExpBufferStr(query, "SELECT tableoid, oid, srvname, "
10516 : "srvowner, "
10517 : "srvfdw, srvtype, srvversion, srvacl, "
10518 : "acldefault('S', srvowner) AS acldefault, "
10519 : "array_to_string(ARRAY("
10520 : "SELECT quote_ident(option_name) || ' ' || "
10521 : "quote_literal(option_value) "
10522 : "FROM pg_options_to_table(srvoptions) "
10523 : "ORDER BY option_name"
10524 : "), E',\n ') AS srvoptions "
10525 : "FROM pg_foreign_server");
10526 :
10527 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10528 :
10529 376 : ntups = PQntuples(res);
10530 :
10531 376 : srvinfo = (ForeignServerInfo *) pg_malloc(ntups * sizeof(ForeignServerInfo));
10532 :
10533 376 : i_tableoid = PQfnumber(res, "tableoid");
10534 376 : i_oid = PQfnumber(res, "oid");
10535 376 : i_srvname = PQfnumber(res, "srvname");
10536 376 : i_srvowner = PQfnumber(res, "srvowner");
10537 376 : i_srvfdw = PQfnumber(res, "srvfdw");
10538 376 : i_srvtype = PQfnumber(res, "srvtype");
10539 376 : i_srvversion = PQfnumber(res, "srvversion");
10540 376 : i_srvacl = PQfnumber(res, "srvacl");
10541 376 : i_acldefault = PQfnumber(res, "acldefault");
10542 376 : i_srvoptions = PQfnumber(res, "srvoptions");
10543 :
10544 526 : for (i = 0; i < ntups; i++)
10545 : {
10546 150 : srvinfo[i].dobj.objType = DO_FOREIGN_SERVER;
10547 150 : srvinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10548 150 : srvinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10549 150 : AssignDumpId(&srvinfo[i].dobj);
10550 150 : srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
10551 150 : srvinfo[i].dobj.namespace = NULL;
10552 150 : srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
10553 150 : srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10554 150 : srvinfo[i].dacl.privtype = 0;
10555 150 : srvinfo[i].dacl.initprivs = NULL;
10556 150 : srvinfo[i].rolname = getRoleName(PQgetvalue(res, i, i_srvowner));
10557 150 : srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
10558 150 : srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
10559 150 : srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
10560 150 : srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
10561 :
10562 : /* Decide whether we want to dump it */
10563 150 : selectDumpableObject(&(srvinfo[i].dobj), fout);
10564 :
10565 : /* Servers have user mappings */
10566 150 : srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
10567 :
10568 : /* Mark whether server has an ACL */
10569 150 : if (!PQgetisnull(res, i, i_srvacl))
10570 90 : srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10571 : }
10572 :
10573 376 : PQclear(res);
10574 :
10575 376 : destroyPQExpBuffer(query);
10576 376 : }
10577 :
10578 : /*
10579 : * getDefaultACLs:
10580 : * get information about all default ACL information in the system catalogs
10581 : */
10582 : void
10583 376 : getDefaultACLs(Archive *fout)
10584 : {
10585 376 : DumpOptions *dopt = fout->dopt;
10586 : DefaultACLInfo *daclinfo;
10587 : PQExpBuffer query;
10588 : PGresult *res;
10589 : int i_oid;
10590 : int i_tableoid;
10591 : int i_defaclrole;
10592 : int i_defaclnamespace;
10593 : int i_defaclobjtype;
10594 : int i_defaclacl;
10595 : int i_acldefault;
10596 : int i,
10597 : ntups;
10598 :
10599 376 : query = createPQExpBuffer();
10600 :
10601 : /*
10602 : * Global entries (with defaclnamespace=0) replace the hard-wired default
10603 : * ACL for their object type. We should dump them as deltas from the
10604 : * default ACL, since that will be used as a starting point for
10605 : * interpreting the ALTER DEFAULT PRIVILEGES commands. On the other hand,
10606 : * non-global entries can only add privileges not revoke them. We must
10607 : * dump those as-is (i.e., as deltas from an empty ACL).
10608 : *
10609 : * We can use defaclobjtype as the object type for acldefault(), except
10610 : * for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be converted to
10611 : * 's'.
10612 : */
10613 376 : appendPQExpBufferStr(query,
10614 : "SELECT oid, tableoid, "
10615 : "defaclrole, "
10616 : "defaclnamespace, "
10617 : "defaclobjtype, "
10618 : "defaclacl, "
10619 : "CASE WHEN defaclnamespace = 0 THEN "
10620 : "acldefault(CASE WHEN defaclobjtype = 'S' "
10621 : "THEN 's'::\"char\" ELSE defaclobjtype END, "
10622 : "defaclrole) ELSE '{}' END AS acldefault "
10623 : "FROM pg_default_acl");
10624 :
10625 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10626 :
10627 376 : ntups = PQntuples(res);
10628 :
10629 376 : daclinfo = (DefaultACLInfo *) pg_malloc(ntups * sizeof(DefaultACLInfo));
10630 :
10631 376 : i_oid = PQfnumber(res, "oid");
10632 376 : i_tableoid = PQfnumber(res, "tableoid");
10633 376 : i_defaclrole = PQfnumber(res, "defaclrole");
10634 376 : i_defaclnamespace = PQfnumber(res, "defaclnamespace");
10635 376 : i_defaclobjtype = PQfnumber(res, "defaclobjtype");
10636 376 : i_defaclacl = PQfnumber(res, "defaclacl");
10637 376 : i_acldefault = PQfnumber(res, "acldefault");
10638 :
10639 764 : for (i = 0; i < ntups; i++)
10640 : {
10641 388 : Oid nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
10642 :
10643 388 : daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
10644 388 : daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
10645 388 : daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
10646 388 : AssignDumpId(&daclinfo[i].dobj);
10647 : /* cheesy ... is it worth coming up with a better object name? */
10648 388 : daclinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_defaclobjtype));
10649 :
10650 388 : if (nspid != InvalidOid)
10651 180 : daclinfo[i].dobj.namespace = findNamespace(nspid);
10652 : else
10653 208 : daclinfo[i].dobj.namespace = NULL;
10654 :
10655 388 : daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
10656 388 : daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
10657 388 : daclinfo[i].dacl.privtype = 0;
10658 388 : daclinfo[i].dacl.initprivs = NULL;
10659 388 : daclinfo[i].defaclrole = getRoleName(PQgetvalue(res, i, i_defaclrole));
10660 388 : daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
10661 :
10662 : /* Default ACLs are ACLs, of course */
10663 388 : daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
10664 :
10665 : /* Decide whether we want to dump it */
10666 388 : selectDumpableDefaultACL(&(daclinfo[i]), dopt);
10667 : }
10668 :
10669 376 : PQclear(res);
10670 :
10671 376 : destroyPQExpBuffer(query);
10672 376 : }
10673 :
10674 : /*
10675 : * getRoleName -- look up the name of a role, given its OID
10676 : *
10677 : * In current usage, we don't expect failures, so error out for a bad OID.
10678 : */
10679 : static const char *
10680 1188648 : getRoleName(const char *roleoid_str)
10681 : {
10682 1188648 : Oid roleoid = atooid(roleoid_str);
10683 :
10684 : /*
10685 : * Do binary search to find the appropriate item.
10686 : */
10687 1188648 : if (nrolenames > 0)
10688 : {
10689 1188648 : RoleNameItem *low = &rolenames[0];
10690 1188648 : RoleNameItem *high = &rolenames[nrolenames - 1];
10691 :
10692 4754024 : while (low <= high)
10693 : {
10694 4754024 : RoleNameItem *middle = low + (high - low) / 2;
10695 :
10696 4754024 : if (roleoid < middle->roleoid)
10697 3563614 : high = middle - 1;
10698 1190410 : else if (roleoid > middle->roleoid)
10699 1762 : low = middle + 1;
10700 : else
10701 1188648 : return middle->rolename; /* found a match */
10702 : }
10703 : }
10704 :
10705 0 : pg_fatal("role with OID %u does not exist", roleoid);
10706 : return NULL; /* keep compiler quiet */
10707 : }
10708 :
10709 : /*
10710 : * collectRoleNames --
10711 : *
10712 : * Construct a table of all known roles.
10713 : * The table is sorted by OID for speed in lookup.
10714 : */
10715 : static void
10716 378 : collectRoleNames(Archive *fout)
10717 : {
10718 : PGresult *res;
10719 : const char *query;
10720 : int i;
10721 :
10722 378 : query = "SELECT oid, rolname FROM pg_catalog.pg_roles ORDER BY 1";
10723 :
10724 378 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
10725 :
10726 378 : nrolenames = PQntuples(res);
10727 :
10728 378 : rolenames = (RoleNameItem *) pg_malloc(nrolenames * sizeof(RoleNameItem));
10729 :
10730 7414 : for (i = 0; i < nrolenames; i++)
10731 : {
10732 7036 : rolenames[i].roleoid = atooid(PQgetvalue(res, i, 0));
10733 7036 : rolenames[i].rolename = pg_strdup(PQgetvalue(res, i, 1));
10734 : }
10735 :
10736 378 : PQclear(res);
10737 378 : }
10738 :
10739 : /*
10740 : * getAdditionalACLs
10741 : *
10742 : * We have now created all the DumpableObjects, and collected the ACL data
10743 : * that appears in the directly-associated catalog entries. However, there's
10744 : * more ACL-related info to collect. If any of a table's columns have ACLs,
10745 : * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
10746 : * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
10747 : * Also, in versions having the pg_init_privs catalog, read that and load the
10748 : * information into the relevant DumpableObjects.
10749 : */
10750 : static void
10751 372 : getAdditionalACLs(Archive *fout)
10752 : {
10753 372 : PQExpBuffer query = createPQExpBuffer();
10754 : PGresult *res;
10755 : int ntups,
10756 : i;
10757 :
10758 : /* Check for per-column ACLs */
10759 372 : appendPQExpBufferStr(query,
10760 : "SELECT DISTINCT attrelid FROM pg_attribute "
10761 : "WHERE attacl IS NOT NULL");
10762 :
10763 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10764 :
10765 372 : ntups = PQntuples(res);
10766 1084 : for (i = 0; i < ntups; i++)
10767 : {
10768 712 : Oid relid = atooid(PQgetvalue(res, i, 0));
10769 : TableInfo *tblinfo;
10770 :
10771 712 : tblinfo = findTableByOid(relid);
10772 : /* OK to ignore tables we haven't got a DumpableObject for */
10773 712 : if (tblinfo)
10774 : {
10775 712 : tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
10776 712 : tblinfo->hascolumnACLs = true;
10777 : }
10778 : }
10779 372 : PQclear(res);
10780 :
10781 : /* Fetch initial-privileges data */
10782 372 : if (fout->remoteVersion >= 90600)
10783 : {
10784 372 : printfPQExpBuffer(query,
10785 : "SELECT objoid, classoid, objsubid, privtype, initprivs "
10786 : "FROM pg_init_privs");
10787 :
10788 372 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
10789 :
10790 372 : ntups = PQntuples(res);
10791 88408 : for (i = 0; i < ntups; i++)
10792 : {
10793 88036 : Oid objoid = atooid(PQgetvalue(res, i, 0));
10794 88036 : Oid classoid = atooid(PQgetvalue(res, i, 1));
10795 88036 : int objsubid = atoi(PQgetvalue(res, i, 2));
10796 88036 : char privtype = *(PQgetvalue(res, i, 3));
10797 88036 : char *initprivs = PQgetvalue(res, i, 4);
10798 : CatalogId objId;
10799 : DumpableObject *dobj;
10800 :
10801 88036 : objId.tableoid = classoid;
10802 88036 : objId.oid = objoid;
10803 88036 : dobj = findObjectByCatalogId(objId);
10804 : /* OK to ignore entries we haven't got a DumpableObject for */
10805 88036 : if (dobj)
10806 : {
10807 : /* Cope with sub-object initprivs */
10808 63200 : if (objsubid != 0)
10809 : {
10810 7488 : if (dobj->objType == DO_TABLE)
10811 : {
10812 : /* For a column initprivs, set the table's ACL flags */
10813 7488 : dobj->components |= DUMP_COMPONENT_ACL;
10814 7488 : ((TableInfo *) dobj)->hascolumnACLs = true;
10815 : }
10816 : else
10817 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10818 : classoid, objoid, objsubid);
10819 7852 : continue;
10820 : }
10821 :
10822 : /*
10823 : * We ignore any pg_init_privs.initprivs entry for the public
10824 : * schema, as explained in getNamespaces().
10825 : */
10826 55712 : if (dobj->objType == DO_NAMESPACE &&
10827 736 : strcmp(dobj->name, "public") == 0)
10828 364 : continue;
10829 :
10830 : /* Else it had better be of a type we think has ACLs */
10831 55348 : if (dobj->objType == DO_NAMESPACE ||
10832 54976 : dobj->objType == DO_TYPE ||
10833 54928 : dobj->objType == DO_FUNC ||
10834 54744 : dobj->objType == DO_AGG ||
10835 54696 : dobj->objType == DO_TABLE ||
10836 0 : dobj->objType == DO_PROCLANG ||
10837 0 : dobj->objType == DO_FDW ||
10838 0 : dobj->objType == DO_FOREIGN_SERVER)
10839 55348 : {
10840 55348 : DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
10841 :
10842 55348 : daobj->dacl.privtype = privtype;
10843 55348 : daobj->dacl.initprivs = pstrdup(initprivs);
10844 : }
10845 : else
10846 0 : pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
10847 : classoid, objoid, objsubid);
10848 : }
10849 : }
10850 372 : PQclear(res);
10851 : }
10852 :
10853 372 : destroyPQExpBuffer(query);
10854 372 : }
10855 :
10856 : /*
10857 : * dumpCommentExtended --
10858 : *
10859 : * This routine is used to dump any comments associated with the
10860 : * object handed to this routine. The routine takes the object type
10861 : * and object name (ready to print, except for schema decoration), plus
10862 : * the namespace and owner of the object (for labeling the ArchiveEntry),
10863 : * plus catalog ID and subid which are the lookup key for pg_description,
10864 : * plus the dump ID for the object (for setting a dependency).
10865 : * If a matching pg_description entry is found, it is dumped.
10866 : *
10867 : * Note: in some cases, such as comments for triggers and rules, the "type"
10868 : * string really looks like, e.g., "TRIGGER name ON". This is a bit of a hack
10869 : * but it doesn't seem worth complicating the API for all callers to make
10870 : * it cleaner.
10871 : *
10872 : * Note: although this routine takes a dumpId for dependency purposes,
10873 : * that purpose is just to mark the dependency in the emitted dump file
10874 : * for possible future use by pg_restore. We do NOT use it for determining
10875 : * ordering of the comment in the dump file, because this routine is called
10876 : * after dependency sorting occurs. This routine should be called just after
10877 : * calling ArchiveEntry() for the specified object.
10878 : */
10879 : static void
10880 12960 : dumpCommentExtended(Archive *fout, const char *type,
10881 : const char *name, const char *namespace,
10882 : const char *owner, CatalogId catalogId,
10883 : int subid, DumpId dumpId,
10884 : const char *initdb_comment)
10885 : {
10886 12960 : DumpOptions *dopt = fout->dopt;
10887 : CommentItem *comments;
10888 : int ncomments;
10889 :
10890 : /* do nothing, if --no-comments is supplied */
10891 12960 : if (dopt->no_comments)
10892 0 : return;
10893 :
10894 : /* Comments are schema not data ... except LO comments are data */
10895 12960 : if (strcmp(type, "LARGE OBJECT") != 0)
10896 : {
10897 12842 : if (!dopt->dumpSchema)
10898 0 : return;
10899 : }
10900 : else
10901 : {
10902 : /* We do dump LO comments in binary-upgrade mode */
10903 118 : if (!dopt->dumpData && !dopt->binary_upgrade)
10904 0 : return;
10905 : }
10906 :
10907 : /* Search for comments associated with catalogId, using table */
10908 12960 : ncomments = findComments(catalogId.tableoid, catalogId.oid,
10909 : &comments);
10910 :
10911 : /* Is there one matching the subid? */
10912 12960 : while (ncomments > 0)
10913 : {
10914 12868 : if (comments->objsubid == subid)
10915 12868 : break;
10916 0 : comments++;
10917 0 : ncomments--;
10918 : }
10919 :
10920 12960 : if (initdb_comment != NULL)
10921 : {
10922 : static CommentItem empty_comment = {.descr = ""};
10923 :
10924 : /*
10925 : * initdb creates this object with a comment. Skip dumping the
10926 : * initdb-provided comment, which would complicate matters for
10927 : * non-superuser use of pg_dump. When the DBA has removed initdb's
10928 : * comment, replicate that.
10929 : */
10930 234 : if (ncomments == 0)
10931 : {
10932 8 : comments = &empty_comment;
10933 8 : ncomments = 1;
10934 : }
10935 226 : else if (strcmp(comments->descr, initdb_comment) == 0)
10936 226 : ncomments = 0;
10937 : }
10938 :
10939 : /* If a comment exists, build COMMENT ON statement */
10940 12960 : if (ncomments > 0)
10941 : {
10942 12650 : PQExpBuffer query = createPQExpBuffer();
10943 12650 : PQExpBuffer tag = createPQExpBuffer();
10944 :
10945 12650 : appendPQExpBuffer(query, "COMMENT ON %s ", type);
10946 12650 : if (namespace && *namespace)
10947 12294 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
10948 12650 : appendPQExpBuffer(query, "%s IS ", name);
10949 12650 : appendStringLiteralAH(query, comments->descr, fout);
10950 12650 : appendPQExpBufferStr(query, ";\n");
10951 :
10952 12650 : appendPQExpBuffer(tag, "%s %s", type, name);
10953 :
10954 : /*
10955 : * We mark comments as SECTION_NONE because they really belong in the
10956 : * same section as their parent, whether that is pre-data or
10957 : * post-data.
10958 : */
10959 12650 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
10960 12650 : ARCHIVE_OPTS(.tag = tag->data,
10961 : .namespace = namespace,
10962 : .owner = owner,
10963 : .description = "COMMENT",
10964 : .section = SECTION_NONE,
10965 : .createStmt = query->data,
10966 : .deps = &dumpId,
10967 : .nDeps = 1));
10968 :
10969 12650 : destroyPQExpBuffer(query);
10970 12650 : destroyPQExpBuffer(tag);
10971 : }
10972 : }
10973 :
10974 : /*
10975 : * dumpComment --
10976 : *
10977 : * Typical simplification of the above function.
10978 : */
10979 : static inline void
10980 12644 : dumpComment(Archive *fout, const char *type,
10981 : const char *name, const char *namespace,
10982 : const char *owner, CatalogId catalogId,
10983 : int subid, DumpId dumpId)
10984 : {
10985 12644 : dumpCommentExtended(fout, type, name, namespace, owner,
10986 : catalogId, subid, dumpId, NULL);
10987 12644 : }
10988 :
10989 : /*
10990 : * appendNamedArgument --
10991 : *
10992 : * Convenience routine for constructing parameters of the form:
10993 : * 'paraname', 'value'::type
10994 : */
10995 : static void
10996 10992 : appendNamedArgument(PQExpBuffer out, Archive *fout, const char *argname,
10997 : const char *argtype, const char *argval)
10998 : {
10999 10992 : appendPQExpBufferStr(out, ",\n\t");
11000 :
11001 10992 : appendStringLiteralAH(out, argname, fout);
11002 10992 : appendPQExpBufferStr(out, ", ");
11003 :
11004 10992 : appendStringLiteralAH(out, argval, fout);
11005 10992 : appendPQExpBuffer(out, "::%s", argtype);
11006 10992 : }
11007 :
11008 : /*
11009 : * fetchAttributeStats --
11010 : *
11011 : * Fetch next batch of attribute statistics for dumpRelationStats_dumper().
11012 : */
11013 : static PGresult *
11014 2070 : fetchAttributeStats(Archive *fout)
11015 : {
11016 2070 : ArchiveHandle *AH = (ArchiveHandle *) fout;
11017 2070 : PQExpBuffer nspnames = createPQExpBuffer();
11018 2070 : PQExpBuffer relnames = createPQExpBuffer();
11019 2070 : int count = 0;
11020 2070 : PGresult *res = NULL;
11021 : static TocEntry *te;
11022 : static bool restarted;
11023 2070 : int max_rels = MAX_ATTR_STATS_RELS;
11024 :
11025 : /*
11026 : * Our query for retrieving statistics for multiple relations uses WITH
11027 : * ORDINALITY and multi-argument UNNEST(), both of which were introduced
11028 : * in v9.4. For older versions, we resort to gathering statistics for a
11029 : * single relation at a time.
11030 : */
11031 2070 : if (fout->remoteVersion < 90400)
11032 0 : max_rels = 1;
11033 :
11034 : /* If we're just starting, set our TOC pointer. */
11035 2070 : if (!te)
11036 124 : te = AH->toc->next;
11037 :
11038 : /*
11039 : * We can't easily avoid a second TOC scan for the tar format because it
11040 : * writes restore.sql separately, which means we must execute the queries
11041 : * twice. This feels risky, but there is no known reason it should
11042 : * generate different output than the first pass. Even if it does, the
11043 : * worst-case scenario is that restore.sql might have different statistics
11044 : * data than the archive.
11045 : */
11046 2070 : if (!restarted && te == AH->toc && AH->format == archTar)
11047 : {
11048 2 : te = AH->toc->next;
11049 2 : restarted = true;
11050 : }
11051 :
11052 2070 : appendPQExpBufferChar(nspnames, '{');
11053 2070 : appendPQExpBufferChar(relnames, '{');
11054 :
11055 : /*
11056 : * Scan the TOC for the next set of relevant stats entries. We assume
11057 : * that statistics are dumped in the order they are listed in the TOC.
11058 : * This is perhaps not the sturdiest assumption, so we verify it matches
11059 : * reality in dumpRelationStats_dumper().
11060 : */
11061 31320 : for (; te != AH->toc && count < max_rels; te = te->next)
11062 : {
11063 29250 : if ((te->reqs & REQ_STATS) != 0 &&
11064 6456 : strcmp(te->desc, "STATISTICS DATA") == 0)
11065 : {
11066 6456 : appendPGArray(nspnames, te->namespace);
11067 6456 : appendPGArray(relnames, te->tag);
11068 6456 : count++;
11069 : }
11070 : }
11071 :
11072 2070 : appendPQExpBufferChar(nspnames, '}');
11073 2070 : appendPQExpBufferChar(relnames, '}');
11074 :
11075 : /* Execute the query for the next batch of relations. */
11076 2070 : if (count > 0)
11077 : {
11078 210 : PQExpBuffer query = createPQExpBuffer();
11079 :
11080 210 : appendPQExpBufferStr(query, "EXECUTE getAttributeStats(");
11081 210 : appendStringLiteralAH(query, nspnames->data, fout);
11082 210 : appendPQExpBufferStr(query, "::pg_catalog.name[],");
11083 210 : appendStringLiteralAH(query, relnames->data, fout);
11084 210 : appendPQExpBufferStr(query, "::pg_catalog.name[])");
11085 210 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11086 210 : destroyPQExpBuffer(query);
11087 : }
11088 :
11089 2070 : destroyPQExpBuffer(nspnames);
11090 2070 : destroyPQExpBuffer(relnames);
11091 2070 : return res;
11092 : }
11093 :
11094 : /*
11095 : * dumpRelationStats_dumper --
11096 : *
11097 : * Generate command to import stats into the relation on the new database.
11098 : * This routine is called by the Archiver when it wants the statistics to be
11099 : * dumped.
11100 : */
11101 : static char *
11102 6456 : dumpRelationStats_dumper(Archive *fout, const void *userArg, const TocEntry *te)
11103 : {
11104 6456 : const RelStatsInfo *rsinfo = (RelStatsInfo *) userArg;
11105 : static PGresult *res;
11106 : static int rownum;
11107 : PQExpBuffer query;
11108 : PQExpBufferData out_data;
11109 6456 : PQExpBuffer out = &out_data;
11110 : int i_schemaname;
11111 : int i_tablename;
11112 : int i_attname;
11113 : int i_inherited;
11114 : int i_null_frac;
11115 : int i_avg_width;
11116 : int i_n_distinct;
11117 : int i_most_common_vals;
11118 : int i_most_common_freqs;
11119 : int i_histogram_bounds;
11120 : int i_correlation;
11121 : int i_most_common_elems;
11122 : int i_most_common_elem_freqs;
11123 : int i_elem_count_histogram;
11124 : int i_range_length_histogram;
11125 : int i_range_empty_frac;
11126 : int i_range_bounds_histogram;
11127 : static TocEntry *expected_te;
11128 :
11129 : /*
11130 : * fetchAttributeStats() assumes that the statistics are dumped in the
11131 : * order they are listed in the TOC. We verify that here for safety.
11132 : */
11133 6456 : if (!expected_te)
11134 124 : expected_te = ((ArchiveHandle *) fout)->toc;
11135 :
11136 6456 : expected_te = expected_te->next;
11137 25708 : while ((expected_te->reqs & REQ_STATS) == 0 ||
11138 6456 : strcmp(expected_te->desc, "STATISTICS DATA") != 0)
11139 19252 : expected_te = expected_te->next;
11140 :
11141 6456 : if (te != expected_te)
11142 0 : pg_fatal("statistics dumped out of order (current: %d %s %s, expected: %d %s %s)",
11143 : te->dumpId, te->desc, te->tag,
11144 : expected_te->dumpId, expected_te->desc, expected_te->tag);
11145 :
11146 6456 : query = createPQExpBuffer();
11147 6456 : if (!fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS])
11148 : {
11149 124 : appendPQExpBufferStr(query,
11150 : "PREPARE getAttributeStats(pg_catalog.name[], pg_catalog.name[]) AS\n"
11151 : "SELECT s.schemaname, s.tablename, s.attname, s.inherited, "
11152 : "s.null_frac, s.avg_width, s.n_distinct, "
11153 : "s.most_common_vals, s.most_common_freqs, "
11154 : "s.histogram_bounds, s.correlation, "
11155 : "s.most_common_elems, s.most_common_elem_freqs, "
11156 : "s.elem_count_histogram, ");
11157 :
11158 124 : if (fout->remoteVersion >= 170000)
11159 124 : appendPQExpBufferStr(query,
11160 : "s.range_length_histogram, "
11161 : "s.range_empty_frac, "
11162 : "s.range_bounds_histogram ");
11163 : else
11164 0 : appendPQExpBufferStr(query,
11165 : "NULL AS range_length_histogram,"
11166 : "NULL AS range_empty_frac,"
11167 : "NULL AS range_bounds_histogram ");
11168 :
11169 : /*
11170 : * The results must be in the order of the relations supplied in the
11171 : * parameters to ensure we remain in sync as we walk through the TOC.
11172 : * The redundant filter clause on s.tablename = ANY(...) seems
11173 : * sufficient to convince the planner to use
11174 : * pg_class_relname_nsp_index, which avoids a full scan of pg_stats.
11175 : * This may not work for all versions.
11176 : *
11177 : * Our query for retrieving statistics for multiple relations uses
11178 : * WITH ORDINALITY and multi-argument UNNEST(), both of which were
11179 : * introduced in v9.4. For older versions, we resort to gathering
11180 : * statistics for a single relation at a time.
11181 : */
11182 124 : if (fout->remoteVersion >= 90400)
11183 124 : appendPQExpBufferStr(query,
11184 : "FROM pg_catalog.pg_stats s "
11185 : "JOIN unnest($1, $2) WITH ORDINALITY AS u (schemaname, tablename, ord) "
11186 : "ON s.schemaname = u.schemaname "
11187 : "AND s.tablename = u.tablename "
11188 : "WHERE s.tablename = ANY($2) "
11189 : "ORDER BY u.ord, s.attname, s.inherited");
11190 : else
11191 0 : appendPQExpBufferStr(query,
11192 : "FROM pg_catalog.pg_stats s "
11193 : "WHERE s.schemaname = $1[1] "
11194 : "AND s.tablename = $2[1] "
11195 : "ORDER BY s.attname, s.inherited");
11196 :
11197 124 : ExecuteSqlStatement(fout, query->data);
11198 :
11199 124 : fout->is_prepared[PREPQUERY_GETATTRIBUTESTATS] = true;
11200 124 : resetPQExpBuffer(query);
11201 : }
11202 :
11203 6456 : initPQExpBuffer(out);
11204 :
11205 : /* restore relation stats */
11206 6456 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_relation_stats(\n");
11207 6456 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11208 : fout->remoteVersion);
11209 6456 : appendPQExpBufferStr(out, "\t'schemaname', ");
11210 6456 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11211 6456 : appendPQExpBufferStr(out, ",\n");
11212 6456 : appendPQExpBufferStr(out, "\t'relname', ");
11213 6456 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11214 6456 : appendPQExpBufferStr(out, ",\n");
11215 6456 : appendPQExpBuffer(out, "\t'relpages', '%d'::integer,\n", rsinfo->relpages);
11216 :
11217 : /*
11218 : * Before v14, a reltuples value of 0 was ambiguous: it could either mean
11219 : * the relation is empty, or it could mean that it hadn't yet been
11220 : * vacuumed or analyzed. (Newer versions use -1 for the latter case.)
11221 : * This ambiguity allegedly can cause the planner to choose inefficient
11222 : * plans after restoring to v18 or newer. To deal with this, let's just
11223 : * set reltuples to -1 in that case.
11224 : */
11225 6456 : if (fout->remoteVersion < 140000 && strcmp("0", rsinfo->reltuples) == 0)
11226 0 : appendPQExpBufferStr(out, "\t'reltuples', '-1'::real,\n");
11227 : else
11228 6456 : appendPQExpBuffer(out, "\t'reltuples', '%s'::real,\n", rsinfo->reltuples);
11229 :
11230 6456 : appendPQExpBuffer(out, "\t'relallvisible', '%d'::integer",
11231 6456 : rsinfo->relallvisible);
11232 :
11233 6456 : if (fout->remoteVersion >= 180000)
11234 6456 : appendPQExpBuffer(out, ",\n\t'relallfrozen', '%d'::integer", rsinfo->relallfrozen);
11235 :
11236 6456 : appendPQExpBufferStr(out, "\n);\n");
11237 :
11238 : /* Fetch the next batch of attribute statistics if needed. */
11239 6456 : if (rownum >= PQntuples(res))
11240 : {
11241 2070 : PQclear(res);
11242 2070 : res = fetchAttributeStats(fout);
11243 2070 : rownum = 0;
11244 : }
11245 :
11246 6456 : i_schemaname = PQfnumber(res, "schemaname");
11247 6456 : i_tablename = PQfnumber(res, "tablename");
11248 6456 : i_attname = PQfnumber(res, "attname");
11249 6456 : i_inherited = PQfnumber(res, "inherited");
11250 6456 : i_null_frac = PQfnumber(res, "null_frac");
11251 6456 : i_avg_width = PQfnumber(res, "avg_width");
11252 6456 : i_n_distinct = PQfnumber(res, "n_distinct");
11253 6456 : i_most_common_vals = PQfnumber(res, "most_common_vals");
11254 6456 : i_most_common_freqs = PQfnumber(res, "most_common_freqs");
11255 6456 : i_histogram_bounds = PQfnumber(res, "histogram_bounds");
11256 6456 : i_correlation = PQfnumber(res, "correlation");
11257 6456 : i_most_common_elems = PQfnumber(res, "most_common_elems");
11258 6456 : i_most_common_elem_freqs = PQfnumber(res, "most_common_elem_freqs");
11259 6456 : i_elem_count_histogram = PQfnumber(res, "elem_count_histogram");
11260 6456 : i_range_length_histogram = PQfnumber(res, "range_length_histogram");
11261 6456 : i_range_empty_frac = PQfnumber(res, "range_empty_frac");
11262 6456 : i_range_bounds_histogram = PQfnumber(res, "range_bounds_histogram");
11263 :
11264 : /* restore attribute stats */
11265 8110 : for (; rownum < PQntuples(res); rownum++)
11266 : {
11267 : const char *attname;
11268 :
11269 : /* Stop if the next stat row in our cache isn't for this relation. */
11270 6040 : if (strcmp(te->tag, PQgetvalue(res, rownum, i_tablename)) != 0 ||
11271 1654 : strcmp(te->namespace, PQgetvalue(res, rownum, i_schemaname)) != 0)
11272 : break;
11273 :
11274 1654 : appendPQExpBufferStr(out, "SELECT * FROM pg_catalog.pg_restore_attribute_stats(\n");
11275 1654 : appendPQExpBuffer(out, "\t'version', '%d'::integer,\n",
11276 : fout->remoteVersion);
11277 1654 : appendPQExpBufferStr(out, "\t'schemaname', ");
11278 1654 : appendStringLiteralAH(out, rsinfo->dobj.namespace->dobj.name, fout);
11279 1654 : appendPQExpBufferStr(out, ",\n\t'relname', ");
11280 1654 : appendStringLiteralAH(out, rsinfo->dobj.name, fout);
11281 :
11282 1654 : if (PQgetisnull(res, rownum, i_attname))
11283 0 : pg_fatal("unexpected null attname");
11284 1654 : attname = PQgetvalue(res, rownum, i_attname);
11285 :
11286 : /*
11287 : * Indexes look up attname in indAttNames to derive attnum, all others
11288 : * use attname directly. We must specify attnum for indexes, since
11289 : * their attnames are not necessarily stable across dump/reload.
11290 : */
11291 1654 : if (rsinfo->nindAttNames == 0)
11292 : {
11293 1584 : appendPQExpBufferStr(out, ",\n\t'attname', ");
11294 1584 : appendStringLiteralAH(out, attname, fout);
11295 : }
11296 : else
11297 : {
11298 70 : bool found = false;
11299 :
11300 132 : for (int i = 0; i < rsinfo->nindAttNames; i++)
11301 : {
11302 132 : if (strcmp(attname, rsinfo->indAttNames[i]) == 0)
11303 : {
11304 70 : appendPQExpBuffer(out, ",\n\t'attnum', '%d'::smallint",
11305 : i + 1);
11306 70 : found = true;
11307 70 : break;
11308 : }
11309 : }
11310 :
11311 70 : if (!found)
11312 0 : pg_fatal("could not find index attname \"%s\"", attname);
11313 : }
11314 :
11315 1654 : if (!PQgetisnull(res, rownum, i_inherited))
11316 1654 : appendNamedArgument(out, fout, "inherited", "boolean",
11317 1654 : PQgetvalue(res, rownum, i_inherited));
11318 1654 : if (!PQgetisnull(res, rownum, i_null_frac))
11319 1654 : appendNamedArgument(out, fout, "null_frac", "real",
11320 1654 : PQgetvalue(res, rownum, i_null_frac));
11321 1654 : if (!PQgetisnull(res, rownum, i_avg_width))
11322 1654 : appendNamedArgument(out, fout, "avg_width", "integer",
11323 1654 : PQgetvalue(res, rownum, i_avg_width));
11324 1654 : if (!PQgetisnull(res, rownum, i_n_distinct))
11325 1654 : appendNamedArgument(out, fout, "n_distinct", "real",
11326 1654 : PQgetvalue(res, rownum, i_n_distinct));
11327 1654 : if (!PQgetisnull(res, rownum, i_most_common_vals))
11328 858 : appendNamedArgument(out, fout, "most_common_vals", "text",
11329 858 : PQgetvalue(res, rownum, i_most_common_vals));
11330 1654 : if (!PQgetisnull(res, rownum, i_most_common_freqs))
11331 858 : appendNamedArgument(out, fout, "most_common_freqs", "real[]",
11332 858 : PQgetvalue(res, rownum, i_most_common_freqs));
11333 1654 : if (!PQgetisnull(res, rownum, i_histogram_bounds))
11334 1016 : appendNamedArgument(out, fout, "histogram_bounds", "text",
11335 1016 : PQgetvalue(res, rownum, i_histogram_bounds));
11336 1654 : if (!PQgetisnull(res, rownum, i_correlation))
11337 1574 : appendNamedArgument(out, fout, "correlation", "real",
11338 1574 : PQgetvalue(res, rownum, i_correlation));
11339 1654 : if (!PQgetisnull(res, rownum, i_most_common_elems))
11340 16 : appendNamedArgument(out, fout, "most_common_elems", "text",
11341 16 : PQgetvalue(res, rownum, i_most_common_elems));
11342 1654 : if (!PQgetisnull(res, rownum, i_most_common_elem_freqs))
11343 16 : appendNamedArgument(out, fout, "most_common_elem_freqs", "real[]",
11344 16 : PQgetvalue(res, rownum, i_most_common_elem_freqs));
11345 1654 : if (!PQgetisnull(res, rownum, i_elem_count_histogram))
11346 14 : appendNamedArgument(out, fout, "elem_count_histogram", "real[]",
11347 14 : PQgetvalue(res, rownum, i_elem_count_histogram));
11348 1654 : if (fout->remoteVersion >= 170000)
11349 : {
11350 1654 : if (!PQgetisnull(res, rownum, i_range_length_histogram))
11351 8 : appendNamedArgument(out, fout, "range_length_histogram", "text",
11352 8 : PQgetvalue(res, rownum, i_range_length_histogram));
11353 1654 : if (!PQgetisnull(res, rownum, i_range_empty_frac))
11354 8 : appendNamedArgument(out, fout, "range_empty_frac", "real",
11355 8 : PQgetvalue(res, rownum, i_range_empty_frac));
11356 1654 : if (!PQgetisnull(res, rownum, i_range_bounds_histogram))
11357 8 : appendNamedArgument(out, fout, "range_bounds_histogram", "text",
11358 8 : PQgetvalue(res, rownum, i_range_bounds_histogram));
11359 : }
11360 1654 : appendPQExpBufferStr(out, "\n);\n");
11361 : }
11362 :
11363 6456 : destroyPQExpBuffer(query);
11364 6456 : return out->data;
11365 : }
11366 :
11367 : /*
11368 : * dumpRelationStats --
11369 : *
11370 : * Make an ArchiveEntry for the relation statistics. The Archiver will take
11371 : * care of gathering the statistics and generating the restore commands when
11372 : * they are needed.
11373 : */
11374 : static void
11375 6594 : dumpRelationStats(Archive *fout, const RelStatsInfo *rsinfo)
11376 : {
11377 6594 : const DumpableObject *dobj = &rsinfo->dobj;
11378 :
11379 : /* nothing to do if we are not dumping statistics */
11380 6594 : if (!fout->dopt->dumpStatistics)
11381 0 : return;
11382 :
11383 6594 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11384 6594 : ARCHIVE_OPTS(.tag = dobj->name,
11385 : .namespace = dobj->namespace->dobj.name,
11386 : .description = "STATISTICS DATA",
11387 : .section = rsinfo->section,
11388 : .defnFn = dumpRelationStats_dumper,
11389 : .defnArg = rsinfo,
11390 : .deps = dobj->dependencies,
11391 : .nDeps = dobj->nDeps));
11392 : }
11393 :
11394 : /*
11395 : * dumpTableComment --
11396 : *
11397 : * As above, but dump comments for both the specified table (or view)
11398 : * and its columns.
11399 : */
11400 : static void
11401 148 : dumpTableComment(Archive *fout, const TableInfo *tbinfo,
11402 : const char *reltypename)
11403 : {
11404 148 : DumpOptions *dopt = fout->dopt;
11405 : CommentItem *comments;
11406 : int ncomments;
11407 : PQExpBuffer query;
11408 : PQExpBuffer tag;
11409 :
11410 : /* do nothing, if --no-comments is supplied */
11411 148 : if (dopt->no_comments)
11412 0 : return;
11413 :
11414 : /* Comments are SCHEMA not data */
11415 148 : if (!dopt->dumpSchema)
11416 0 : return;
11417 :
11418 : /* Search for comments associated with relation, using table */
11419 148 : ncomments = findComments(tbinfo->dobj.catId.tableoid,
11420 148 : tbinfo->dobj.catId.oid,
11421 : &comments);
11422 :
11423 : /* If comments exist, build COMMENT ON statements */
11424 148 : if (ncomments <= 0)
11425 0 : return;
11426 :
11427 148 : query = createPQExpBuffer();
11428 148 : tag = createPQExpBuffer();
11429 :
11430 424 : while (ncomments > 0)
11431 : {
11432 276 : const char *descr = comments->descr;
11433 276 : int objsubid = comments->objsubid;
11434 :
11435 276 : if (objsubid == 0)
11436 : {
11437 64 : resetPQExpBuffer(tag);
11438 64 : appendPQExpBuffer(tag, "%s %s", reltypename,
11439 64 : fmtId(tbinfo->dobj.name));
11440 :
11441 64 : resetPQExpBuffer(query);
11442 64 : appendPQExpBuffer(query, "COMMENT ON %s %s IS ", reltypename,
11443 64 : fmtQualifiedDumpable(tbinfo));
11444 64 : appendStringLiteralAH(query, descr, fout);
11445 64 : appendPQExpBufferStr(query, ";\n");
11446 :
11447 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11448 64 : ARCHIVE_OPTS(.tag = tag->data,
11449 : .namespace = tbinfo->dobj.namespace->dobj.name,
11450 : .owner = tbinfo->rolname,
11451 : .description = "COMMENT",
11452 : .section = SECTION_NONE,
11453 : .createStmt = query->data,
11454 : .deps = &(tbinfo->dobj.dumpId),
11455 : .nDeps = 1));
11456 : }
11457 212 : else if (objsubid > 0 && objsubid <= tbinfo->numatts)
11458 : {
11459 212 : resetPQExpBuffer(tag);
11460 212 : appendPQExpBuffer(tag, "COLUMN %s.",
11461 212 : fmtId(tbinfo->dobj.name));
11462 212 : appendPQExpBufferStr(tag, fmtId(tbinfo->attnames[objsubid - 1]));
11463 :
11464 212 : resetPQExpBuffer(query);
11465 212 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
11466 212 : fmtQualifiedDumpable(tbinfo));
11467 212 : appendPQExpBuffer(query, "%s IS ",
11468 212 : fmtId(tbinfo->attnames[objsubid - 1]));
11469 212 : appendStringLiteralAH(query, descr, fout);
11470 212 : appendPQExpBufferStr(query, ";\n");
11471 :
11472 212 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
11473 212 : ARCHIVE_OPTS(.tag = tag->data,
11474 : .namespace = tbinfo->dobj.namespace->dobj.name,
11475 : .owner = tbinfo->rolname,
11476 : .description = "COMMENT",
11477 : .section = SECTION_NONE,
11478 : .createStmt = query->data,
11479 : .deps = &(tbinfo->dobj.dumpId),
11480 : .nDeps = 1));
11481 : }
11482 :
11483 276 : comments++;
11484 276 : ncomments--;
11485 : }
11486 :
11487 148 : destroyPQExpBuffer(query);
11488 148 : destroyPQExpBuffer(tag);
11489 : }
11490 :
11491 : /*
11492 : * findComments --
11493 : *
11494 : * Find the comment(s), if any, associated with the given object. All the
11495 : * objsubid values associated with the given classoid/objoid are found with
11496 : * one search.
11497 : */
11498 : static int
11499 13172 : findComments(Oid classoid, Oid objoid, CommentItem **items)
11500 : {
11501 13172 : CommentItem *middle = NULL;
11502 : CommentItem *low;
11503 : CommentItem *high;
11504 : int nmatch;
11505 :
11506 : /*
11507 : * Do binary search to find some item matching the object.
11508 : */
11509 13172 : low = &comments[0];
11510 13172 : high = &comments[ncomments - 1];
11511 131094 : while (low <= high)
11512 : {
11513 131002 : middle = low + (high - low) / 2;
11514 :
11515 131002 : if (classoid < middle->classoid)
11516 14904 : high = middle - 1;
11517 116098 : else if (classoid > middle->classoid)
11518 14270 : low = middle + 1;
11519 101828 : else if (objoid < middle->objoid)
11520 43102 : high = middle - 1;
11521 58726 : else if (objoid > middle->objoid)
11522 45646 : low = middle + 1;
11523 : else
11524 13080 : break; /* found a match */
11525 : }
11526 :
11527 13172 : if (low > high) /* no matches */
11528 : {
11529 92 : *items = NULL;
11530 92 : return 0;
11531 : }
11532 :
11533 : /*
11534 : * Now determine how many items match the object. The search loop
11535 : * invariant still holds: only items between low and high inclusive could
11536 : * match.
11537 : */
11538 13080 : nmatch = 1;
11539 13208 : while (middle > low)
11540 : {
11541 6142 : if (classoid != middle[-1].classoid ||
11542 5844 : objoid != middle[-1].objoid)
11543 : break;
11544 128 : middle--;
11545 128 : nmatch++;
11546 : }
11547 :
11548 13080 : *items = middle;
11549 :
11550 13080 : middle += nmatch;
11551 13080 : while (middle <= high)
11552 : {
11553 7062 : if (classoid != middle->classoid ||
11554 6392 : objoid != middle->objoid)
11555 : break;
11556 0 : middle++;
11557 0 : nmatch++;
11558 : }
11559 :
11560 13080 : return nmatch;
11561 : }
11562 :
11563 : /*
11564 : * collectComments --
11565 : *
11566 : * Construct a table of all comments available for database objects;
11567 : * also set the has-comment component flag for each relevant object.
11568 : *
11569 : * We used to do per-object queries for the comments, but it's much faster
11570 : * to pull them all over at once, and on most databases the memory cost
11571 : * isn't high.
11572 : *
11573 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
11574 : */
11575 : static void
11576 376 : collectComments(Archive *fout)
11577 : {
11578 : PGresult *res;
11579 : PQExpBuffer query;
11580 : int i_description;
11581 : int i_classoid;
11582 : int i_objoid;
11583 : int i_objsubid;
11584 : int ntups;
11585 : int i;
11586 : DumpableObject *dobj;
11587 :
11588 376 : query = createPQExpBuffer();
11589 :
11590 376 : appendPQExpBufferStr(query, "SELECT description, classoid, objoid, objsubid "
11591 : "FROM pg_catalog.pg_description "
11592 : "ORDER BY classoid, objoid, objsubid");
11593 :
11594 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
11595 :
11596 : /* Construct lookup table containing OIDs in numeric form */
11597 :
11598 376 : i_description = PQfnumber(res, "description");
11599 376 : i_classoid = PQfnumber(res, "classoid");
11600 376 : i_objoid = PQfnumber(res, "objoid");
11601 376 : i_objsubid = PQfnumber(res, "objsubid");
11602 :
11603 376 : ntups = PQntuples(res);
11604 :
11605 376 : comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
11606 376 : ncomments = 0;
11607 376 : dobj = NULL;
11608 :
11609 2006530 : for (i = 0; i < ntups; i++)
11610 : {
11611 : CatalogId objId;
11612 : int subid;
11613 :
11614 2006154 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
11615 2006154 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
11616 2006154 : subid = atoi(PQgetvalue(res, i, i_objsubid));
11617 :
11618 : /* We needn't remember comments that don't match any dumpable object */
11619 2006154 : if (dobj == NULL ||
11620 721562 : dobj->catId.tableoid != objId.tableoid ||
11621 716952 : dobj->catId.oid != objId.oid)
11622 2005974 : dobj = findObjectByCatalogId(objId);
11623 2006154 : if (dobj == NULL)
11624 1284228 : continue;
11625 :
11626 : /*
11627 : * Comments on columns of composite types are linked to the type's
11628 : * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
11629 : * in the type's own DumpableObject.
11630 : */
11631 721926 : if (subid != 0 && dobj->objType == DO_TABLE &&
11632 388 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
11633 90 : {
11634 : TypeInfo *cTypeInfo;
11635 :
11636 90 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
11637 90 : if (cTypeInfo)
11638 90 : cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
11639 : }
11640 : else
11641 721836 : dobj->components |= DUMP_COMPONENT_COMMENT;
11642 :
11643 721926 : comments[ncomments].descr = pg_strdup(PQgetvalue(res, i, i_description));
11644 721926 : comments[ncomments].classoid = objId.tableoid;
11645 721926 : comments[ncomments].objoid = objId.oid;
11646 721926 : comments[ncomments].objsubid = subid;
11647 721926 : ncomments++;
11648 : }
11649 :
11650 376 : PQclear(res);
11651 376 : destroyPQExpBuffer(query);
11652 376 : }
11653 :
11654 : /*
11655 : * dumpDumpableObject
11656 : *
11657 : * This routine and its subsidiaries are responsible for creating
11658 : * ArchiveEntries (TOC objects) for each object to be dumped.
11659 : */
11660 : static void
11661 1393392 : dumpDumpableObject(Archive *fout, DumpableObject *dobj)
11662 : {
11663 : /*
11664 : * Clear any dump-request bits for components that don't exist for this
11665 : * object. (This makes it safe to initially use DUMP_COMPONENT_ALL as the
11666 : * request for every kind of object.)
11667 : */
11668 1393392 : dobj->dump &= dobj->components;
11669 :
11670 : /* Now, short-circuit if there's nothing to be done here. */
11671 1393392 : if (dobj->dump == 0)
11672 1240416 : return;
11673 :
11674 152976 : switch (dobj->objType)
11675 : {
11676 978 : case DO_NAMESPACE:
11677 978 : dumpNamespace(fout, (const NamespaceInfo *) dobj);
11678 978 : break;
11679 48 : case DO_EXTENSION:
11680 48 : dumpExtension(fout, (const ExtensionInfo *) dobj);
11681 48 : break;
11682 1840 : case DO_TYPE:
11683 1840 : dumpType(fout, (const TypeInfo *) dobj);
11684 1840 : break;
11685 146 : case DO_SHELL_TYPE:
11686 146 : dumpShellType(fout, (const ShellTypeInfo *) dobj);
11687 146 : break;
11688 3656 : case DO_FUNC:
11689 3656 : dumpFunc(fout, (const FuncInfo *) dobj);
11690 3656 : break;
11691 584 : case DO_AGG:
11692 584 : dumpAgg(fout, (const AggInfo *) dobj);
11693 584 : break;
11694 5008 : case DO_OPERATOR:
11695 5008 : dumpOpr(fout, (const OprInfo *) dobj);
11696 5008 : break;
11697 160 : case DO_ACCESS_METHOD:
11698 160 : dumpAccessMethod(fout, (const AccessMethodInfo *) dobj);
11699 160 : break;
11700 1320 : case DO_OPCLASS:
11701 1320 : dumpOpclass(fout, (const OpclassInfo *) dobj);
11702 1320 : break;
11703 1098 : case DO_OPFAMILY:
11704 1098 : dumpOpfamily(fout, (const OpfamilyInfo *) dobj);
11705 1098 : break;
11706 5074 : case DO_COLLATION:
11707 5074 : dumpCollation(fout, (const CollInfo *) dobj);
11708 5074 : break;
11709 844 : case DO_CONVERSION:
11710 844 : dumpConversion(fout, (const ConvInfo *) dobj);
11711 844 : break;
11712 62652 : case DO_TABLE:
11713 62652 : dumpTable(fout, (const TableInfo *) dobj);
11714 62652 : break;
11715 2782 : case DO_TABLE_ATTACH:
11716 2782 : dumpTableAttach(fout, (const TableAttachInfo *) dobj);
11717 2782 : break;
11718 2064 : case DO_ATTRDEF:
11719 2064 : dumpAttrDef(fout, (const AttrDefInfo *) dobj);
11720 2064 : break;
11721 5200 : case DO_INDEX:
11722 5200 : dumpIndex(fout, (const IndxInfo *) dobj);
11723 5200 : break;
11724 1148 : case DO_INDEX_ATTACH:
11725 1148 : dumpIndexAttach(fout, (const IndexAttachInfo *) dobj);
11726 1148 : break;
11727 266 : case DO_STATSEXT:
11728 266 : dumpStatisticsExt(fout, (const StatsExtInfo *) dobj);
11729 266 : break;
11730 690 : case DO_REFRESH_MATVIEW:
11731 690 : refreshMatViewData(fout, (const TableDataInfo *) dobj);
11732 690 : break;
11733 2258 : case DO_RULE:
11734 2258 : dumpRule(fout, (const RuleInfo *) dobj);
11735 2258 : break;
11736 1046 : case DO_TRIGGER:
11737 1046 : dumpTrigger(fout, (const TriggerInfo *) dobj);
11738 1046 : break;
11739 84 : case DO_EVENT_TRIGGER:
11740 84 : dumpEventTrigger(fout, (const EventTriggerInfo *) dobj);
11741 84 : break;
11742 4636 : case DO_CONSTRAINT:
11743 4636 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11744 4636 : break;
11745 342 : case DO_FK_CONSTRAINT:
11746 342 : dumpConstraint(fout, (const ConstraintInfo *) dobj);
11747 342 : break;
11748 164 : case DO_PROCLANG:
11749 164 : dumpProcLang(fout, (const ProcLangInfo *) dobj);
11750 164 : break;
11751 134 : case DO_CAST:
11752 134 : dumpCast(fout, (const CastInfo *) dobj);
11753 134 : break;
11754 84 : case DO_TRANSFORM:
11755 84 : dumpTransform(fout, (const TransformInfo *) dobj);
11756 84 : break;
11757 786 : case DO_SEQUENCE_SET:
11758 786 : dumpSequenceData(fout, (const TableDataInfo *) dobj);
11759 786 : break;
11760 8452 : case DO_TABLE_DATA:
11761 8452 : dumpTableData(fout, (const TableDataInfo *) dobj);
11762 8452 : break;
11763 28158 : case DO_DUMMY_TYPE:
11764 : /* table rowtypes and array types are never dumped separately */
11765 28158 : break;
11766 82 : case DO_TSPARSER:
11767 82 : dumpTSParser(fout, (const TSParserInfo *) dobj);
11768 82 : break;
11769 346 : case DO_TSDICT:
11770 346 : dumpTSDictionary(fout, (const TSDictInfo *) dobj);
11771 346 : break;
11772 106 : case DO_TSTEMPLATE:
11773 106 : dumpTSTemplate(fout, (const TSTemplateInfo *) dobj);
11774 106 : break;
11775 296 : case DO_TSCONFIG:
11776 296 : dumpTSConfig(fout, (const TSConfigInfo *) dobj);
11777 296 : break;
11778 104 : case DO_FDW:
11779 104 : dumpForeignDataWrapper(fout, (const FdwInfo *) dobj);
11780 104 : break;
11781 112 : case DO_FOREIGN_SERVER:
11782 112 : dumpForeignServer(fout, (const ForeignServerInfo *) dobj);
11783 112 : break;
11784 320 : case DO_DEFAULT_ACL:
11785 320 : dumpDefaultACL(fout, (const DefaultACLInfo *) dobj);
11786 320 : break;
11787 168 : case DO_LARGE_OBJECT:
11788 168 : dumpLO(fout, (const LoInfo *) dobj);
11789 168 : break;
11790 180 : case DO_LARGE_OBJECT_DATA:
11791 180 : if (dobj->dump & DUMP_COMPONENT_DATA)
11792 : {
11793 : LoInfo *loinfo;
11794 : TocEntry *te;
11795 :
11796 180 : loinfo = (LoInfo *) findObjectByDumpId(dobj->dependencies[0]);
11797 180 : if (loinfo == NULL)
11798 0 : pg_fatal("missing metadata for large objects \"%s\"",
11799 : dobj->name);
11800 :
11801 180 : te = ArchiveEntry(fout, dobj->catId, dobj->dumpId,
11802 180 : ARCHIVE_OPTS(.tag = dobj->name,
11803 : .owner = loinfo->rolname,
11804 : .description = "BLOBS",
11805 : .section = SECTION_DATA,
11806 : .deps = dobj->dependencies,
11807 : .nDeps = dobj->nDeps,
11808 : .dumpFn = dumpLOs,
11809 : .dumpArg = loinfo));
11810 :
11811 : /*
11812 : * Set the TocEntry's dataLength in case we are doing a
11813 : * parallel dump and want to order dump jobs by table size.
11814 : * (We need some size estimate for every TocEntry with a
11815 : * DataDumper function.) We don't currently have any cheap
11816 : * way to estimate the size of LOs, but fortunately it doesn't
11817 : * matter too much as long as we get large batches of LOs
11818 : * processed reasonably early. Assume 8K per blob.
11819 : */
11820 180 : te->dataLength = loinfo->numlos * (pgoff_t) 8192;
11821 : }
11822 180 : break;
11823 652 : case DO_POLICY:
11824 652 : dumpPolicy(fout, (const PolicyInfo *) dobj);
11825 652 : break;
11826 570 : case DO_PUBLICATION:
11827 570 : dumpPublication(fout, (const PublicationInfo *) dobj);
11828 570 : break;
11829 568 : case DO_PUBLICATION_REL:
11830 568 : dumpPublicationTable(fout, (const PublicationRelInfo *) dobj);
11831 568 : break;
11832 198 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
11833 198 : dumpPublicationNamespace(fout,
11834 : (const PublicationSchemaInfo *) dobj);
11835 198 : break;
11836 220 : case DO_SUBSCRIPTION:
11837 220 : dumpSubscription(fout, (const SubscriptionInfo *) dobj);
11838 220 : break;
11839 6 : case DO_SUBSCRIPTION_REL:
11840 6 : dumpSubscriptionTable(fout, (const SubRelInfo *) dobj);
11841 6 : break;
11842 6594 : case DO_REL_STATS:
11843 6594 : dumpRelationStats(fout, (const RelStatsInfo *) dobj);
11844 6594 : break;
11845 752 : case DO_PRE_DATA_BOUNDARY:
11846 : case DO_POST_DATA_BOUNDARY:
11847 : /* never dumped, nothing to do */
11848 752 : break;
11849 : }
11850 : }
11851 :
11852 : /*
11853 : * dumpNamespace
11854 : * writes out to fout the queries to recreate a user-defined namespace
11855 : */
11856 : static void
11857 978 : dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
11858 : {
11859 978 : DumpOptions *dopt = fout->dopt;
11860 : PQExpBuffer q;
11861 : PQExpBuffer delq;
11862 : char *qnspname;
11863 :
11864 : /* Do nothing if not dumping schema */
11865 978 : if (!dopt->dumpSchema)
11866 56 : return;
11867 :
11868 922 : q = createPQExpBuffer();
11869 922 : delq = createPQExpBuffer();
11870 :
11871 922 : qnspname = pg_strdup(fmtId(nspinfo->dobj.name));
11872 :
11873 922 : if (nspinfo->create)
11874 : {
11875 616 : appendPQExpBuffer(delq, "DROP SCHEMA %s;\n", qnspname);
11876 616 : appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname);
11877 : }
11878 : else
11879 : {
11880 : /* see selectDumpableNamespace() */
11881 306 : appendPQExpBufferStr(delq,
11882 : "-- *not* dropping schema, since initdb creates it\n");
11883 306 : appendPQExpBufferStr(q,
11884 : "-- *not* creating schema, since initdb creates it\n");
11885 : }
11886 :
11887 922 : if (dopt->binary_upgrade)
11888 188 : binary_upgrade_extension_member(q, &nspinfo->dobj,
11889 : "SCHEMA", qnspname, NULL);
11890 :
11891 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
11892 370 : ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId,
11893 370 : ARCHIVE_OPTS(.tag = nspinfo->dobj.name,
11894 : .owner = nspinfo->rolname,
11895 : .description = "SCHEMA",
11896 : .section = SECTION_PRE_DATA,
11897 : .createStmt = q->data,
11898 : .dropStmt = delq->data));
11899 :
11900 : /* Dump Schema Comments and Security Labels */
11901 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
11902 : {
11903 316 : const char *initdb_comment = NULL;
11904 :
11905 316 : if (!nspinfo->create && strcmp(qnspname, "public") == 0)
11906 234 : initdb_comment = "standard public schema";
11907 316 : dumpCommentExtended(fout, "SCHEMA", qnspname,
11908 316 : NULL, nspinfo->rolname,
11909 316 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId,
11910 : initdb_comment);
11911 : }
11912 :
11913 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
11914 0 : dumpSecLabel(fout, "SCHEMA", qnspname,
11915 0 : NULL, nspinfo->rolname,
11916 0 : nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
11917 :
11918 922 : if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
11919 718 : dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
11920 : qnspname, NULL, NULL,
11921 718 : NULL, nspinfo->rolname, &nspinfo->dacl);
11922 :
11923 922 : free(qnspname);
11924 :
11925 922 : destroyPQExpBuffer(q);
11926 922 : destroyPQExpBuffer(delq);
11927 : }
11928 :
11929 : /*
11930 : * dumpExtension
11931 : * writes out to fout the queries to recreate an extension
11932 : */
11933 : static void
11934 48 : dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
11935 : {
11936 48 : DumpOptions *dopt = fout->dopt;
11937 : PQExpBuffer q;
11938 : PQExpBuffer delq;
11939 : char *qextname;
11940 :
11941 : /* Do nothing if not dumping schema */
11942 48 : if (!dopt->dumpSchema)
11943 2 : return;
11944 :
11945 46 : q = createPQExpBuffer();
11946 46 : delq = createPQExpBuffer();
11947 :
11948 46 : qextname = pg_strdup(fmtId(extinfo->dobj.name));
11949 :
11950 46 : appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname);
11951 :
11952 46 : if (!dopt->binary_upgrade)
11953 : {
11954 : /*
11955 : * In a regular dump, we simply create the extension, intentionally
11956 : * not specifying a version, so that the destination installation's
11957 : * default version is used.
11958 : *
11959 : * Use of IF NOT EXISTS here is unlike our behavior for other object
11960 : * types; but there are various scenarios in which it's convenient to
11961 : * manually create the desired extension before restoring, so we
11962 : * prefer to allow it to exist already.
11963 : */
11964 34 : appendPQExpBuffer(q, "CREATE EXTENSION IF NOT EXISTS %s WITH SCHEMA %s;\n",
11965 34 : qextname, fmtId(extinfo->namespace));
11966 : }
11967 : else
11968 : {
11969 : /*
11970 : * In binary-upgrade mode, it's critical to reproduce the state of the
11971 : * database exactly, so our procedure is to create an empty extension,
11972 : * restore all the contained objects normally, and add them to the
11973 : * extension one by one. This function performs just the first of
11974 : * those steps. binary_upgrade_extension_member() takes care of
11975 : * adding member objects as they're created.
11976 : */
11977 : int i;
11978 : int n;
11979 :
11980 12 : appendPQExpBufferStr(q, "-- For binary upgrade, create an empty extension and insert objects into it\n");
11981 :
11982 : /*
11983 : * We unconditionally create the extension, so we must drop it if it
11984 : * exists. This could happen if the user deleted 'plpgsql' and then
11985 : * readded it, causing its oid to be greater than g_last_builtin_oid.
11986 : */
11987 12 : appendPQExpBuffer(q, "DROP EXTENSION IF EXISTS %s;\n", qextname);
11988 :
11989 12 : appendPQExpBufferStr(q,
11990 : "SELECT pg_catalog.binary_upgrade_create_empty_extension(");
11991 12 : appendStringLiteralAH(q, extinfo->dobj.name, fout);
11992 12 : appendPQExpBufferStr(q, ", ");
11993 12 : appendStringLiteralAH(q, extinfo->namespace, fout);
11994 12 : appendPQExpBufferStr(q, ", ");
11995 12 : appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false");
11996 12 : appendStringLiteralAH(q, extinfo->extversion, fout);
11997 12 : appendPQExpBufferStr(q, ", ");
11998 :
11999 : /*
12000 : * Note that we're pushing extconfig (an OID array) back into
12001 : * pg_extension exactly as-is. This is OK because pg_class OIDs are
12002 : * preserved in binary upgrade.
12003 : */
12004 12 : if (strlen(extinfo->extconfig) > 2)
12005 2 : appendStringLiteralAH(q, extinfo->extconfig, fout);
12006 : else
12007 10 : appendPQExpBufferStr(q, "NULL");
12008 12 : appendPQExpBufferStr(q, ", ");
12009 12 : if (strlen(extinfo->extcondition) > 2)
12010 2 : appendStringLiteralAH(q, extinfo->extcondition, fout);
12011 : else
12012 10 : appendPQExpBufferStr(q, "NULL");
12013 12 : appendPQExpBufferStr(q, ", ");
12014 12 : appendPQExpBufferStr(q, "ARRAY[");
12015 12 : n = 0;
12016 24 : for (i = 0; i < extinfo->dobj.nDeps; i++)
12017 : {
12018 : DumpableObject *extobj;
12019 :
12020 12 : extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]);
12021 12 : if (extobj && extobj->objType == DO_EXTENSION)
12022 : {
12023 0 : if (n++ > 0)
12024 0 : appendPQExpBufferChar(q, ',');
12025 0 : appendStringLiteralAH(q, extobj->name, fout);
12026 : }
12027 : }
12028 12 : appendPQExpBufferStr(q, "]::pg_catalog.text[]");
12029 12 : appendPQExpBufferStr(q, ");\n");
12030 : }
12031 :
12032 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12033 46 : ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId,
12034 46 : ARCHIVE_OPTS(.tag = extinfo->dobj.name,
12035 : .description = "EXTENSION",
12036 : .section = SECTION_PRE_DATA,
12037 : .createStmt = q->data,
12038 : .dropStmt = delq->data));
12039 :
12040 : /* Dump Extension Comments */
12041 46 : if (extinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12042 46 : dumpComment(fout, "EXTENSION", qextname,
12043 : NULL, "",
12044 46 : extinfo->dobj.catId, 0, extinfo->dobj.dumpId);
12045 :
12046 46 : free(qextname);
12047 :
12048 46 : destroyPQExpBuffer(q);
12049 46 : destroyPQExpBuffer(delq);
12050 : }
12051 :
12052 : /*
12053 : * dumpType
12054 : * writes out to fout the queries to recreate a user-defined type
12055 : */
12056 : static void
12057 1840 : dumpType(Archive *fout, const TypeInfo *tyinfo)
12058 : {
12059 1840 : DumpOptions *dopt = fout->dopt;
12060 :
12061 : /* Do nothing if not dumping schema */
12062 1840 : if (!dopt->dumpSchema)
12063 98 : return;
12064 :
12065 : /* Dump out in proper style */
12066 1742 : if (tyinfo->typtype == TYPTYPE_BASE)
12067 560 : dumpBaseType(fout, tyinfo);
12068 1182 : else if (tyinfo->typtype == TYPTYPE_DOMAIN)
12069 304 : dumpDomain(fout, tyinfo);
12070 878 : else if (tyinfo->typtype == TYPTYPE_COMPOSITE)
12071 260 : dumpCompositeType(fout, tyinfo);
12072 618 : else if (tyinfo->typtype == TYPTYPE_ENUM)
12073 170 : dumpEnumType(fout, tyinfo);
12074 448 : else if (tyinfo->typtype == TYPTYPE_RANGE)
12075 224 : dumpRangeType(fout, tyinfo);
12076 224 : else if (tyinfo->typtype == TYPTYPE_PSEUDO && !tyinfo->isDefined)
12077 74 : dumpUndefinedType(fout, tyinfo);
12078 : else
12079 150 : pg_log_warning("typtype of data type \"%s\" appears to be invalid",
12080 : tyinfo->dobj.name);
12081 : }
12082 :
12083 : /*
12084 : * dumpEnumType
12085 : * writes out to fout the queries to recreate a user-defined enum type
12086 : */
12087 : static void
12088 170 : dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
12089 : {
12090 170 : DumpOptions *dopt = fout->dopt;
12091 170 : PQExpBuffer q = createPQExpBuffer();
12092 170 : PQExpBuffer delq = createPQExpBuffer();
12093 170 : PQExpBuffer query = createPQExpBuffer();
12094 : PGresult *res;
12095 : int num,
12096 : i;
12097 : Oid enum_oid;
12098 : char *qtypname;
12099 : char *qualtypname;
12100 : char *label;
12101 : int i_enumlabel;
12102 : int i_oid;
12103 :
12104 170 : if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
12105 : {
12106 : /* Set up query for enum-specific details */
12107 80 : appendPQExpBufferStr(query,
12108 : "PREPARE dumpEnumType(pg_catalog.oid) AS\n"
12109 : "SELECT oid, enumlabel "
12110 : "FROM pg_catalog.pg_enum "
12111 : "WHERE enumtypid = $1 "
12112 : "ORDER BY enumsortorder");
12113 :
12114 80 : ExecuteSqlStatement(fout, query->data);
12115 :
12116 80 : fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
12117 : }
12118 :
12119 170 : printfPQExpBuffer(query,
12120 : "EXECUTE dumpEnumType('%u')",
12121 170 : tyinfo->dobj.catId.oid);
12122 :
12123 170 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12124 :
12125 170 : num = PQntuples(res);
12126 :
12127 170 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12128 170 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12129 :
12130 : /*
12131 : * CASCADE shouldn't be required here as for normal types since the I/O
12132 : * functions are generic and do not get dropped.
12133 : */
12134 170 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12135 :
12136 170 : if (dopt->binary_upgrade)
12137 12 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12138 12 : tyinfo->dobj.catId.oid,
12139 : false, false);
12140 :
12141 170 : appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
12142 : qualtypname);
12143 :
12144 170 : if (!dopt->binary_upgrade)
12145 : {
12146 158 : i_enumlabel = PQfnumber(res, "enumlabel");
12147 :
12148 : /* Labels with server-assigned oids */
12149 964 : for (i = 0; i < num; i++)
12150 : {
12151 806 : label = PQgetvalue(res, i, i_enumlabel);
12152 806 : if (i > 0)
12153 648 : appendPQExpBufferChar(q, ',');
12154 806 : appendPQExpBufferStr(q, "\n ");
12155 806 : appendStringLiteralAH(q, label, fout);
12156 : }
12157 : }
12158 :
12159 170 : appendPQExpBufferStr(q, "\n);\n");
12160 :
12161 170 : if (dopt->binary_upgrade)
12162 : {
12163 12 : i_oid = PQfnumber(res, "oid");
12164 12 : i_enumlabel = PQfnumber(res, "enumlabel");
12165 :
12166 : /* Labels with dump-assigned (preserved) oids */
12167 124 : for (i = 0; i < num; i++)
12168 : {
12169 112 : enum_oid = atooid(PQgetvalue(res, i, i_oid));
12170 112 : label = PQgetvalue(res, i, i_enumlabel);
12171 :
12172 112 : if (i == 0)
12173 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, must preserve pg_enum oids\n");
12174 112 : appendPQExpBuffer(q,
12175 : "SELECT pg_catalog.binary_upgrade_set_next_pg_enum_oid('%u'::pg_catalog.oid);\n",
12176 : enum_oid);
12177 112 : appendPQExpBuffer(q, "ALTER TYPE %s ADD VALUE ", qualtypname);
12178 112 : appendStringLiteralAH(q, label, fout);
12179 112 : appendPQExpBufferStr(q, ";\n\n");
12180 : }
12181 : }
12182 :
12183 170 : if (dopt->binary_upgrade)
12184 12 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12185 : "TYPE", qtypname,
12186 12 : tyinfo->dobj.namespace->dobj.name);
12187 :
12188 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12189 170 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12190 170 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12191 : .namespace = tyinfo->dobj.namespace->dobj.name,
12192 : .owner = tyinfo->rolname,
12193 : .description = "TYPE",
12194 : .section = SECTION_PRE_DATA,
12195 : .createStmt = q->data,
12196 : .dropStmt = delq->data));
12197 :
12198 : /* Dump Type Comments and Security Labels */
12199 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12200 64 : dumpComment(fout, "TYPE", qtypname,
12201 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12202 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12203 :
12204 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12205 0 : dumpSecLabel(fout, "TYPE", qtypname,
12206 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12207 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12208 :
12209 170 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12210 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12211 : qtypname, NULL,
12212 64 : tyinfo->dobj.namespace->dobj.name,
12213 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12214 :
12215 170 : PQclear(res);
12216 170 : destroyPQExpBuffer(q);
12217 170 : destroyPQExpBuffer(delq);
12218 170 : destroyPQExpBuffer(query);
12219 170 : free(qtypname);
12220 170 : free(qualtypname);
12221 170 : }
12222 :
12223 : /*
12224 : * dumpRangeType
12225 : * writes out to fout the queries to recreate a user-defined range type
12226 : */
12227 : static void
12228 224 : dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
12229 : {
12230 224 : DumpOptions *dopt = fout->dopt;
12231 224 : PQExpBuffer q = createPQExpBuffer();
12232 224 : PQExpBuffer delq = createPQExpBuffer();
12233 224 : PQExpBuffer query = createPQExpBuffer();
12234 : PGresult *res;
12235 : Oid collationOid;
12236 : char *qtypname;
12237 : char *qualtypname;
12238 : char *procname;
12239 :
12240 224 : if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
12241 : {
12242 : /* Set up query for range-specific details */
12243 80 : appendPQExpBufferStr(query,
12244 : "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
12245 :
12246 80 : appendPQExpBufferStr(query,
12247 : "SELECT ");
12248 :
12249 80 : if (fout->remoteVersion >= 140000)
12250 80 : appendPQExpBufferStr(query,
12251 : "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
12252 : else
12253 0 : appendPQExpBufferStr(query,
12254 : "NULL AS rngmultitype, ");
12255 :
12256 80 : appendPQExpBufferStr(query,
12257 : "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
12258 : "opc.opcname AS opcname, "
12259 : "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
12260 : " WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
12261 : "opc.opcdefault, "
12262 : "CASE WHEN rngcollation = st.typcollation THEN 0 "
12263 : " ELSE rngcollation END AS collation, "
12264 : "rngcanonical, rngsubdiff "
12265 : "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
12266 : " pg_catalog.pg_opclass opc "
12267 : "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
12268 : "rngtypid = $1");
12269 :
12270 80 : ExecuteSqlStatement(fout, query->data);
12271 :
12272 80 : fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
12273 : }
12274 :
12275 224 : printfPQExpBuffer(query,
12276 : "EXECUTE dumpRangeType('%u')",
12277 224 : tyinfo->dobj.catId.oid);
12278 :
12279 224 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12280 :
12281 224 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12282 224 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12283 :
12284 : /*
12285 : * CASCADE shouldn't be required here as for normal types since the I/O
12286 : * functions are generic and do not get dropped.
12287 : */
12288 224 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12289 :
12290 224 : if (dopt->binary_upgrade)
12291 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12292 16 : tyinfo->dobj.catId.oid,
12293 : false, true);
12294 :
12295 224 : appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
12296 : qualtypname);
12297 :
12298 224 : appendPQExpBuffer(q, "\n subtype = %s",
12299 : PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
12300 :
12301 224 : if (!PQgetisnull(res, 0, PQfnumber(res, "rngmultitype")))
12302 224 : appendPQExpBuffer(q, ",\n multirange_type_name = %s",
12303 : PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
12304 :
12305 : /* print subtype_opclass only if not default for subtype */
12306 224 : if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
12307 : {
12308 64 : char *opcname = PQgetvalue(res, 0, PQfnumber(res, "opcname"));
12309 64 : char *nspname = PQgetvalue(res, 0, PQfnumber(res, "opcnsp"));
12310 :
12311 64 : appendPQExpBuffer(q, ",\n subtype_opclass = %s.",
12312 : fmtId(nspname));
12313 64 : appendPQExpBufferStr(q, fmtId(opcname));
12314 : }
12315 :
12316 224 : collationOid = atooid(PQgetvalue(res, 0, PQfnumber(res, "collation")));
12317 224 : if (OidIsValid(collationOid))
12318 : {
12319 74 : CollInfo *coll = findCollationByOid(collationOid);
12320 :
12321 74 : if (coll)
12322 74 : appendPQExpBuffer(q, ",\n collation = %s",
12323 74 : fmtQualifiedDumpable(coll));
12324 : }
12325 :
12326 224 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngcanonical"));
12327 224 : if (strcmp(procname, "-") != 0)
12328 18 : appendPQExpBuffer(q, ",\n canonical = %s", procname);
12329 :
12330 224 : procname = PQgetvalue(res, 0, PQfnumber(res, "rngsubdiff"));
12331 224 : if (strcmp(procname, "-") != 0)
12332 46 : appendPQExpBuffer(q, ",\n subtype_diff = %s", procname);
12333 :
12334 224 : appendPQExpBufferStr(q, "\n);\n");
12335 :
12336 224 : if (dopt->binary_upgrade)
12337 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12338 : "TYPE", qtypname,
12339 16 : tyinfo->dobj.namespace->dobj.name);
12340 :
12341 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12342 224 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12343 224 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12344 : .namespace = tyinfo->dobj.namespace->dobj.name,
12345 : .owner = tyinfo->rolname,
12346 : .description = "TYPE",
12347 : .section = SECTION_PRE_DATA,
12348 : .createStmt = q->data,
12349 : .dropStmt = delq->data));
12350 :
12351 : /* Dump Type Comments and Security Labels */
12352 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12353 100 : dumpComment(fout, "TYPE", qtypname,
12354 100 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12355 100 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12356 :
12357 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12358 0 : dumpSecLabel(fout, "TYPE", qtypname,
12359 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12360 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12361 :
12362 224 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12363 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12364 : qtypname, NULL,
12365 64 : tyinfo->dobj.namespace->dobj.name,
12366 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12367 :
12368 224 : PQclear(res);
12369 224 : destroyPQExpBuffer(q);
12370 224 : destroyPQExpBuffer(delq);
12371 224 : destroyPQExpBuffer(query);
12372 224 : free(qtypname);
12373 224 : free(qualtypname);
12374 224 : }
12375 :
12376 : /*
12377 : * dumpUndefinedType
12378 : * writes out to fout the queries to recreate a !typisdefined type
12379 : *
12380 : * This is a shell type, but we use different terminology to distinguish
12381 : * this case from where we have to emit a shell type definition to break
12382 : * circular dependencies. An undefined type shouldn't ever have anything
12383 : * depending on it.
12384 : */
12385 : static void
12386 74 : dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
12387 : {
12388 74 : DumpOptions *dopt = fout->dopt;
12389 74 : PQExpBuffer q = createPQExpBuffer();
12390 74 : PQExpBuffer delq = createPQExpBuffer();
12391 : char *qtypname;
12392 : char *qualtypname;
12393 :
12394 74 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12395 74 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12396 :
12397 74 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
12398 :
12399 74 : if (dopt->binary_upgrade)
12400 4 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12401 4 : tyinfo->dobj.catId.oid,
12402 : false, false);
12403 :
12404 74 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
12405 : qualtypname);
12406 :
12407 74 : if (dopt->binary_upgrade)
12408 4 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12409 : "TYPE", qtypname,
12410 4 : tyinfo->dobj.namespace->dobj.name);
12411 :
12412 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12413 74 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12414 74 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12415 : .namespace = tyinfo->dobj.namespace->dobj.name,
12416 : .owner = tyinfo->rolname,
12417 : .description = "TYPE",
12418 : .section = SECTION_PRE_DATA,
12419 : .createStmt = q->data,
12420 : .dropStmt = delq->data));
12421 :
12422 : /* Dump Type Comments and Security Labels */
12423 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12424 64 : dumpComment(fout, "TYPE", qtypname,
12425 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12426 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12427 :
12428 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12429 0 : dumpSecLabel(fout, "TYPE", qtypname,
12430 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12431 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12432 :
12433 74 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12434 0 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12435 : qtypname, NULL,
12436 0 : tyinfo->dobj.namespace->dobj.name,
12437 0 : NULL, tyinfo->rolname, &tyinfo->dacl);
12438 :
12439 74 : destroyPQExpBuffer(q);
12440 74 : destroyPQExpBuffer(delq);
12441 74 : free(qtypname);
12442 74 : free(qualtypname);
12443 74 : }
12444 :
12445 : /*
12446 : * dumpBaseType
12447 : * writes out to fout the queries to recreate a user-defined base type
12448 : */
12449 : static void
12450 560 : dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
12451 : {
12452 560 : DumpOptions *dopt = fout->dopt;
12453 560 : PQExpBuffer q = createPQExpBuffer();
12454 560 : PQExpBuffer delq = createPQExpBuffer();
12455 560 : PQExpBuffer query = createPQExpBuffer();
12456 : PGresult *res;
12457 : char *qtypname;
12458 : char *qualtypname;
12459 : char *typlen;
12460 : char *typinput;
12461 : char *typoutput;
12462 : char *typreceive;
12463 : char *typsend;
12464 : char *typmodin;
12465 : char *typmodout;
12466 : char *typanalyze;
12467 : char *typsubscript;
12468 : Oid typreceiveoid;
12469 : Oid typsendoid;
12470 : Oid typmodinoid;
12471 : Oid typmodoutoid;
12472 : Oid typanalyzeoid;
12473 : Oid typsubscriptoid;
12474 : char *typcategory;
12475 : char *typispreferred;
12476 : char *typdelim;
12477 : char *typbyval;
12478 : char *typalign;
12479 : char *typstorage;
12480 : char *typcollatable;
12481 : char *typdefault;
12482 560 : bool typdefault_is_literal = false;
12483 :
12484 560 : if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
12485 : {
12486 : /* Set up query for type-specific details */
12487 80 : appendPQExpBufferStr(query,
12488 : "PREPARE dumpBaseType(pg_catalog.oid) AS\n"
12489 : "SELECT typlen, "
12490 : "typinput, typoutput, typreceive, typsend, "
12491 : "typreceive::pg_catalog.oid AS typreceiveoid, "
12492 : "typsend::pg_catalog.oid AS typsendoid, "
12493 : "typanalyze, "
12494 : "typanalyze::pg_catalog.oid AS typanalyzeoid, "
12495 : "typdelim, typbyval, typalign, typstorage, "
12496 : "typmodin, typmodout, "
12497 : "typmodin::pg_catalog.oid AS typmodinoid, "
12498 : "typmodout::pg_catalog.oid AS typmodoutoid, "
12499 : "typcategory, typispreferred, "
12500 : "(typcollation <> 0) AS typcollatable, "
12501 : "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault, ");
12502 :
12503 80 : if (fout->remoteVersion >= 140000)
12504 80 : appendPQExpBufferStr(query,
12505 : "typsubscript, "
12506 : "typsubscript::pg_catalog.oid AS typsubscriptoid ");
12507 : else
12508 0 : appendPQExpBufferStr(query,
12509 : "'-' AS typsubscript, 0 AS typsubscriptoid ");
12510 :
12511 80 : appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
12512 : "WHERE oid = $1");
12513 :
12514 80 : ExecuteSqlStatement(fout, query->data);
12515 :
12516 80 : fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
12517 : }
12518 :
12519 560 : printfPQExpBuffer(query,
12520 : "EXECUTE dumpBaseType('%u')",
12521 560 : tyinfo->dobj.catId.oid);
12522 :
12523 560 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12524 :
12525 560 : typlen = PQgetvalue(res, 0, PQfnumber(res, "typlen"));
12526 560 : typinput = PQgetvalue(res, 0, PQfnumber(res, "typinput"));
12527 560 : typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
12528 560 : typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
12529 560 : typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
12530 560 : typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
12531 560 : typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
12532 560 : typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
12533 560 : typsubscript = PQgetvalue(res, 0, PQfnumber(res, "typsubscript"));
12534 560 : typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
12535 560 : typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
12536 560 : typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
12537 560 : typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
12538 560 : typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
12539 560 : typsubscriptoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsubscriptoid")));
12540 560 : typcategory = PQgetvalue(res, 0, PQfnumber(res, "typcategory"));
12541 560 : typispreferred = PQgetvalue(res, 0, PQfnumber(res, "typispreferred"));
12542 560 : typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
12543 560 : typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
12544 560 : typalign = PQgetvalue(res, 0, PQfnumber(res, "typalign"));
12545 560 : typstorage = PQgetvalue(res, 0, PQfnumber(res, "typstorage"));
12546 560 : typcollatable = PQgetvalue(res, 0, PQfnumber(res, "typcollatable"));
12547 560 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12548 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12549 560 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12550 : {
12551 84 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12552 84 : typdefault_is_literal = true; /* it needs quotes */
12553 : }
12554 : else
12555 476 : typdefault = NULL;
12556 :
12557 560 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12558 560 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12559 :
12560 : /*
12561 : * The reason we include CASCADE is that the circular dependency between
12562 : * the type and its I/O functions makes it impossible to drop the type any
12563 : * other way.
12564 : */
12565 560 : appendPQExpBuffer(delq, "DROP TYPE %s CASCADE;\n", qualtypname);
12566 :
12567 : /*
12568 : * We might already have a shell type, but setting pg_type_oid is
12569 : * harmless, and in any case we'd better set the array type OID.
12570 : */
12571 560 : if (dopt->binary_upgrade)
12572 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12573 16 : tyinfo->dobj.catId.oid,
12574 : false, false);
12575 :
12576 560 : appendPQExpBuffer(q,
12577 : "CREATE TYPE %s (\n"
12578 : " INTERNALLENGTH = %s",
12579 : qualtypname,
12580 560 : (strcmp(typlen, "-1") == 0) ? "variable" : typlen);
12581 :
12582 : /* regproc result is sufficiently quoted already */
12583 560 : appendPQExpBuffer(q, ",\n INPUT = %s", typinput);
12584 560 : appendPQExpBuffer(q, ",\n OUTPUT = %s", typoutput);
12585 560 : if (OidIsValid(typreceiveoid))
12586 414 : appendPQExpBuffer(q, ",\n RECEIVE = %s", typreceive);
12587 560 : if (OidIsValid(typsendoid))
12588 414 : appendPQExpBuffer(q, ",\n SEND = %s", typsend);
12589 560 : if (OidIsValid(typmodinoid))
12590 70 : appendPQExpBuffer(q, ",\n TYPMOD_IN = %s", typmodin);
12591 560 : if (OidIsValid(typmodoutoid))
12592 70 : appendPQExpBuffer(q, ",\n TYPMOD_OUT = %s", typmodout);
12593 560 : if (OidIsValid(typanalyzeoid))
12594 6 : appendPQExpBuffer(q, ",\n ANALYZE = %s", typanalyze);
12595 :
12596 560 : if (strcmp(typcollatable, "t") == 0)
12597 60 : appendPQExpBufferStr(q, ",\n COLLATABLE = true");
12598 :
12599 560 : if (typdefault != NULL)
12600 : {
12601 84 : appendPQExpBufferStr(q, ",\n DEFAULT = ");
12602 84 : if (typdefault_is_literal)
12603 84 : appendStringLiteralAH(q, typdefault, fout);
12604 : else
12605 0 : appendPQExpBufferStr(q, typdefault);
12606 : }
12607 :
12608 560 : if (OidIsValid(typsubscriptoid))
12609 58 : appendPQExpBuffer(q, ",\n SUBSCRIPT = %s", typsubscript);
12610 :
12611 560 : if (OidIsValid(tyinfo->typelem))
12612 52 : appendPQExpBuffer(q, ",\n ELEMENT = %s",
12613 52 : getFormattedTypeName(fout, tyinfo->typelem,
12614 : zeroIsError));
12615 :
12616 560 : if (strcmp(typcategory, "U") != 0)
12617 : {
12618 316 : appendPQExpBufferStr(q, ",\n CATEGORY = ");
12619 316 : appendStringLiteralAH(q, typcategory, fout);
12620 : }
12621 :
12622 560 : if (strcmp(typispreferred, "t") == 0)
12623 58 : appendPQExpBufferStr(q, ",\n PREFERRED = true");
12624 :
12625 560 : if (typdelim && strcmp(typdelim, ",") != 0)
12626 : {
12627 6 : appendPQExpBufferStr(q, ",\n DELIMITER = ");
12628 6 : appendStringLiteralAH(q, typdelim, fout);
12629 : }
12630 :
12631 560 : if (*typalign == TYPALIGN_CHAR)
12632 24 : appendPQExpBufferStr(q, ",\n ALIGNMENT = char");
12633 536 : else if (*typalign == TYPALIGN_SHORT)
12634 12 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int2");
12635 524 : else if (*typalign == TYPALIGN_INT)
12636 374 : appendPQExpBufferStr(q, ",\n ALIGNMENT = int4");
12637 150 : else if (*typalign == TYPALIGN_DOUBLE)
12638 150 : appendPQExpBufferStr(q, ",\n ALIGNMENT = double");
12639 :
12640 560 : if (*typstorage == TYPSTORAGE_PLAIN)
12641 410 : appendPQExpBufferStr(q, ",\n STORAGE = plain");
12642 150 : else if (*typstorage == TYPSTORAGE_EXTERNAL)
12643 0 : appendPQExpBufferStr(q, ",\n STORAGE = external");
12644 150 : else if (*typstorage == TYPSTORAGE_EXTENDED)
12645 132 : appendPQExpBufferStr(q, ",\n STORAGE = extended");
12646 18 : else if (*typstorage == TYPSTORAGE_MAIN)
12647 18 : appendPQExpBufferStr(q, ",\n STORAGE = main");
12648 :
12649 560 : if (strcmp(typbyval, "t") == 0)
12650 268 : appendPQExpBufferStr(q, ",\n PASSEDBYVALUE");
12651 :
12652 560 : appendPQExpBufferStr(q, "\n);\n");
12653 :
12654 560 : if (dopt->binary_upgrade)
12655 16 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12656 : "TYPE", qtypname,
12657 16 : tyinfo->dobj.namespace->dobj.name);
12658 :
12659 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12660 560 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12661 560 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12662 : .namespace = tyinfo->dobj.namespace->dobj.name,
12663 : .owner = tyinfo->rolname,
12664 : .description = "TYPE",
12665 : .section = SECTION_PRE_DATA,
12666 : .createStmt = q->data,
12667 : .dropStmt = delq->data));
12668 :
12669 : /* Dump Type Comments and Security Labels */
12670 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12671 490 : dumpComment(fout, "TYPE", qtypname,
12672 490 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12673 490 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12674 :
12675 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12676 0 : dumpSecLabel(fout, "TYPE", qtypname,
12677 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12678 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12679 :
12680 560 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12681 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12682 : qtypname, NULL,
12683 64 : tyinfo->dobj.namespace->dobj.name,
12684 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12685 :
12686 560 : PQclear(res);
12687 560 : destroyPQExpBuffer(q);
12688 560 : destroyPQExpBuffer(delq);
12689 560 : destroyPQExpBuffer(query);
12690 560 : free(qtypname);
12691 560 : free(qualtypname);
12692 560 : }
12693 :
12694 : /*
12695 : * dumpDomain
12696 : * writes out to fout the queries to recreate a user-defined domain
12697 : */
12698 : static void
12699 304 : dumpDomain(Archive *fout, const TypeInfo *tyinfo)
12700 : {
12701 304 : DumpOptions *dopt = fout->dopt;
12702 304 : PQExpBuffer q = createPQExpBuffer();
12703 304 : PQExpBuffer delq = createPQExpBuffer();
12704 304 : PQExpBuffer query = createPQExpBuffer();
12705 : PGresult *res;
12706 : int i;
12707 : char *qtypname;
12708 : char *qualtypname;
12709 : char *typnotnull;
12710 : char *typdefn;
12711 : char *typdefault;
12712 : Oid typcollation;
12713 304 : bool typdefault_is_literal = false;
12714 :
12715 304 : if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
12716 : {
12717 : /* Set up query for domain-specific details */
12718 74 : appendPQExpBufferStr(query,
12719 : "PREPARE dumpDomain(pg_catalog.oid) AS\n");
12720 :
12721 74 : appendPQExpBufferStr(query, "SELECT t.typnotnull, "
12722 : "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
12723 : "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
12724 : "t.typdefault, "
12725 : "CASE WHEN t.typcollation <> u.typcollation "
12726 : "THEN t.typcollation ELSE 0 END AS typcollation "
12727 : "FROM pg_catalog.pg_type t "
12728 : "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
12729 : "WHERE t.oid = $1");
12730 :
12731 74 : ExecuteSqlStatement(fout, query->data);
12732 :
12733 74 : fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
12734 : }
12735 :
12736 304 : printfPQExpBuffer(query,
12737 : "EXECUTE dumpDomain('%u')",
12738 304 : tyinfo->dobj.catId.oid);
12739 :
12740 304 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
12741 :
12742 304 : typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
12743 304 : typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
12744 304 : if (!PQgetisnull(res, 0, PQfnumber(res, "typdefaultbin")))
12745 74 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefaultbin"));
12746 230 : else if (!PQgetisnull(res, 0, PQfnumber(res, "typdefault")))
12747 : {
12748 0 : typdefault = PQgetvalue(res, 0, PQfnumber(res, "typdefault"));
12749 0 : typdefault_is_literal = true; /* it needs quotes */
12750 : }
12751 : else
12752 230 : typdefault = NULL;
12753 304 : typcollation = atooid(PQgetvalue(res, 0, PQfnumber(res, "typcollation")));
12754 :
12755 304 : if (dopt->binary_upgrade)
12756 50 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12757 50 : tyinfo->dobj.catId.oid,
12758 : true, /* force array type */
12759 : false); /* force multirange type */
12760 :
12761 304 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12762 304 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12763 :
12764 304 : appendPQExpBuffer(q,
12765 : "CREATE DOMAIN %s AS %s",
12766 : qualtypname,
12767 : typdefn);
12768 :
12769 : /* Print collation only if different from base type's collation */
12770 304 : if (OidIsValid(typcollation))
12771 : {
12772 : CollInfo *coll;
12773 :
12774 64 : coll = findCollationByOid(typcollation);
12775 64 : if (coll)
12776 64 : appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
12777 : }
12778 :
12779 : /*
12780 : * Print a not-null constraint if there's one. In servers older than 17
12781 : * these don't have names, so just print it unadorned; in newer ones they
12782 : * do, but most of the time it's going to be the standard generated one,
12783 : * so omit the name in that case also.
12784 : */
12785 304 : if (typnotnull[0] == 't')
12786 : {
12787 94 : if (fout->remoteVersion < 170000 || tyinfo->notnull == NULL)
12788 0 : appendPQExpBufferStr(q, " NOT NULL");
12789 : else
12790 : {
12791 94 : ConstraintInfo *notnull = tyinfo->notnull;
12792 :
12793 94 : if (!notnull->separate)
12794 : {
12795 : char *default_name;
12796 :
12797 : /* XXX should match ChooseConstraintName better */
12798 94 : default_name = psprintf("%s_not_null", tyinfo->dobj.name);
12799 :
12800 94 : if (strcmp(default_name, notnull->dobj.name) == 0)
12801 30 : appendPQExpBufferStr(q, " NOT NULL");
12802 : else
12803 64 : appendPQExpBuffer(q, " CONSTRAINT %s %s",
12804 64 : fmtId(notnull->dobj.name), notnull->condef);
12805 94 : free(default_name);
12806 : }
12807 : }
12808 : }
12809 :
12810 304 : if (typdefault != NULL)
12811 : {
12812 74 : appendPQExpBufferStr(q, " DEFAULT ");
12813 74 : if (typdefault_is_literal)
12814 0 : appendStringLiteralAH(q, typdefault, fout);
12815 : else
12816 74 : appendPQExpBufferStr(q, typdefault);
12817 : }
12818 :
12819 304 : PQclear(res);
12820 :
12821 : /*
12822 : * Add any CHECK constraints for the domain
12823 : */
12824 518 : for (i = 0; i < tyinfo->nDomChecks; i++)
12825 : {
12826 214 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12827 :
12828 214 : if (!domcheck->separate && domcheck->contype == 'c')
12829 204 : appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
12830 204 : fmtId(domcheck->dobj.name), domcheck->condef);
12831 : }
12832 :
12833 304 : appendPQExpBufferStr(q, ";\n");
12834 :
12835 304 : appendPQExpBuffer(delq, "DROP DOMAIN %s;\n", qualtypname);
12836 :
12837 304 : if (dopt->binary_upgrade)
12838 50 : binary_upgrade_extension_member(q, &tyinfo->dobj,
12839 : "DOMAIN", qtypname,
12840 50 : tyinfo->dobj.namespace->dobj.name);
12841 :
12842 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
12843 304 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
12844 304 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
12845 : .namespace = tyinfo->dobj.namespace->dobj.name,
12846 : .owner = tyinfo->rolname,
12847 : .description = "DOMAIN",
12848 : .section = SECTION_PRE_DATA,
12849 : .createStmt = q->data,
12850 : .dropStmt = delq->data));
12851 :
12852 : /* Dump Domain Comments and Security Labels */
12853 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
12854 0 : dumpComment(fout, "DOMAIN", qtypname,
12855 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12856 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12857 :
12858 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
12859 0 : dumpSecLabel(fout, "DOMAIN", qtypname,
12860 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
12861 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
12862 :
12863 304 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
12864 64 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
12865 : qtypname, NULL,
12866 64 : tyinfo->dobj.namespace->dobj.name,
12867 64 : NULL, tyinfo->rolname, &tyinfo->dacl);
12868 :
12869 : /* Dump any per-constraint comments */
12870 518 : for (i = 0; i < tyinfo->nDomChecks; i++)
12871 : {
12872 214 : ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12873 : PQExpBuffer conprefix;
12874 :
12875 : /* but only if the constraint itself was dumped here */
12876 214 : if (domcheck->separate)
12877 10 : continue;
12878 :
12879 204 : conprefix = createPQExpBuffer();
12880 204 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12881 204 : fmtId(domcheck->dobj.name));
12882 :
12883 204 : if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
12884 64 : dumpComment(fout, conprefix->data, qtypname,
12885 64 : tyinfo->dobj.namespace->dobj.name,
12886 64 : tyinfo->rolname,
12887 64 : domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
12888 :
12889 204 : destroyPQExpBuffer(conprefix);
12890 : }
12891 :
12892 : /*
12893 : * And a comment on the not-null constraint, if there's one -- but only if
12894 : * the constraint itself was dumped here
12895 : */
12896 304 : if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
12897 : {
12898 94 : PQExpBuffer conprefix = createPQExpBuffer();
12899 :
12900 94 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
12901 94 : fmtId(tyinfo->notnull->dobj.name));
12902 :
12903 94 : if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
12904 64 : dumpComment(fout, conprefix->data, qtypname,
12905 64 : tyinfo->dobj.namespace->dobj.name,
12906 64 : tyinfo->rolname,
12907 64 : tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
12908 94 : destroyPQExpBuffer(conprefix);
12909 : }
12910 :
12911 304 : destroyPQExpBuffer(q);
12912 304 : destroyPQExpBuffer(delq);
12913 304 : destroyPQExpBuffer(query);
12914 304 : free(qtypname);
12915 304 : free(qualtypname);
12916 304 : }
12917 :
12918 : /*
12919 : * dumpCompositeType
12920 : * writes out to fout the queries to recreate a user-defined stand-alone
12921 : * composite type
12922 : */
12923 : static void
12924 260 : dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
12925 : {
12926 260 : DumpOptions *dopt = fout->dopt;
12927 260 : PQExpBuffer q = createPQExpBuffer();
12928 260 : PQExpBuffer dropped = createPQExpBuffer();
12929 260 : PQExpBuffer delq = createPQExpBuffer();
12930 260 : PQExpBuffer query = createPQExpBuffer();
12931 : PGresult *res;
12932 : char *qtypname;
12933 : char *qualtypname;
12934 : int ntups;
12935 : int i_attname;
12936 : int i_atttypdefn;
12937 : int i_attlen;
12938 : int i_attalign;
12939 : int i_attisdropped;
12940 : int i_attcollation;
12941 : int i;
12942 : int actual_atts;
12943 :
12944 260 : if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
12945 : {
12946 : /*
12947 : * Set up query for type-specific details.
12948 : *
12949 : * Since we only want to dump COLLATE clauses for attributes whose
12950 : * collation is different from their type's default, we use a CASE
12951 : * here to suppress uninteresting attcollations cheaply. atttypid
12952 : * will be 0 for dropped columns; collation does not matter for those.
12953 : */
12954 110 : appendPQExpBufferStr(query,
12955 : "PREPARE dumpCompositeType(pg_catalog.oid) AS\n"
12956 : "SELECT a.attname, a.attnum, "
12957 : "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
12958 : "a.attlen, a.attalign, a.attisdropped, "
12959 : "CASE WHEN a.attcollation <> at.typcollation "
12960 : "THEN a.attcollation ELSE 0 END AS attcollation "
12961 : "FROM pg_catalog.pg_type ct "
12962 : "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
12963 : "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
12964 : "WHERE ct.oid = $1 "
12965 : "ORDER BY a.attnum");
12966 :
12967 110 : ExecuteSqlStatement(fout, query->data);
12968 :
12969 110 : fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
12970 : }
12971 :
12972 260 : printfPQExpBuffer(query,
12973 : "EXECUTE dumpCompositeType('%u')",
12974 260 : tyinfo->dobj.catId.oid);
12975 :
12976 260 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
12977 :
12978 260 : ntups = PQntuples(res);
12979 :
12980 260 : i_attname = PQfnumber(res, "attname");
12981 260 : i_atttypdefn = PQfnumber(res, "atttypdefn");
12982 260 : i_attlen = PQfnumber(res, "attlen");
12983 260 : i_attalign = PQfnumber(res, "attalign");
12984 260 : i_attisdropped = PQfnumber(res, "attisdropped");
12985 260 : i_attcollation = PQfnumber(res, "attcollation");
12986 :
12987 260 : if (dopt->binary_upgrade)
12988 : {
12989 36 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
12990 36 : tyinfo->dobj.catId.oid,
12991 : false, false);
12992 36 : binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid);
12993 : }
12994 :
12995 260 : qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
12996 260 : qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
12997 :
12998 260 : appendPQExpBuffer(q, "CREATE TYPE %s AS (",
12999 : qualtypname);
13000 :
13001 260 : actual_atts = 0;
13002 824 : for (i = 0; i < ntups; i++)
13003 : {
13004 : char *attname;
13005 : char *atttypdefn;
13006 : char *attlen;
13007 : char *attalign;
13008 : bool attisdropped;
13009 : Oid attcollation;
13010 :
13011 564 : attname = PQgetvalue(res, i, i_attname);
13012 564 : atttypdefn = PQgetvalue(res, i, i_atttypdefn);
13013 564 : attlen = PQgetvalue(res, i, i_attlen);
13014 564 : attalign = PQgetvalue(res, i, i_attalign);
13015 564 : attisdropped = (PQgetvalue(res, i, i_attisdropped)[0] == 't');
13016 564 : attcollation = atooid(PQgetvalue(res, i, i_attcollation));
13017 :
13018 564 : if (attisdropped && !dopt->binary_upgrade)
13019 16 : continue;
13020 :
13021 : /* Format properly if not first attr */
13022 548 : if (actual_atts++ > 0)
13023 288 : appendPQExpBufferChar(q, ',');
13024 548 : appendPQExpBufferStr(q, "\n\t");
13025 :
13026 548 : if (!attisdropped)
13027 : {
13028 544 : appendPQExpBuffer(q, "%s %s", fmtId(attname), atttypdefn);
13029 :
13030 : /* Add collation if not default for the column type */
13031 544 : if (OidIsValid(attcollation))
13032 : {
13033 : CollInfo *coll;
13034 :
13035 0 : coll = findCollationByOid(attcollation);
13036 0 : if (coll)
13037 0 : appendPQExpBuffer(q, " COLLATE %s",
13038 0 : fmtQualifiedDumpable(coll));
13039 : }
13040 : }
13041 : else
13042 : {
13043 : /*
13044 : * This is a dropped attribute and we're in binary_upgrade mode.
13045 : * Insert a placeholder for it in the CREATE TYPE command, and set
13046 : * length and alignment with direct UPDATE to the catalogs
13047 : * afterwards. See similar code in dumpTableSchema().
13048 : */
13049 4 : appendPQExpBuffer(q, "%s INTEGER /* dummy */", fmtId(attname));
13050 :
13051 : /* stash separately for insertion after the CREATE TYPE */
13052 4 : appendPQExpBufferStr(dropped,
13053 : "\n-- For binary upgrade, recreate dropped column.\n");
13054 4 : appendPQExpBuffer(dropped, "UPDATE pg_catalog.pg_attribute\n"
13055 : "SET attlen = %s, "
13056 : "attalign = '%s', attbyval = false\n"
13057 : "WHERE attname = ", attlen, attalign);
13058 4 : appendStringLiteralAH(dropped, attname, fout);
13059 4 : appendPQExpBufferStr(dropped, "\n AND attrelid = ");
13060 4 : appendStringLiteralAH(dropped, qualtypname, fout);
13061 4 : appendPQExpBufferStr(dropped, "::pg_catalog.regclass;\n");
13062 :
13063 4 : appendPQExpBuffer(dropped, "ALTER TYPE %s ",
13064 : qualtypname);
13065 4 : appendPQExpBuffer(dropped, "DROP ATTRIBUTE %s;\n",
13066 : fmtId(attname));
13067 : }
13068 : }
13069 260 : appendPQExpBufferStr(q, "\n);\n");
13070 260 : appendPQExpBufferStr(q, dropped->data);
13071 :
13072 260 : appendPQExpBuffer(delq, "DROP TYPE %s;\n", qualtypname);
13073 :
13074 260 : if (dopt->binary_upgrade)
13075 36 : binary_upgrade_extension_member(q, &tyinfo->dobj,
13076 : "TYPE", qtypname,
13077 36 : tyinfo->dobj.namespace->dobj.name);
13078 :
13079 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13080 226 : ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId,
13081 226 : ARCHIVE_OPTS(.tag = tyinfo->dobj.name,
13082 : .namespace = tyinfo->dobj.namespace->dobj.name,
13083 : .owner = tyinfo->rolname,
13084 : .description = "TYPE",
13085 : .section = SECTION_PRE_DATA,
13086 : .createStmt = q->data,
13087 : .dropStmt = delq->data));
13088 :
13089 :
13090 : /* Dump Type Comments and Security Labels */
13091 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13092 64 : dumpComment(fout, "TYPE", qtypname,
13093 64 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13094 64 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13095 :
13096 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13097 0 : dumpSecLabel(fout, "TYPE", qtypname,
13098 0 : tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
13099 0 : tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
13100 :
13101 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_ACL)
13102 36 : dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
13103 : qtypname, NULL,
13104 36 : tyinfo->dobj.namespace->dobj.name,
13105 36 : NULL, tyinfo->rolname, &tyinfo->dacl);
13106 :
13107 : /* Dump any per-column comments */
13108 260 : if (tyinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13109 64 : dumpCompositeTypeColComments(fout, tyinfo, res);
13110 :
13111 260 : PQclear(res);
13112 260 : destroyPQExpBuffer(q);
13113 260 : destroyPQExpBuffer(dropped);
13114 260 : destroyPQExpBuffer(delq);
13115 260 : destroyPQExpBuffer(query);
13116 260 : free(qtypname);
13117 260 : free(qualtypname);
13118 260 : }
13119 :
13120 : /*
13121 : * dumpCompositeTypeColComments
13122 : * writes out to fout the queries to recreate comments on the columns of
13123 : * a user-defined stand-alone composite type.
13124 : *
13125 : * The caller has already made a query to collect the names and attnums
13126 : * of the type's columns, so we just pass that result into here rather
13127 : * than reading them again.
13128 : */
13129 : static void
13130 64 : dumpCompositeTypeColComments(Archive *fout, const TypeInfo *tyinfo,
13131 : PGresult *res)
13132 : {
13133 : CommentItem *comments;
13134 : int ncomments;
13135 : PQExpBuffer query;
13136 : PQExpBuffer target;
13137 : int i;
13138 : int ntups;
13139 : int i_attname;
13140 : int i_attnum;
13141 : int i_attisdropped;
13142 :
13143 : /* do nothing, if --no-comments is supplied */
13144 64 : if (fout->dopt->no_comments)
13145 0 : return;
13146 :
13147 : /* Search for comments associated with type's pg_class OID */
13148 64 : ncomments = findComments(RelationRelationId, tyinfo->typrelid,
13149 : &comments);
13150 :
13151 : /* If no comments exist, we're done */
13152 64 : if (ncomments <= 0)
13153 0 : return;
13154 :
13155 : /* Build COMMENT ON statements */
13156 64 : query = createPQExpBuffer();
13157 64 : target = createPQExpBuffer();
13158 :
13159 64 : ntups = PQntuples(res);
13160 64 : i_attnum = PQfnumber(res, "attnum");
13161 64 : i_attname = PQfnumber(res, "attname");
13162 64 : i_attisdropped = PQfnumber(res, "attisdropped");
13163 128 : while (ncomments > 0)
13164 : {
13165 : const char *attname;
13166 :
13167 64 : attname = NULL;
13168 64 : for (i = 0; i < ntups; i++)
13169 : {
13170 64 : if (atoi(PQgetvalue(res, i, i_attnum)) == comments->objsubid &&
13171 64 : PQgetvalue(res, i, i_attisdropped)[0] != 't')
13172 : {
13173 64 : attname = PQgetvalue(res, i, i_attname);
13174 64 : break;
13175 : }
13176 : }
13177 64 : if (attname) /* just in case we don't find it */
13178 : {
13179 64 : const char *descr = comments->descr;
13180 :
13181 64 : resetPQExpBuffer(target);
13182 64 : appendPQExpBuffer(target, "COLUMN %s.",
13183 64 : fmtId(tyinfo->dobj.name));
13184 64 : appendPQExpBufferStr(target, fmtId(attname));
13185 :
13186 64 : resetPQExpBuffer(query);
13187 64 : appendPQExpBuffer(query, "COMMENT ON COLUMN %s.",
13188 64 : fmtQualifiedDumpable(tyinfo));
13189 64 : appendPQExpBuffer(query, "%s IS ", fmtId(attname));
13190 64 : appendStringLiteralAH(query, descr, fout);
13191 64 : appendPQExpBufferStr(query, ";\n");
13192 :
13193 64 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
13194 64 : ARCHIVE_OPTS(.tag = target->data,
13195 : .namespace = tyinfo->dobj.namespace->dobj.name,
13196 : .owner = tyinfo->rolname,
13197 : .description = "COMMENT",
13198 : .section = SECTION_NONE,
13199 : .createStmt = query->data,
13200 : .deps = &(tyinfo->dobj.dumpId),
13201 : .nDeps = 1));
13202 : }
13203 :
13204 64 : comments++;
13205 64 : ncomments--;
13206 : }
13207 :
13208 64 : destroyPQExpBuffer(query);
13209 64 : destroyPQExpBuffer(target);
13210 : }
13211 :
13212 : /*
13213 : * dumpShellType
13214 : * writes out to fout the queries to create a shell type
13215 : *
13216 : * We dump a shell definition in advance of the I/O functions for the type.
13217 : */
13218 : static void
13219 146 : dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
13220 : {
13221 146 : DumpOptions *dopt = fout->dopt;
13222 : PQExpBuffer q;
13223 :
13224 : /* Do nothing if not dumping schema */
13225 146 : if (!dopt->dumpSchema)
13226 12 : return;
13227 :
13228 134 : q = createPQExpBuffer();
13229 :
13230 : /*
13231 : * Note the lack of a DROP command for the shell type; any required DROP
13232 : * is driven off the base type entry, instead. This interacts with
13233 : * _printTocEntry()'s use of the presence of a DROP command to decide
13234 : * whether an entry needs an ALTER OWNER command. We don't want to alter
13235 : * the shell type's owner immediately on creation; that should happen only
13236 : * after it's filled in, otherwise the backend complains.
13237 : */
13238 :
13239 134 : if (dopt->binary_upgrade)
13240 16 : binary_upgrade_set_type_oids_by_type_oid(fout, q,
13241 16 : stinfo->baseType->dobj.catId.oid,
13242 : false, false);
13243 :
13244 134 : appendPQExpBuffer(q, "CREATE TYPE %s;\n",
13245 134 : fmtQualifiedDumpable(stinfo));
13246 :
13247 134 : if (stinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13248 134 : ArchiveEntry(fout, stinfo->dobj.catId, stinfo->dobj.dumpId,
13249 134 : ARCHIVE_OPTS(.tag = stinfo->dobj.name,
13250 : .namespace = stinfo->dobj.namespace->dobj.name,
13251 : .owner = stinfo->baseType->rolname,
13252 : .description = "SHELL TYPE",
13253 : .section = SECTION_PRE_DATA,
13254 : .createStmt = q->data));
13255 :
13256 134 : destroyPQExpBuffer(q);
13257 : }
13258 :
13259 : /*
13260 : * dumpProcLang
13261 : * writes out to fout the queries to recreate a user-defined
13262 : * procedural language
13263 : */
13264 : static void
13265 164 : dumpProcLang(Archive *fout, const ProcLangInfo *plang)
13266 : {
13267 164 : DumpOptions *dopt = fout->dopt;
13268 : PQExpBuffer defqry;
13269 : PQExpBuffer delqry;
13270 : bool useParams;
13271 : char *qlanname;
13272 : FuncInfo *funcInfo;
13273 164 : FuncInfo *inlineInfo = NULL;
13274 164 : FuncInfo *validatorInfo = NULL;
13275 :
13276 : /* Do nothing if not dumping schema */
13277 164 : if (!dopt->dumpSchema)
13278 26 : return;
13279 :
13280 : /*
13281 : * Try to find the support function(s). It is not an error if we don't
13282 : * find them --- if the functions are in the pg_catalog schema, as is
13283 : * standard in 8.1 and up, then we won't have loaded them. (In this case
13284 : * we will emit a parameterless CREATE LANGUAGE command, which will
13285 : * require PL template knowledge in the backend to reload.)
13286 : */
13287 :
13288 138 : funcInfo = findFuncByOid(plang->lanplcallfoid);
13289 138 : if (funcInfo != NULL && !funcInfo->dobj.dump)
13290 4 : funcInfo = NULL; /* treat not-dumped same as not-found */
13291 :
13292 138 : if (OidIsValid(plang->laninline))
13293 : {
13294 76 : inlineInfo = findFuncByOid(plang->laninline);
13295 76 : if (inlineInfo != NULL && !inlineInfo->dobj.dump)
13296 2 : inlineInfo = NULL;
13297 : }
13298 :
13299 138 : if (OidIsValid(plang->lanvalidator))
13300 : {
13301 76 : validatorInfo = findFuncByOid(plang->lanvalidator);
13302 76 : if (validatorInfo != NULL && !validatorInfo->dobj.dump)
13303 2 : validatorInfo = NULL;
13304 : }
13305 :
13306 : /*
13307 : * If the functions are dumpable then emit a complete CREATE LANGUAGE with
13308 : * parameters. Otherwise, we'll write a parameterless command, which will
13309 : * be interpreted as CREATE EXTENSION.
13310 : */
13311 60 : useParams = (funcInfo != NULL &&
13312 258 : (inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
13313 60 : (validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
13314 :
13315 138 : defqry = createPQExpBuffer();
13316 138 : delqry = createPQExpBuffer();
13317 :
13318 138 : qlanname = pg_strdup(fmtId(plang->dobj.name));
13319 :
13320 138 : appendPQExpBuffer(delqry, "DROP PROCEDURAL LANGUAGE %s;\n",
13321 : qlanname);
13322 :
13323 138 : if (useParams)
13324 : {
13325 60 : appendPQExpBuffer(defqry, "CREATE %sPROCEDURAL LANGUAGE %s",
13326 60 : plang->lanpltrusted ? "TRUSTED " : "",
13327 : qlanname);
13328 60 : appendPQExpBuffer(defqry, " HANDLER %s",
13329 60 : fmtQualifiedDumpable(funcInfo));
13330 60 : if (OidIsValid(plang->laninline))
13331 0 : appendPQExpBuffer(defqry, " INLINE %s",
13332 0 : fmtQualifiedDumpable(inlineInfo));
13333 60 : if (OidIsValid(plang->lanvalidator))
13334 0 : appendPQExpBuffer(defqry, " VALIDATOR %s",
13335 0 : fmtQualifiedDumpable(validatorInfo));
13336 : }
13337 : else
13338 : {
13339 : /*
13340 : * If not dumping parameters, then use CREATE OR REPLACE so that the
13341 : * command will not fail if the language is preinstalled in the target
13342 : * database.
13343 : *
13344 : * Modern servers will interpret this as CREATE EXTENSION IF NOT
13345 : * EXISTS; perhaps we should emit that instead? But it might just add
13346 : * confusion.
13347 : */
13348 78 : appendPQExpBuffer(defqry, "CREATE OR REPLACE PROCEDURAL LANGUAGE %s",
13349 : qlanname);
13350 : }
13351 138 : appendPQExpBufferStr(defqry, ";\n");
13352 :
13353 138 : if (dopt->binary_upgrade)
13354 4 : binary_upgrade_extension_member(defqry, &plang->dobj,
13355 : "LANGUAGE", qlanname, NULL);
13356 :
13357 138 : if (plang->dobj.dump & DUMP_COMPONENT_DEFINITION)
13358 62 : ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
13359 62 : ARCHIVE_OPTS(.tag = plang->dobj.name,
13360 : .owner = plang->lanowner,
13361 : .description = "PROCEDURAL LANGUAGE",
13362 : .section = SECTION_PRE_DATA,
13363 : .createStmt = defqry->data,
13364 : .dropStmt = delqry->data,
13365 : ));
13366 :
13367 : /* Dump Proc Lang Comments and Security Labels */
13368 138 : if (plang->dobj.dump & DUMP_COMPONENT_COMMENT)
13369 0 : dumpComment(fout, "LANGUAGE", qlanname,
13370 0 : NULL, plang->lanowner,
13371 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13372 :
13373 138 : if (plang->dobj.dump & DUMP_COMPONENT_SECLABEL)
13374 0 : dumpSecLabel(fout, "LANGUAGE", qlanname,
13375 0 : NULL, plang->lanowner,
13376 0 : plang->dobj.catId, 0, plang->dobj.dumpId);
13377 :
13378 138 : if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
13379 76 : dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
13380 : qlanname, NULL, NULL,
13381 76 : NULL, plang->lanowner, &plang->dacl);
13382 :
13383 138 : free(qlanname);
13384 :
13385 138 : destroyPQExpBuffer(defqry);
13386 138 : destroyPQExpBuffer(delqry);
13387 : }
13388 :
13389 : /*
13390 : * format_function_arguments: generate function name and argument list
13391 : *
13392 : * This is used when we can rely on pg_get_function_arguments to format
13393 : * the argument list. Note, however, that pg_get_function_arguments
13394 : * does not special-case zero-argument aggregates.
13395 : */
13396 : static char *
13397 8204 : format_function_arguments(const FuncInfo *finfo, const char *funcargs, bool is_agg)
13398 : {
13399 : PQExpBufferData fn;
13400 :
13401 8204 : initPQExpBuffer(&fn);
13402 8204 : appendPQExpBufferStr(&fn, fmtId(finfo->dobj.name));
13403 8204 : if (is_agg && finfo->nargs == 0)
13404 160 : appendPQExpBufferStr(&fn, "(*)");
13405 : else
13406 8044 : appendPQExpBuffer(&fn, "(%s)", funcargs);
13407 8204 : return fn.data;
13408 : }
13409 :
13410 : /*
13411 : * format_function_signature: generate function name and argument list
13412 : *
13413 : * Only a minimal list of input argument types is generated; this is
13414 : * sufficient to reference the function, but not to define it.
13415 : *
13416 : * If honor_quotes is false then the function name is never quoted.
13417 : * This is appropriate for use in TOC tags, but not in SQL commands.
13418 : */
13419 : static char *
13420 4318 : format_function_signature(Archive *fout, const FuncInfo *finfo, bool honor_quotes)
13421 : {
13422 : PQExpBufferData fn;
13423 : int j;
13424 :
13425 4318 : initPQExpBuffer(&fn);
13426 4318 : if (honor_quotes)
13427 786 : appendPQExpBuffer(&fn, "%s(", fmtId(finfo->dobj.name));
13428 : else
13429 3532 : appendPQExpBuffer(&fn, "%s(", finfo->dobj.name);
13430 7920 : for (j = 0; j < finfo->nargs; j++)
13431 : {
13432 3602 : if (j > 0)
13433 844 : appendPQExpBufferStr(&fn, ", ");
13434 :
13435 3602 : appendPQExpBufferStr(&fn,
13436 3602 : getFormattedTypeName(fout, finfo->argtypes[j],
13437 : zeroIsError));
13438 : }
13439 4318 : appendPQExpBufferChar(&fn, ')');
13440 4318 : return fn.data;
13441 : }
13442 :
13443 :
13444 : /*
13445 : * dumpFunc:
13446 : * dump out one function
13447 : */
13448 : static void
13449 3656 : dumpFunc(Archive *fout, const FuncInfo *finfo)
13450 : {
13451 3656 : DumpOptions *dopt = fout->dopt;
13452 : PQExpBuffer query;
13453 : PQExpBuffer q;
13454 : PQExpBuffer delqry;
13455 : PQExpBuffer asPart;
13456 : PGresult *res;
13457 : char *funcsig; /* identity signature */
13458 3656 : char *funcfullsig = NULL; /* full signature */
13459 : char *funcsig_tag;
13460 : char *qual_funcsig;
13461 : char *proretset;
13462 : char *prosrc;
13463 : char *probin;
13464 : char *prosqlbody;
13465 : char *funcargs;
13466 : char *funciargs;
13467 : char *funcresult;
13468 : char *protrftypes;
13469 : char *prokind;
13470 : char *provolatile;
13471 : char *proisstrict;
13472 : char *prosecdef;
13473 : char *proleakproof;
13474 : char *proconfig;
13475 : char *procost;
13476 : char *prorows;
13477 : char *prosupport;
13478 : char *proparallel;
13479 : char *lanname;
13480 3656 : char **configitems = NULL;
13481 3656 : int nconfigitems = 0;
13482 : const char *keyword;
13483 :
13484 : /* Do nothing if not dumping schema */
13485 3656 : if (!dopt->dumpSchema)
13486 124 : return;
13487 :
13488 3532 : query = createPQExpBuffer();
13489 3532 : q = createPQExpBuffer();
13490 3532 : delqry = createPQExpBuffer();
13491 3532 : asPart = createPQExpBuffer();
13492 :
13493 3532 : if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
13494 : {
13495 : /* Set up query for function-specific details */
13496 132 : appendPQExpBufferStr(query,
13497 : "PREPARE dumpFunc(pg_catalog.oid) AS\n");
13498 :
13499 132 : appendPQExpBufferStr(query,
13500 : "SELECT\n"
13501 : "proretset,\n"
13502 : "prosrc,\n"
13503 : "probin,\n"
13504 : "provolatile,\n"
13505 : "proisstrict,\n"
13506 : "prosecdef,\n"
13507 : "lanname,\n"
13508 : "proconfig,\n"
13509 : "procost,\n"
13510 : "prorows,\n"
13511 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
13512 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
13513 : "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n"
13514 : "proleakproof,\n");
13515 :
13516 132 : if (fout->remoteVersion >= 90500)
13517 132 : appendPQExpBufferStr(query,
13518 : "array_to_string(protrftypes, ' ') AS protrftypes,\n");
13519 : else
13520 0 : appendPQExpBufferStr(query,
13521 : "NULL AS protrftypes,\n");
13522 :
13523 132 : if (fout->remoteVersion >= 90600)
13524 132 : appendPQExpBufferStr(query,
13525 : "proparallel,\n");
13526 : else
13527 0 : appendPQExpBufferStr(query,
13528 : "'u' AS proparallel,\n");
13529 :
13530 132 : if (fout->remoteVersion >= 110000)
13531 132 : appendPQExpBufferStr(query,
13532 : "prokind,\n");
13533 : else
13534 0 : appendPQExpBufferStr(query,
13535 : "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
13536 :
13537 132 : if (fout->remoteVersion >= 120000)
13538 132 : appendPQExpBufferStr(query,
13539 : "prosupport,\n");
13540 : else
13541 0 : appendPQExpBufferStr(query,
13542 : "'-' AS prosupport,\n");
13543 :
13544 132 : if (fout->remoteVersion >= 140000)
13545 132 : appendPQExpBufferStr(query,
13546 : "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
13547 : else
13548 0 : appendPQExpBufferStr(query,
13549 : "NULL AS prosqlbody\n");
13550 :
13551 132 : appendPQExpBufferStr(query,
13552 : "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
13553 : "WHERE p.oid = $1 "
13554 : "AND l.oid = p.prolang");
13555 :
13556 132 : ExecuteSqlStatement(fout, query->data);
13557 :
13558 132 : fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
13559 : }
13560 :
13561 3532 : printfPQExpBuffer(query,
13562 : "EXECUTE dumpFunc('%u')",
13563 3532 : finfo->dobj.catId.oid);
13564 :
13565 3532 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
13566 :
13567 3532 : proretset = PQgetvalue(res, 0, PQfnumber(res, "proretset"));
13568 3532 : if (PQgetisnull(res, 0, PQfnumber(res, "prosqlbody")))
13569 : {
13570 3436 : prosrc = PQgetvalue(res, 0, PQfnumber(res, "prosrc"));
13571 3436 : probin = PQgetvalue(res, 0, PQfnumber(res, "probin"));
13572 3436 : prosqlbody = NULL;
13573 : }
13574 : else
13575 : {
13576 96 : prosrc = NULL;
13577 96 : probin = NULL;
13578 96 : prosqlbody = PQgetvalue(res, 0, PQfnumber(res, "prosqlbody"));
13579 : }
13580 3532 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
13581 3532 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
13582 3532 : funcresult = PQgetvalue(res, 0, PQfnumber(res, "funcresult"));
13583 3532 : protrftypes = PQgetvalue(res, 0, PQfnumber(res, "protrftypes"));
13584 3532 : prokind = PQgetvalue(res, 0, PQfnumber(res, "prokind"));
13585 3532 : provolatile = PQgetvalue(res, 0, PQfnumber(res, "provolatile"));
13586 3532 : proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict"));
13587 3532 : prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef"));
13588 3532 : proleakproof = PQgetvalue(res, 0, PQfnumber(res, "proleakproof"));
13589 3532 : proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
13590 3532 : procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
13591 3532 : prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
13592 3532 : prosupport = PQgetvalue(res, 0, PQfnumber(res, "prosupport"));
13593 3532 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
13594 3532 : lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
13595 :
13596 : /*
13597 : * See backend/commands/functioncmds.c for details of how the 'AS' clause
13598 : * is used.
13599 : */
13600 3532 : if (prosqlbody)
13601 : {
13602 96 : appendPQExpBufferStr(asPart, prosqlbody);
13603 : }
13604 3436 : else if (probin[0] != '\0')
13605 : {
13606 294 : appendPQExpBufferStr(asPart, "AS ");
13607 294 : appendStringLiteralAH(asPart, probin, fout);
13608 294 : if (prosrc[0] != '\0')
13609 : {
13610 294 : appendPQExpBufferStr(asPart, ", ");
13611 :
13612 : /*
13613 : * where we have bin, use dollar quoting if allowed and src
13614 : * contains quote or backslash; else use regular quoting.
13615 : */
13616 294 : if (dopt->disable_dollar_quoting ||
13617 294 : (strchr(prosrc, '\'') == NULL && strchr(prosrc, '\\') == NULL))
13618 294 : appendStringLiteralAH(asPart, prosrc, fout);
13619 : else
13620 0 : appendStringLiteralDQ(asPart, prosrc, NULL);
13621 : }
13622 : }
13623 : else
13624 : {
13625 3142 : appendPQExpBufferStr(asPart, "AS ");
13626 : /* with no bin, dollar quote src unconditionally if allowed */
13627 3142 : if (dopt->disable_dollar_quoting)
13628 0 : appendStringLiteralAH(asPart, prosrc, fout);
13629 : else
13630 3142 : appendStringLiteralDQ(asPart, prosrc, NULL);
13631 : }
13632 :
13633 3532 : if (*proconfig)
13634 : {
13635 30 : if (!parsePGArray(proconfig, &configitems, &nconfigitems))
13636 0 : pg_fatal("could not parse %s array", "proconfig");
13637 : }
13638 : else
13639 : {
13640 3502 : configitems = NULL;
13641 3502 : nconfigitems = 0;
13642 : }
13643 :
13644 3532 : funcfullsig = format_function_arguments(finfo, funcargs, false);
13645 3532 : funcsig = format_function_arguments(finfo, funciargs, false);
13646 :
13647 3532 : funcsig_tag = format_function_signature(fout, finfo, false);
13648 :
13649 3532 : qual_funcsig = psprintf("%s.%s",
13650 3532 : fmtId(finfo->dobj.namespace->dobj.name),
13651 : funcsig);
13652 :
13653 3532 : if (prokind[0] == PROKIND_PROCEDURE)
13654 184 : keyword = "PROCEDURE";
13655 : else
13656 3348 : keyword = "FUNCTION"; /* works for window functions too */
13657 :
13658 3532 : appendPQExpBuffer(delqry, "DROP %s %s;\n",
13659 : keyword, qual_funcsig);
13660 :
13661 7064 : appendPQExpBuffer(q, "CREATE %s %s.%s",
13662 : keyword,
13663 3532 : fmtId(finfo->dobj.namespace->dobj.name),
13664 : funcfullsig ? funcfullsig :
13665 : funcsig);
13666 :
13667 3532 : if (prokind[0] == PROKIND_PROCEDURE)
13668 : /* no result type to output */ ;
13669 3348 : else if (funcresult)
13670 3348 : appendPQExpBuffer(q, " RETURNS %s", funcresult);
13671 : else
13672 0 : appendPQExpBuffer(q, " RETURNS %s%s",
13673 0 : (proretset[0] == 't') ? "SETOF " : "",
13674 0 : getFormattedTypeName(fout, finfo->prorettype,
13675 : zeroIsError));
13676 :
13677 3532 : appendPQExpBuffer(q, "\n LANGUAGE %s", fmtId(lanname));
13678 :
13679 3532 : if (*protrftypes)
13680 : {
13681 0 : Oid *typeids = pg_malloc(FUNC_MAX_ARGS * sizeof(Oid));
13682 : int i;
13683 :
13684 0 : appendPQExpBufferStr(q, " TRANSFORM ");
13685 0 : parseOidArray(protrftypes, typeids, FUNC_MAX_ARGS);
13686 0 : for (i = 0; typeids[i]; i++)
13687 : {
13688 0 : if (i != 0)
13689 0 : appendPQExpBufferStr(q, ", ");
13690 0 : appendPQExpBuffer(q, "FOR TYPE %s",
13691 0 : getFormattedTypeName(fout, typeids[i], zeroAsNone));
13692 : }
13693 :
13694 0 : free(typeids);
13695 : }
13696 :
13697 3532 : if (prokind[0] == PROKIND_WINDOW)
13698 10 : appendPQExpBufferStr(q, " WINDOW");
13699 :
13700 3532 : if (provolatile[0] != PROVOLATILE_VOLATILE)
13701 : {
13702 702 : if (provolatile[0] == PROVOLATILE_IMMUTABLE)
13703 660 : appendPQExpBufferStr(q, " IMMUTABLE");
13704 42 : else if (provolatile[0] == PROVOLATILE_STABLE)
13705 42 : appendPQExpBufferStr(q, " STABLE");
13706 0 : else if (provolatile[0] != PROVOLATILE_VOLATILE)
13707 0 : pg_fatal("unrecognized provolatile value for function \"%s\"",
13708 : finfo->dobj.name);
13709 : }
13710 :
13711 3532 : if (proisstrict[0] == 't')
13712 716 : appendPQExpBufferStr(q, " STRICT");
13713 :
13714 3532 : if (prosecdef[0] == 't')
13715 0 : appendPQExpBufferStr(q, " SECURITY DEFINER");
13716 :
13717 3532 : if (proleakproof[0] == 't')
13718 20 : appendPQExpBufferStr(q, " LEAKPROOF");
13719 :
13720 : /*
13721 : * COST and ROWS are emitted only if present and not default, so as not to
13722 : * break backwards-compatibility of the dump without need. Keep this code
13723 : * in sync with the defaults in functioncmds.c.
13724 : */
13725 3532 : if (strcmp(procost, "0") != 0)
13726 : {
13727 3532 : if (strcmp(lanname, "internal") == 0 || strcmp(lanname, "c") == 0)
13728 : {
13729 : /* default cost is 1 */
13730 752 : if (strcmp(procost, "1") != 0)
13731 0 : appendPQExpBuffer(q, " COST %s", procost);
13732 : }
13733 : else
13734 : {
13735 : /* default cost is 100 */
13736 2780 : if (strcmp(procost, "100") != 0)
13737 12 : appendPQExpBuffer(q, " COST %s", procost);
13738 : }
13739 : }
13740 3532 : if (proretset[0] == 't' &&
13741 374 : strcmp(prorows, "0") != 0 && strcmp(prorows, "1000") != 0)
13742 0 : appendPQExpBuffer(q, " ROWS %s", prorows);
13743 :
13744 3532 : if (strcmp(prosupport, "-") != 0)
13745 : {
13746 : /* We rely on regprocout to provide quoting and qualification */
13747 84 : appendPQExpBuffer(q, " SUPPORT %s", prosupport);
13748 : }
13749 :
13750 3532 : if (proparallel[0] != PROPARALLEL_UNSAFE)
13751 : {
13752 232 : if (proparallel[0] == PROPARALLEL_SAFE)
13753 222 : appendPQExpBufferStr(q, " PARALLEL SAFE");
13754 10 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
13755 10 : appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
13756 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
13757 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
13758 : finfo->dobj.name);
13759 : }
13760 :
13761 3612 : for (int i = 0; i < nconfigitems; i++)
13762 : {
13763 : /* we feel free to scribble on configitems[] here */
13764 80 : char *configitem = configitems[i];
13765 : char *pos;
13766 :
13767 80 : pos = strchr(configitem, '=');
13768 80 : if (pos == NULL)
13769 0 : continue;
13770 80 : *pos++ = '\0';
13771 80 : appendPQExpBuffer(q, "\n SET %s TO ", fmtId(configitem));
13772 :
13773 : /*
13774 : * Variables that are marked GUC_LIST_QUOTE were already fully quoted
13775 : * by flatten_set_variable_args() before they were put into the
13776 : * proconfig array. However, because the quoting rules used there
13777 : * aren't exactly like SQL's, we have to break the list value apart
13778 : * and then quote the elements as string literals. (The elements may
13779 : * be double-quoted as-is, but we can't just feed them to the SQL
13780 : * parser; it would do the wrong thing with elements that are
13781 : * zero-length or longer than NAMEDATALEN.) Also, we need a special
13782 : * case for empty lists.
13783 : *
13784 : * Variables that are not so marked should just be emitted as simple
13785 : * string literals. If the variable is not known to
13786 : * variable_is_guc_list_quote(), we'll do that; this makes it unsafe
13787 : * to use GUC_LIST_QUOTE for extension variables.
13788 : */
13789 80 : if (variable_is_guc_list_quote(configitem))
13790 : {
13791 : char **namelist;
13792 : char **nameptr;
13793 :
13794 : /* Parse string into list of identifiers */
13795 : /* this shouldn't fail really */
13796 30 : if (SplitGUCList(pos, ',', &namelist))
13797 : {
13798 : /* Special case: represent an empty list as NULL */
13799 30 : if (*namelist == NULL)
13800 10 : appendPQExpBufferStr(q, "NULL");
13801 80 : for (nameptr = namelist; *nameptr; nameptr++)
13802 : {
13803 50 : if (nameptr != namelist)
13804 30 : appendPQExpBufferStr(q, ", ");
13805 50 : appendStringLiteralAH(q, *nameptr, fout);
13806 : }
13807 : }
13808 30 : pg_free(namelist);
13809 : }
13810 : else
13811 50 : appendStringLiteralAH(q, pos, fout);
13812 : }
13813 :
13814 3532 : appendPQExpBuffer(q, "\n %s;\n", asPart->data);
13815 :
13816 3532 : append_depends_on_extension(fout, q, &finfo->dobj,
13817 : "pg_catalog.pg_proc", keyword,
13818 : qual_funcsig);
13819 :
13820 3532 : if (dopt->binary_upgrade)
13821 590 : binary_upgrade_extension_member(q, &finfo->dobj,
13822 : keyword, funcsig,
13823 590 : finfo->dobj.namespace->dobj.name);
13824 :
13825 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
13826 3340 : ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId,
13827 3340 : ARCHIVE_OPTS(.tag = funcsig_tag,
13828 : .namespace = finfo->dobj.namespace->dobj.name,
13829 : .owner = finfo->rolname,
13830 : .description = keyword,
13831 : .section = finfo->postponed_def ?
13832 : SECTION_POST_DATA : SECTION_PRE_DATA,
13833 : .createStmt = q->data,
13834 : .dropStmt = delqry->data));
13835 :
13836 : /* Dump Function Comments and Security Labels */
13837 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_COMMENT)
13838 18 : dumpComment(fout, keyword, funcsig,
13839 18 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13840 18 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13841 :
13842 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
13843 0 : dumpSecLabel(fout, keyword, funcsig,
13844 0 : finfo->dobj.namespace->dobj.name, finfo->rolname,
13845 0 : finfo->dobj.catId, 0, finfo->dobj.dumpId);
13846 :
13847 3532 : if (finfo->dobj.dump & DUMP_COMPONENT_ACL)
13848 200 : dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
13849 : funcsig, NULL,
13850 200 : finfo->dobj.namespace->dobj.name,
13851 200 : NULL, finfo->rolname, &finfo->dacl);
13852 :
13853 3532 : PQclear(res);
13854 :
13855 3532 : destroyPQExpBuffer(query);
13856 3532 : destroyPQExpBuffer(q);
13857 3532 : destroyPQExpBuffer(delqry);
13858 3532 : destroyPQExpBuffer(asPart);
13859 3532 : free(funcsig);
13860 3532 : free(funcfullsig);
13861 3532 : free(funcsig_tag);
13862 3532 : free(qual_funcsig);
13863 3532 : free(configitems);
13864 : }
13865 :
13866 :
13867 : /*
13868 : * Dump a user-defined cast
13869 : */
13870 : static void
13871 134 : dumpCast(Archive *fout, const CastInfo *cast)
13872 : {
13873 134 : DumpOptions *dopt = fout->dopt;
13874 : PQExpBuffer defqry;
13875 : PQExpBuffer delqry;
13876 : PQExpBuffer labelq;
13877 : PQExpBuffer castargs;
13878 134 : FuncInfo *funcInfo = NULL;
13879 : const char *sourceType;
13880 : const char *targetType;
13881 :
13882 : /* Do nothing if not dumping schema */
13883 134 : if (!dopt->dumpSchema)
13884 12 : return;
13885 :
13886 : /* Cannot dump if we don't have the cast function's info */
13887 122 : if (OidIsValid(cast->castfunc))
13888 : {
13889 72 : funcInfo = findFuncByOid(cast->castfunc);
13890 72 : if (funcInfo == NULL)
13891 0 : pg_fatal("could not find function definition for function with OID %u",
13892 : cast->castfunc);
13893 : }
13894 :
13895 122 : defqry = createPQExpBuffer();
13896 122 : delqry = createPQExpBuffer();
13897 122 : labelq = createPQExpBuffer();
13898 122 : castargs = createPQExpBuffer();
13899 :
13900 122 : sourceType = getFormattedTypeName(fout, cast->castsource, zeroAsNone);
13901 122 : targetType = getFormattedTypeName(fout, cast->casttarget, zeroAsNone);
13902 122 : appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n",
13903 : sourceType, targetType);
13904 :
13905 122 : appendPQExpBuffer(defqry, "CREATE CAST (%s AS %s) ",
13906 : sourceType, targetType);
13907 :
13908 122 : switch (cast->castmethod)
13909 : {
13910 50 : case COERCION_METHOD_BINARY:
13911 50 : appendPQExpBufferStr(defqry, "WITHOUT FUNCTION");
13912 50 : break;
13913 0 : case COERCION_METHOD_INOUT:
13914 0 : appendPQExpBufferStr(defqry, "WITH INOUT");
13915 0 : break;
13916 72 : case COERCION_METHOD_FUNCTION:
13917 72 : if (funcInfo)
13918 : {
13919 72 : char *fsig = format_function_signature(fout, funcInfo, true);
13920 :
13921 : /*
13922 : * Always qualify the function name (format_function_signature
13923 : * won't qualify it).
13924 : */
13925 72 : appendPQExpBuffer(defqry, "WITH FUNCTION %s.%s",
13926 72 : fmtId(funcInfo->dobj.namespace->dobj.name), fsig);
13927 72 : free(fsig);
13928 : }
13929 : else
13930 0 : pg_log_warning("bogus value in pg_cast.castfunc or pg_cast.castmethod field");
13931 72 : break;
13932 0 : default:
13933 0 : pg_log_warning("bogus value in pg_cast.castmethod field");
13934 : }
13935 :
13936 122 : if (cast->castcontext == 'a')
13937 62 : appendPQExpBufferStr(defqry, " AS ASSIGNMENT");
13938 60 : else if (cast->castcontext == 'i')
13939 20 : appendPQExpBufferStr(defqry, " AS IMPLICIT");
13940 122 : appendPQExpBufferStr(defqry, ";\n");
13941 :
13942 122 : appendPQExpBuffer(labelq, "CAST (%s AS %s)",
13943 : sourceType, targetType);
13944 :
13945 122 : appendPQExpBuffer(castargs, "(%s AS %s)",
13946 : sourceType, targetType);
13947 :
13948 122 : if (dopt->binary_upgrade)
13949 14 : binary_upgrade_extension_member(defqry, &cast->dobj,
13950 14 : "CAST", castargs->data, NULL);
13951 :
13952 122 : if (cast->dobj.dump & DUMP_COMPONENT_DEFINITION)
13953 122 : ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
13954 122 : ARCHIVE_OPTS(.tag = labelq->data,
13955 : .description = "CAST",
13956 : .section = SECTION_PRE_DATA,
13957 : .createStmt = defqry->data,
13958 : .dropStmt = delqry->data));
13959 :
13960 : /* Dump Cast Comments */
13961 122 : if (cast->dobj.dump & DUMP_COMPONENT_COMMENT)
13962 0 : dumpComment(fout, "CAST", castargs->data,
13963 : NULL, "",
13964 0 : cast->dobj.catId, 0, cast->dobj.dumpId);
13965 :
13966 122 : destroyPQExpBuffer(defqry);
13967 122 : destroyPQExpBuffer(delqry);
13968 122 : destroyPQExpBuffer(labelq);
13969 122 : destroyPQExpBuffer(castargs);
13970 : }
13971 :
13972 : /*
13973 : * Dump a transform
13974 : */
13975 : static void
13976 84 : dumpTransform(Archive *fout, const TransformInfo *transform)
13977 : {
13978 84 : DumpOptions *dopt = fout->dopt;
13979 : PQExpBuffer defqry;
13980 : PQExpBuffer delqry;
13981 : PQExpBuffer labelq;
13982 : PQExpBuffer transformargs;
13983 84 : FuncInfo *fromsqlFuncInfo = NULL;
13984 84 : FuncInfo *tosqlFuncInfo = NULL;
13985 : char *lanname;
13986 : const char *transformType;
13987 :
13988 : /* Do nothing if not dumping schema */
13989 84 : if (!dopt->dumpSchema)
13990 12 : return;
13991 :
13992 : /* Cannot dump if we don't have the transform functions' info */
13993 72 : if (OidIsValid(transform->trffromsql))
13994 : {
13995 72 : fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
13996 72 : if (fromsqlFuncInfo == NULL)
13997 0 : pg_fatal("could not find function definition for function with OID %u",
13998 : transform->trffromsql);
13999 : }
14000 72 : if (OidIsValid(transform->trftosql))
14001 : {
14002 72 : tosqlFuncInfo = findFuncByOid(transform->trftosql);
14003 72 : if (tosqlFuncInfo == NULL)
14004 0 : pg_fatal("could not find function definition for function with OID %u",
14005 : transform->trftosql);
14006 : }
14007 :
14008 72 : defqry = createPQExpBuffer();
14009 72 : delqry = createPQExpBuffer();
14010 72 : labelq = createPQExpBuffer();
14011 72 : transformargs = createPQExpBuffer();
14012 :
14013 72 : lanname = get_language_name(fout, transform->trflang);
14014 72 : transformType = getFormattedTypeName(fout, transform->trftype, zeroAsNone);
14015 :
14016 72 : appendPQExpBuffer(delqry, "DROP TRANSFORM FOR %s LANGUAGE %s;\n",
14017 : transformType, lanname);
14018 :
14019 72 : appendPQExpBuffer(defqry, "CREATE TRANSFORM FOR %s LANGUAGE %s (",
14020 : transformType, lanname);
14021 :
14022 72 : if (!transform->trffromsql && !transform->trftosql)
14023 0 : pg_log_warning("bogus transform definition, at least one of trffromsql and trftosql should be nonzero");
14024 :
14025 72 : if (transform->trffromsql)
14026 : {
14027 72 : if (fromsqlFuncInfo)
14028 : {
14029 72 : char *fsig = format_function_signature(fout, fromsqlFuncInfo, true);
14030 :
14031 : /*
14032 : * Always qualify the function name (format_function_signature
14033 : * won't qualify it).
14034 : */
14035 72 : appendPQExpBuffer(defqry, "FROM SQL WITH FUNCTION %s.%s",
14036 72 : fmtId(fromsqlFuncInfo->dobj.namespace->dobj.name), fsig);
14037 72 : free(fsig);
14038 : }
14039 : else
14040 0 : pg_log_warning("bogus value in pg_transform.trffromsql field");
14041 : }
14042 :
14043 72 : if (transform->trftosql)
14044 : {
14045 72 : if (transform->trffromsql)
14046 72 : appendPQExpBufferStr(defqry, ", ");
14047 :
14048 72 : if (tosqlFuncInfo)
14049 : {
14050 72 : char *fsig = format_function_signature(fout, tosqlFuncInfo, true);
14051 :
14052 : /*
14053 : * Always qualify the function name (format_function_signature
14054 : * won't qualify it).
14055 : */
14056 72 : appendPQExpBuffer(defqry, "TO SQL WITH FUNCTION %s.%s",
14057 72 : fmtId(tosqlFuncInfo->dobj.namespace->dobj.name), fsig);
14058 72 : free(fsig);
14059 : }
14060 : else
14061 0 : pg_log_warning("bogus value in pg_transform.trftosql field");
14062 : }
14063 :
14064 72 : appendPQExpBufferStr(defqry, ");\n");
14065 :
14066 72 : appendPQExpBuffer(labelq, "TRANSFORM FOR %s LANGUAGE %s",
14067 : transformType, lanname);
14068 :
14069 72 : appendPQExpBuffer(transformargs, "FOR %s LANGUAGE %s",
14070 : transformType, lanname);
14071 :
14072 72 : if (dopt->binary_upgrade)
14073 4 : binary_upgrade_extension_member(defqry, &transform->dobj,
14074 4 : "TRANSFORM", transformargs->data, NULL);
14075 :
14076 72 : if (transform->dobj.dump & DUMP_COMPONENT_DEFINITION)
14077 72 : ArchiveEntry(fout, transform->dobj.catId, transform->dobj.dumpId,
14078 72 : ARCHIVE_OPTS(.tag = labelq->data,
14079 : .description = "TRANSFORM",
14080 : .section = SECTION_PRE_DATA,
14081 : .createStmt = defqry->data,
14082 : .dropStmt = delqry->data,
14083 : .deps = transform->dobj.dependencies,
14084 : .nDeps = transform->dobj.nDeps));
14085 :
14086 : /* Dump Transform Comments */
14087 72 : if (transform->dobj.dump & DUMP_COMPONENT_COMMENT)
14088 0 : dumpComment(fout, "TRANSFORM", transformargs->data,
14089 : NULL, "",
14090 0 : transform->dobj.catId, 0, transform->dobj.dumpId);
14091 :
14092 72 : free(lanname);
14093 72 : destroyPQExpBuffer(defqry);
14094 72 : destroyPQExpBuffer(delqry);
14095 72 : destroyPQExpBuffer(labelq);
14096 72 : destroyPQExpBuffer(transformargs);
14097 : }
14098 :
14099 :
14100 : /*
14101 : * dumpOpr
14102 : * write out a single operator definition
14103 : */
14104 : static void
14105 5008 : dumpOpr(Archive *fout, const OprInfo *oprinfo)
14106 : {
14107 5008 : DumpOptions *dopt = fout->dopt;
14108 : PQExpBuffer query;
14109 : PQExpBuffer q;
14110 : PQExpBuffer delq;
14111 : PQExpBuffer oprid;
14112 : PQExpBuffer details;
14113 : PGresult *res;
14114 : int i_oprkind;
14115 : int i_oprcode;
14116 : int i_oprleft;
14117 : int i_oprright;
14118 : int i_oprcom;
14119 : int i_oprnegate;
14120 : int i_oprrest;
14121 : int i_oprjoin;
14122 : int i_oprcanmerge;
14123 : int i_oprcanhash;
14124 : char *oprkind;
14125 : char *oprcode;
14126 : char *oprleft;
14127 : char *oprright;
14128 : char *oprcom;
14129 : char *oprnegate;
14130 : char *oprrest;
14131 : char *oprjoin;
14132 : char *oprcanmerge;
14133 : char *oprcanhash;
14134 : char *oprregproc;
14135 : char *oprref;
14136 :
14137 : /* Do nothing if not dumping schema */
14138 5008 : if (!dopt->dumpSchema)
14139 12 : return;
14140 :
14141 : /*
14142 : * some operators are invalid because they were the result of user
14143 : * defining operators before commutators exist
14144 : */
14145 4996 : if (!OidIsValid(oprinfo->oprcode))
14146 28 : return;
14147 :
14148 4968 : query = createPQExpBuffer();
14149 4968 : q = createPQExpBuffer();
14150 4968 : delq = createPQExpBuffer();
14151 4968 : oprid = createPQExpBuffer();
14152 4968 : details = createPQExpBuffer();
14153 :
14154 4968 : if (!fout->is_prepared[PREPQUERY_DUMPOPR])
14155 : {
14156 : /* Set up query for operator-specific details */
14157 80 : appendPQExpBufferStr(query,
14158 : "PREPARE dumpOpr(pg_catalog.oid) AS\n"
14159 : "SELECT oprkind, "
14160 : "oprcode::pg_catalog.regprocedure, "
14161 : "oprleft::pg_catalog.regtype, "
14162 : "oprright::pg_catalog.regtype, "
14163 : "oprcom, "
14164 : "oprnegate, "
14165 : "oprrest::pg_catalog.regprocedure, "
14166 : "oprjoin::pg_catalog.regprocedure, "
14167 : "oprcanmerge, oprcanhash "
14168 : "FROM pg_catalog.pg_operator "
14169 : "WHERE oid = $1");
14170 :
14171 80 : ExecuteSqlStatement(fout, query->data);
14172 :
14173 80 : fout->is_prepared[PREPQUERY_DUMPOPR] = true;
14174 : }
14175 :
14176 4968 : printfPQExpBuffer(query,
14177 : "EXECUTE dumpOpr('%u')",
14178 4968 : oprinfo->dobj.catId.oid);
14179 :
14180 4968 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14181 :
14182 4968 : i_oprkind = PQfnumber(res, "oprkind");
14183 4968 : i_oprcode = PQfnumber(res, "oprcode");
14184 4968 : i_oprleft = PQfnumber(res, "oprleft");
14185 4968 : i_oprright = PQfnumber(res, "oprright");
14186 4968 : i_oprcom = PQfnumber(res, "oprcom");
14187 4968 : i_oprnegate = PQfnumber(res, "oprnegate");
14188 4968 : i_oprrest = PQfnumber(res, "oprrest");
14189 4968 : i_oprjoin = PQfnumber(res, "oprjoin");
14190 4968 : i_oprcanmerge = PQfnumber(res, "oprcanmerge");
14191 4968 : i_oprcanhash = PQfnumber(res, "oprcanhash");
14192 :
14193 4968 : oprkind = PQgetvalue(res, 0, i_oprkind);
14194 4968 : oprcode = PQgetvalue(res, 0, i_oprcode);
14195 4968 : oprleft = PQgetvalue(res, 0, i_oprleft);
14196 4968 : oprright = PQgetvalue(res, 0, i_oprright);
14197 4968 : oprcom = PQgetvalue(res, 0, i_oprcom);
14198 4968 : oprnegate = PQgetvalue(res, 0, i_oprnegate);
14199 4968 : oprrest = PQgetvalue(res, 0, i_oprrest);
14200 4968 : oprjoin = PQgetvalue(res, 0, i_oprjoin);
14201 4968 : oprcanmerge = PQgetvalue(res, 0, i_oprcanmerge);
14202 4968 : oprcanhash = PQgetvalue(res, 0, i_oprcanhash);
14203 :
14204 : /* In PG14 upwards postfix operator support does not exist anymore. */
14205 4968 : if (strcmp(oprkind, "r") == 0)
14206 0 : pg_log_warning("postfix operators are not supported anymore (operator \"%s\")",
14207 : oprcode);
14208 :
14209 4968 : oprregproc = convertRegProcReference(oprcode);
14210 4968 : if (oprregproc)
14211 : {
14212 4968 : appendPQExpBuffer(details, " FUNCTION = %s", oprregproc);
14213 4968 : free(oprregproc);
14214 : }
14215 :
14216 4968 : appendPQExpBuffer(oprid, "%s (",
14217 4968 : oprinfo->dobj.name);
14218 :
14219 : /*
14220 : * right unary means there's a left arg and left unary means there's a
14221 : * right arg. (Although the "r" case is dead code for PG14 and later,
14222 : * continue to support it in case we're dumping from an old server.)
14223 : */
14224 4968 : if (strcmp(oprkind, "r") == 0 ||
14225 4968 : strcmp(oprkind, "b") == 0)
14226 : {
14227 4682 : appendPQExpBuffer(details, ",\n LEFTARG = %s", oprleft);
14228 4682 : appendPQExpBufferStr(oprid, oprleft);
14229 : }
14230 : else
14231 286 : appendPQExpBufferStr(oprid, "NONE");
14232 :
14233 4968 : if (strcmp(oprkind, "l") == 0 ||
14234 4682 : strcmp(oprkind, "b") == 0)
14235 : {
14236 4968 : appendPQExpBuffer(details, ",\n RIGHTARG = %s", oprright);
14237 4968 : appendPQExpBuffer(oprid, ", %s)", oprright);
14238 : }
14239 : else
14240 0 : appendPQExpBufferStr(oprid, ", NONE)");
14241 :
14242 4968 : oprref = getFormattedOperatorName(oprcom);
14243 4968 : if (oprref)
14244 : {
14245 3322 : appendPQExpBuffer(details, ",\n COMMUTATOR = %s", oprref);
14246 3322 : free(oprref);
14247 : }
14248 :
14249 4968 : oprref = getFormattedOperatorName(oprnegate);
14250 4968 : if (oprref)
14251 : {
14252 2326 : appendPQExpBuffer(details, ",\n NEGATOR = %s", oprref);
14253 2326 : free(oprref);
14254 : }
14255 :
14256 4968 : if (strcmp(oprcanmerge, "t") == 0)
14257 370 : appendPQExpBufferStr(details, ",\n MERGES");
14258 :
14259 4968 : if (strcmp(oprcanhash, "t") == 0)
14260 276 : appendPQExpBufferStr(details, ",\n HASHES");
14261 :
14262 4968 : oprregproc = convertRegProcReference(oprrest);
14263 4968 : if (oprregproc)
14264 : {
14265 3028 : appendPQExpBuffer(details, ",\n RESTRICT = %s", oprregproc);
14266 3028 : free(oprregproc);
14267 : }
14268 :
14269 4968 : oprregproc = convertRegProcReference(oprjoin);
14270 4968 : if (oprregproc)
14271 : {
14272 3028 : appendPQExpBuffer(details, ",\n JOIN = %s", oprregproc);
14273 3028 : free(oprregproc);
14274 : }
14275 :
14276 4968 : appendPQExpBuffer(delq, "DROP OPERATOR %s.%s;\n",
14277 4968 : fmtId(oprinfo->dobj.namespace->dobj.name),
14278 : oprid->data);
14279 :
14280 4968 : appendPQExpBuffer(q, "CREATE OPERATOR %s.%s (\n%s\n);\n",
14281 4968 : fmtId(oprinfo->dobj.namespace->dobj.name),
14282 4968 : oprinfo->dobj.name, details->data);
14283 :
14284 4968 : if (dopt->binary_upgrade)
14285 24 : binary_upgrade_extension_member(q, &oprinfo->dobj,
14286 24 : "OPERATOR", oprid->data,
14287 24 : oprinfo->dobj.namespace->dobj.name);
14288 :
14289 4968 : if (oprinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14290 4968 : ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId,
14291 4968 : ARCHIVE_OPTS(.tag = oprinfo->dobj.name,
14292 : .namespace = oprinfo->dobj.namespace->dobj.name,
14293 : .owner = oprinfo->rolname,
14294 : .description = "OPERATOR",
14295 : .section = SECTION_PRE_DATA,
14296 : .createStmt = q->data,
14297 : .dropStmt = delq->data));
14298 :
14299 : /* Dump Operator Comments */
14300 4968 : if (oprinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14301 4794 : dumpComment(fout, "OPERATOR", oprid->data,
14302 4794 : oprinfo->dobj.namespace->dobj.name, oprinfo->rolname,
14303 4794 : oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId);
14304 :
14305 4968 : PQclear(res);
14306 :
14307 4968 : destroyPQExpBuffer(query);
14308 4968 : destroyPQExpBuffer(q);
14309 4968 : destroyPQExpBuffer(delq);
14310 4968 : destroyPQExpBuffer(oprid);
14311 4968 : destroyPQExpBuffer(details);
14312 : }
14313 :
14314 : /*
14315 : * Convert a function reference obtained from pg_operator
14316 : *
14317 : * Returns allocated string of what to print, or NULL if function references
14318 : * is InvalidOid. Returned string is expected to be free'd by the caller.
14319 : *
14320 : * The input is a REGPROCEDURE display; we have to strip the argument-types
14321 : * part.
14322 : */
14323 : static char *
14324 14904 : convertRegProcReference(const char *proc)
14325 : {
14326 : char *name;
14327 : char *paren;
14328 : bool inquote;
14329 :
14330 : /* In all cases "-" means a null reference */
14331 14904 : if (strcmp(proc, "-") == 0)
14332 3880 : return NULL;
14333 :
14334 11024 : name = pg_strdup(proc);
14335 : /* find non-double-quoted left paren */
14336 11024 : inquote = false;
14337 132864 : for (paren = name; *paren; paren++)
14338 : {
14339 132864 : if (*paren == '(' && !inquote)
14340 : {
14341 11024 : *paren = '\0';
14342 11024 : break;
14343 : }
14344 121840 : if (*paren == '"')
14345 100 : inquote = !inquote;
14346 : }
14347 11024 : return name;
14348 : }
14349 :
14350 : /*
14351 : * getFormattedOperatorName - retrieve the operator name for the
14352 : * given operator OID (presented in string form).
14353 : *
14354 : * Returns an allocated string, or NULL if the given OID is invalid.
14355 : * Caller is responsible for free'ing result string.
14356 : *
14357 : * What we produce has the format "OPERATOR(schema.oprname)". This is only
14358 : * useful in commands where the operator's argument types can be inferred from
14359 : * context. We always schema-qualify the name, though. The predecessor to
14360 : * this code tried to skip the schema qualification if possible, but that led
14361 : * to wrong results in corner cases, such as if an operator and its negator
14362 : * are in different schemas.
14363 : */
14364 : static char *
14365 10506 : getFormattedOperatorName(const char *oproid)
14366 : {
14367 : OprInfo *oprInfo;
14368 :
14369 : /* In all cases "0" means a null reference */
14370 10506 : if (strcmp(oproid, "0") == 0)
14371 4858 : return NULL;
14372 :
14373 5648 : oprInfo = findOprByOid(atooid(oproid));
14374 5648 : if (oprInfo == NULL)
14375 : {
14376 0 : pg_log_warning("could not find operator with OID %s",
14377 : oproid);
14378 0 : return NULL;
14379 : }
14380 :
14381 5648 : return psprintf("OPERATOR(%s.%s)",
14382 5648 : fmtId(oprInfo->dobj.namespace->dobj.name),
14383 : oprInfo->dobj.name);
14384 : }
14385 :
14386 : /*
14387 : * Convert a function OID obtained from pg_ts_parser or pg_ts_template
14388 : *
14389 : * It is sufficient to use REGPROC rather than REGPROCEDURE, since the
14390 : * argument lists of these functions are predetermined. Note that the
14391 : * caller should ensure we are in the proper schema, because the results
14392 : * are search path dependent!
14393 : */
14394 : static char *
14395 410 : convertTSFunction(Archive *fout, Oid funcOid)
14396 : {
14397 : char *result;
14398 : char query[128];
14399 : PGresult *res;
14400 :
14401 410 : snprintf(query, sizeof(query),
14402 : "SELECT '%u'::pg_catalog.regproc", funcOid);
14403 410 : res = ExecuteSqlQueryForSingleRow(fout, query);
14404 :
14405 410 : result = pg_strdup(PQgetvalue(res, 0, 0));
14406 :
14407 410 : PQclear(res);
14408 :
14409 410 : return result;
14410 : }
14411 :
14412 : /*
14413 : * dumpAccessMethod
14414 : * write out a single access method definition
14415 : */
14416 : static void
14417 160 : dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
14418 : {
14419 160 : DumpOptions *dopt = fout->dopt;
14420 : PQExpBuffer q;
14421 : PQExpBuffer delq;
14422 : char *qamname;
14423 :
14424 : /* Do nothing if not dumping schema */
14425 160 : if (!dopt->dumpSchema)
14426 24 : return;
14427 :
14428 136 : q = createPQExpBuffer();
14429 136 : delq = createPQExpBuffer();
14430 :
14431 136 : qamname = pg_strdup(fmtId(aminfo->dobj.name));
14432 :
14433 136 : appendPQExpBuffer(q, "CREATE ACCESS METHOD %s ", qamname);
14434 :
14435 136 : switch (aminfo->amtype)
14436 : {
14437 64 : case AMTYPE_INDEX:
14438 64 : appendPQExpBufferStr(q, "TYPE INDEX ");
14439 64 : break;
14440 72 : case AMTYPE_TABLE:
14441 72 : appendPQExpBufferStr(q, "TYPE TABLE ");
14442 72 : break;
14443 0 : default:
14444 0 : pg_log_warning("invalid type \"%c\" of access method \"%s\"",
14445 : aminfo->amtype, qamname);
14446 0 : destroyPQExpBuffer(q);
14447 0 : destroyPQExpBuffer(delq);
14448 0 : free(qamname);
14449 0 : return;
14450 : }
14451 :
14452 136 : appendPQExpBuffer(q, "HANDLER %s;\n", aminfo->amhandler);
14453 :
14454 136 : appendPQExpBuffer(delq, "DROP ACCESS METHOD %s;\n",
14455 : qamname);
14456 :
14457 136 : if (dopt->binary_upgrade)
14458 8 : binary_upgrade_extension_member(q, &aminfo->dobj,
14459 : "ACCESS METHOD", qamname, NULL);
14460 :
14461 136 : if (aminfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14462 136 : ArchiveEntry(fout, aminfo->dobj.catId, aminfo->dobj.dumpId,
14463 136 : ARCHIVE_OPTS(.tag = aminfo->dobj.name,
14464 : .description = "ACCESS METHOD",
14465 : .section = SECTION_PRE_DATA,
14466 : .createStmt = q->data,
14467 : .dropStmt = delq->data));
14468 :
14469 : /* Dump Access Method Comments */
14470 136 : if (aminfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14471 0 : dumpComment(fout, "ACCESS METHOD", qamname,
14472 : NULL, "",
14473 0 : aminfo->dobj.catId, 0, aminfo->dobj.dumpId);
14474 :
14475 136 : destroyPQExpBuffer(q);
14476 136 : destroyPQExpBuffer(delq);
14477 136 : free(qamname);
14478 : }
14479 :
14480 : /*
14481 : * dumpOpclass
14482 : * write out a single operator class definition
14483 : */
14484 : static void
14485 1320 : dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
14486 : {
14487 1320 : DumpOptions *dopt = fout->dopt;
14488 : PQExpBuffer query;
14489 : PQExpBuffer q;
14490 : PQExpBuffer delq;
14491 : PQExpBuffer nameusing;
14492 : PGresult *res;
14493 : int ntups;
14494 : int i_opcintype;
14495 : int i_opckeytype;
14496 : int i_opcdefault;
14497 : int i_opcfamily;
14498 : int i_opcfamilyname;
14499 : int i_opcfamilynsp;
14500 : int i_amname;
14501 : int i_amopstrategy;
14502 : int i_amopopr;
14503 : int i_sortfamily;
14504 : int i_sortfamilynsp;
14505 : int i_amprocnum;
14506 : int i_amproc;
14507 : int i_amproclefttype;
14508 : int i_amprocrighttype;
14509 : char *opcintype;
14510 : char *opckeytype;
14511 : char *opcdefault;
14512 : char *opcfamily;
14513 : char *opcfamilyname;
14514 : char *opcfamilynsp;
14515 : char *amname;
14516 : char *amopstrategy;
14517 : char *amopopr;
14518 : char *sortfamily;
14519 : char *sortfamilynsp;
14520 : char *amprocnum;
14521 : char *amproc;
14522 : char *amproclefttype;
14523 : char *amprocrighttype;
14524 : bool needComma;
14525 : int i;
14526 :
14527 : /* Do nothing if not dumping schema */
14528 1320 : if (!dopt->dumpSchema)
14529 36 : return;
14530 :
14531 1284 : query = createPQExpBuffer();
14532 1284 : q = createPQExpBuffer();
14533 1284 : delq = createPQExpBuffer();
14534 1284 : nameusing = createPQExpBuffer();
14535 :
14536 : /* Get additional fields from the pg_opclass row */
14537 1284 : appendPQExpBuffer(query, "SELECT opcintype::pg_catalog.regtype, "
14538 : "opckeytype::pg_catalog.regtype, "
14539 : "opcdefault, opcfamily, "
14540 : "opfname AS opcfamilyname, "
14541 : "nspname AS opcfamilynsp, "
14542 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opcmethod) AS amname "
14543 : "FROM pg_catalog.pg_opclass c "
14544 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = opcfamily "
14545 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14546 : "WHERE c.oid = '%u'::pg_catalog.oid",
14547 1284 : opcinfo->dobj.catId.oid);
14548 :
14549 1284 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14550 :
14551 1284 : i_opcintype = PQfnumber(res, "opcintype");
14552 1284 : i_opckeytype = PQfnumber(res, "opckeytype");
14553 1284 : i_opcdefault = PQfnumber(res, "opcdefault");
14554 1284 : i_opcfamily = PQfnumber(res, "opcfamily");
14555 1284 : i_opcfamilyname = PQfnumber(res, "opcfamilyname");
14556 1284 : i_opcfamilynsp = PQfnumber(res, "opcfamilynsp");
14557 1284 : i_amname = PQfnumber(res, "amname");
14558 :
14559 : /* opcintype may still be needed after we PQclear res */
14560 1284 : opcintype = pg_strdup(PQgetvalue(res, 0, i_opcintype));
14561 1284 : opckeytype = PQgetvalue(res, 0, i_opckeytype);
14562 1284 : opcdefault = PQgetvalue(res, 0, i_opcdefault);
14563 : /* opcfamily will still be needed after we PQclear res */
14564 1284 : opcfamily = pg_strdup(PQgetvalue(res, 0, i_opcfamily));
14565 1284 : opcfamilyname = PQgetvalue(res, 0, i_opcfamilyname);
14566 1284 : opcfamilynsp = PQgetvalue(res, 0, i_opcfamilynsp);
14567 : /* amname will still be needed after we PQclear res */
14568 1284 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14569 :
14570 1284 : appendPQExpBuffer(delq, "DROP OPERATOR CLASS %s",
14571 1284 : fmtQualifiedDumpable(opcinfo));
14572 1284 : appendPQExpBuffer(delq, " USING %s;\n",
14573 : fmtId(amname));
14574 :
14575 : /* Build the fixed portion of the CREATE command */
14576 1284 : appendPQExpBuffer(q, "CREATE OPERATOR CLASS %s\n ",
14577 1284 : fmtQualifiedDumpable(opcinfo));
14578 1284 : if (strcmp(opcdefault, "t") == 0)
14579 714 : appendPQExpBufferStr(q, "DEFAULT ");
14580 1284 : appendPQExpBuffer(q, "FOR TYPE %s USING %s",
14581 : opcintype,
14582 : fmtId(amname));
14583 1284 : if (strlen(opcfamilyname) > 0)
14584 : {
14585 1284 : appendPQExpBufferStr(q, " FAMILY ");
14586 1284 : appendPQExpBuffer(q, "%s.", fmtId(opcfamilynsp));
14587 1284 : appendPQExpBufferStr(q, fmtId(opcfamilyname));
14588 : }
14589 1284 : appendPQExpBufferStr(q, " AS\n ");
14590 :
14591 1284 : needComma = false;
14592 :
14593 1284 : if (strcmp(opckeytype, "-") != 0)
14594 : {
14595 504 : appendPQExpBuffer(q, "STORAGE %s",
14596 : opckeytype);
14597 504 : needComma = true;
14598 : }
14599 :
14600 1284 : PQclear(res);
14601 :
14602 : /*
14603 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14604 : *
14605 : * Print only those opfamily members that are tied to the opclass by
14606 : * pg_depend entries.
14607 : */
14608 1284 : resetPQExpBuffer(query);
14609 1284 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14610 : "amopopr::pg_catalog.regoperator, "
14611 : "opfname AS sortfamily, "
14612 : "nspname AS sortfamilynsp "
14613 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14614 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14615 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14616 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14617 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14618 : "AND refobjid = '%u'::pg_catalog.oid "
14619 : "AND amopfamily = '%s'::pg_catalog.oid "
14620 : "ORDER BY amopstrategy",
14621 1284 : opcinfo->dobj.catId.oid,
14622 : opcfamily);
14623 :
14624 1284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14625 :
14626 1284 : ntups = PQntuples(res);
14627 :
14628 1284 : i_amopstrategy = PQfnumber(res, "amopstrategy");
14629 1284 : i_amopopr = PQfnumber(res, "amopopr");
14630 1284 : i_sortfamily = PQfnumber(res, "sortfamily");
14631 1284 : i_sortfamilynsp = PQfnumber(res, "sortfamilynsp");
14632 :
14633 1688 : for (i = 0; i < ntups; i++)
14634 : {
14635 404 : amopstrategy = PQgetvalue(res, i, i_amopstrategy);
14636 404 : amopopr = PQgetvalue(res, i, i_amopopr);
14637 404 : sortfamily = PQgetvalue(res, i, i_sortfamily);
14638 404 : sortfamilynsp = PQgetvalue(res, i, i_sortfamilynsp);
14639 :
14640 404 : if (needComma)
14641 256 : appendPQExpBufferStr(q, " ,\n ");
14642 :
14643 404 : appendPQExpBuffer(q, "OPERATOR %s %s",
14644 : amopstrategy, amopopr);
14645 :
14646 404 : if (strlen(sortfamily) > 0)
14647 : {
14648 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14649 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14650 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14651 : }
14652 :
14653 404 : needComma = true;
14654 : }
14655 :
14656 1284 : PQclear(res);
14657 :
14658 : /*
14659 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14660 : *
14661 : * Print only those opfamily members that are tied to the opclass by
14662 : * pg_depend entries.
14663 : *
14664 : * We print the amproclefttype/amprocrighttype even though in most cases
14665 : * the backend could deduce the right values, because of the corner case
14666 : * of a btree sort support function for a cross-type comparison.
14667 : */
14668 1284 : resetPQExpBuffer(query);
14669 :
14670 1284 : appendPQExpBuffer(query, "SELECT amprocnum, "
14671 : "amproc::pg_catalog.regprocedure, "
14672 : "amproclefttype::pg_catalog.regtype, "
14673 : "amprocrighttype::pg_catalog.regtype "
14674 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14675 : "WHERE refclassid = 'pg_catalog.pg_opclass'::pg_catalog.regclass "
14676 : "AND refobjid = '%u'::pg_catalog.oid "
14677 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14678 : "AND objid = ap.oid "
14679 : "ORDER BY amprocnum",
14680 1284 : opcinfo->dobj.catId.oid);
14681 :
14682 1284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14683 :
14684 1284 : ntups = PQntuples(res);
14685 :
14686 1284 : i_amprocnum = PQfnumber(res, "amprocnum");
14687 1284 : i_amproc = PQfnumber(res, "amproc");
14688 1284 : i_amproclefttype = PQfnumber(res, "amproclefttype");
14689 1284 : i_amprocrighttype = PQfnumber(res, "amprocrighttype");
14690 :
14691 1348 : for (i = 0; i < ntups; i++)
14692 : {
14693 64 : amprocnum = PQgetvalue(res, i, i_amprocnum);
14694 64 : amproc = PQgetvalue(res, i, i_amproc);
14695 64 : amproclefttype = PQgetvalue(res, i, i_amproclefttype);
14696 64 : amprocrighttype = PQgetvalue(res, i, i_amprocrighttype);
14697 :
14698 64 : if (needComma)
14699 64 : appendPQExpBufferStr(q, " ,\n ");
14700 :
14701 64 : appendPQExpBuffer(q, "FUNCTION %s", amprocnum);
14702 :
14703 64 : if (*amproclefttype && *amprocrighttype)
14704 64 : appendPQExpBuffer(q, " (%s, %s)", amproclefttype, amprocrighttype);
14705 :
14706 64 : appendPQExpBuffer(q, " %s", amproc);
14707 :
14708 64 : needComma = true;
14709 : }
14710 :
14711 1284 : PQclear(res);
14712 :
14713 : /*
14714 : * If needComma is still false it means we haven't added anything after
14715 : * the AS keyword. To avoid printing broken SQL, append a dummy STORAGE
14716 : * clause with the same datatype. This isn't sanctioned by the
14717 : * documentation, but actually DefineOpClass will treat it as a no-op.
14718 : */
14719 1284 : if (!needComma)
14720 632 : appendPQExpBuffer(q, "STORAGE %s", opcintype);
14721 :
14722 1284 : appendPQExpBufferStr(q, ";\n");
14723 :
14724 1284 : appendPQExpBufferStr(nameusing, fmtId(opcinfo->dobj.name));
14725 1284 : appendPQExpBuffer(nameusing, " USING %s",
14726 : fmtId(amname));
14727 :
14728 1284 : if (dopt->binary_upgrade)
14729 12 : binary_upgrade_extension_member(q, &opcinfo->dobj,
14730 12 : "OPERATOR CLASS", nameusing->data,
14731 12 : opcinfo->dobj.namespace->dobj.name);
14732 :
14733 1284 : if (opcinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14734 1284 : ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId,
14735 1284 : ARCHIVE_OPTS(.tag = opcinfo->dobj.name,
14736 : .namespace = opcinfo->dobj.namespace->dobj.name,
14737 : .owner = opcinfo->rolname,
14738 : .description = "OPERATOR CLASS",
14739 : .section = SECTION_PRE_DATA,
14740 : .createStmt = q->data,
14741 : .dropStmt = delq->data));
14742 :
14743 : /* Dump Operator Class Comments */
14744 1284 : if (opcinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14745 0 : dumpComment(fout, "OPERATOR CLASS", nameusing->data,
14746 0 : opcinfo->dobj.namespace->dobj.name, opcinfo->rolname,
14747 0 : opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId);
14748 :
14749 1284 : free(opcintype);
14750 1284 : free(opcfamily);
14751 1284 : free(amname);
14752 1284 : destroyPQExpBuffer(query);
14753 1284 : destroyPQExpBuffer(q);
14754 1284 : destroyPQExpBuffer(delq);
14755 1284 : destroyPQExpBuffer(nameusing);
14756 : }
14757 :
14758 : /*
14759 : * dumpOpfamily
14760 : * write out a single operator family definition
14761 : *
14762 : * Note: this also dumps any "loose" operator members that aren't bound to a
14763 : * specific opclass within the opfamily.
14764 : */
14765 : static void
14766 1098 : dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
14767 : {
14768 1098 : DumpOptions *dopt = fout->dopt;
14769 : PQExpBuffer query;
14770 : PQExpBuffer q;
14771 : PQExpBuffer delq;
14772 : PQExpBuffer nameusing;
14773 : PGresult *res;
14774 : PGresult *res_ops;
14775 : PGresult *res_procs;
14776 : int ntups;
14777 : int i_amname;
14778 : int i_amopstrategy;
14779 : int i_amopopr;
14780 : int i_sortfamily;
14781 : int i_sortfamilynsp;
14782 : int i_amprocnum;
14783 : int i_amproc;
14784 : int i_amproclefttype;
14785 : int i_amprocrighttype;
14786 : char *amname;
14787 : char *amopstrategy;
14788 : char *amopopr;
14789 : char *sortfamily;
14790 : char *sortfamilynsp;
14791 : char *amprocnum;
14792 : char *amproc;
14793 : char *amproclefttype;
14794 : char *amprocrighttype;
14795 : bool needComma;
14796 : int i;
14797 :
14798 : /* Do nothing if not dumping schema */
14799 1098 : if (!dopt->dumpSchema)
14800 24 : return;
14801 :
14802 1074 : query = createPQExpBuffer();
14803 1074 : q = createPQExpBuffer();
14804 1074 : delq = createPQExpBuffer();
14805 1074 : nameusing = createPQExpBuffer();
14806 :
14807 : /*
14808 : * Fetch only those opfamily members that are tied directly to the
14809 : * opfamily by pg_depend entries.
14810 : */
14811 1074 : appendPQExpBuffer(query, "SELECT amopstrategy, "
14812 : "amopopr::pg_catalog.regoperator, "
14813 : "opfname AS sortfamily, "
14814 : "nspname AS sortfamilynsp "
14815 : "FROM pg_catalog.pg_amop ao JOIN pg_catalog.pg_depend ON "
14816 : "(classid = 'pg_catalog.pg_amop'::pg_catalog.regclass AND objid = ao.oid) "
14817 : "LEFT JOIN pg_catalog.pg_opfamily f ON f.oid = amopsortfamily "
14818 : "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = opfnamespace "
14819 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14820 : "AND refobjid = '%u'::pg_catalog.oid "
14821 : "AND amopfamily = '%u'::pg_catalog.oid "
14822 : "ORDER BY amopstrategy",
14823 1074 : opfinfo->dobj.catId.oid,
14824 1074 : opfinfo->dobj.catId.oid);
14825 :
14826 1074 : res_ops = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14827 :
14828 1074 : resetPQExpBuffer(query);
14829 :
14830 1074 : appendPQExpBuffer(query, "SELECT amprocnum, "
14831 : "amproc::pg_catalog.regprocedure, "
14832 : "amproclefttype::pg_catalog.regtype, "
14833 : "amprocrighttype::pg_catalog.regtype "
14834 : "FROM pg_catalog.pg_amproc ap, pg_catalog.pg_depend "
14835 : "WHERE refclassid = 'pg_catalog.pg_opfamily'::pg_catalog.regclass "
14836 : "AND refobjid = '%u'::pg_catalog.oid "
14837 : "AND classid = 'pg_catalog.pg_amproc'::pg_catalog.regclass "
14838 : "AND objid = ap.oid "
14839 : "ORDER BY amprocnum",
14840 1074 : opfinfo->dobj.catId.oid);
14841 :
14842 1074 : res_procs = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14843 :
14844 : /* Get additional fields from the pg_opfamily row */
14845 1074 : resetPQExpBuffer(query);
14846 :
14847 1074 : appendPQExpBuffer(query, "SELECT "
14848 : "(SELECT amname FROM pg_catalog.pg_am WHERE oid = opfmethod) AS amname "
14849 : "FROM pg_catalog.pg_opfamily "
14850 : "WHERE oid = '%u'::pg_catalog.oid",
14851 1074 : opfinfo->dobj.catId.oid);
14852 :
14853 1074 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
14854 :
14855 1074 : i_amname = PQfnumber(res, "amname");
14856 :
14857 : /* amname will still be needed after we PQclear res */
14858 1074 : amname = pg_strdup(PQgetvalue(res, 0, i_amname));
14859 :
14860 1074 : appendPQExpBuffer(delq, "DROP OPERATOR FAMILY %s",
14861 1074 : fmtQualifiedDumpable(opfinfo));
14862 1074 : appendPQExpBuffer(delq, " USING %s;\n",
14863 : fmtId(amname));
14864 :
14865 : /* Build the fixed portion of the CREATE command */
14866 1074 : appendPQExpBuffer(q, "CREATE OPERATOR FAMILY %s",
14867 1074 : fmtQualifiedDumpable(opfinfo));
14868 1074 : appendPQExpBuffer(q, " USING %s;\n",
14869 : fmtId(amname));
14870 :
14871 1074 : PQclear(res);
14872 :
14873 : /* Do we need an ALTER to add loose members? */
14874 1074 : if (PQntuples(res_ops) > 0 || PQntuples(res_procs) > 0)
14875 : {
14876 94 : appendPQExpBuffer(q, "ALTER OPERATOR FAMILY %s",
14877 94 : fmtQualifiedDumpable(opfinfo));
14878 94 : appendPQExpBuffer(q, " USING %s ADD\n ",
14879 : fmtId(amname));
14880 :
14881 94 : needComma = false;
14882 :
14883 : /*
14884 : * Now fetch and print the OPERATOR entries (pg_amop rows).
14885 : */
14886 94 : ntups = PQntuples(res_ops);
14887 :
14888 94 : i_amopstrategy = PQfnumber(res_ops, "amopstrategy");
14889 94 : i_amopopr = PQfnumber(res_ops, "amopopr");
14890 94 : i_sortfamily = PQfnumber(res_ops, "sortfamily");
14891 94 : i_sortfamilynsp = PQfnumber(res_ops, "sortfamilynsp");
14892 :
14893 414 : for (i = 0; i < ntups; i++)
14894 : {
14895 320 : amopstrategy = PQgetvalue(res_ops, i, i_amopstrategy);
14896 320 : amopopr = PQgetvalue(res_ops, i, i_amopopr);
14897 320 : sortfamily = PQgetvalue(res_ops, i, i_sortfamily);
14898 320 : sortfamilynsp = PQgetvalue(res_ops, i, i_sortfamilynsp);
14899 :
14900 320 : if (needComma)
14901 256 : appendPQExpBufferStr(q, " ,\n ");
14902 :
14903 320 : appendPQExpBuffer(q, "OPERATOR %s %s",
14904 : amopstrategy, amopopr);
14905 :
14906 320 : if (strlen(sortfamily) > 0)
14907 : {
14908 0 : appendPQExpBufferStr(q, " FOR ORDER BY ");
14909 0 : appendPQExpBuffer(q, "%s.", fmtId(sortfamilynsp));
14910 0 : appendPQExpBufferStr(q, fmtId(sortfamily));
14911 : }
14912 :
14913 320 : needComma = true;
14914 : }
14915 :
14916 : /*
14917 : * Now fetch and print the FUNCTION entries (pg_amproc rows).
14918 : */
14919 94 : ntups = PQntuples(res_procs);
14920 :
14921 94 : i_amprocnum = PQfnumber(res_procs, "amprocnum");
14922 94 : i_amproc = PQfnumber(res_procs, "amproc");
14923 94 : i_amproclefttype = PQfnumber(res_procs, "amproclefttype");
14924 94 : i_amprocrighttype = PQfnumber(res_procs, "amprocrighttype");
14925 :
14926 444 : for (i = 0; i < ntups; i++)
14927 : {
14928 350 : amprocnum = PQgetvalue(res_procs, i, i_amprocnum);
14929 350 : amproc = PQgetvalue(res_procs, i, i_amproc);
14930 350 : amproclefttype = PQgetvalue(res_procs, i, i_amproclefttype);
14931 350 : amprocrighttype = PQgetvalue(res_procs, i, i_amprocrighttype);
14932 :
14933 350 : if (needComma)
14934 320 : appendPQExpBufferStr(q, " ,\n ");
14935 :
14936 350 : appendPQExpBuffer(q, "FUNCTION %s (%s, %s) %s",
14937 : amprocnum, amproclefttype, amprocrighttype,
14938 : amproc);
14939 :
14940 350 : needComma = true;
14941 : }
14942 :
14943 94 : appendPQExpBufferStr(q, ";\n");
14944 : }
14945 :
14946 1074 : appendPQExpBufferStr(nameusing, fmtId(opfinfo->dobj.name));
14947 1074 : appendPQExpBuffer(nameusing, " USING %s",
14948 : fmtId(amname));
14949 :
14950 1074 : if (dopt->binary_upgrade)
14951 18 : binary_upgrade_extension_member(q, &opfinfo->dobj,
14952 18 : "OPERATOR FAMILY", nameusing->data,
14953 18 : opfinfo->dobj.namespace->dobj.name);
14954 :
14955 1074 : if (opfinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
14956 1074 : ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId,
14957 1074 : ARCHIVE_OPTS(.tag = opfinfo->dobj.name,
14958 : .namespace = opfinfo->dobj.namespace->dobj.name,
14959 : .owner = opfinfo->rolname,
14960 : .description = "OPERATOR FAMILY",
14961 : .section = SECTION_PRE_DATA,
14962 : .createStmt = q->data,
14963 : .dropStmt = delq->data));
14964 :
14965 : /* Dump Operator Family Comments */
14966 1074 : if (opfinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
14967 0 : dumpComment(fout, "OPERATOR FAMILY", nameusing->data,
14968 0 : opfinfo->dobj.namespace->dobj.name, opfinfo->rolname,
14969 0 : opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId);
14970 :
14971 1074 : free(amname);
14972 1074 : PQclear(res_ops);
14973 1074 : PQclear(res_procs);
14974 1074 : destroyPQExpBuffer(query);
14975 1074 : destroyPQExpBuffer(q);
14976 1074 : destroyPQExpBuffer(delq);
14977 1074 : destroyPQExpBuffer(nameusing);
14978 : }
14979 :
14980 : /*
14981 : * dumpCollation
14982 : * write out a single collation definition
14983 : */
14984 : static void
14985 5074 : dumpCollation(Archive *fout, const CollInfo *collinfo)
14986 : {
14987 5074 : DumpOptions *dopt = fout->dopt;
14988 : PQExpBuffer query;
14989 : PQExpBuffer q;
14990 : PQExpBuffer delq;
14991 : char *qcollname;
14992 : PGresult *res;
14993 : int i_collprovider;
14994 : int i_collisdeterministic;
14995 : int i_collcollate;
14996 : int i_collctype;
14997 : int i_colllocale;
14998 : int i_collicurules;
14999 : const char *collprovider;
15000 : const char *collcollate;
15001 : const char *collctype;
15002 : const char *colllocale;
15003 : const char *collicurules;
15004 :
15005 : /* Do nothing if not dumping schema */
15006 5074 : if (!dopt->dumpSchema)
15007 24 : return;
15008 :
15009 5050 : query = createPQExpBuffer();
15010 5050 : q = createPQExpBuffer();
15011 5050 : delq = createPQExpBuffer();
15012 :
15013 5050 : qcollname = pg_strdup(fmtId(collinfo->dobj.name));
15014 :
15015 : /* Get collation-specific details */
15016 5050 : appendPQExpBufferStr(query, "SELECT ");
15017 :
15018 5050 : if (fout->remoteVersion >= 100000)
15019 5050 : appendPQExpBufferStr(query,
15020 : "collprovider, "
15021 : "collversion, ");
15022 : else
15023 0 : appendPQExpBufferStr(query,
15024 : "'c' AS collprovider, "
15025 : "NULL AS collversion, ");
15026 :
15027 5050 : if (fout->remoteVersion >= 120000)
15028 5050 : appendPQExpBufferStr(query,
15029 : "collisdeterministic, ");
15030 : else
15031 0 : appendPQExpBufferStr(query,
15032 : "true AS collisdeterministic, ");
15033 :
15034 5050 : if (fout->remoteVersion >= 170000)
15035 5050 : appendPQExpBufferStr(query,
15036 : "colllocale, ");
15037 0 : else if (fout->remoteVersion >= 150000)
15038 0 : appendPQExpBufferStr(query,
15039 : "colliculocale AS colllocale, ");
15040 : else
15041 0 : appendPQExpBufferStr(query,
15042 : "NULL AS colllocale, ");
15043 :
15044 5050 : if (fout->remoteVersion >= 160000)
15045 5050 : appendPQExpBufferStr(query,
15046 : "collicurules, ");
15047 : else
15048 0 : appendPQExpBufferStr(query,
15049 : "NULL AS collicurules, ");
15050 :
15051 5050 : appendPQExpBuffer(query,
15052 : "collcollate, "
15053 : "collctype "
15054 : "FROM pg_catalog.pg_collation c "
15055 : "WHERE c.oid = '%u'::pg_catalog.oid",
15056 5050 : collinfo->dobj.catId.oid);
15057 :
15058 5050 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15059 :
15060 5050 : i_collprovider = PQfnumber(res, "collprovider");
15061 5050 : i_collisdeterministic = PQfnumber(res, "collisdeterministic");
15062 5050 : i_collcollate = PQfnumber(res, "collcollate");
15063 5050 : i_collctype = PQfnumber(res, "collctype");
15064 5050 : i_colllocale = PQfnumber(res, "colllocale");
15065 5050 : i_collicurules = PQfnumber(res, "collicurules");
15066 :
15067 5050 : collprovider = PQgetvalue(res, 0, i_collprovider);
15068 :
15069 5050 : if (!PQgetisnull(res, 0, i_collcollate))
15070 92 : collcollate = PQgetvalue(res, 0, i_collcollate);
15071 : else
15072 4958 : collcollate = NULL;
15073 :
15074 5050 : if (!PQgetisnull(res, 0, i_collctype))
15075 92 : collctype = PQgetvalue(res, 0, i_collctype);
15076 : else
15077 4958 : collctype = NULL;
15078 :
15079 : /*
15080 : * Before version 15, collcollate and collctype were of type NAME and
15081 : * non-nullable. Treat empty strings as NULL for consistency.
15082 : */
15083 5050 : if (fout->remoteVersion < 150000)
15084 : {
15085 0 : if (collcollate[0] == '\0')
15086 0 : collcollate = NULL;
15087 0 : if (collctype[0] == '\0')
15088 0 : collctype = NULL;
15089 : }
15090 :
15091 5050 : if (!PQgetisnull(res, 0, i_colllocale))
15092 4952 : colllocale = PQgetvalue(res, 0, i_colllocale);
15093 : else
15094 98 : colllocale = NULL;
15095 :
15096 5050 : if (!PQgetisnull(res, 0, i_collicurules))
15097 0 : collicurules = PQgetvalue(res, 0, i_collicurules);
15098 : else
15099 5050 : collicurules = NULL;
15100 :
15101 5050 : appendPQExpBuffer(delq, "DROP COLLATION %s;\n",
15102 5050 : fmtQualifiedDumpable(collinfo));
15103 :
15104 5050 : appendPQExpBuffer(q, "CREATE COLLATION %s (",
15105 5050 : fmtQualifiedDumpable(collinfo));
15106 :
15107 5050 : appendPQExpBufferStr(q, "provider = ");
15108 5050 : if (collprovider[0] == 'b')
15109 38 : appendPQExpBufferStr(q, "builtin");
15110 5012 : else if (collprovider[0] == 'c')
15111 92 : appendPQExpBufferStr(q, "libc");
15112 4920 : else if (collprovider[0] == 'i')
15113 4914 : appendPQExpBufferStr(q, "icu");
15114 6 : else if (collprovider[0] == 'd')
15115 : /* to allow dumping pg_catalog; not accepted on input */
15116 6 : appendPQExpBufferStr(q, "default");
15117 : else
15118 0 : pg_fatal("unrecognized collation provider: %s",
15119 : collprovider);
15120 :
15121 5050 : if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
15122 0 : appendPQExpBufferStr(q, ", deterministic = false");
15123 :
15124 5050 : if (collprovider[0] == 'd')
15125 : {
15126 6 : if (collcollate || collctype || colllocale || collicurules)
15127 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15128 :
15129 : /* no locale -- the default collation cannot be reloaded anyway */
15130 : }
15131 5044 : else if (collprovider[0] == 'b')
15132 : {
15133 38 : if (collcollate || collctype || !colllocale || collicurules)
15134 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15135 :
15136 38 : appendPQExpBufferStr(q, ", locale = ");
15137 38 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15138 : fout);
15139 : }
15140 5006 : else if (collprovider[0] == 'i')
15141 : {
15142 4914 : if (fout->remoteVersion >= 150000)
15143 : {
15144 4914 : if (collcollate || collctype || !colllocale)
15145 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15146 :
15147 4914 : appendPQExpBufferStr(q, ", locale = ");
15148 4914 : appendStringLiteralAH(q, colllocale ? colllocale : "",
15149 : fout);
15150 : }
15151 : else
15152 : {
15153 0 : if (!collcollate || !collctype || colllocale ||
15154 0 : strcmp(collcollate, collctype) != 0)
15155 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15156 :
15157 0 : appendPQExpBufferStr(q, ", locale = ");
15158 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15159 : }
15160 :
15161 4914 : if (collicurules)
15162 : {
15163 0 : appendPQExpBufferStr(q, ", rules = ");
15164 0 : appendStringLiteralAH(q, collicurules ? collicurules : "", fout);
15165 : }
15166 : }
15167 92 : else if (collprovider[0] == 'c')
15168 : {
15169 92 : if (colllocale || collicurules || !collcollate || !collctype)
15170 0 : pg_log_warning("invalid collation \"%s\"", qcollname);
15171 :
15172 92 : if (collcollate && collctype && strcmp(collcollate, collctype) == 0)
15173 : {
15174 92 : appendPQExpBufferStr(q, ", locale = ");
15175 92 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15176 : }
15177 : else
15178 : {
15179 0 : appendPQExpBufferStr(q, ", lc_collate = ");
15180 0 : appendStringLiteralAH(q, collcollate ? collcollate : "", fout);
15181 0 : appendPQExpBufferStr(q, ", lc_ctype = ");
15182 0 : appendStringLiteralAH(q, collctype ? collctype : "", fout);
15183 : }
15184 : }
15185 : else
15186 0 : pg_fatal("unrecognized collation provider: %s", collprovider);
15187 :
15188 : /*
15189 : * For binary upgrade, carry over the collation version. For normal
15190 : * dump/restore, omit the version, so that it is computed upon restore.
15191 : */
15192 5050 : if (dopt->binary_upgrade)
15193 : {
15194 : int i_collversion;
15195 :
15196 10 : i_collversion = PQfnumber(res, "collversion");
15197 10 : if (!PQgetisnull(res, 0, i_collversion))
15198 : {
15199 8 : appendPQExpBufferStr(q, ", version = ");
15200 8 : appendStringLiteralAH(q,
15201 : PQgetvalue(res, 0, i_collversion),
15202 : fout);
15203 : }
15204 : }
15205 :
15206 5050 : appendPQExpBufferStr(q, ");\n");
15207 :
15208 5050 : if (dopt->binary_upgrade)
15209 10 : binary_upgrade_extension_member(q, &collinfo->dobj,
15210 : "COLLATION", qcollname,
15211 10 : collinfo->dobj.namespace->dobj.name);
15212 :
15213 5050 : if (collinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15214 5050 : ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId,
15215 5050 : ARCHIVE_OPTS(.tag = collinfo->dobj.name,
15216 : .namespace = collinfo->dobj.namespace->dobj.name,
15217 : .owner = collinfo->rolname,
15218 : .description = "COLLATION",
15219 : .section = SECTION_PRE_DATA,
15220 : .createStmt = q->data,
15221 : .dropStmt = delq->data));
15222 :
15223 : /* Dump Collation Comments */
15224 5050 : if (collinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15225 4862 : dumpComment(fout, "COLLATION", qcollname,
15226 4862 : collinfo->dobj.namespace->dobj.name, collinfo->rolname,
15227 4862 : collinfo->dobj.catId, 0, collinfo->dobj.dumpId);
15228 :
15229 5050 : PQclear(res);
15230 :
15231 5050 : destroyPQExpBuffer(query);
15232 5050 : destroyPQExpBuffer(q);
15233 5050 : destroyPQExpBuffer(delq);
15234 5050 : free(qcollname);
15235 : }
15236 :
15237 : /*
15238 : * dumpConversion
15239 : * write out a single conversion definition
15240 : */
15241 : static void
15242 844 : dumpConversion(Archive *fout, const ConvInfo *convinfo)
15243 : {
15244 844 : DumpOptions *dopt = fout->dopt;
15245 : PQExpBuffer query;
15246 : PQExpBuffer q;
15247 : PQExpBuffer delq;
15248 : char *qconvname;
15249 : PGresult *res;
15250 : int i_conforencoding;
15251 : int i_contoencoding;
15252 : int i_conproc;
15253 : int i_condefault;
15254 : const char *conforencoding;
15255 : const char *contoencoding;
15256 : const char *conproc;
15257 : bool condefault;
15258 :
15259 : /* Do nothing if not dumping schema */
15260 844 : if (!dopt->dumpSchema)
15261 12 : return;
15262 :
15263 832 : query = createPQExpBuffer();
15264 832 : q = createPQExpBuffer();
15265 832 : delq = createPQExpBuffer();
15266 :
15267 832 : qconvname = pg_strdup(fmtId(convinfo->dobj.name));
15268 :
15269 : /* Get conversion-specific details */
15270 832 : appendPQExpBuffer(query, "SELECT "
15271 : "pg_catalog.pg_encoding_to_char(conforencoding) AS conforencoding, "
15272 : "pg_catalog.pg_encoding_to_char(contoencoding) AS contoencoding, "
15273 : "conproc, condefault "
15274 : "FROM pg_catalog.pg_conversion c "
15275 : "WHERE c.oid = '%u'::pg_catalog.oid",
15276 832 : convinfo->dobj.catId.oid);
15277 :
15278 832 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15279 :
15280 832 : i_conforencoding = PQfnumber(res, "conforencoding");
15281 832 : i_contoencoding = PQfnumber(res, "contoencoding");
15282 832 : i_conproc = PQfnumber(res, "conproc");
15283 832 : i_condefault = PQfnumber(res, "condefault");
15284 :
15285 832 : conforencoding = PQgetvalue(res, 0, i_conforencoding);
15286 832 : contoencoding = PQgetvalue(res, 0, i_contoencoding);
15287 832 : conproc = PQgetvalue(res, 0, i_conproc);
15288 832 : condefault = (PQgetvalue(res, 0, i_condefault)[0] == 't');
15289 :
15290 832 : appendPQExpBuffer(delq, "DROP CONVERSION %s;\n",
15291 832 : fmtQualifiedDumpable(convinfo));
15292 :
15293 832 : appendPQExpBuffer(q, "CREATE %sCONVERSION %s FOR ",
15294 : (condefault) ? "DEFAULT " : "",
15295 832 : fmtQualifiedDumpable(convinfo));
15296 832 : appendStringLiteralAH(q, conforencoding, fout);
15297 832 : appendPQExpBufferStr(q, " TO ");
15298 832 : appendStringLiteralAH(q, contoencoding, fout);
15299 : /* regproc output is already sufficiently quoted */
15300 832 : appendPQExpBuffer(q, " FROM %s;\n", conproc);
15301 :
15302 832 : if (dopt->binary_upgrade)
15303 2 : binary_upgrade_extension_member(q, &convinfo->dobj,
15304 : "CONVERSION", qconvname,
15305 2 : convinfo->dobj.namespace->dobj.name);
15306 :
15307 832 : if (convinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15308 832 : ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId,
15309 832 : ARCHIVE_OPTS(.tag = convinfo->dobj.name,
15310 : .namespace = convinfo->dobj.namespace->dobj.name,
15311 : .owner = convinfo->rolname,
15312 : .description = "CONVERSION",
15313 : .section = SECTION_PRE_DATA,
15314 : .createStmt = q->data,
15315 : .dropStmt = delq->data));
15316 :
15317 : /* Dump Conversion Comments */
15318 832 : if (convinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15319 832 : dumpComment(fout, "CONVERSION", qconvname,
15320 832 : convinfo->dobj.namespace->dobj.name, convinfo->rolname,
15321 832 : convinfo->dobj.catId, 0, convinfo->dobj.dumpId);
15322 :
15323 832 : PQclear(res);
15324 :
15325 832 : destroyPQExpBuffer(query);
15326 832 : destroyPQExpBuffer(q);
15327 832 : destroyPQExpBuffer(delq);
15328 832 : free(qconvname);
15329 : }
15330 :
15331 : /*
15332 : * format_aggregate_signature: generate aggregate name and argument list
15333 : *
15334 : * The argument type names are qualified if needed. The aggregate name
15335 : * is never qualified.
15336 : */
15337 : static char *
15338 570 : format_aggregate_signature(const AggInfo *agginfo, Archive *fout, bool honor_quotes)
15339 : {
15340 : PQExpBufferData buf;
15341 : int j;
15342 :
15343 570 : initPQExpBuffer(&buf);
15344 570 : if (honor_quotes)
15345 0 : appendPQExpBufferStr(&buf, fmtId(agginfo->aggfn.dobj.name));
15346 : else
15347 570 : appendPQExpBufferStr(&buf, agginfo->aggfn.dobj.name);
15348 :
15349 570 : if (agginfo->aggfn.nargs == 0)
15350 80 : appendPQExpBufferStr(&buf, "(*)");
15351 : else
15352 : {
15353 490 : appendPQExpBufferChar(&buf, '(');
15354 1070 : for (j = 0; j < agginfo->aggfn.nargs; j++)
15355 580 : appendPQExpBuffer(&buf, "%s%s",
15356 : (j > 0) ? ", " : "",
15357 : getFormattedTypeName(fout,
15358 580 : agginfo->aggfn.argtypes[j],
15359 : zeroIsError));
15360 490 : appendPQExpBufferChar(&buf, ')');
15361 : }
15362 570 : return buf.data;
15363 : }
15364 :
15365 : /*
15366 : * dumpAgg
15367 : * write out a single aggregate definition
15368 : */
15369 : static void
15370 584 : dumpAgg(Archive *fout, const AggInfo *agginfo)
15371 : {
15372 584 : DumpOptions *dopt = fout->dopt;
15373 : PQExpBuffer query;
15374 : PQExpBuffer q;
15375 : PQExpBuffer delq;
15376 : PQExpBuffer details;
15377 : char *aggsig; /* identity signature */
15378 584 : char *aggfullsig = NULL; /* full signature */
15379 : char *aggsig_tag;
15380 : PGresult *res;
15381 : int i_agginitval;
15382 : int i_aggminitval;
15383 : const char *aggtransfn;
15384 : const char *aggfinalfn;
15385 : const char *aggcombinefn;
15386 : const char *aggserialfn;
15387 : const char *aggdeserialfn;
15388 : const char *aggmtransfn;
15389 : const char *aggminvtransfn;
15390 : const char *aggmfinalfn;
15391 : bool aggfinalextra;
15392 : bool aggmfinalextra;
15393 : char aggfinalmodify;
15394 : char aggmfinalmodify;
15395 : const char *aggsortop;
15396 : char *aggsortconvop;
15397 : char aggkind;
15398 : const char *aggtranstype;
15399 : const char *aggtransspace;
15400 : const char *aggmtranstype;
15401 : const char *aggmtransspace;
15402 : const char *agginitval;
15403 : const char *aggminitval;
15404 : const char *proparallel;
15405 : char defaultfinalmodify;
15406 :
15407 : /* Do nothing if not dumping schema */
15408 584 : if (!dopt->dumpSchema)
15409 14 : return;
15410 :
15411 570 : query = createPQExpBuffer();
15412 570 : q = createPQExpBuffer();
15413 570 : delq = createPQExpBuffer();
15414 570 : details = createPQExpBuffer();
15415 :
15416 570 : if (!fout->is_prepared[PREPQUERY_DUMPAGG])
15417 : {
15418 : /* Set up query for aggregate-specific details */
15419 110 : appendPQExpBufferStr(query,
15420 : "PREPARE dumpAgg(pg_catalog.oid) AS\n");
15421 :
15422 110 : appendPQExpBufferStr(query,
15423 : "SELECT "
15424 : "aggtransfn,\n"
15425 : "aggfinalfn,\n"
15426 : "aggtranstype::pg_catalog.regtype,\n"
15427 : "agginitval,\n"
15428 : "aggsortop,\n"
15429 : "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
15430 : "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
15431 :
15432 110 : if (fout->remoteVersion >= 90400)
15433 110 : appendPQExpBufferStr(query,
15434 : "aggkind,\n"
15435 : "aggmtransfn,\n"
15436 : "aggminvtransfn,\n"
15437 : "aggmfinalfn,\n"
15438 : "aggmtranstype::pg_catalog.regtype,\n"
15439 : "aggfinalextra,\n"
15440 : "aggmfinalextra,\n"
15441 : "aggtransspace,\n"
15442 : "aggmtransspace,\n"
15443 : "aggminitval,\n");
15444 : else
15445 0 : appendPQExpBufferStr(query,
15446 : "'n' AS aggkind,\n"
15447 : "'-' AS aggmtransfn,\n"
15448 : "'-' AS aggminvtransfn,\n"
15449 : "'-' AS aggmfinalfn,\n"
15450 : "0 AS aggmtranstype,\n"
15451 : "false AS aggfinalextra,\n"
15452 : "false AS aggmfinalextra,\n"
15453 : "0 AS aggtransspace,\n"
15454 : "0 AS aggmtransspace,\n"
15455 : "NULL AS aggminitval,\n");
15456 :
15457 110 : if (fout->remoteVersion >= 90600)
15458 110 : appendPQExpBufferStr(query,
15459 : "aggcombinefn,\n"
15460 : "aggserialfn,\n"
15461 : "aggdeserialfn,\n"
15462 : "proparallel,\n");
15463 : else
15464 0 : appendPQExpBufferStr(query,
15465 : "'-' AS aggcombinefn,\n"
15466 : "'-' AS aggserialfn,\n"
15467 : "'-' AS aggdeserialfn,\n"
15468 : "'u' AS proparallel,\n");
15469 :
15470 110 : if (fout->remoteVersion >= 110000)
15471 110 : appendPQExpBufferStr(query,
15472 : "aggfinalmodify,\n"
15473 : "aggmfinalmodify\n");
15474 : else
15475 0 : appendPQExpBufferStr(query,
15476 : "'0' AS aggfinalmodify,\n"
15477 : "'0' AS aggmfinalmodify\n");
15478 :
15479 110 : appendPQExpBufferStr(query,
15480 : "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
15481 : "WHERE a.aggfnoid = p.oid "
15482 : "AND p.oid = $1");
15483 :
15484 110 : ExecuteSqlStatement(fout, query->data);
15485 :
15486 110 : fout->is_prepared[PREPQUERY_DUMPAGG] = true;
15487 : }
15488 :
15489 570 : printfPQExpBuffer(query,
15490 : "EXECUTE dumpAgg('%u')",
15491 570 : agginfo->aggfn.dobj.catId.oid);
15492 :
15493 570 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15494 :
15495 570 : i_agginitval = PQfnumber(res, "agginitval");
15496 570 : i_aggminitval = PQfnumber(res, "aggminitval");
15497 :
15498 570 : aggtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggtransfn"));
15499 570 : aggfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggfinalfn"));
15500 570 : aggcombinefn = PQgetvalue(res, 0, PQfnumber(res, "aggcombinefn"));
15501 570 : aggserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggserialfn"));
15502 570 : aggdeserialfn = PQgetvalue(res, 0, PQfnumber(res, "aggdeserialfn"));
15503 570 : aggmtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggmtransfn"));
15504 570 : aggminvtransfn = PQgetvalue(res, 0, PQfnumber(res, "aggminvtransfn"));
15505 570 : aggmfinalfn = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalfn"));
15506 570 : aggfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggfinalextra"))[0] == 't');
15507 570 : aggmfinalextra = (PQgetvalue(res, 0, PQfnumber(res, "aggmfinalextra"))[0] == 't');
15508 570 : aggfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggfinalmodify"))[0];
15509 570 : aggmfinalmodify = PQgetvalue(res, 0, PQfnumber(res, "aggmfinalmodify"))[0];
15510 570 : aggsortop = PQgetvalue(res, 0, PQfnumber(res, "aggsortop"));
15511 570 : aggkind = PQgetvalue(res, 0, PQfnumber(res, "aggkind"))[0];
15512 570 : aggtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggtranstype"));
15513 570 : aggtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggtransspace"));
15514 570 : aggmtranstype = PQgetvalue(res, 0, PQfnumber(res, "aggmtranstype"));
15515 570 : aggmtransspace = PQgetvalue(res, 0, PQfnumber(res, "aggmtransspace"));
15516 570 : agginitval = PQgetvalue(res, 0, i_agginitval);
15517 570 : aggminitval = PQgetvalue(res, 0, i_aggminitval);
15518 570 : proparallel = PQgetvalue(res, 0, PQfnumber(res, "proparallel"));
15519 :
15520 : {
15521 : char *funcargs;
15522 : char *funciargs;
15523 :
15524 570 : funcargs = PQgetvalue(res, 0, PQfnumber(res, "funcargs"));
15525 570 : funciargs = PQgetvalue(res, 0, PQfnumber(res, "funciargs"));
15526 570 : aggfullsig = format_function_arguments(&agginfo->aggfn, funcargs, true);
15527 570 : aggsig = format_function_arguments(&agginfo->aggfn, funciargs, true);
15528 : }
15529 :
15530 570 : aggsig_tag = format_aggregate_signature(agginfo, fout, false);
15531 :
15532 : /* identify default modify flag for aggkind (must match DefineAggregate) */
15533 570 : defaultfinalmodify = (aggkind == AGGKIND_NORMAL) ? AGGMODIFY_READ_ONLY : AGGMODIFY_READ_WRITE;
15534 : /* replace omitted flags for old versions */
15535 570 : if (aggfinalmodify == '0')
15536 0 : aggfinalmodify = defaultfinalmodify;
15537 570 : if (aggmfinalmodify == '0')
15538 0 : aggmfinalmodify = defaultfinalmodify;
15539 :
15540 : /* regproc and regtype output is already sufficiently quoted */
15541 570 : appendPQExpBuffer(details, " SFUNC = %s,\n STYPE = %s",
15542 : aggtransfn, aggtranstype);
15543 :
15544 570 : if (strcmp(aggtransspace, "0") != 0)
15545 : {
15546 10 : appendPQExpBuffer(details, ",\n SSPACE = %s",
15547 : aggtransspace);
15548 : }
15549 :
15550 570 : if (!PQgetisnull(res, 0, i_agginitval))
15551 : {
15552 414 : appendPQExpBufferStr(details, ",\n INITCOND = ");
15553 414 : appendStringLiteralAH(details, agginitval, fout);
15554 : }
15555 :
15556 570 : if (strcmp(aggfinalfn, "-") != 0)
15557 : {
15558 264 : appendPQExpBuffer(details, ",\n FINALFUNC = %s",
15559 : aggfinalfn);
15560 264 : if (aggfinalextra)
15561 20 : appendPQExpBufferStr(details, ",\n FINALFUNC_EXTRA");
15562 264 : if (aggfinalmodify != defaultfinalmodify)
15563 : {
15564 64 : switch (aggfinalmodify)
15565 : {
15566 0 : case AGGMODIFY_READ_ONLY:
15567 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_ONLY");
15568 0 : break;
15569 64 : case AGGMODIFY_SHAREABLE:
15570 64 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = SHAREABLE");
15571 64 : break;
15572 0 : case AGGMODIFY_READ_WRITE:
15573 0 : appendPQExpBufferStr(details, ",\n FINALFUNC_MODIFY = READ_WRITE");
15574 0 : break;
15575 0 : default:
15576 0 : pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
15577 : agginfo->aggfn.dobj.name);
15578 : break;
15579 : }
15580 : }
15581 : }
15582 :
15583 570 : if (strcmp(aggcombinefn, "-") != 0)
15584 0 : appendPQExpBuffer(details, ",\n COMBINEFUNC = %s", aggcombinefn);
15585 :
15586 570 : if (strcmp(aggserialfn, "-") != 0)
15587 0 : appendPQExpBuffer(details, ",\n SERIALFUNC = %s", aggserialfn);
15588 :
15589 570 : if (strcmp(aggdeserialfn, "-") != 0)
15590 0 : appendPQExpBuffer(details, ",\n DESERIALFUNC = %s", aggdeserialfn);
15591 :
15592 570 : if (strcmp(aggmtransfn, "-") != 0)
15593 : {
15594 60 : appendPQExpBuffer(details, ",\n MSFUNC = %s,\n MINVFUNC = %s,\n MSTYPE = %s",
15595 : aggmtransfn,
15596 : aggminvtransfn,
15597 : aggmtranstype);
15598 : }
15599 :
15600 570 : if (strcmp(aggmtransspace, "0") != 0)
15601 : {
15602 0 : appendPQExpBuffer(details, ",\n MSSPACE = %s",
15603 : aggmtransspace);
15604 : }
15605 :
15606 570 : if (!PQgetisnull(res, 0, i_aggminitval))
15607 : {
15608 20 : appendPQExpBufferStr(details, ",\n MINITCOND = ");
15609 20 : appendStringLiteralAH(details, aggminitval, fout);
15610 : }
15611 :
15612 570 : if (strcmp(aggmfinalfn, "-") != 0)
15613 : {
15614 0 : appendPQExpBuffer(details, ",\n MFINALFUNC = %s",
15615 : aggmfinalfn);
15616 0 : if (aggmfinalextra)
15617 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_EXTRA");
15618 0 : if (aggmfinalmodify != defaultfinalmodify)
15619 : {
15620 0 : switch (aggmfinalmodify)
15621 : {
15622 0 : case AGGMODIFY_READ_ONLY:
15623 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_ONLY");
15624 0 : break;
15625 0 : case AGGMODIFY_SHAREABLE:
15626 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = SHAREABLE");
15627 0 : break;
15628 0 : case AGGMODIFY_READ_WRITE:
15629 0 : appendPQExpBufferStr(details, ",\n MFINALFUNC_MODIFY = READ_WRITE");
15630 0 : break;
15631 0 : default:
15632 0 : pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
15633 : agginfo->aggfn.dobj.name);
15634 : break;
15635 : }
15636 : }
15637 : }
15638 :
15639 570 : aggsortconvop = getFormattedOperatorName(aggsortop);
15640 570 : if (aggsortconvop)
15641 : {
15642 0 : appendPQExpBuffer(details, ",\n SORTOP = %s",
15643 : aggsortconvop);
15644 0 : free(aggsortconvop);
15645 : }
15646 :
15647 570 : if (aggkind == AGGKIND_HYPOTHETICAL)
15648 10 : appendPQExpBufferStr(details, ",\n HYPOTHETICAL");
15649 :
15650 570 : if (proparallel[0] != PROPARALLEL_UNSAFE)
15651 : {
15652 10 : if (proparallel[0] == PROPARALLEL_SAFE)
15653 10 : appendPQExpBufferStr(details, ",\n PARALLEL = safe");
15654 0 : else if (proparallel[0] == PROPARALLEL_RESTRICTED)
15655 0 : appendPQExpBufferStr(details, ",\n PARALLEL = restricted");
15656 0 : else if (proparallel[0] != PROPARALLEL_UNSAFE)
15657 0 : pg_fatal("unrecognized proparallel value for function \"%s\"",
15658 : agginfo->aggfn.dobj.name);
15659 : }
15660 :
15661 570 : appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
15662 570 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15663 : aggsig);
15664 :
15665 1140 : appendPQExpBuffer(q, "CREATE AGGREGATE %s.%s (\n%s\n);\n",
15666 570 : fmtId(agginfo->aggfn.dobj.namespace->dobj.name),
15667 : aggfullsig ? aggfullsig : aggsig, details->data);
15668 :
15669 570 : if (dopt->binary_upgrade)
15670 98 : binary_upgrade_extension_member(q, &agginfo->aggfn.dobj,
15671 : "AGGREGATE", aggsig,
15672 98 : agginfo->aggfn.dobj.namespace->dobj.name);
15673 :
15674 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_DEFINITION)
15675 536 : ArchiveEntry(fout, agginfo->aggfn.dobj.catId,
15676 536 : agginfo->aggfn.dobj.dumpId,
15677 536 : ARCHIVE_OPTS(.tag = aggsig_tag,
15678 : .namespace = agginfo->aggfn.dobj.namespace->dobj.name,
15679 : .owner = agginfo->aggfn.rolname,
15680 : .description = "AGGREGATE",
15681 : .section = SECTION_PRE_DATA,
15682 : .createStmt = q->data,
15683 : .dropStmt = delq->data));
15684 :
15685 : /* Dump Aggregate Comments */
15686 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_COMMENT)
15687 20 : dumpComment(fout, "AGGREGATE", aggsig,
15688 20 : agginfo->aggfn.dobj.namespace->dobj.name,
15689 20 : agginfo->aggfn.rolname,
15690 20 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15691 :
15692 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_SECLABEL)
15693 0 : dumpSecLabel(fout, "AGGREGATE", aggsig,
15694 0 : agginfo->aggfn.dobj.namespace->dobj.name,
15695 0 : agginfo->aggfn.rolname,
15696 0 : agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
15697 :
15698 : /*
15699 : * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
15700 : * command look like a function's GRANT; in particular this affects the
15701 : * syntax for zero-argument aggregates and ordered-set aggregates.
15702 : */
15703 570 : free(aggsig);
15704 :
15705 570 : aggsig = format_function_signature(fout, &agginfo->aggfn, true);
15706 :
15707 570 : if (agginfo->aggfn.dobj.dump & DUMP_COMPONENT_ACL)
15708 36 : dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
15709 : "FUNCTION", aggsig, NULL,
15710 36 : agginfo->aggfn.dobj.namespace->dobj.name,
15711 36 : NULL, agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
15712 :
15713 570 : free(aggsig);
15714 570 : free(aggfullsig);
15715 570 : free(aggsig_tag);
15716 :
15717 570 : PQclear(res);
15718 :
15719 570 : destroyPQExpBuffer(query);
15720 570 : destroyPQExpBuffer(q);
15721 570 : destroyPQExpBuffer(delq);
15722 570 : destroyPQExpBuffer(details);
15723 : }
15724 :
15725 : /*
15726 : * dumpTSParser
15727 : * write out a single text search parser
15728 : */
15729 : static void
15730 82 : dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
15731 : {
15732 82 : DumpOptions *dopt = fout->dopt;
15733 : PQExpBuffer q;
15734 : PQExpBuffer delq;
15735 : char *qprsname;
15736 :
15737 : /* Do nothing if not dumping schema */
15738 82 : if (!dopt->dumpSchema)
15739 12 : return;
15740 :
15741 70 : q = createPQExpBuffer();
15742 70 : delq = createPQExpBuffer();
15743 :
15744 70 : qprsname = pg_strdup(fmtId(prsinfo->dobj.name));
15745 :
15746 70 : appendPQExpBuffer(q, "CREATE TEXT SEARCH PARSER %s (\n",
15747 70 : fmtQualifiedDumpable(prsinfo));
15748 :
15749 70 : appendPQExpBuffer(q, " START = %s,\n",
15750 70 : convertTSFunction(fout, prsinfo->prsstart));
15751 70 : appendPQExpBuffer(q, " GETTOKEN = %s,\n",
15752 70 : convertTSFunction(fout, prsinfo->prstoken));
15753 70 : appendPQExpBuffer(q, " END = %s,\n",
15754 70 : convertTSFunction(fout, prsinfo->prsend));
15755 70 : if (prsinfo->prsheadline != InvalidOid)
15756 6 : appendPQExpBuffer(q, " HEADLINE = %s,\n",
15757 6 : convertTSFunction(fout, prsinfo->prsheadline));
15758 70 : appendPQExpBuffer(q, " LEXTYPES = %s );\n",
15759 70 : convertTSFunction(fout, prsinfo->prslextype));
15760 :
15761 70 : appendPQExpBuffer(delq, "DROP TEXT SEARCH PARSER %s;\n",
15762 70 : fmtQualifiedDumpable(prsinfo));
15763 :
15764 70 : if (dopt->binary_upgrade)
15765 2 : binary_upgrade_extension_member(q, &prsinfo->dobj,
15766 : "TEXT SEARCH PARSER", qprsname,
15767 2 : prsinfo->dobj.namespace->dobj.name);
15768 :
15769 70 : if (prsinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15770 70 : ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId,
15771 70 : ARCHIVE_OPTS(.tag = prsinfo->dobj.name,
15772 : .namespace = prsinfo->dobj.namespace->dobj.name,
15773 : .description = "TEXT SEARCH PARSER",
15774 : .section = SECTION_PRE_DATA,
15775 : .createStmt = q->data,
15776 : .dropStmt = delq->data));
15777 :
15778 : /* Dump Parser Comments */
15779 70 : if (prsinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15780 70 : dumpComment(fout, "TEXT SEARCH PARSER", qprsname,
15781 70 : prsinfo->dobj.namespace->dobj.name, "",
15782 70 : prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId);
15783 :
15784 70 : destroyPQExpBuffer(q);
15785 70 : destroyPQExpBuffer(delq);
15786 70 : free(qprsname);
15787 : }
15788 :
15789 : /*
15790 : * dumpTSDictionary
15791 : * write out a single text search dictionary
15792 : */
15793 : static void
15794 346 : dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
15795 : {
15796 346 : DumpOptions *dopt = fout->dopt;
15797 : PQExpBuffer q;
15798 : PQExpBuffer delq;
15799 : PQExpBuffer query;
15800 : char *qdictname;
15801 : PGresult *res;
15802 : char *nspname;
15803 : char *tmplname;
15804 :
15805 : /* Do nothing if not dumping schema */
15806 346 : if (!dopt->dumpSchema)
15807 12 : return;
15808 :
15809 334 : q = createPQExpBuffer();
15810 334 : delq = createPQExpBuffer();
15811 334 : query = createPQExpBuffer();
15812 :
15813 334 : qdictname = pg_strdup(fmtId(dictinfo->dobj.name));
15814 :
15815 : /* Fetch name and namespace of the dictionary's template */
15816 334 : appendPQExpBuffer(query, "SELECT nspname, tmplname "
15817 : "FROM pg_ts_template p, pg_namespace n "
15818 : "WHERE p.oid = '%u' AND n.oid = tmplnamespace",
15819 334 : dictinfo->dicttemplate);
15820 334 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15821 334 : nspname = PQgetvalue(res, 0, 0);
15822 334 : tmplname = PQgetvalue(res, 0, 1);
15823 :
15824 334 : appendPQExpBuffer(q, "CREATE TEXT SEARCH DICTIONARY %s (\n",
15825 334 : fmtQualifiedDumpable(dictinfo));
15826 :
15827 334 : appendPQExpBufferStr(q, " TEMPLATE = ");
15828 334 : appendPQExpBuffer(q, "%s.", fmtId(nspname));
15829 334 : appendPQExpBufferStr(q, fmtId(tmplname));
15830 :
15831 334 : PQclear(res);
15832 :
15833 : /* the dictinitoption can be dumped straight into the command */
15834 334 : if (dictinfo->dictinitoption)
15835 264 : appendPQExpBuffer(q, ",\n %s", dictinfo->dictinitoption);
15836 :
15837 334 : appendPQExpBufferStr(q, " );\n");
15838 :
15839 334 : appendPQExpBuffer(delq, "DROP TEXT SEARCH DICTIONARY %s;\n",
15840 334 : fmtQualifiedDumpable(dictinfo));
15841 :
15842 334 : if (dopt->binary_upgrade)
15843 20 : binary_upgrade_extension_member(q, &dictinfo->dobj,
15844 : "TEXT SEARCH DICTIONARY", qdictname,
15845 20 : dictinfo->dobj.namespace->dobj.name);
15846 :
15847 334 : if (dictinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15848 334 : ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId,
15849 334 : ARCHIVE_OPTS(.tag = dictinfo->dobj.name,
15850 : .namespace = dictinfo->dobj.namespace->dobj.name,
15851 : .owner = dictinfo->rolname,
15852 : .description = "TEXT SEARCH DICTIONARY",
15853 : .section = SECTION_PRE_DATA,
15854 : .createStmt = q->data,
15855 : .dropStmt = delq->data));
15856 :
15857 : /* Dump Dictionary Comments */
15858 334 : if (dictinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15859 244 : dumpComment(fout, "TEXT SEARCH DICTIONARY", qdictname,
15860 244 : dictinfo->dobj.namespace->dobj.name, dictinfo->rolname,
15861 244 : dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId);
15862 :
15863 334 : destroyPQExpBuffer(q);
15864 334 : destroyPQExpBuffer(delq);
15865 334 : destroyPQExpBuffer(query);
15866 334 : free(qdictname);
15867 : }
15868 :
15869 : /*
15870 : * dumpTSTemplate
15871 : * write out a single text search template
15872 : */
15873 : static void
15874 106 : dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
15875 : {
15876 106 : DumpOptions *dopt = fout->dopt;
15877 : PQExpBuffer q;
15878 : PQExpBuffer delq;
15879 : char *qtmplname;
15880 :
15881 : /* Do nothing if not dumping schema */
15882 106 : if (!dopt->dumpSchema)
15883 12 : return;
15884 :
15885 94 : q = createPQExpBuffer();
15886 94 : delq = createPQExpBuffer();
15887 :
15888 94 : qtmplname = pg_strdup(fmtId(tmplinfo->dobj.name));
15889 :
15890 94 : appendPQExpBuffer(q, "CREATE TEXT SEARCH TEMPLATE %s (\n",
15891 94 : fmtQualifiedDumpable(tmplinfo));
15892 :
15893 94 : if (tmplinfo->tmplinit != InvalidOid)
15894 30 : appendPQExpBuffer(q, " INIT = %s,\n",
15895 30 : convertTSFunction(fout, tmplinfo->tmplinit));
15896 94 : appendPQExpBuffer(q, " LEXIZE = %s );\n",
15897 94 : convertTSFunction(fout, tmplinfo->tmpllexize));
15898 :
15899 94 : appendPQExpBuffer(delq, "DROP TEXT SEARCH TEMPLATE %s;\n",
15900 94 : fmtQualifiedDumpable(tmplinfo));
15901 :
15902 94 : if (dopt->binary_upgrade)
15903 2 : binary_upgrade_extension_member(q, &tmplinfo->dobj,
15904 : "TEXT SEARCH TEMPLATE", qtmplname,
15905 2 : tmplinfo->dobj.namespace->dobj.name);
15906 :
15907 94 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
15908 94 : ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId,
15909 94 : ARCHIVE_OPTS(.tag = tmplinfo->dobj.name,
15910 : .namespace = tmplinfo->dobj.namespace->dobj.name,
15911 : .description = "TEXT SEARCH TEMPLATE",
15912 : .section = SECTION_PRE_DATA,
15913 : .createStmt = q->data,
15914 : .dropStmt = delq->data));
15915 :
15916 : /* Dump Template Comments */
15917 94 : if (tmplinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
15918 94 : dumpComment(fout, "TEXT SEARCH TEMPLATE", qtmplname,
15919 94 : tmplinfo->dobj.namespace->dobj.name, "",
15920 94 : tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId);
15921 :
15922 94 : destroyPQExpBuffer(q);
15923 94 : destroyPQExpBuffer(delq);
15924 94 : free(qtmplname);
15925 : }
15926 :
15927 : /*
15928 : * dumpTSConfig
15929 : * write out a single text search configuration
15930 : */
15931 : static void
15932 296 : dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
15933 : {
15934 296 : DumpOptions *dopt = fout->dopt;
15935 : PQExpBuffer q;
15936 : PQExpBuffer delq;
15937 : PQExpBuffer query;
15938 : char *qcfgname;
15939 : PGresult *res;
15940 : char *nspname;
15941 : char *prsname;
15942 : int ntups,
15943 : i;
15944 : int i_tokenname;
15945 : int i_dictname;
15946 :
15947 : /* Do nothing if not dumping schema */
15948 296 : if (!dopt->dumpSchema)
15949 12 : return;
15950 :
15951 284 : q = createPQExpBuffer();
15952 284 : delq = createPQExpBuffer();
15953 284 : query = createPQExpBuffer();
15954 :
15955 284 : qcfgname = pg_strdup(fmtId(cfginfo->dobj.name));
15956 :
15957 : /* Fetch name and namespace of the config's parser */
15958 284 : appendPQExpBuffer(query, "SELECT nspname, prsname "
15959 : "FROM pg_ts_parser p, pg_namespace n "
15960 : "WHERE p.oid = '%u' AND n.oid = prsnamespace",
15961 284 : cfginfo->cfgparser);
15962 284 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
15963 284 : nspname = PQgetvalue(res, 0, 0);
15964 284 : prsname = PQgetvalue(res, 0, 1);
15965 :
15966 284 : appendPQExpBuffer(q, "CREATE TEXT SEARCH CONFIGURATION %s (\n",
15967 284 : fmtQualifiedDumpable(cfginfo));
15968 :
15969 284 : appendPQExpBuffer(q, " PARSER = %s.", fmtId(nspname));
15970 284 : appendPQExpBuffer(q, "%s );\n", fmtId(prsname));
15971 :
15972 284 : PQclear(res);
15973 :
15974 284 : resetPQExpBuffer(query);
15975 284 : appendPQExpBuffer(query,
15976 : "SELECT\n"
15977 : " ( SELECT alias FROM pg_catalog.ts_token_type('%u'::pg_catalog.oid) AS t\n"
15978 : " WHERE t.tokid = m.maptokentype ) AS tokenname,\n"
15979 : " m.mapdict::pg_catalog.regdictionary AS dictname\n"
15980 : "FROM pg_catalog.pg_ts_config_map AS m\n"
15981 : "WHERE m.mapcfg = '%u'\n"
15982 : "ORDER BY m.mapcfg, m.maptokentype, m.mapseqno",
15983 284 : cfginfo->cfgparser, cfginfo->dobj.catId.oid);
15984 :
15985 284 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15986 284 : ntups = PQntuples(res);
15987 :
15988 284 : i_tokenname = PQfnumber(res, "tokenname");
15989 284 : i_dictname = PQfnumber(res, "dictname");
15990 :
15991 5950 : for (i = 0; i < ntups; i++)
15992 : {
15993 5666 : char *tokenname = PQgetvalue(res, i, i_tokenname);
15994 5666 : char *dictname = PQgetvalue(res, i, i_dictname);
15995 :
15996 5666 : if (i == 0 ||
15997 5382 : strcmp(tokenname, PQgetvalue(res, i - 1, i_tokenname)) != 0)
15998 : {
15999 : /* starting a new token type, so start a new command */
16000 5396 : if (i > 0)
16001 5112 : appendPQExpBufferStr(q, ";\n");
16002 5396 : appendPQExpBuffer(q, "\nALTER TEXT SEARCH CONFIGURATION %s\n",
16003 5396 : fmtQualifiedDumpable(cfginfo));
16004 : /* tokenname needs quoting, dictname does NOT */
16005 5396 : appendPQExpBuffer(q, " ADD MAPPING FOR %s WITH %s",
16006 : fmtId(tokenname), dictname);
16007 : }
16008 : else
16009 270 : appendPQExpBuffer(q, ", %s", dictname);
16010 : }
16011 :
16012 284 : if (ntups > 0)
16013 284 : appendPQExpBufferStr(q, ";\n");
16014 :
16015 284 : PQclear(res);
16016 :
16017 284 : appendPQExpBuffer(delq, "DROP TEXT SEARCH CONFIGURATION %s;\n",
16018 284 : fmtQualifiedDumpable(cfginfo));
16019 :
16020 284 : if (dopt->binary_upgrade)
16021 10 : binary_upgrade_extension_member(q, &cfginfo->dobj,
16022 : "TEXT SEARCH CONFIGURATION", qcfgname,
16023 10 : cfginfo->dobj.namespace->dobj.name);
16024 :
16025 284 : if (cfginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16026 284 : ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId,
16027 284 : ARCHIVE_OPTS(.tag = cfginfo->dobj.name,
16028 : .namespace = cfginfo->dobj.namespace->dobj.name,
16029 : .owner = cfginfo->rolname,
16030 : .description = "TEXT SEARCH CONFIGURATION",
16031 : .section = SECTION_PRE_DATA,
16032 : .createStmt = q->data,
16033 : .dropStmt = delq->data));
16034 :
16035 : /* Dump Configuration Comments */
16036 284 : if (cfginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16037 244 : dumpComment(fout, "TEXT SEARCH CONFIGURATION", qcfgname,
16038 244 : cfginfo->dobj.namespace->dobj.name, cfginfo->rolname,
16039 244 : cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId);
16040 :
16041 284 : destroyPQExpBuffer(q);
16042 284 : destroyPQExpBuffer(delq);
16043 284 : destroyPQExpBuffer(query);
16044 284 : free(qcfgname);
16045 : }
16046 :
16047 : /*
16048 : * dumpForeignDataWrapper
16049 : * write out a single foreign-data wrapper definition
16050 : */
16051 : static void
16052 104 : dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
16053 : {
16054 104 : DumpOptions *dopt = fout->dopt;
16055 : PQExpBuffer q;
16056 : PQExpBuffer delq;
16057 : char *qfdwname;
16058 :
16059 : /* Do nothing if not dumping schema */
16060 104 : if (!dopt->dumpSchema)
16061 14 : return;
16062 :
16063 90 : q = createPQExpBuffer();
16064 90 : delq = createPQExpBuffer();
16065 :
16066 90 : qfdwname = pg_strdup(fmtId(fdwinfo->dobj.name));
16067 :
16068 90 : appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s",
16069 : qfdwname);
16070 :
16071 90 : if (strcmp(fdwinfo->fdwhandler, "-") != 0)
16072 0 : appendPQExpBuffer(q, " HANDLER %s", fdwinfo->fdwhandler);
16073 :
16074 90 : if (strcmp(fdwinfo->fdwvalidator, "-") != 0)
16075 0 : appendPQExpBuffer(q, " VALIDATOR %s", fdwinfo->fdwvalidator);
16076 :
16077 90 : if (strlen(fdwinfo->fdwoptions) > 0)
16078 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", fdwinfo->fdwoptions);
16079 :
16080 90 : appendPQExpBufferStr(q, ";\n");
16081 :
16082 90 : appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n",
16083 : qfdwname);
16084 :
16085 90 : if (dopt->binary_upgrade)
16086 4 : binary_upgrade_extension_member(q, &fdwinfo->dobj,
16087 : "FOREIGN DATA WRAPPER", qfdwname,
16088 : NULL);
16089 :
16090 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16091 90 : ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId,
16092 90 : ARCHIVE_OPTS(.tag = fdwinfo->dobj.name,
16093 : .owner = fdwinfo->rolname,
16094 : .description = "FOREIGN DATA WRAPPER",
16095 : .section = SECTION_PRE_DATA,
16096 : .createStmt = q->data,
16097 : .dropStmt = delq->data));
16098 :
16099 : /* Dump Foreign Data Wrapper Comments */
16100 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16101 0 : dumpComment(fout, "FOREIGN DATA WRAPPER", qfdwname,
16102 0 : NULL, fdwinfo->rolname,
16103 0 : fdwinfo->dobj.catId, 0, fdwinfo->dobj.dumpId);
16104 :
16105 : /* Handle the ACL */
16106 90 : if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
16107 62 : dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
16108 : "FOREIGN DATA WRAPPER", qfdwname, NULL, NULL,
16109 62 : NULL, fdwinfo->rolname, &fdwinfo->dacl);
16110 :
16111 90 : free(qfdwname);
16112 :
16113 90 : destroyPQExpBuffer(q);
16114 90 : destroyPQExpBuffer(delq);
16115 : }
16116 :
16117 : /*
16118 : * dumpForeignServer
16119 : * write out a foreign server definition
16120 : */
16121 : static void
16122 112 : dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
16123 : {
16124 112 : DumpOptions *dopt = fout->dopt;
16125 : PQExpBuffer q;
16126 : PQExpBuffer delq;
16127 : PQExpBuffer query;
16128 : PGresult *res;
16129 : char *qsrvname;
16130 : char *fdwname;
16131 :
16132 : /* Do nothing if not dumping schema */
16133 112 : if (!dopt->dumpSchema)
16134 18 : return;
16135 :
16136 94 : q = createPQExpBuffer();
16137 94 : delq = createPQExpBuffer();
16138 94 : query = createPQExpBuffer();
16139 :
16140 94 : qsrvname = pg_strdup(fmtId(srvinfo->dobj.name));
16141 :
16142 : /* look up the foreign-data wrapper */
16143 94 : appendPQExpBuffer(query, "SELECT fdwname "
16144 : "FROM pg_foreign_data_wrapper w "
16145 : "WHERE w.oid = '%u'",
16146 94 : srvinfo->srvfdw);
16147 94 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
16148 94 : fdwname = PQgetvalue(res, 0, 0);
16149 :
16150 94 : appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname);
16151 94 : if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0)
16152 : {
16153 0 : appendPQExpBufferStr(q, " TYPE ");
16154 0 : appendStringLiteralAH(q, srvinfo->srvtype, fout);
16155 : }
16156 94 : if (srvinfo->srvversion && strlen(srvinfo->srvversion) > 0)
16157 : {
16158 0 : appendPQExpBufferStr(q, " VERSION ");
16159 0 : appendStringLiteralAH(q, srvinfo->srvversion, fout);
16160 : }
16161 :
16162 94 : appendPQExpBufferStr(q, " FOREIGN DATA WRAPPER ");
16163 94 : appendPQExpBufferStr(q, fmtId(fdwname));
16164 :
16165 94 : if (srvinfo->srvoptions && strlen(srvinfo->srvoptions) > 0)
16166 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", srvinfo->srvoptions);
16167 :
16168 94 : appendPQExpBufferStr(q, ";\n");
16169 :
16170 94 : appendPQExpBuffer(delq, "DROP SERVER %s;\n",
16171 : qsrvname);
16172 :
16173 94 : if (dopt->binary_upgrade)
16174 4 : binary_upgrade_extension_member(q, &srvinfo->dobj,
16175 : "SERVER", qsrvname, NULL);
16176 :
16177 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16178 94 : ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId,
16179 94 : ARCHIVE_OPTS(.tag = srvinfo->dobj.name,
16180 : .owner = srvinfo->rolname,
16181 : .description = "SERVER",
16182 : .section = SECTION_PRE_DATA,
16183 : .createStmt = q->data,
16184 : .dropStmt = delq->data));
16185 :
16186 : /* Dump Foreign Server Comments */
16187 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
16188 0 : dumpComment(fout, "SERVER", qsrvname,
16189 0 : NULL, srvinfo->rolname,
16190 0 : srvinfo->dobj.catId, 0, srvinfo->dobj.dumpId);
16191 :
16192 : /* Handle the ACL */
16193 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
16194 62 : dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
16195 : "FOREIGN SERVER", qsrvname, NULL, NULL,
16196 62 : NULL, srvinfo->rolname, &srvinfo->dacl);
16197 :
16198 : /* Dump user mappings */
16199 94 : if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
16200 94 : dumpUserMappings(fout,
16201 94 : srvinfo->dobj.name, NULL,
16202 94 : srvinfo->rolname,
16203 94 : srvinfo->dobj.catId, srvinfo->dobj.dumpId);
16204 :
16205 94 : PQclear(res);
16206 :
16207 94 : free(qsrvname);
16208 :
16209 94 : destroyPQExpBuffer(q);
16210 94 : destroyPQExpBuffer(delq);
16211 94 : destroyPQExpBuffer(query);
16212 : }
16213 :
16214 : /*
16215 : * dumpUserMappings
16216 : *
16217 : * This routine is used to dump any user mappings associated with the
16218 : * server handed to this routine. Should be called after ArchiveEntry()
16219 : * for the server.
16220 : */
16221 : static void
16222 94 : dumpUserMappings(Archive *fout,
16223 : const char *servername, const char *namespace,
16224 : const char *owner,
16225 : CatalogId catalogId, DumpId dumpId)
16226 : {
16227 : PQExpBuffer q;
16228 : PQExpBuffer delq;
16229 : PQExpBuffer query;
16230 : PQExpBuffer tag;
16231 : PGresult *res;
16232 : int ntups;
16233 : int i_usename;
16234 : int i_umoptions;
16235 : int i;
16236 :
16237 94 : q = createPQExpBuffer();
16238 94 : tag = createPQExpBuffer();
16239 94 : delq = createPQExpBuffer();
16240 94 : query = createPQExpBuffer();
16241 :
16242 : /*
16243 : * We read from the publicly accessible view pg_user_mappings, so as not
16244 : * to fail if run by a non-superuser. Note that the view will show
16245 : * umoptions as null if the user hasn't got privileges for the associated
16246 : * server; this means that pg_dump will dump such a mapping, but with no
16247 : * OPTIONS clause. A possible alternative is to skip such mappings
16248 : * altogether, but it's not clear that that's an improvement.
16249 : */
16250 94 : appendPQExpBuffer(query,
16251 : "SELECT usename, "
16252 : "array_to_string(ARRAY("
16253 : "SELECT quote_ident(option_name) || ' ' || "
16254 : "quote_literal(option_value) "
16255 : "FROM pg_options_to_table(umoptions) "
16256 : "ORDER BY option_name"
16257 : "), E',\n ') AS umoptions "
16258 : "FROM pg_user_mappings "
16259 : "WHERE srvid = '%u' "
16260 : "ORDER BY usename",
16261 : catalogId.oid);
16262 :
16263 94 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16264 :
16265 94 : ntups = PQntuples(res);
16266 94 : i_usename = PQfnumber(res, "usename");
16267 94 : i_umoptions = PQfnumber(res, "umoptions");
16268 :
16269 156 : for (i = 0; i < ntups; i++)
16270 : {
16271 : char *usename;
16272 : char *umoptions;
16273 :
16274 62 : usename = PQgetvalue(res, i, i_usename);
16275 62 : umoptions = PQgetvalue(res, i, i_umoptions);
16276 :
16277 62 : resetPQExpBuffer(q);
16278 62 : appendPQExpBuffer(q, "CREATE USER MAPPING FOR %s", fmtId(usename));
16279 62 : appendPQExpBuffer(q, " SERVER %s", fmtId(servername));
16280 :
16281 62 : if (umoptions && strlen(umoptions) > 0)
16282 0 : appendPQExpBuffer(q, " OPTIONS (\n %s\n)", umoptions);
16283 :
16284 62 : appendPQExpBufferStr(q, ";\n");
16285 :
16286 62 : resetPQExpBuffer(delq);
16287 62 : appendPQExpBuffer(delq, "DROP USER MAPPING FOR %s", fmtId(usename));
16288 62 : appendPQExpBuffer(delq, " SERVER %s;\n", fmtId(servername));
16289 :
16290 62 : resetPQExpBuffer(tag);
16291 62 : appendPQExpBuffer(tag, "USER MAPPING %s SERVER %s",
16292 : usename, servername);
16293 :
16294 62 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16295 62 : ARCHIVE_OPTS(.tag = tag->data,
16296 : .namespace = namespace,
16297 : .owner = owner,
16298 : .description = "USER MAPPING",
16299 : .section = SECTION_PRE_DATA,
16300 : .createStmt = q->data,
16301 : .dropStmt = delq->data));
16302 : }
16303 :
16304 94 : PQclear(res);
16305 :
16306 94 : destroyPQExpBuffer(query);
16307 94 : destroyPQExpBuffer(delq);
16308 94 : destroyPQExpBuffer(tag);
16309 94 : destroyPQExpBuffer(q);
16310 94 : }
16311 :
16312 : /*
16313 : * Write out default privileges information
16314 : */
16315 : static void
16316 320 : dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
16317 : {
16318 320 : DumpOptions *dopt = fout->dopt;
16319 : PQExpBuffer q;
16320 : PQExpBuffer tag;
16321 : const char *type;
16322 :
16323 : /* Do nothing if not dumping schema, or if we're skipping ACLs */
16324 320 : if (!dopt->dumpSchema || dopt->aclsSkip)
16325 56 : return;
16326 :
16327 264 : q = createPQExpBuffer();
16328 264 : tag = createPQExpBuffer();
16329 :
16330 264 : switch (daclinfo->defaclobjtype)
16331 : {
16332 122 : case DEFACLOBJ_RELATION:
16333 122 : type = "TABLES";
16334 122 : break;
16335 0 : case DEFACLOBJ_SEQUENCE:
16336 0 : type = "SEQUENCES";
16337 0 : break;
16338 122 : case DEFACLOBJ_FUNCTION:
16339 122 : type = "FUNCTIONS";
16340 122 : break;
16341 20 : case DEFACLOBJ_TYPE:
16342 20 : type = "TYPES";
16343 20 : break;
16344 0 : case DEFACLOBJ_NAMESPACE:
16345 0 : type = "SCHEMAS";
16346 0 : break;
16347 0 : case DEFACLOBJ_LARGEOBJECT:
16348 0 : type = "LARGE OBJECTS";
16349 0 : break;
16350 0 : default:
16351 : /* shouldn't get here */
16352 0 : pg_fatal("unrecognized object type in default privileges: %d",
16353 : (int) daclinfo->defaclobjtype);
16354 : type = ""; /* keep compiler quiet */
16355 : }
16356 :
16357 264 : appendPQExpBuffer(tag, "DEFAULT PRIVILEGES FOR %s", type);
16358 :
16359 : /* build the actual command(s) for this tuple */
16360 264 : if (!buildDefaultACLCommands(type,
16361 264 : daclinfo->dobj.namespace != NULL ?
16362 124 : daclinfo->dobj.namespace->dobj.name : NULL,
16363 264 : daclinfo->dacl.acl,
16364 264 : daclinfo->dacl.acldefault,
16365 264 : daclinfo->defaclrole,
16366 : fout->remoteVersion,
16367 : q))
16368 0 : pg_fatal("could not parse default ACL list (%s)",
16369 : daclinfo->dacl.acl);
16370 :
16371 264 : if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
16372 264 : ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
16373 264 : ARCHIVE_OPTS(.tag = tag->data,
16374 : .namespace = daclinfo->dobj.namespace ?
16375 : daclinfo->dobj.namespace->dobj.name : NULL,
16376 : .owner = daclinfo->defaclrole,
16377 : .description = "DEFAULT ACL",
16378 : .section = SECTION_POST_DATA,
16379 : .createStmt = q->data));
16380 :
16381 264 : destroyPQExpBuffer(tag);
16382 264 : destroyPQExpBuffer(q);
16383 : }
16384 :
16385 : /*----------
16386 : * Write out grant/revoke information
16387 : *
16388 : * 'objDumpId' is the dump ID of the underlying object.
16389 : * 'altDumpId' can be a second dumpId that the ACL entry must also depend on,
16390 : * or InvalidDumpId if there is no need for a second dependency.
16391 : * 'type' must be one of
16392 : * TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
16393 : * FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT.
16394 : * 'name' is the formatted name of the object. Must be quoted etc. already.
16395 : * 'subname' is the formatted name of the sub-object, if any. Must be quoted.
16396 : * (Currently we assume that subname is only provided for table columns.)
16397 : * 'nspname' is the namespace the object is in (NULL if none).
16398 : * 'tag' is the tag to use for the ACL TOC entry; typically, this is NULL
16399 : * to use the default for the object type.
16400 : * 'owner' is the owner, NULL if there is no owner (for languages).
16401 : * 'dacl' is the DumpableAcl struct for the object.
16402 : *
16403 : * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
16404 : * no ACL entry was created.
16405 : *----------
16406 : */
16407 : static DumpId
16408 58024 : dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
16409 : const char *type, const char *name, const char *subname,
16410 : const char *nspname, const char *tag, const char *owner,
16411 : const DumpableAcl *dacl)
16412 : {
16413 58024 : DumpId aclDumpId = InvalidDumpId;
16414 58024 : DumpOptions *dopt = fout->dopt;
16415 58024 : const char *acls = dacl->acl;
16416 58024 : const char *acldefault = dacl->acldefault;
16417 58024 : char privtype = dacl->privtype;
16418 58024 : const char *initprivs = dacl->initprivs;
16419 : const char *baseacls;
16420 : PQExpBuffer sql;
16421 :
16422 : /* Do nothing if ACL dump is not enabled */
16423 58024 : if (dopt->aclsSkip)
16424 652 : return InvalidDumpId;
16425 :
16426 : /* --data-only skips ACLs *except* large object ACLs */
16427 57372 : if (!dopt->dumpSchema && strcmp(type, "LARGE OBJECT") != 0)
16428 0 : return InvalidDumpId;
16429 :
16430 57372 : sql = createPQExpBuffer();
16431 :
16432 : /*
16433 : * In binary upgrade mode, we don't run an extension's script but instead
16434 : * dump out the objects independently and then recreate them. To preserve
16435 : * any initial privileges which were set on extension objects, we need to
16436 : * compute the set of GRANT and REVOKE commands necessary to get from the
16437 : * default privileges of an object to its initial privileges as recorded
16438 : * in pg_init_privs.
16439 : *
16440 : * At restore time, we apply these commands after having called
16441 : * binary_upgrade_set_record_init_privs(true). That tells the backend to
16442 : * copy the results into pg_init_privs. This is how we preserve the
16443 : * contents of that catalog across binary upgrades.
16444 : */
16445 57372 : if (dopt->binary_upgrade && privtype == 'e' &&
16446 26 : initprivs && *initprivs != '\0')
16447 : {
16448 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
16449 26 : if (!buildACLCommands(name, subname, nspname, type,
16450 : initprivs, acldefault, owner,
16451 : "", fout->remoteVersion, sql))
16452 0 : pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
16453 : initprivs, acldefault, name, type);
16454 26 : appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
16455 : }
16456 :
16457 : /*
16458 : * Now figure the GRANT and REVOKE commands needed to get to the object's
16459 : * actual current ACL, starting from the initprivs if given, else from the
16460 : * object-type-specific default. Also, while buildACLCommands will assume
16461 : * that a NULL/empty acls string means it needn't do anything, what that
16462 : * actually represents is the object-type-specific default; so we need to
16463 : * substitute the acldefault string to get the right results in that case.
16464 : */
16465 57372 : if (initprivs && *initprivs != '\0')
16466 : {
16467 53916 : baseacls = initprivs;
16468 53916 : if (acls == NULL || *acls == '\0')
16469 34 : acls = acldefault;
16470 : }
16471 : else
16472 3456 : baseacls = acldefault;
16473 :
16474 57372 : if (!buildACLCommands(name, subname, nspname, type,
16475 : acls, baseacls, owner,
16476 : "", fout->remoteVersion, sql))
16477 0 : pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
16478 : acls, baseacls, name, type);
16479 :
16480 57372 : if (sql->len > 0)
16481 : {
16482 3570 : PQExpBuffer tagbuf = createPQExpBuffer();
16483 : DumpId aclDeps[2];
16484 3570 : int nDeps = 0;
16485 :
16486 3570 : if (tag)
16487 0 : appendPQExpBufferStr(tagbuf, tag);
16488 3570 : else if (subname)
16489 2094 : appendPQExpBuffer(tagbuf, "COLUMN %s.%s", name, subname);
16490 : else
16491 1476 : appendPQExpBuffer(tagbuf, "%s %s", type, name);
16492 :
16493 3570 : aclDeps[nDeps++] = objDumpId;
16494 3570 : if (altDumpId != InvalidDumpId)
16495 1926 : aclDeps[nDeps++] = altDumpId;
16496 :
16497 3570 : aclDumpId = createDumpId();
16498 :
16499 3570 : ArchiveEntry(fout, nilCatalogId, aclDumpId,
16500 3570 : ARCHIVE_OPTS(.tag = tagbuf->data,
16501 : .namespace = nspname,
16502 : .owner = owner,
16503 : .description = "ACL",
16504 : .section = SECTION_NONE,
16505 : .createStmt = sql->data,
16506 : .deps = aclDeps,
16507 : .nDeps = nDeps));
16508 :
16509 3570 : destroyPQExpBuffer(tagbuf);
16510 : }
16511 :
16512 57372 : destroyPQExpBuffer(sql);
16513 :
16514 57372 : return aclDumpId;
16515 : }
16516 :
16517 : /*
16518 : * dumpSecLabel
16519 : *
16520 : * This routine is used to dump any security labels associated with the
16521 : * object handed to this routine. The routine takes the object type
16522 : * and object name (ready to print, except for schema decoration), plus
16523 : * the namespace and owner of the object (for labeling the ArchiveEntry),
16524 : * plus catalog ID and subid which are the lookup key for pg_seclabel,
16525 : * plus the dump ID for the object (for setting a dependency).
16526 : * If a matching pg_seclabel entry is found, it is dumped.
16527 : *
16528 : * Note: although this routine takes a dumpId for dependency purposes,
16529 : * that purpose is just to mark the dependency in the emitted dump file
16530 : * for possible future use by pg_restore. We do NOT use it for determining
16531 : * ordering of the label in the dump file, because this routine is called
16532 : * after dependency sorting occurs. This routine should be called just after
16533 : * calling ArchiveEntry() for the specified object.
16534 : */
16535 : static void
16536 20 : dumpSecLabel(Archive *fout, const char *type, const char *name,
16537 : const char *namespace, const char *owner,
16538 : CatalogId catalogId, int subid, DumpId dumpId)
16539 : {
16540 20 : DumpOptions *dopt = fout->dopt;
16541 : SecLabelItem *labels;
16542 : int nlabels;
16543 : int i;
16544 : PQExpBuffer query;
16545 :
16546 : /* do nothing, if --no-security-labels is supplied */
16547 20 : if (dopt->no_security_labels)
16548 0 : return;
16549 :
16550 : /*
16551 : * Security labels are schema not data ... except large object labels are
16552 : * data
16553 : */
16554 20 : if (strcmp(type, "LARGE OBJECT") != 0)
16555 : {
16556 0 : if (!dopt->dumpSchema)
16557 0 : return;
16558 : }
16559 : else
16560 : {
16561 : /* We do dump large object security labels in binary-upgrade mode */
16562 20 : if (!dopt->dumpData && !dopt->binary_upgrade)
16563 0 : return;
16564 : }
16565 :
16566 : /* Search for security labels associated with catalogId, using table */
16567 20 : nlabels = findSecLabels(catalogId.tableoid, catalogId.oid, &labels);
16568 :
16569 20 : query = createPQExpBuffer();
16570 :
16571 30 : for (i = 0; i < nlabels; i++)
16572 : {
16573 : /*
16574 : * Ignore label entries for which the subid doesn't match.
16575 : */
16576 10 : if (labels[i].objsubid != subid)
16577 0 : continue;
16578 :
16579 10 : appendPQExpBuffer(query,
16580 : "SECURITY LABEL FOR %s ON %s ",
16581 10 : fmtId(labels[i].provider), type);
16582 10 : if (namespace && *namespace)
16583 0 : appendPQExpBuffer(query, "%s.", fmtId(namespace));
16584 10 : appendPQExpBuffer(query, "%s IS ", name);
16585 10 : appendStringLiteralAH(query, labels[i].label, fout);
16586 10 : appendPQExpBufferStr(query, ";\n");
16587 : }
16588 :
16589 20 : if (query->len > 0)
16590 : {
16591 10 : PQExpBuffer tag = createPQExpBuffer();
16592 :
16593 10 : appendPQExpBuffer(tag, "%s %s", type, name);
16594 10 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16595 10 : ARCHIVE_OPTS(.tag = tag->data,
16596 : .namespace = namespace,
16597 : .owner = owner,
16598 : .description = "SECURITY LABEL",
16599 : .section = SECTION_NONE,
16600 : .createStmt = query->data,
16601 : .deps = &dumpId,
16602 : .nDeps = 1));
16603 10 : destroyPQExpBuffer(tag);
16604 : }
16605 :
16606 20 : destroyPQExpBuffer(query);
16607 : }
16608 :
16609 : /*
16610 : * dumpTableSecLabel
16611 : *
16612 : * As above, but dump security label for both the specified table (or view)
16613 : * and its columns.
16614 : */
16615 : static void
16616 0 : dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypename)
16617 : {
16618 0 : DumpOptions *dopt = fout->dopt;
16619 : SecLabelItem *labels;
16620 : int nlabels;
16621 : int i;
16622 : PQExpBuffer query;
16623 : PQExpBuffer target;
16624 :
16625 : /* do nothing, if --no-security-labels is supplied */
16626 0 : if (dopt->no_security_labels)
16627 0 : return;
16628 :
16629 : /* SecLabel are SCHEMA not data */
16630 0 : if (!dopt->dumpSchema)
16631 0 : return;
16632 :
16633 : /* Search for comments associated with relation, using table */
16634 0 : nlabels = findSecLabels(tbinfo->dobj.catId.tableoid,
16635 0 : tbinfo->dobj.catId.oid,
16636 : &labels);
16637 :
16638 : /* If security labels exist, build SECURITY LABEL statements */
16639 0 : if (nlabels <= 0)
16640 0 : return;
16641 :
16642 0 : query = createPQExpBuffer();
16643 0 : target = createPQExpBuffer();
16644 :
16645 0 : for (i = 0; i < nlabels; i++)
16646 : {
16647 : const char *colname;
16648 0 : const char *provider = labels[i].provider;
16649 0 : const char *label = labels[i].label;
16650 0 : int objsubid = labels[i].objsubid;
16651 :
16652 0 : resetPQExpBuffer(target);
16653 0 : if (objsubid == 0)
16654 : {
16655 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16656 0 : fmtQualifiedDumpable(tbinfo));
16657 : }
16658 : else
16659 : {
16660 0 : colname = getAttrName(objsubid, tbinfo);
16661 : /* first fmtXXX result must be consumed before calling again */
16662 0 : appendPQExpBuffer(target, "COLUMN %s",
16663 0 : fmtQualifiedDumpable(tbinfo));
16664 0 : appendPQExpBuffer(target, ".%s", fmtId(colname));
16665 : }
16666 0 : appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
16667 : fmtId(provider), target->data);
16668 0 : appendStringLiteralAH(query, label, fout);
16669 0 : appendPQExpBufferStr(query, ";\n");
16670 : }
16671 0 : if (query->len > 0)
16672 : {
16673 0 : resetPQExpBuffer(target);
16674 0 : appendPQExpBuffer(target, "%s %s", reltypename,
16675 0 : fmtId(tbinfo->dobj.name));
16676 0 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
16677 0 : ARCHIVE_OPTS(.tag = target->data,
16678 : .namespace = tbinfo->dobj.namespace->dobj.name,
16679 : .owner = tbinfo->rolname,
16680 : .description = "SECURITY LABEL",
16681 : .section = SECTION_NONE,
16682 : .createStmt = query->data,
16683 : .deps = &(tbinfo->dobj.dumpId),
16684 : .nDeps = 1));
16685 : }
16686 0 : destroyPQExpBuffer(query);
16687 0 : destroyPQExpBuffer(target);
16688 : }
16689 :
16690 : /*
16691 : * findSecLabels
16692 : *
16693 : * Find the security label(s), if any, associated with the given object.
16694 : * All the objsubid values associated with the given classoid/objoid are
16695 : * found with one search.
16696 : */
16697 : static int
16698 20 : findSecLabels(Oid classoid, Oid objoid, SecLabelItem **items)
16699 : {
16700 20 : SecLabelItem *middle = NULL;
16701 : SecLabelItem *low;
16702 : SecLabelItem *high;
16703 : int nmatch;
16704 :
16705 20 : if (nseclabels <= 0) /* no labels, so no match is possible */
16706 : {
16707 0 : *items = NULL;
16708 0 : return 0;
16709 : }
16710 :
16711 : /*
16712 : * Do binary search to find some item matching the object.
16713 : */
16714 20 : low = &seclabels[0];
16715 20 : high = &seclabels[nseclabels - 1];
16716 30 : while (low <= high)
16717 : {
16718 20 : middle = low + (high - low) / 2;
16719 :
16720 20 : if (classoid < middle->classoid)
16721 0 : high = middle - 1;
16722 20 : else if (classoid > middle->classoid)
16723 0 : low = middle + 1;
16724 20 : else if (objoid < middle->objoid)
16725 10 : high = middle - 1;
16726 10 : else if (objoid > middle->objoid)
16727 0 : low = middle + 1;
16728 : else
16729 10 : break; /* found a match */
16730 : }
16731 :
16732 20 : if (low > high) /* no matches */
16733 : {
16734 10 : *items = NULL;
16735 10 : return 0;
16736 : }
16737 :
16738 : /*
16739 : * Now determine how many items match the object. The search loop
16740 : * invariant still holds: only items between low and high inclusive could
16741 : * match.
16742 : */
16743 10 : nmatch = 1;
16744 10 : while (middle > low)
16745 : {
16746 0 : if (classoid != middle[-1].classoid ||
16747 0 : objoid != middle[-1].objoid)
16748 : break;
16749 0 : middle--;
16750 0 : nmatch++;
16751 : }
16752 :
16753 10 : *items = middle;
16754 :
16755 10 : middle += nmatch;
16756 10 : while (middle <= high)
16757 : {
16758 0 : if (classoid != middle->classoid ||
16759 0 : objoid != middle->objoid)
16760 : break;
16761 0 : middle++;
16762 0 : nmatch++;
16763 : }
16764 :
16765 10 : return nmatch;
16766 : }
16767 :
16768 : /*
16769 : * collectSecLabels
16770 : *
16771 : * Construct a table of all security labels available for database objects;
16772 : * also set the has-seclabel component flag for each relevant object.
16773 : *
16774 : * The table is sorted by classoid/objid/objsubid for speed in lookup.
16775 : */
16776 : static void
16777 376 : collectSecLabels(Archive *fout)
16778 : {
16779 : PGresult *res;
16780 : PQExpBuffer query;
16781 : int i_label;
16782 : int i_provider;
16783 : int i_classoid;
16784 : int i_objoid;
16785 : int i_objsubid;
16786 : int ntups;
16787 : int i;
16788 : DumpableObject *dobj;
16789 :
16790 376 : query = createPQExpBuffer();
16791 :
16792 376 : appendPQExpBufferStr(query,
16793 : "SELECT label, provider, classoid, objoid, objsubid "
16794 : "FROM pg_catalog.pg_seclabels "
16795 : "ORDER BY classoid, objoid, objsubid");
16796 :
16797 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16798 :
16799 : /* Construct lookup table containing OIDs in numeric form */
16800 376 : i_label = PQfnumber(res, "label");
16801 376 : i_provider = PQfnumber(res, "provider");
16802 376 : i_classoid = PQfnumber(res, "classoid");
16803 376 : i_objoid = PQfnumber(res, "objoid");
16804 376 : i_objsubid = PQfnumber(res, "objsubid");
16805 :
16806 376 : ntups = PQntuples(res);
16807 :
16808 376 : seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
16809 376 : nseclabels = 0;
16810 376 : dobj = NULL;
16811 :
16812 386 : for (i = 0; i < ntups; i++)
16813 : {
16814 : CatalogId objId;
16815 : int subid;
16816 :
16817 10 : objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
16818 10 : objId.oid = atooid(PQgetvalue(res, i, i_objoid));
16819 10 : subid = atoi(PQgetvalue(res, i, i_objsubid));
16820 :
16821 : /* We needn't remember labels that don't match any dumpable object */
16822 10 : if (dobj == NULL ||
16823 0 : dobj->catId.tableoid != objId.tableoid ||
16824 0 : dobj->catId.oid != objId.oid)
16825 10 : dobj = findObjectByCatalogId(objId);
16826 10 : if (dobj == NULL)
16827 0 : continue;
16828 :
16829 : /*
16830 : * Labels on columns of composite types are linked to the type's
16831 : * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
16832 : * in the type's own DumpableObject.
16833 : */
16834 10 : if (subid != 0 && dobj->objType == DO_TABLE &&
16835 0 : ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
16836 0 : {
16837 : TypeInfo *cTypeInfo;
16838 :
16839 0 : cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
16840 0 : if (cTypeInfo)
16841 0 : cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
16842 : }
16843 : else
16844 10 : dobj->components |= DUMP_COMPONENT_SECLABEL;
16845 :
16846 10 : seclabels[nseclabels].label = pg_strdup(PQgetvalue(res, i, i_label));
16847 10 : seclabels[nseclabels].provider = pg_strdup(PQgetvalue(res, i, i_provider));
16848 10 : seclabels[nseclabels].classoid = objId.tableoid;
16849 10 : seclabels[nseclabels].objoid = objId.oid;
16850 10 : seclabels[nseclabels].objsubid = subid;
16851 10 : nseclabels++;
16852 : }
16853 :
16854 376 : PQclear(res);
16855 376 : destroyPQExpBuffer(query);
16856 376 : }
16857 :
16858 : /*
16859 : * dumpTable
16860 : * write out to fout the declarations (not data) of a user-defined table
16861 : */
16862 : static void
16863 62652 : dumpTable(Archive *fout, const TableInfo *tbinfo)
16864 : {
16865 62652 : DumpOptions *dopt = fout->dopt;
16866 62652 : DumpId tableAclDumpId = InvalidDumpId;
16867 : char *namecopy;
16868 :
16869 : /* Do nothing if not dumping schema */
16870 62652 : if (!dopt->dumpSchema)
16871 3016 : return;
16872 :
16873 59636 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
16874 : {
16875 13194 : if (tbinfo->relkind == RELKIND_SEQUENCE)
16876 750 : dumpSequence(fout, tbinfo);
16877 : else
16878 12444 : dumpTableSchema(fout, tbinfo);
16879 : }
16880 :
16881 : /* Handle the ACL here */
16882 59636 : namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
16883 59636 : if (tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
16884 : {
16885 47884 : const char *objtype =
16886 47884 : (tbinfo->relkind == RELKIND_SEQUENCE) ? "SEQUENCE" : "TABLE";
16887 :
16888 : tableAclDumpId =
16889 47884 : dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
16890 : objtype, namecopy, NULL,
16891 47884 : tbinfo->dobj.namespace->dobj.name,
16892 47884 : NULL, tbinfo->rolname, &tbinfo->dacl);
16893 : }
16894 :
16895 : /*
16896 : * Handle column ACLs, if any. Note: we pull these with a separate query
16897 : * rather than trying to fetch them during getTableAttrs, so that we won't
16898 : * miss ACLs on system columns. Doing it this way also allows us to dump
16899 : * ACLs for catalogs that we didn't mark "interesting" back in getTables.
16900 : */
16901 59636 : if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
16902 : {
16903 566 : PQExpBuffer query = createPQExpBuffer();
16904 : PGresult *res;
16905 : int i;
16906 :
16907 566 : if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
16908 : {
16909 : /* Set up query for column ACLs */
16910 324 : appendPQExpBufferStr(query,
16911 : "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
16912 :
16913 324 : if (fout->remoteVersion >= 90600)
16914 : {
16915 : /*
16916 : * In principle we should call acldefault('c', relowner) to
16917 : * get the default ACL for a column. However, we don't
16918 : * currently store the numeric OID of the relowner in
16919 : * TableInfo. We could convert the owner name using regrole,
16920 : * but that creates a risk of failure due to concurrent role
16921 : * renames. Given that the default ACL for columns is empty
16922 : * and is likely to stay that way, it's not worth extra cycles
16923 : * and risk to avoid hard-wiring that knowledge here.
16924 : */
16925 324 : appendPQExpBufferStr(query,
16926 : "SELECT at.attname, "
16927 : "at.attacl, "
16928 : "'{}' AS acldefault, "
16929 : "pip.privtype, pip.initprivs "
16930 : "FROM pg_catalog.pg_attribute at "
16931 : "LEFT JOIN pg_catalog.pg_init_privs pip ON "
16932 : "(at.attrelid = pip.objoid "
16933 : "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
16934 : "AND at.attnum = pip.objsubid) "
16935 : "WHERE at.attrelid = $1 AND "
16936 : "NOT at.attisdropped "
16937 : "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
16938 : "ORDER BY at.attnum");
16939 : }
16940 : else
16941 : {
16942 0 : appendPQExpBufferStr(query,
16943 : "SELECT attname, attacl, '{}' AS acldefault, "
16944 : "NULL AS privtype, NULL AS initprivs "
16945 : "FROM pg_catalog.pg_attribute "
16946 : "WHERE attrelid = $1 AND NOT attisdropped "
16947 : "AND attacl IS NOT NULL "
16948 : "ORDER BY attnum");
16949 : }
16950 :
16951 324 : ExecuteSqlStatement(fout, query->data);
16952 :
16953 324 : fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
16954 : }
16955 :
16956 566 : printfPQExpBuffer(query,
16957 : "EXECUTE getColumnACLs('%u')",
16958 566 : tbinfo->dobj.catId.oid);
16959 :
16960 566 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
16961 :
16962 9020 : for (i = 0; i < PQntuples(res); i++)
16963 : {
16964 8454 : char *attname = PQgetvalue(res, i, 0);
16965 8454 : char *attacl = PQgetvalue(res, i, 1);
16966 8454 : char *acldefault = PQgetvalue(res, i, 2);
16967 8454 : char privtype = *(PQgetvalue(res, i, 3));
16968 8454 : char *initprivs = PQgetvalue(res, i, 4);
16969 : DumpableAcl coldacl;
16970 : char *attnamecopy;
16971 :
16972 8454 : coldacl.acl = attacl;
16973 8454 : coldacl.acldefault = acldefault;
16974 8454 : coldacl.privtype = privtype;
16975 8454 : coldacl.initprivs = initprivs;
16976 8454 : attnamecopy = pg_strdup(fmtId(attname));
16977 :
16978 : /*
16979 : * Column's GRANT type is always TABLE. Each column ACL depends
16980 : * on the table-level ACL, since we can restore column ACLs in
16981 : * parallel but the table-level ACL has to be done first.
16982 : */
16983 8454 : dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
16984 : "TABLE", namecopy, attnamecopy,
16985 8454 : tbinfo->dobj.namespace->dobj.name,
16986 8454 : NULL, tbinfo->rolname, &coldacl);
16987 8454 : free(attnamecopy);
16988 : }
16989 566 : PQclear(res);
16990 566 : destroyPQExpBuffer(query);
16991 : }
16992 :
16993 59636 : free(namecopy);
16994 : }
16995 :
16996 : /*
16997 : * Create the AS clause for a view or materialized view. The semicolon is
16998 : * stripped because a materialized view must add a WITH NO DATA clause.
16999 : *
17000 : * This returns a new buffer which must be freed by the caller.
17001 : */
17002 : static PQExpBuffer
17003 1736 : createViewAsClause(Archive *fout, const TableInfo *tbinfo)
17004 : {
17005 1736 : PQExpBuffer query = createPQExpBuffer();
17006 1736 : PQExpBuffer result = createPQExpBuffer();
17007 : PGresult *res;
17008 : int len;
17009 :
17010 : /* Fetch the view definition */
17011 1736 : appendPQExpBuffer(query,
17012 : "SELECT pg_catalog.pg_get_viewdef('%u'::pg_catalog.oid) AS viewdef",
17013 1736 : tbinfo->dobj.catId.oid);
17014 :
17015 1736 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
17016 :
17017 1736 : if (PQntuples(res) != 1)
17018 : {
17019 0 : if (PQntuples(res) < 1)
17020 0 : pg_fatal("query to obtain definition of view \"%s\" returned no data",
17021 : tbinfo->dobj.name);
17022 : else
17023 0 : pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
17024 : tbinfo->dobj.name);
17025 : }
17026 :
17027 1736 : len = PQgetlength(res, 0, 0);
17028 :
17029 1736 : if (len == 0)
17030 0 : pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
17031 : tbinfo->dobj.name);
17032 :
17033 : /* Strip off the trailing semicolon so that other things may follow. */
17034 : Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
17035 1736 : appendBinaryPQExpBuffer(result, PQgetvalue(res, 0, 0), len - 1);
17036 :
17037 1736 : PQclear(res);
17038 1736 : destroyPQExpBuffer(query);
17039 :
17040 1736 : return result;
17041 : }
17042 :
17043 : /*
17044 : * Create a dummy AS clause for a view. This is used when the real view
17045 : * definition has to be postponed because of circular dependencies.
17046 : * We must duplicate the view's external properties -- column names and types
17047 : * (including collation) -- so that it works for subsequent references.
17048 : *
17049 : * This returns a new buffer which must be freed by the caller.
17050 : */
17051 : static PQExpBuffer
17052 40 : createDummyViewAsClause(Archive *fout, const TableInfo *tbinfo)
17053 : {
17054 40 : PQExpBuffer result = createPQExpBuffer();
17055 : int j;
17056 :
17057 40 : appendPQExpBufferStr(result, "SELECT");
17058 :
17059 80 : for (j = 0; j < tbinfo->numatts; j++)
17060 : {
17061 40 : if (j > 0)
17062 20 : appendPQExpBufferChar(result, ',');
17063 40 : appendPQExpBufferStr(result, "\n ");
17064 :
17065 40 : appendPQExpBuffer(result, "NULL::%s", tbinfo->atttypnames[j]);
17066 :
17067 : /*
17068 : * Must add collation if not default for the type, because CREATE OR
17069 : * REPLACE VIEW won't change it
17070 : */
17071 40 : if (OidIsValid(tbinfo->attcollation[j]))
17072 : {
17073 : CollInfo *coll;
17074 :
17075 0 : coll = findCollationByOid(tbinfo->attcollation[j]);
17076 0 : if (coll)
17077 0 : appendPQExpBuffer(result, " COLLATE %s",
17078 0 : fmtQualifiedDumpable(coll));
17079 : }
17080 :
17081 40 : appendPQExpBuffer(result, " AS %s", fmtId(tbinfo->attnames[j]));
17082 : }
17083 :
17084 40 : return result;
17085 : }
17086 :
17087 : /*
17088 : * dumpTableSchema
17089 : * write the declaration (not data) of one user-defined table or view
17090 : */
17091 : static void
17092 12444 : dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
17093 : {
17094 12444 : DumpOptions *dopt = fout->dopt;
17095 12444 : PQExpBuffer q = createPQExpBuffer();
17096 12444 : PQExpBuffer delq = createPQExpBuffer();
17097 12444 : PQExpBuffer extra = createPQExpBuffer();
17098 : char *qrelname;
17099 : char *qualrelname;
17100 : int numParents;
17101 : TableInfo **parents;
17102 : int actual_atts; /* number of attrs in this CREATE statement */
17103 : const char *reltypename;
17104 : char *storage;
17105 : int j,
17106 : k;
17107 :
17108 : /* We had better have loaded per-column details about this table */
17109 : Assert(tbinfo->interesting);
17110 :
17111 12444 : qrelname = pg_strdup(fmtId(tbinfo->dobj.name));
17112 12444 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
17113 :
17114 12444 : if (tbinfo->hasoids)
17115 0 : pg_log_warning("WITH OIDS is not supported anymore (table \"%s\")",
17116 : qrelname);
17117 :
17118 12444 : if (dopt->binary_upgrade)
17119 1742 : binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);
17120 :
17121 : /* Is it a table or a view? */
17122 12444 : if (tbinfo->relkind == RELKIND_VIEW)
17123 : {
17124 : PQExpBuffer result;
17125 :
17126 : /*
17127 : * Note: keep this code in sync with the is_view case in dumpRule()
17128 : */
17129 :
17130 1066 : reltypename = "VIEW";
17131 :
17132 1066 : appendPQExpBuffer(delq, "DROP VIEW %s;\n", qualrelname);
17133 :
17134 1066 : if (dopt->binary_upgrade)
17135 104 : binary_upgrade_set_pg_class_oids(fout, q,
17136 104 : tbinfo->dobj.catId.oid);
17137 :
17138 1066 : appendPQExpBuffer(q, "CREATE VIEW %s", qualrelname);
17139 :
17140 1066 : if (tbinfo->dummy_view)
17141 20 : result = createDummyViewAsClause(fout, tbinfo);
17142 : else
17143 : {
17144 1046 : if (nonemptyReloptions(tbinfo->reloptions))
17145 : {
17146 122 : appendPQExpBufferStr(q, " WITH (");
17147 122 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17148 122 : appendPQExpBufferChar(q, ')');
17149 : }
17150 1046 : result = createViewAsClause(fout, tbinfo);
17151 : }
17152 1066 : appendPQExpBuffer(q, " AS\n%s", result->data);
17153 1066 : destroyPQExpBuffer(result);
17154 :
17155 1066 : if (tbinfo->checkoption != NULL && !tbinfo->dummy_view)
17156 64 : appendPQExpBuffer(q, "\n WITH %s CHECK OPTION", tbinfo->checkoption);
17157 1066 : appendPQExpBufferStr(q, ";\n");
17158 : }
17159 : else
17160 : {
17161 11378 : char *partkeydef = NULL;
17162 11378 : char *ftoptions = NULL;
17163 11378 : char *srvname = NULL;
17164 11378 : const char *foreign = "";
17165 :
17166 : /*
17167 : * Set reltypename, and collect any relkind-specific data that we
17168 : * didn't fetch during getTables().
17169 : */
17170 11378 : switch (tbinfo->relkind)
17171 : {
17172 1152 : case RELKIND_PARTITIONED_TABLE:
17173 : {
17174 1152 : PQExpBuffer query = createPQExpBuffer();
17175 : PGresult *res;
17176 :
17177 1152 : reltypename = "TABLE";
17178 :
17179 : /* retrieve partition key definition */
17180 1152 : appendPQExpBuffer(query,
17181 : "SELECT pg_get_partkeydef('%u')",
17182 1152 : tbinfo->dobj.catId.oid);
17183 1152 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17184 1152 : partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
17185 1152 : PQclear(res);
17186 1152 : destroyPQExpBuffer(query);
17187 1152 : break;
17188 : }
17189 68 : case RELKIND_FOREIGN_TABLE:
17190 : {
17191 68 : PQExpBuffer query = createPQExpBuffer();
17192 : PGresult *res;
17193 : int i_srvname;
17194 : int i_ftoptions;
17195 :
17196 68 : reltypename = "FOREIGN TABLE";
17197 :
17198 : /* retrieve name of foreign server and generic options */
17199 68 : appendPQExpBuffer(query,
17200 : "SELECT fs.srvname, "
17201 : "pg_catalog.array_to_string(ARRAY("
17202 : "SELECT pg_catalog.quote_ident(option_name) || "
17203 : "' ' || pg_catalog.quote_literal(option_value) "
17204 : "FROM pg_catalog.pg_options_to_table(ftoptions) "
17205 : "ORDER BY option_name"
17206 : "), E',\n ') AS ftoptions "
17207 : "FROM pg_catalog.pg_foreign_table ft "
17208 : "JOIN pg_catalog.pg_foreign_server fs "
17209 : "ON (fs.oid = ft.ftserver) "
17210 : "WHERE ft.ftrelid = '%u'",
17211 68 : tbinfo->dobj.catId.oid);
17212 68 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
17213 68 : i_srvname = PQfnumber(res, "srvname");
17214 68 : i_ftoptions = PQfnumber(res, "ftoptions");
17215 68 : srvname = pg_strdup(PQgetvalue(res, 0, i_srvname));
17216 68 : ftoptions = pg_strdup(PQgetvalue(res, 0, i_ftoptions));
17217 68 : PQclear(res);
17218 68 : destroyPQExpBuffer(query);
17219 :
17220 68 : foreign = "FOREIGN ";
17221 68 : break;
17222 : }
17223 670 : case RELKIND_MATVIEW:
17224 670 : reltypename = "MATERIALIZED VIEW";
17225 670 : break;
17226 9488 : default:
17227 9488 : reltypename = "TABLE";
17228 9488 : break;
17229 : }
17230 :
17231 11378 : numParents = tbinfo->numParents;
17232 11378 : parents = tbinfo->parents;
17233 :
17234 11378 : appendPQExpBuffer(delq, "DROP %s %s;\n", reltypename, qualrelname);
17235 :
17236 11378 : if (dopt->binary_upgrade)
17237 1638 : binary_upgrade_set_pg_class_oids(fout, q,
17238 1638 : tbinfo->dobj.catId.oid);
17239 :
17240 : /*
17241 : * PostgreSQL 18 has disabled UNLOGGED for partitioned tables, so
17242 : * ignore it when dumping if it was set in this case.
17243 : */
17244 11378 : appendPQExpBuffer(q, "CREATE %s%s %s",
17245 11378 : (tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED &&
17246 40 : tbinfo->relkind != RELKIND_PARTITIONED_TABLE) ?
17247 : "UNLOGGED " : "",
17248 : reltypename,
17249 : qualrelname);
17250 :
17251 : /*
17252 : * Attach to type, if reloftype; except in case of a binary upgrade,
17253 : * we dump the table normally and attach it to the type afterward.
17254 : */
17255 11378 : if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
17256 48 : appendPQExpBuffer(q, " OF %s",
17257 48 : getFormattedTypeName(fout, tbinfo->reloftype,
17258 : zeroIsError));
17259 :
17260 11378 : if (tbinfo->relkind != RELKIND_MATVIEW)
17261 : {
17262 : /* Dump the attributes */
17263 10708 : actual_atts = 0;
17264 50316 : for (j = 0; j < tbinfo->numatts; j++)
17265 : {
17266 : /*
17267 : * Normally, dump if it's locally defined in this table, and
17268 : * not dropped. But for binary upgrade, we'll dump all the
17269 : * columns, and then fix up the dropped and nonlocal cases
17270 : * below.
17271 : */
17272 39608 : if (shouldPrintColumn(dopt, tbinfo, j))
17273 : {
17274 : bool print_default;
17275 : bool print_notnull;
17276 :
17277 : /*
17278 : * Default value --- suppress if to be printed separately
17279 : * or not at all.
17280 : */
17281 77370 : print_default = (tbinfo->attrdefs[j] != NULL &&
17282 39634 : tbinfo->attrdefs[j]->dobj.dump &&
17283 1992 : !tbinfo->attrdefs[j]->separate);
17284 :
17285 : /*
17286 : * Not Null constraint --- print it if it is locally
17287 : * defined, or if binary upgrade. (In the latter case, we
17288 : * reset conislocal below.)
17289 : */
17290 42104 : print_notnull = (tbinfo->notnull_constrs[j] != NULL &&
17291 4462 : (tbinfo->notnull_islocal[j] ||
17292 1234 : dopt->binary_upgrade ||
17293 1062 : tbinfo->ispartition));
17294 :
17295 : /*
17296 : * Skip column if fully defined by reloftype, except in
17297 : * binary upgrade
17298 : */
17299 37642 : if (OidIsValid(tbinfo->reloftype) &&
17300 100 : !print_default && !print_notnull &&
17301 60 : !dopt->binary_upgrade)
17302 48 : continue;
17303 :
17304 : /* Format properly if not first attr */
17305 37594 : if (actual_atts == 0)
17306 10056 : appendPQExpBufferStr(q, " (");
17307 : else
17308 27538 : appendPQExpBufferChar(q, ',');
17309 37594 : appendPQExpBufferStr(q, "\n ");
17310 37594 : actual_atts++;
17311 :
17312 : /* Attribute name */
17313 37594 : appendPQExpBufferStr(q, fmtId(tbinfo->attnames[j]));
17314 :
17315 37594 : if (tbinfo->attisdropped[j])
17316 : {
17317 : /*
17318 : * ALTER TABLE DROP COLUMN clears
17319 : * pg_attribute.atttypid, so we will not have gotten a
17320 : * valid type name; insert INTEGER as a stopgap. We'll
17321 : * clean things up later.
17322 : */
17323 168 : appendPQExpBufferStr(q, " INTEGER /* dummy */");
17324 : /* and skip to the next column */
17325 168 : continue;
17326 : }
17327 :
17328 : /*
17329 : * Attribute type; print it except when creating a typed
17330 : * table ('OF type_name'), but in binary-upgrade mode,
17331 : * print it in that case too.
17332 : */
17333 37426 : if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
17334 : {
17335 37394 : appendPQExpBuffer(q, " %s",
17336 37394 : tbinfo->atttypnames[j]);
17337 : }
17338 :
17339 37426 : if (print_default)
17340 : {
17341 1732 : if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_STORED)
17342 554 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s) STORED",
17343 554 : tbinfo->attrdefs[j]->adef_expr);
17344 1178 : else if (tbinfo->attgenerated[j] == ATTRIBUTE_GENERATED_VIRTUAL)
17345 446 : appendPQExpBuffer(q, " GENERATED ALWAYS AS (%s)",
17346 446 : tbinfo->attrdefs[j]->adef_expr);
17347 : else
17348 732 : appendPQExpBuffer(q, " DEFAULT %s",
17349 732 : tbinfo->attrdefs[j]->adef_expr);
17350 : }
17351 :
17352 37426 : if (print_notnull)
17353 : {
17354 4400 : if (tbinfo->notnull_constrs[j][0] == '\0')
17355 3112 : appendPQExpBufferStr(q, " NOT NULL");
17356 : else
17357 1288 : appendPQExpBuffer(q, " CONSTRAINT %s NOT NULL",
17358 1288 : fmtId(tbinfo->notnull_constrs[j]));
17359 :
17360 4400 : if (tbinfo->notnull_noinh[j])
17361 0 : appendPQExpBufferStr(q, " NO INHERIT");
17362 : }
17363 :
17364 : /* Add collation if not default for the type */
17365 37426 : if (OidIsValid(tbinfo->attcollation[j]))
17366 : {
17367 : CollInfo *coll;
17368 :
17369 394 : coll = findCollationByOid(tbinfo->attcollation[j]);
17370 394 : if (coll)
17371 394 : appendPQExpBuffer(q, " COLLATE %s",
17372 394 : fmtQualifiedDumpable(coll));
17373 : }
17374 : }
17375 :
17376 : /*
17377 : * On the other hand, if we choose not to print a column
17378 : * (likely because it is created by inheritance), but the
17379 : * column has a locally-defined not-null constraint, we need
17380 : * to dump the constraint as a standalone object.
17381 : *
17382 : * This syntax isn't SQL-conforming, but if you wanted
17383 : * standard output you wouldn't be creating non-standard
17384 : * objects to begin with.
17385 : */
17386 39392 : if (!shouldPrintColumn(dopt, tbinfo, j) &&
17387 1966 : !tbinfo->attisdropped[j] &&
17388 1240 : tbinfo->notnull_constrs[j] != NULL &&
17389 354 : tbinfo->notnull_islocal[j])
17390 : {
17391 : /* Format properly if not first attr */
17392 118 : if (actual_atts == 0)
17393 110 : appendPQExpBufferStr(q, " (");
17394 : else
17395 8 : appendPQExpBufferChar(q, ',');
17396 118 : appendPQExpBufferStr(q, "\n ");
17397 118 : actual_atts++;
17398 :
17399 118 : if (tbinfo->notnull_constrs[j][0] == '\0')
17400 16 : appendPQExpBuffer(q, "NOT NULL %s",
17401 16 : fmtId(tbinfo->attnames[j]));
17402 : else
17403 204 : appendPQExpBuffer(q, "CONSTRAINT %s NOT NULL %s",
17404 102 : tbinfo->notnull_constrs[j],
17405 102 : fmtId(tbinfo->attnames[j]));
17406 : }
17407 : }
17408 :
17409 : /*
17410 : * Add non-inherited CHECK constraints, if any.
17411 : *
17412 : * For partitions, we need to include check constraints even if
17413 : * they're not defined locally, because the ALTER TABLE ATTACH
17414 : * PARTITION that we'll emit later expects the constraint to be
17415 : * there. (No need to fix conislocal: ATTACH PARTITION does that)
17416 : */
17417 11854 : for (j = 0; j < tbinfo->ncheck; j++)
17418 : {
17419 1146 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
17420 :
17421 1146 : if (constr->separate ||
17422 1006 : (!constr->conislocal && !tbinfo->ispartition))
17423 214 : continue;
17424 :
17425 932 : if (actual_atts == 0)
17426 32 : appendPQExpBufferStr(q, " (\n ");
17427 : else
17428 900 : appendPQExpBufferStr(q, ",\n ");
17429 :
17430 932 : appendPQExpBuffer(q, "CONSTRAINT %s ",
17431 932 : fmtId(constr->dobj.name));
17432 932 : appendPQExpBufferStr(q, constr->condef);
17433 :
17434 932 : actual_atts++;
17435 : }
17436 :
17437 10708 : if (actual_atts)
17438 10198 : appendPQExpBufferStr(q, "\n)");
17439 510 : else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
17440 : {
17441 : /*
17442 : * No attributes? we must have a parenthesized attribute list,
17443 : * even though empty, when not using the OF TYPE syntax.
17444 : */
17445 486 : appendPQExpBufferStr(q, " (\n)");
17446 : }
17447 :
17448 : /*
17449 : * Emit the INHERITS clause (not for partitions), except in
17450 : * binary-upgrade mode.
17451 : */
17452 10708 : if (numParents > 0 && !tbinfo->ispartition &&
17453 978 : !dopt->binary_upgrade)
17454 : {
17455 850 : appendPQExpBufferStr(q, "\nINHERITS (");
17456 1842 : for (k = 0; k < numParents; k++)
17457 : {
17458 992 : TableInfo *parentRel = parents[k];
17459 :
17460 992 : if (k > 0)
17461 142 : appendPQExpBufferStr(q, ", ");
17462 992 : appendPQExpBufferStr(q, fmtQualifiedDumpable(parentRel));
17463 : }
17464 850 : appendPQExpBufferChar(q, ')');
17465 : }
17466 :
17467 10708 : if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17468 1152 : appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);
17469 :
17470 10708 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
17471 68 : appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
17472 : }
17473 :
17474 22470 : if (nonemptyReloptions(tbinfo->reloptions) ||
17475 11092 : nonemptyReloptions(tbinfo->toast_reloptions))
17476 : {
17477 286 : bool addcomma = false;
17478 :
17479 286 : appendPQExpBufferStr(q, "\nWITH (");
17480 286 : if (nonemptyReloptions(tbinfo->reloptions))
17481 : {
17482 286 : addcomma = true;
17483 286 : appendReloptionsArrayAH(q, tbinfo->reloptions, "", fout);
17484 : }
17485 286 : if (nonemptyReloptions(tbinfo->toast_reloptions))
17486 : {
17487 10 : if (addcomma)
17488 10 : appendPQExpBufferStr(q, ", ");
17489 10 : appendReloptionsArrayAH(q, tbinfo->toast_reloptions, "toast.",
17490 : fout);
17491 : }
17492 286 : appendPQExpBufferChar(q, ')');
17493 : }
17494 :
17495 : /* Dump generic options if any */
17496 11378 : if (ftoptions && ftoptions[0])
17497 64 : appendPQExpBuffer(q, "\nOPTIONS (\n %s\n)", ftoptions);
17498 :
17499 : /*
17500 : * For materialized views, create the AS clause just like a view. At
17501 : * this point, we always mark the view as not populated.
17502 : */
17503 11378 : if (tbinfo->relkind == RELKIND_MATVIEW)
17504 : {
17505 : PQExpBuffer result;
17506 :
17507 670 : result = createViewAsClause(fout, tbinfo);
17508 670 : appendPQExpBuffer(q, " AS\n%s\n WITH NO DATA;\n",
17509 : result->data);
17510 670 : destroyPQExpBuffer(result);
17511 : }
17512 : else
17513 10708 : appendPQExpBufferStr(q, ";\n");
17514 :
17515 : /* Materialized views can depend on extensions */
17516 11378 : if (tbinfo->relkind == RELKIND_MATVIEW)
17517 670 : append_depends_on_extension(fout, q, &tbinfo->dobj,
17518 : "pg_catalog.pg_class",
17519 : "MATERIALIZED VIEW",
17520 : qualrelname);
17521 :
17522 : /*
17523 : * in binary upgrade mode, update the catalog with any missing values
17524 : * that might be present.
17525 : */
17526 11378 : if (dopt->binary_upgrade)
17527 : {
17528 7928 : for (j = 0; j < tbinfo->numatts; j++)
17529 : {
17530 6290 : if (tbinfo->attmissingval[j][0] != '\0')
17531 : {
17532 4 : appendPQExpBufferStr(q, "\n-- set missing value.\n");
17533 4 : appendPQExpBufferStr(q,
17534 : "SELECT pg_catalog.binary_upgrade_set_missing_value(");
17535 4 : appendStringLiteralAH(q, qualrelname, fout);
17536 4 : appendPQExpBufferStr(q, "::pg_catalog.regclass,");
17537 4 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17538 4 : appendPQExpBufferChar(q, ',');
17539 4 : appendStringLiteralAH(q, tbinfo->attmissingval[j], fout);
17540 4 : appendPQExpBufferStr(q, ");\n\n");
17541 : }
17542 : }
17543 : }
17544 :
17545 : /*
17546 : * To create binary-compatible heap files, we have to ensure the same
17547 : * physical column order, including dropped columns, as in the
17548 : * original. Therefore, we create dropped columns above and drop them
17549 : * here, also updating their attlen/attalign values so that the
17550 : * dropped column can be skipped properly. (We do not bother with
17551 : * restoring the original attbyval setting.) Also, inheritance
17552 : * relationships are set up by doing ALTER TABLE INHERIT rather than
17553 : * using an INHERITS clause --- the latter would possibly mess up the
17554 : * column order. That also means we have to take care about setting
17555 : * attislocal correctly, plus fix up any inherited CHECK constraints.
17556 : * Analogously, we set up typed tables using ALTER TABLE / OF here.
17557 : *
17558 : * We process foreign and partitioned tables here, even though they
17559 : * lack heap storage, because they can participate in inheritance
17560 : * relationships and we want this stuff to be consistent across the
17561 : * inheritance tree. We can exclude indexes, toast tables, sequences
17562 : * and matviews, even though they have storage, because we don't
17563 : * support altering or dropping columns in them, nor can they be part
17564 : * of inheritance trees.
17565 : */
17566 11378 : if (dopt->binary_upgrade &&
17567 1638 : (tbinfo->relkind == RELKIND_RELATION ||
17568 222 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
17569 220 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
17570 : {
17571 : bool firstitem;
17572 : bool firstitem_extra;
17573 :
17574 : /*
17575 : * Drop any dropped columns. Merge the pg_attribute manipulations
17576 : * into a single SQL command, so that we don't cause repeated
17577 : * relcache flushes on the target table. Otherwise we risk O(N^2)
17578 : * relcache bloat while dropping N columns.
17579 : */
17580 1604 : resetPQExpBuffer(extra);
17581 1604 : firstitem = true;
17582 7852 : for (j = 0; j < tbinfo->numatts; j++)
17583 : {
17584 6248 : if (tbinfo->attisdropped[j])
17585 : {
17586 168 : if (firstitem)
17587 : {
17588 76 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
17589 : "UPDATE pg_catalog.pg_attribute\n"
17590 : "SET attlen = v.dlen, "
17591 : "attalign = v.dalign, "
17592 : "attbyval = false\n"
17593 : "FROM (VALUES ");
17594 76 : firstitem = false;
17595 : }
17596 : else
17597 92 : appendPQExpBufferStr(q, ",\n ");
17598 168 : appendPQExpBufferChar(q, '(');
17599 168 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17600 168 : appendPQExpBuffer(q, ", %d, '%c')",
17601 168 : tbinfo->attlen[j],
17602 168 : tbinfo->attalign[j]);
17603 : /* The ALTER ... DROP COLUMN commands must come after */
17604 168 : appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
17605 : foreign, qualrelname);
17606 168 : appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
17607 168 : fmtId(tbinfo->attnames[j]));
17608 : }
17609 : }
17610 1604 : if (!firstitem)
17611 : {
17612 76 : appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
17613 : "WHERE attrelid = ");
17614 76 : appendStringLiteralAH(q, qualrelname, fout);
17615 76 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17616 : " AND attname = v.dname;\n");
17617 : /* Now we can issue the actual DROP COLUMN commands */
17618 76 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17619 : }
17620 :
17621 : /*
17622 : * Fix up inherited columns. As above, do the pg_attribute
17623 : * manipulations in a single SQL command.
17624 : */
17625 1604 : firstitem = true;
17626 7852 : for (j = 0; j < tbinfo->numatts; j++)
17627 : {
17628 6248 : if (!tbinfo->attisdropped[j] &&
17629 6080 : !tbinfo->attislocal[j])
17630 : {
17631 1212 : if (firstitem)
17632 : {
17633 538 : appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
17634 538 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
17635 : "SET attislocal = false\n"
17636 : "WHERE attrelid = ");
17637 538 : appendStringLiteralAH(q, qualrelname, fout);
17638 538 : appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
17639 : " AND attname IN (");
17640 538 : firstitem = false;
17641 : }
17642 : else
17643 674 : appendPQExpBufferStr(q, ", ");
17644 1212 : appendStringLiteralAH(q, tbinfo->attnames[j], fout);
17645 : }
17646 : }
17647 1604 : if (!firstitem)
17648 538 : appendPQExpBufferStr(q, ");\n");
17649 :
17650 : /*
17651 : * Fix up not-null constraints that come from inheritance. As
17652 : * above, do the pg_constraint manipulations in a single SQL
17653 : * command. (Actually, two in special cases, if we're doing an
17654 : * upgrade from < 18).
17655 : */
17656 1604 : firstitem = true;
17657 1604 : firstitem_extra = true;
17658 1604 : resetPQExpBuffer(extra);
17659 7852 : for (j = 0; j < tbinfo->numatts; j++)
17660 : {
17661 : /*
17662 : * If a not-null constraint comes from inheritance, reset
17663 : * conislocal. The inhcount is fixed by ALTER TABLE INHERIT,
17664 : * below. Special hack: in versions < 18, columns with no
17665 : * local definition need their constraint to be matched by
17666 : * column number in conkeys instead of by constraint name,
17667 : * because the latter is not available. (We distinguish the
17668 : * case because the constraint name is the empty string.)
17669 : */
17670 6248 : if (tbinfo->notnull_constrs[j] != NULL &&
17671 590 : !tbinfo->notnull_islocal[j])
17672 : {
17673 172 : if (tbinfo->notnull_constrs[j][0] != '\0')
17674 : {
17675 146 : if (firstitem)
17676 : {
17677 126 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
17678 : "SET conislocal = false\n"
17679 : "WHERE contype = 'n' AND conrelid = ");
17680 126 : appendStringLiteralAH(q, qualrelname, fout);
17681 126 : appendPQExpBufferStr(q, "::pg_catalog.regclass AND\n"
17682 : "conname IN (");
17683 126 : firstitem = false;
17684 : }
17685 : else
17686 20 : appendPQExpBufferStr(q, ", ");
17687 146 : appendStringLiteralAH(q, tbinfo->notnull_constrs[j], fout);
17688 : }
17689 : else
17690 : {
17691 26 : if (firstitem_extra)
17692 : {
17693 26 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17694 : "SET conislocal = false\n"
17695 : "WHERE contype = 'n' AND conrelid = ");
17696 26 : appendStringLiteralAH(extra, qualrelname, fout);
17697 26 : appendPQExpBufferStr(extra, "::pg_catalog.regclass AND\n"
17698 : "conkey IN (");
17699 26 : firstitem_extra = false;
17700 : }
17701 : else
17702 0 : appendPQExpBufferStr(extra, ", ");
17703 26 : appendPQExpBuffer(extra, "'{%d}'", j + 1);
17704 : }
17705 : }
17706 : }
17707 1604 : if (!firstitem)
17708 126 : appendPQExpBufferStr(q, ");\n");
17709 1604 : if (!firstitem_extra)
17710 26 : appendPQExpBufferStr(extra, ");\n");
17711 :
17712 1604 : if (extra->len > 0)
17713 26 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17714 :
17715 : /*
17716 : * Add inherited CHECK constraints, if any.
17717 : *
17718 : * For partitions, they were already dumped, and conislocal
17719 : * doesn't need fixing.
17720 : *
17721 : * As above, issue only one direct manipulation of pg_constraint.
17722 : * Although it is tempting to merge the ALTER ADD CONSTRAINT
17723 : * commands into one as well, refrain for now due to concern about
17724 : * possible backend memory bloat if there are many such
17725 : * constraints.
17726 : */
17727 1604 : resetPQExpBuffer(extra);
17728 1604 : firstitem = true;
17729 1728 : for (k = 0; k < tbinfo->ncheck; k++)
17730 : {
17731 124 : ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
17732 :
17733 124 : if (constr->separate || constr->conislocal || tbinfo->ispartition)
17734 120 : continue;
17735 :
17736 4 : if (firstitem)
17737 4 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
17738 4 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
17739 : foreign, qualrelname,
17740 4 : fmtId(constr->dobj.name),
17741 : constr->condef);
17742 : /* Update pg_constraint after all the ALTER TABLEs */
17743 4 : if (firstitem)
17744 : {
17745 4 : appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
17746 : "SET conislocal = false\n"
17747 : "WHERE contype = 'c' AND conrelid = ");
17748 4 : appendStringLiteralAH(extra, qualrelname, fout);
17749 4 : appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
17750 4 : appendPQExpBufferStr(extra, " AND conname IN (");
17751 4 : firstitem = false;
17752 : }
17753 : else
17754 0 : appendPQExpBufferStr(extra, ", ");
17755 4 : appendStringLiteralAH(extra, constr->dobj.name, fout);
17756 : }
17757 1604 : if (!firstitem)
17758 : {
17759 4 : appendPQExpBufferStr(extra, ");\n");
17760 4 : appendBinaryPQExpBuffer(q, extra->data, extra->len);
17761 : }
17762 :
17763 1604 : if (numParents > 0 && !tbinfo->ispartition)
17764 : {
17765 128 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inheritance this way.\n");
17766 278 : for (k = 0; k < numParents; k++)
17767 : {
17768 150 : TableInfo *parentRel = parents[k];
17769 :
17770 150 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s INHERIT %s;\n", foreign,
17771 : qualrelname,
17772 150 : fmtQualifiedDumpable(parentRel));
17773 : }
17774 : }
17775 :
17776 1604 : if (OidIsValid(tbinfo->reloftype))
17777 : {
17778 12 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
17779 12 : appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
17780 : qualrelname,
17781 12 : getFormattedTypeName(fout, tbinfo->reloftype,
17782 : zeroIsError));
17783 : }
17784 : }
17785 :
17786 : /*
17787 : * In binary_upgrade mode, arrange to restore the old relfrozenxid and
17788 : * relminmxid of all vacuumable relations. (While vacuum.c processes
17789 : * TOAST tables semi-independently, here we see them only as children
17790 : * of other relations; so this "if" lacks RELKIND_TOASTVALUE, and the
17791 : * child toast table is handled below.)
17792 : */
17793 11378 : if (dopt->binary_upgrade &&
17794 1638 : (tbinfo->relkind == RELKIND_RELATION ||
17795 222 : tbinfo->relkind == RELKIND_MATVIEW))
17796 : {
17797 1450 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
17798 1450 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17799 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17800 : "WHERE oid = ",
17801 1450 : tbinfo->frozenxid, tbinfo->minmxid);
17802 1450 : appendStringLiteralAH(q, qualrelname, fout);
17803 1450 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17804 :
17805 1450 : if (tbinfo->toast_oid)
17806 : {
17807 : /*
17808 : * The toast table will have the same OID at restore, so we
17809 : * can safely target it by OID.
17810 : */
17811 554 : appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
17812 554 : appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
17813 : "SET relfrozenxid = '%u', relminmxid = '%u'\n"
17814 : "WHERE oid = '%u';\n",
17815 554 : tbinfo->toast_frozenxid,
17816 554 : tbinfo->toast_minmxid, tbinfo->toast_oid);
17817 : }
17818 : }
17819 :
17820 : /*
17821 : * In binary_upgrade mode, restore matviews' populated status by
17822 : * poking pg_class directly. This is pretty ugly, but we can't use
17823 : * REFRESH MATERIALIZED VIEW since it's possible that some underlying
17824 : * matview is not populated even though this matview is; in any case,
17825 : * we want to transfer the matview's heap storage, not run REFRESH.
17826 : */
17827 11378 : if (dopt->binary_upgrade && tbinfo->relkind == RELKIND_MATVIEW &&
17828 34 : tbinfo->relispopulated)
17829 : {
17830 30 : appendPQExpBufferStr(q, "\n-- For binary upgrade, mark materialized view as populated\n");
17831 30 : appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_class\n"
17832 : "SET relispopulated = 't'\n"
17833 : "WHERE oid = ");
17834 30 : appendStringLiteralAH(q, qualrelname, fout);
17835 30 : appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
17836 : }
17837 :
17838 : /*
17839 : * Dump additional per-column properties that we can't handle in the
17840 : * main CREATE TABLE command.
17841 : */
17842 51804 : for (j = 0; j < tbinfo->numatts; j++)
17843 : {
17844 : /* None of this applies to dropped columns */
17845 40426 : if (tbinfo->attisdropped[j])
17846 894 : continue;
17847 :
17848 : /*
17849 : * Dump per-column statistics information. We only issue an ALTER
17850 : * TABLE statement if the attstattarget entry for this column is
17851 : * not the default value.
17852 : */
17853 39532 : if (tbinfo->attstattarget[j] >= 0)
17854 64 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STATISTICS %d;\n",
17855 : foreign, qualrelname,
17856 64 : fmtId(tbinfo->attnames[j]),
17857 64 : tbinfo->attstattarget[j]);
17858 :
17859 : /*
17860 : * Dump per-column storage information. The statement is only
17861 : * dumped if the storage has been changed from the type's default.
17862 : */
17863 39532 : if (tbinfo->attstorage[j] != tbinfo->typstorage[j])
17864 : {
17865 158 : switch (tbinfo->attstorage[j])
17866 : {
17867 20 : case TYPSTORAGE_PLAIN:
17868 20 : storage = "PLAIN";
17869 20 : break;
17870 74 : case TYPSTORAGE_EXTERNAL:
17871 74 : storage = "EXTERNAL";
17872 74 : break;
17873 0 : case TYPSTORAGE_EXTENDED:
17874 0 : storage = "EXTENDED";
17875 0 : break;
17876 64 : case TYPSTORAGE_MAIN:
17877 64 : storage = "MAIN";
17878 64 : break;
17879 0 : default:
17880 0 : storage = NULL;
17881 : }
17882 :
17883 : /*
17884 : * Only dump the statement if it's a storage type we recognize
17885 : */
17886 158 : if (storage != NULL)
17887 158 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET STORAGE %s;\n",
17888 : foreign, qualrelname,
17889 158 : fmtId(tbinfo->attnames[j]),
17890 : storage);
17891 : }
17892 :
17893 : /*
17894 : * Dump per-column compression, if it's been set.
17895 : */
17896 39532 : if (!dopt->no_toast_compression)
17897 : {
17898 : const char *cmname;
17899 :
17900 39344 : switch (tbinfo->attcompression[j])
17901 : {
17902 142 : case 'p':
17903 142 : cmname = "pglz";
17904 142 : break;
17905 78 : case 'l':
17906 78 : cmname = "lz4";
17907 78 : break;
17908 39124 : default:
17909 39124 : cmname = NULL;
17910 39124 : break;
17911 : }
17912 :
17913 39344 : if (cmname != NULL)
17914 220 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET COMPRESSION %s;\n",
17915 : foreign, qualrelname,
17916 220 : fmtId(tbinfo->attnames[j]),
17917 : cmname);
17918 : }
17919 :
17920 : /*
17921 : * Dump per-column attributes.
17922 : */
17923 39532 : if (tbinfo->attoptions[j][0] != '\0')
17924 64 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET (%s);\n",
17925 : foreign, qualrelname,
17926 64 : fmtId(tbinfo->attnames[j]),
17927 64 : tbinfo->attoptions[j]);
17928 :
17929 : /*
17930 : * Dump per-column fdw options.
17931 : */
17932 39532 : if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
17933 68 : tbinfo->attfdwoptions[j][0] != '\0')
17934 64 : appendPQExpBuffer(q,
17935 : "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n"
17936 : " %s\n"
17937 : ");\n",
17938 : qualrelname,
17939 64 : fmtId(tbinfo->attnames[j]),
17940 64 : tbinfo->attfdwoptions[j]);
17941 : } /* end loop over columns */
17942 :
17943 11378 : free(partkeydef);
17944 11378 : free(ftoptions);
17945 11378 : free(srvname);
17946 : }
17947 :
17948 : /*
17949 : * dump properties we only have ALTER TABLE syntax for
17950 : */
17951 12444 : if ((tbinfo->relkind == RELKIND_RELATION ||
17952 2956 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE ||
17953 1804 : tbinfo->relkind == RELKIND_MATVIEW) &&
17954 11310 : tbinfo->relreplident != REPLICA_IDENTITY_DEFAULT)
17955 : {
17956 384 : if (tbinfo->relreplident == REPLICA_IDENTITY_INDEX)
17957 : {
17958 : /* nothing to do, will be set when the index is dumped */
17959 : }
17960 384 : else if (tbinfo->relreplident == REPLICA_IDENTITY_NOTHING)
17961 : {
17962 384 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY NOTHING;\n",
17963 : qualrelname);
17964 : }
17965 0 : else if (tbinfo->relreplident == REPLICA_IDENTITY_FULL)
17966 : {
17967 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY FULL;\n",
17968 : qualrelname);
17969 : }
17970 : }
17971 :
17972 12444 : if (tbinfo->forcerowsec)
17973 10 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s FORCE ROW LEVEL SECURITY;\n",
17974 : qualrelname);
17975 :
17976 12444 : if (dopt->binary_upgrade)
17977 1742 : binary_upgrade_extension_member(q, &tbinfo->dobj,
17978 : reltypename, qrelname,
17979 1742 : tbinfo->dobj.namespace->dobj.name);
17980 :
17981 12444 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
17982 : {
17983 12444 : char *tablespace = NULL;
17984 12444 : char *tableam = NULL;
17985 :
17986 : /*
17987 : * _selectTablespace() relies on tablespace-enabled objects in the
17988 : * default tablespace to have a tablespace of "" (empty string) versus
17989 : * non-tablespace-enabled objects to have a tablespace of NULL.
17990 : * getTables() sets tbinfo->reltablespace to "" for the default
17991 : * tablespace (not NULL).
17992 : */
17993 12444 : if (RELKIND_HAS_TABLESPACE(tbinfo->relkind))
17994 11310 : tablespace = tbinfo->reltablespace;
17995 :
17996 12444 : if (RELKIND_HAS_TABLE_AM(tbinfo->relkind) ||
17997 2286 : tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
17998 11310 : tableam = tbinfo->amname;
17999 :
18000 12444 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
18001 12444 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
18002 : .namespace = tbinfo->dobj.namespace->dobj.name,
18003 : .tablespace = tablespace,
18004 : .tableam = tableam,
18005 : .relkind = tbinfo->relkind,
18006 : .owner = tbinfo->rolname,
18007 : .description = reltypename,
18008 : .section = tbinfo->postponed_def ?
18009 : SECTION_POST_DATA : SECTION_PRE_DATA,
18010 : .createStmt = q->data,
18011 : .dropStmt = delq->data));
18012 : }
18013 :
18014 : /* Dump Table Comments */
18015 12444 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18016 148 : dumpTableComment(fout, tbinfo, reltypename);
18017 :
18018 : /* Dump Table Security Labels */
18019 12444 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
18020 0 : dumpTableSecLabel(fout, tbinfo, reltypename);
18021 :
18022 : /*
18023 : * Dump comments for not-null constraints that aren't to be dumped
18024 : * separately (those are processed by collectComments/dumpComment).
18025 : */
18026 12444 : if (!fout->dopt->no_comments && dopt->dumpSchema &&
18027 12444 : fout->remoteVersion >= 180000)
18028 : {
18029 12444 : PQExpBuffer comment = NULL;
18030 12444 : PQExpBuffer tag = NULL;
18031 :
18032 59716 : for (j = 0; j < tbinfo->numatts; j++)
18033 : {
18034 47272 : if (tbinfo->notnull_constrs[j] != NULL &&
18035 4816 : tbinfo->notnull_comment[j] != NULL)
18036 : {
18037 84 : if (comment == NULL)
18038 : {
18039 84 : comment = createPQExpBuffer();
18040 84 : tag = createPQExpBuffer();
18041 : }
18042 : else
18043 : {
18044 0 : resetPQExpBuffer(comment);
18045 0 : resetPQExpBuffer(tag);
18046 : }
18047 :
18048 84 : appendPQExpBuffer(comment, "COMMENT ON CONSTRAINT %s ON %s IS ",
18049 84 : fmtId(tbinfo->notnull_constrs[j]), qualrelname);
18050 84 : appendStringLiteralAH(comment, tbinfo->notnull_comment[j], fout);
18051 84 : appendPQExpBufferStr(comment, ";\n");
18052 :
18053 84 : appendPQExpBuffer(tag, "CONSTRAINT %s ON %s",
18054 84 : fmtId(tbinfo->notnull_constrs[j]), qrelname);
18055 :
18056 84 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
18057 84 : ARCHIVE_OPTS(.tag = tag->data,
18058 : .namespace = tbinfo->dobj.namespace->dobj.name,
18059 : .owner = tbinfo->rolname,
18060 : .description = "COMMENT",
18061 : .section = SECTION_NONE,
18062 : .createStmt = comment->data,
18063 : .deps = &(tbinfo->dobj.dumpId),
18064 : .nDeps = 1));
18065 : }
18066 : }
18067 :
18068 12444 : destroyPQExpBuffer(comment);
18069 12444 : destroyPQExpBuffer(tag);
18070 : }
18071 :
18072 : /* Dump comments on inlined table constraints */
18073 13590 : for (j = 0; j < tbinfo->ncheck; j++)
18074 : {
18075 1146 : ConstraintInfo *constr = &(tbinfo->checkexprs[j]);
18076 :
18077 1146 : if (constr->separate || !constr->conislocal)
18078 488 : continue;
18079 :
18080 658 : if (constr->dobj.dump & DUMP_COMPONENT_COMMENT)
18081 74 : dumpTableConstraintComment(fout, constr);
18082 : }
18083 :
18084 12444 : destroyPQExpBuffer(q);
18085 12444 : destroyPQExpBuffer(delq);
18086 12444 : destroyPQExpBuffer(extra);
18087 12444 : free(qrelname);
18088 12444 : free(qualrelname);
18089 12444 : }
18090 :
18091 : /*
18092 : * dumpTableAttach
18093 : * write to fout the commands to attach a child partition
18094 : *
18095 : * Child partitions are always made by creating them separately
18096 : * and then using ATTACH PARTITION, rather than using
18097 : * CREATE TABLE ... PARTITION OF. This is important for preserving
18098 : * any possible discrepancy in column layout, to allow assigning the
18099 : * correct tablespace if different, and so that it's possible to restore
18100 : * a partition without restoring its parent. (You'll get an error from
18101 : * the ATTACH PARTITION command, but that can be ignored, or skipped
18102 : * using "pg_restore -L" if you prefer.) The last point motivates
18103 : * treating ATTACH PARTITION as a completely separate ArchiveEntry
18104 : * rather than emitting it within the child partition's ArchiveEntry.
18105 : */
18106 : static void
18107 2782 : dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
18108 : {
18109 2782 : DumpOptions *dopt = fout->dopt;
18110 : PQExpBuffer q;
18111 : PGresult *res;
18112 : char *partbound;
18113 :
18114 : /* Do nothing if not dumping schema */
18115 2782 : if (!dopt->dumpSchema)
18116 108 : return;
18117 :
18118 2674 : q = createPQExpBuffer();
18119 :
18120 2674 : if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
18121 : {
18122 : /* Set up query for partbound details */
18123 86 : appendPQExpBufferStr(q,
18124 : "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
18125 :
18126 86 : appendPQExpBufferStr(q,
18127 : "SELECT pg_get_expr(c.relpartbound, c.oid) "
18128 : "FROM pg_class c "
18129 : "WHERE c.oid = $1");
18130 :
18131 86 : ExecuteSqlStatement(fout, q->data);
18132 :
18133 86 : fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
18134 : }
18135 :
18136 2674 : printfPQExpBuffer(q,
18137 : "EXECUTE dumpTableAttach('%u')",
18138 2674 : attachinfo->partitionTbl->dobj.catId.oid);
18139 :
18140 2674 : res = ExecuteSqlQueryForSingleRow(fout, q->data);
18141 2674 : partbound = PQgetvalue(res, 0, 0);
18142 :
18143 : /* Perform ALTER TABLE on the parent */
18144 2674 : printfPQExpBuffer(q,
18145 : "ALTER TABLE ONLY %s ",
18146 2674 : fmtQualifiedDumpable(attachinfo->parentTbl));
18147 2674 : appendPQExpBuffer(q,
18148 : "ATTACH PARTITION %s %s;\n",
18149 2674 : fmtQualifiedDumpable(attachinfo->partitionTbl),
18150 : partbound);
18151 :
18152 : /*
18153 : * There is no point in creating a drop query as the drop is done by table
18154 : * drop. (If you think to change this, see also _printTocEntry().)
18155 : * Although this object doesn't really have ownership as such, set the
18156 : * owner field anyway to ensure that the command is run by the correct
18157 : * role at restore time.
18158 : */
18159 2674 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18160 2674 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18161 : .namespace = attachinfo->dobj.namespace->dobj.name,
18162 : .owner = attachinfo->partitionTbl->rolname,
18163 : .description = "TABLE ATTACH",
18164 : .section = SECTION_PRE_DATA,
18165 : .createStmt = q->data));
18166 :
18167 2674 : PQclear(res);
18168 2674 : destroyPQExpBuffer(q);
18169 : }
18170 :
18171 : /*
18172 : * dumpAttrDef --- dump an attribute's default-value declaration
18173 : */
18174 : static void
18175 2064 : dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
18176 : {
18177 2064 : DumpOptions *dopt = fout->dopt;
18178 2064 : TableInfo *tbinfo = adinfo->adtable;
18179 2064 : int adnum = adinfo->adnum;
18180 : PQExpBuffer q;
18181 : PQExpBuffer delq;
18182 : char *qualrelname;
18183 : char *tag;
18184 : char *foreign;
18185 :
18186 : /* Do nothing if not dumping schema */
18187 2064 : if (!dopt->dumpSchema)
18188 0 : return;
18189 :
18190 : /* Skip if not "separate"; it was dumped in the table's definition */
18191 2064 : if (!adinfo->separate)
18192 1732 : return;
18193 :
18194 332 : q = createPQExpBuffer();
18195 332 : delq = createPQExpBuffer();
18196 :
18197 332 : qualrelname = pg_strdup(fmtQualifiedDumpable(tbinfo));
18198 :
18199 332 : foreign = tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18200 :
18201 332 : appendPQExpBuffer(q,
18202 : "ALTER %sTABLE ONLY %s ALTER COLUMN %s SET DEFAULT %s;\n",
18203 332 : foreign, qualrelname, fmtId(tbinfo->attnames[adnum - 1]),
18204 332 : adinfo->adef_expr);
18205 :
18206 332 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ALTER COLUMN %s DROP DEFAULT;\n",
18207 : foreign, qualrelname,
18208 332 : fmtId(tbinfo->attnames[adnum - 1]));
18209 :
18210 332 : tag = psprintf("%s %s", tbinfo->dobj.name, tbinfo->attnames[adnum - 1]);
18211 :
18212 332 : if (adinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18213 332 : ArchiveEntry(fout, adinfo->dobj.catId, adinfo->dobj.dumpId,
18214 332 : ARCHIVE_OPTS(.tag = tag,
18215 : .namespace = tbinfo->dobj.namespace->dobj.name,
18216 : .owner = tbinfo->rolname,
18217 : .description = "DEFAULT",
18218 : .section = SECTION_PRE_DATA,
18219 : .createStmt = q->data,
18220 : .dropStmt = delq->data));
18221 :
18222 332 : free(tag);
18223 332 : destroyPQExpBuffer(q);
18224 332 : destroyPQExpBuffer(delq);
18225 332 : free(qualrelname);
18226 : }
18227 :
18228 : /*
18229 : * getAttrName: extract the correct name for an attribute
18230 : *
18231 : * The array tblInfo->attnames[] only provides names of user attributes;
18232 : * if a system attribute number is supplied, we have to fake it.
18233 : * We also do a little bit of bounds checking for safety's sake.
18234 : */
18235 : static const char *
18236 4110 : getAttrName(int attrnum, const TableInfo *tblInfo)
18237 : {
18238 4110 : if (attrnum > 0 && attrnum <= tblInfo->numatts)
18239 4110 : return tblInfo->attnames[attrnum - 1];
18240 0 : switch (attrnum)
18241 : {
18242 0 : case SelfItemPointerAttributeNumber:
18243 0 : return "ctid";
18244 0 : case MinTransactionIdAttributeNumber:
18245 0 : return "xmin";
18246 0 : case MinCommandIdAttributeNumber:
18247 0 : return "cmin";
18248 0 : case MaxTransactionIdAttributeNumber:
18249 0 : return "xmax";
18250 0 : case MaxCommandIdAttributeNumber:
18251 0 : return "cmax";
18252 0 : case TableOidAttributeNumber:
18253 0 : return "tableoid";
18254 : }
18255 0 : pg_fatal("invalid column number %d for table \"%s\"",
18256 : attrnum, tblInfo->dobj.name);
18257 : return NULL; /* keep compiler quiet */
18258 : }
18259 :
18260 : /*
18261 : * dumpIndex
18262 : * write out to fout a user-defined index
18263 : */
18264 : static void
18265 5200 : dumpIndex(Archive *fout, const IndxInfo *indxinfo)
18266 : {
18267 5200 : DumpOptions *dopt = fout->dopt;
18268 5200 : TableInfo *tbinfo = indxinfo->indextable;
18269 5200 : bool is_constraint = (indxinfo->indexconstraint != 0);
18270 : PQExpBuffer q;
18271 : PQExpBuffer delq;
18272 : char *qindxname;
18273 : char *qqindxname;
18274 :
18275 : /* Do nothing if not dumping schema */
18276 5200 : if (!dopt->dumpSchema)
18277 234 : return;
18278 :
18279 4966 : q = createPQExpBuffer();
18280 4966 : delq = createPQExpBuffer();
18281 :
18282 4966 : qindxname = pg_strdup(fmtId(indxinfo->dobj.name));
18283 4966 : qqindxname = pg_strdup(fmtQualifiedDumpable(indxinfo));
18284 :
18285 : /*
18286 : * If there's an associated constraint, don't dump the index per se, but
18287 : * do dump any comment for it. (This is safe because dependency ordering
18288 : * will have ensured the constraint is emitted first.) Note that the
18289 : * emitted comment has to be shown as depending on the constraint, not the
18290 : * index, in such cases.
18291 : */
18292 4966 : if (!is_constraint)
18293 : {
18294 2090 : char *indstatcols = indxinfo->indstatcols;
18295 2090 : char *indstatvals = indxinfo->indstatvals;
18296 2090 : char **indstatcolsarray = NULL;
18297 2090 : char **indstatvalsarray = NULL;
18298 2090 : int nstatcols = 0;
18299 2090 : int nstatvals = 0;
18300 :
18301 2090 : if (dopt->binary_upgrade)
18302 316 : binary_upgrade_set_pg_class_oids(fout, q,
18303 316 : indxinfo->dobj.catId.oid);
18304 :
18305 : /* Plain secondary index */
18306 2090 : appendPQExpBuffer(q, "%s;\n", indxinfo->indexdef);
18307 :
18308 : /*
18309 : * Append ALTER TABLE commands as needed to set properties that we
18310 : * only have ALTER TABLE syntax for. Keep this in sync with the
18311 : * similar code in dumpConstraint!
18312 : */
18313 :
18314 : /* If the index is clustered, we need to record that. */
18315 2090 : if (indxinfo->indisclustered)
18316 : {
18317 0 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18318 0 : fmtQualifiedDumpable(tbinfo));
18319 : /* index name is not qualified in this syntax */
18320 0 : appendPQExpBuffer(q, " ON %s;\n",
18321 : qindxname);
18322 : }
18323 :
18324 : /*
18325 : * If the index has any statistics on some of its columns, generate
18326 : * the associated ALTER INDEX queries.
18327 : */
18328 2090 : if (strlen(indstatcols) != 0 || strlen(indstatvals) != 0)
18329 : {
18330 : int j;
18331 :
18332 64 : if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
18333 0 : pg_fatal("could not parse index statistic columns");
18334 64 : if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
18335 0 : pg_fatal("could not parse index statistic values");
18336 64 : if (nstatcols != nstatvals)
18337 0 : pg_fatal("mismatched number of columns and values for index statistics");
18338 :
18339 192 : for (j = 0; j < nstatcols; j++)
18340 : {
18341 128 : appendPQExpBuffer(q, "ALTER INDEX %s ", qqindxname);
18342 :
18343 : /*
18344 : * Note that this is a column number, so no quotes should be
18345 : * used.
18346 : */
18347 128 : appendPQExpBuffer(q, "ALTER COLUMN %s ",
18348 128 : indstatcolsarray[j]);
18349 128 : appendPQExpBuffer(q, "SET STATISTICS %s;\n",
18350 128 : indstatvalsarray[j]);
18351 : }
18352 : }
18353 :
18354 : /* Indexes can depend on extensions */
18355 2090 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18356 : "pg_catalog.pg_class",
18357 : "INDEX", qqindxname);
18358 :
18359 : /* If the index defines identity, we need to record that. */
18360 2090 : if (indxinfo->indisreplident)
18361 : {
18362 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18363 0 : fmtQualifiedDumpable(tbinfo));
18364 : /* index name is not qualified in this syntax */
18365 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18366 : qindxname);
18367 : }
18368 :
18369 : /*
18370 : * If this index is a member of a partitioned index, the backend will
18371 : * not allow us to drop it separately, so don't try. It will go away
18372 : * automatically when we drop either the index's table or the
18373 : * partitioned index. (If, in a selective restore with --clean, we
18374 : * drop neither of those, then this index will not be dropped either.
18375 : * But that's fine, and even if you think it's not, the backend won't
18376 : * let us do differently.)
18377 : */
18378 2090 : if (indxinfo->parentidx == 0)
18379 1726 : appendPQExpBuffer(delq, "DROP INDEX %s;\n", qqindxname);
18380 :
18381 2090 : if (indxinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18382 2090 : ArchiveEntry(fout, indxinfo->dobj.catId, indxinfo->dobj.dumpId,
18383 2090 : ARCHIVE_OPTS(.tag = indxinfo->dobj.name,
18384 : .namespace = tbinfo->dobj.namespace->dobj.name,
18385 : .tablespace = indxinfo->tablespace,
18386 : .owner = tbinfo->rolname,
18387 : .description = "INDEX",
18388 : .section = SECTION_POST_DATA,
18389 : .createStmt = q->data,
18390 : .dropStmt = delq->data));
18391 :
18392 2090 : free(indstatcolsarray);
18393 2090 : free(indstatvalsarray);
18394 : }
18395 :
18396 : /* Dump Index Comments */
18397 4966 : if (indxinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18398 30 : dumpComment(fout, "INDEX", qindxname,
18399 30 : tbinfo->dobj.namespace->dobj.name,
18400 : tbinfo->rolname,
18401 : indxinfo->dobj.catId, 0,
18402 : is_constraint ? indxinfo->indexconstraint :
18403 : indxinfo->dobj.dumpId);
18404 :
18405 4966 : destroyPQExpBuffer(q);
18406 4966 : destroyPQExpBuffer(delq);
18407 4966 : free(qindxname);
18408 4966 : free(qqindxname);
18409 : }
18410 :
18411 : /*
18412 : * dumpIndexAttach
18413 : * write out to fout a partitioned-index attachment clause
18414 : */
18415 : static void
18416 1148 : dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
18417 : {
18418 : /* Do nothing if not dumping schema */
18419 1148 : if (!fout->dopt->dumpSchema)
18420 96 : return;
18421 :
18422 1052 : if (attachinfo->partitionIdx->dobj.dump & DUMP_COMPONENT_DEFINITION)
18423 : {
18424 1052 : PQExpBuffer q = createPQExpBuffer();
18425 :
18426 1052 : appendPQExpBuffer(q, "ALTER INDEX %s ",
18427 1052 : fmtQualifiedDumpable(attachinfo->parentIdx));
18428 1052 : appendPQExpBuffer(q, "ATTACH PARTITION %s;\n",
18429 1052 : fmtQualifiedDumpable(attachinfo->partitionIdx));
18430 :
18431 : /*
18432 : * There is no need for a dropStmt since the drop is done implicitly
18433 : * when we drop either the index's table or the partitioned index.
18434 : * Moreover, since there's no ALTER INDEX DETACH PARTITION command,
18435 : * there's no way to do it anyway. (If you think to change this,
18436 : * consider also what to do with --if-exists.)
18437 : *
18438 : * Although this object doesn't really have ownership as such, set the
18439 : * owner field anyway to ensure that the command is run by the correct
18440 : * role at restore time.
18441 : */
18442 1052 : ArchiveEntry(fout, attachinfo->dobj.catId, attachinfo->dobj.dumpId,
18443 1052 : ARCHIVE_OPTS(.tag = attachinfo->dobj.name,
18444 : .namespace = attachinfo->dobj.namespace->dobj.name,
18445 : .owner = attachinfo->parentIdx->indextable->rolname,
18446 : .description = "INDEX ATTACH",
18447 : .section = SECTION_POST_DATA,
18448 : .createStmt = q->data));
18449 :
18450 1052 : destroyPQExpBuffer(q);
18451 : }
18452 : }
18453 :
18454 : /*
18455 : * dumpStatisticsExt
18456 : * write out to fout an extended statistics object
18457 : */
18458 : static void
18459 266 : dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
18460 : {
18461 266 : DumpOptions *dopt = fout->dopt;
18462 : PQExpBuffer q;
18463 : PQExpBuffer delq;
18464 : PQExpBuffer query;
18465 : char *qstatsextname;
18466 : PGresult *res;
18467 : char *stxdef;
18468 :
18469 : /* Do nothing if not dumping schema */
18470 266 : if (!dopt->dumpSchema)
18471 36 : return;
18472 :
18473 230 : q = createPQExpBuffer();
18474 230 : delq = createPQExpBuffer();
18475 230 : query = createPQExpBuffer();
18476 :
18477 230 : qstatsextname = pg_strdup(fmtId(statsextinfo->dobj.name));
18478 :
18479 230 : appendPQExpBuffer(query, "SELECT "
18480 : "pg_catalog.pg_get_statisticsobjdef('%u'::pg_catalog.oid)",
18481 230 : statsextinfo->dobj.catId.oid);
18482 :
18483 230 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
18484 :
18485 230 : stxdef = PQgetvalue(res, 0, 0);
18486 :
18487 : /* Result of pg_get_statisticsobjdef is complete except for semicolon */
18488 230 : appendPQExpBuffer(q, "%s;\n", stxdef);
18489 :
18490 : /*
18491 : * We only issue an ALTER STATISTICS statement if the stxstattarget entry
18492 : * for this statistics object is not the default value.
18493 : */
18494 230 : if (statsextinfo->stattarget >= 0)
18495 : {
18496 64 : appendPQExpBuffer(q, "ALTER STATISTICS %s ",
18497 64 : fmtQualifiedDumpable(statsextinfo));
18498 64 : appendPQExpBuffer(q, "SET STATISTICS %d;\n",
18499 64 : statsextinfo->stattarget);
18500 : }
18501 :
18502 230 : appendPQExpBuffer(delq, "DROP STATISTICS %s;\n",
18503 230 : fmtQualifiedDumpable(statsextinfo));
18504 :
18505 230 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18506 230 : ArchiveEntry(fout, statsextinfo->dobj.catId,
18507 230 : statsextinfo->dobj.dumpId,
18508 230 : ARCHIVE_OPTS(.tag = statsextinfo->dobj.name,
18509 : .namespace = statsextinfo->dobj.namespace->dobj.name,
18510 : .owner = statsextinfo->rolname,
18511 : .description = "STATISTICS",
18512 : .section = SECTION_POST_DATA,
18513 : .createStmt = q->data,
18514 : .dropStmt = delq->data));
18515 :
18516 : /* Dump Statistics Comments */
18517 230 : if (statsextinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18518 0 : dumpComment(fout, "STATISTICS", qstatsextname,
18519 0 : statsextinfo->dobj.namespace->dobj.name,
18520 0 : statsextinfo->rolname,
18521 : statsextinfo->dobj.catId, 0,
18522 0 : statsextinfo->dobj.dumpId);
18523 :
18524 230 : PQclear(res);
18525 230 : destroyPQExpBuffer(q);
18526 230 : destroyPQExpBuffer(delq);
18527 230 : destroyPQExpBuffer(query);
18528 230 : free(qstatsextname);
18529 : }
18530 :
18531 : /*
18532 : * dumpConstraint
18533 : * write out to fout a user-defined constraint
18534 : */
18535 : static void
18536 4978 : dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
18537 : {
18538 4978 : DumpOptions *dopt = fout->dopt;
18539 4978 : TableInfo *tbinfo = coninfo->contable;
18540 : PQExpBuffer q;
18541 : PQExpBuffer delq;
18542 4978 : char *tag = NULL;
18543 : char *foreign;
18544 :
18545 : /* Do nothing if not dumping schema */
18546 4978 : if (!dopt->dumpSchema)
18547 196 : return;
18548 :
18549 4782 : q = createPQExpBuffer();
18550 4782 : delq = createPQExpBuffer();
18551 :
18552 9256 : foreign = tbinfo &&
18553 4782 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "";
18554 :
18555 4782 : if (coninfo->contype == 'p' ||
18556 2384 : coninfo->contype == 'u' ||
18557 1926 : coninfo->contype == 'x')
18558 2876 : {
18559 : /* Index-related constraint */
18560 : IndxInfo *indxinfo;
18561 : int k;
18562 :
18563 2876 : indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
18564 :
18565 2876 : if (indxinfo == NULL)
18566 0 : pg_fatal("missing index for constraint \"%s\"",
18567 : coninfo->dobj.name);
18568 :
18569 2876 : if (dopt->binary_upgrade)
18570 300 : binary_upgrade_set_pg_class_oids(fout, q,
18571 : indxinfo->dobj.catId.oid);
18572 :
18573 2876 : appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s\n", foreign,
18574 2876 : fmtQualifiedDumpable(tbinfo));
18575 2876 : appendPQExpBuffer(q, " ADD CONSTRAINT %s ",
18576 2876 : fmtId(coninfo->dobj.name));
18577 :
18578 2876 : if (coninfo->condef)
18579 : {
18580 : /* pg_get_constraintdef should have provided everything */
18581 20 : appendPQExpBuffer(q, "%s;\n", coninfo->condef);
18582 : }
18583 : else
18584 : {
18585 2856 : appendPQExpBufferStr(q,
18586 2856 : coninfo->contype == 'p' ? "PRIMARY KEY" : "UNIQUE");
18587 :
18588 : /*
18589 : * PRIMARY KEY constraints should not be using NULLS NOT DISTINCT
18590 : * indexes. Being able to create this was fixed, but we need to
18591 : * make the index distinct in order to be able to restore the
18592 : * dump.
18593 : */
18594 2856 : if (indxinfo->indnullsnotdistinct && coninfo->contype != 'p')
18595 0 : appendPQExpBufferStr(q, " NULLS NOT DISTINCT");
18596 2856 : appendPQExpBufferStr(q, " (");
18597 6886 : for (k = 0; k < indxinfo->indnkeyattrs; k++)
18598 : {
18599 4030 : int indkey = (int) indxinfo->indkeys[k];
18600 : const char *attname;
18601 :
18602 4030 : if (indkey == InvalidAttrNumber)
18603 0 : break;
18604 4030 : attname = getAttrName(indkey, tbinfo);
18605 :
18606 4030 : appendPQExpBuffer(q, "%s%s",
18607 : (k == 0) ? "" : ", ",
18608 : fmtId(attname));
18609 : }
18610 2856 : if (coninfo->conperiod)
18611 208 : appendPQExpBufferStr(q, " WITHOUT OVERLAPS");
18612 :
18613 2856 : if (indxinfo->indnkeyattrs < indxinfo->indnattrs)
18614 40 : appendPQExpBufferStr(q, ") INCLUDE (");
18615 :
18616 2936 : for (k = indxinfo->indnkeyattrs; k < indxinfo->indnattrs; k++)
18617 : {
18618 80 : int indkey = (int) indxinfo->indkeys[k];
18619 : const char *attname;
18620 :
18621 80 : if (indkey == InvalidAttrNumber)
18622 0 : break;
18623 80 : attname = getAttrName(indkey, tbinfo);
18624 :
18625 160 : appendPQExpBuffer(q, "%s%s",
18626 80 : (k == indxinfo->indnkeyattrs) ? "" : ", ",
18627 : fmtId(attname));
18628 : }
18629 :
18630 2856 : appendPQExpBufferChar(q, ')');
18631 :
18632 2856 : if (nonemptyReloptions(indxinfo->indreloptions))
18633 : {
18634 0 : appendPQExpBufferStr(q, " WITH (");
18635 0 : appendReloptionsArrayAH(q, indxinfo->indreloptions, "", fout);
18636 0 : appendPQExpBufferChar(q, ')');
18637 : }
18638 :
18639 2856 : if (coninfo->condeferrable)
18640 : {
18641 50 : appendPQExpBufferStr(q, " DEFERRABLE");
18642 50 : if (coninfo->condeferred)
18643 30 : appendPQExpBufferStr(q, " INITIALLY DEFERRED");
18644 : }
18645 :
18646 2856 : appendPQExpBufferStr(q, ";\n");
18647 : }
18648 :
18649 : /*
18650 : * Append ALTER TABLE commands as needed to set properties that we
18651 : * only have ALTER TABLE syntax for. Keep this in sync with the
18652 : * similar code in dumpIndex!
18653 : */
18654 :
18655 : /* If the index is clustered, we need to record that. */
18656 2876 : if (indxinfo->indisclustered)
18657 : {
18658 64 : appendPQExpBuffer(q, "\nALTER TABLE %s CLUSTER",
18659 64 : fmtQualifiedDumpable(tbinfo));
18660 : /* index name is not qualified in this syntax */
18661 64 : appendPQExpBuffer(q, " ON %s;\n",
18662 64 : fmtId(indxinfo->dobj.name));
18663 : }
18664 :
18665 : /* If the index defines identity, we need to record that. */
18666 2876 : if (indxinfo->indisreplident)
18667 : {
18668 0 : appendPQExpBuffer(q, "\nALTER TABLE ONLY %s REPLICA IDENTITY USING",
18669 0 : fmtQualifiedDumpable(tbinfo));
18670 : /* index name is not qualified in this syntax */
18671 0 : appendPQExpBuffer(q, " INDEX %s;\n",
18672 0 : fmtId(indxinfo->dobj.name));
18673 : }
18674 :
18675 : /* Indexes can depend on extensions */
18676 2876 : append_depends_on_extension(fout, q, &indxinfo->dobj,
18677 : "pg_catalog.pg_class", "INDEX",
18678 2876 : fmtQualifiedDumpable(indxinfo));
18679 :
18680 2876 : appendPQExpBuffer(delq, "ALTER %sTABLE ONLY %s ", foreign,
18681 2876 : fmtQualifiedDumpable(tbinfo));
18682 2876 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18683 2876 : fmtId(coninfo->dobj.name));
18684 :
18685 2876 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18686 :
18687 2876 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18688 2876 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18689 2876 : ARCHIVE_OPTS(.tag = tag,
18690 : .namespace = tbinfo->dobj.namespace->dobj.name,
18691 : .tablespace = indxinfo->tablespace,
18692 : .owner = tbinfo->rolname,
18693 : .description = "CONSTRAINT",
18694 : .section = SECTION_POST_DATA,
18695 : .createStmt = q->data,
18696 : .dropStmt = delq->data));
18697 : }
18698 1906 : else if (coninfo->contype == 'f')
18699 : {
18700 : char *only;
18701 :
18702 : /*
18703 : * Foreign keys on partitioned tables are always declared as
18704 : * inheriting to partitions; for all other cases, emit them as
18705 : * applying ONLY directly to the named table, because that's how they
18706 : * work for regular inherited tables.
18707 : */
18708 318 : only = tbinfo->relkind == RELKIND_PARTITIONED_TABLE ? "" : "ONLY ";
18709 :
18710 : /*
18711 : * XXX Potentially wrap in a 'SET CONSTRAINTS OFF' block so that the
18712 : * current table data is not processed
18713 : */
18714 318 : appendPQExpBuffer(q, "ALTER %sTABLE %s%s\n", foreign,
18715 318 : only, fmtQualifiedDumpable(tbinfo));
18716 318 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18717 318 : fmtId(coninfo->dobj.name),
18718 318 : coninfo->condef);
18719 :
18720 318 : appendPQExpBuffer(delq, "ALTER %sTABLE %s%s ", foreign,
18721 318 : only, fmtQualifiedDumpable(tbinfo));
18722 318 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18723 318 : fmtId(coninfo->dobj.name));
18724 :
18725 318 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18726 :
18727 318 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18728 318 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18729 318 : ARCHIVE_OPTS(.tag = tag,
18730 : .namespace = tbinfo->dobj.namespace->dobj.name,
18731 : .owner = tbinfo->rolname,
18732 : .description = "FK CONSTRAINT",
18733 : .section = SECTION_POST_DATA,
18734 : .createStmt = q->data,
18735 : .dropStmt = delq->data));
18736 : }
18737 1588 : else if ((coninfo->contype == 'c' || coninfo->contype == 'n') && tbinfo)
18738 : {
18739 : /* CHECK or invalid not-null constraint on a table */
18740 :
18741 : /* Ignore if not to be dumped separately, or if it was inherited */
18742 1280 : if (coninfo->separate && coninfo->conislocal)
18743 : {
18744 : const char *keyword;
18745 :
18746 214 : if (coninfo->contype == 'c')
18747 90 : keyword = "CHECK CONSTRAINT";
18748 : else
18749 124 : keyword = "CONSTRAINT";
18750 :
18751 : /* not ONLY since we want it to propagate to children */
18752 214 : appendPQExpBuffer(q, "ALTER %sTABLE %s\n", foreign,
18753 214 : fmtQualifiedDumpable(tbinfo));
18754 214 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18755 214 : fmtId(coninfo->dobj.name),
18756 214 : coninfo->condef);
18757 :
18758 214 : appendPQExpBuffer(delq, "ALTER %sTABLE %s ", foreign,
18759 214 : fmtQualifiedDumpable(tbinfo));
18760 214 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18761 214 : fmtId(coninfo->dobj.name));
18762 :
18763 214 : tag = psprintf("%s %s", tbinfo->dobj.name, coninfo->dobj.name);
18764 :
18765 214 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18766 214 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18767 214 : ARCHIVE_OPTS(.tag = tag,
18768 : .namespace = tbinfo->dobj.namespace->dobj.name,
18769 : .owner = tbinfo->rolname,
18770 : .description = keyword,
18771 : .section = SECTION_POST_DATA,
18772 : .createStmt = q->data,
18773 : .dropStmt = delq->data));
18774 : }
18775 : }
18776 308 : else if (tbinfo == NULL)
18777 : {
18778 : /* CHECK, NOT NULL constraint on a domain */
18779 308 : TypeInfo *tyinfo = coninfo->condomain;
18780 :
18781 : Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
18782 :
18783 : /* Ignore if not to be dumped separately */
18784 308 : if (coninfo->separate)
18785 : {
18786 : const char *keyword;
18787 :
18788 10 : if (coninfo->contype == 'c')
18789 10 : keyword = "CHECK CONSTRAINT";
18790 : else
18791 0 : keyword = "CONSTRAINT";
18792 :
18793 10 : appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
18794 10 : fmtQualifiedDumpable(tyinfo));
18795 10 : appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
18796 10 : fmtId(coninfo->dobj.name),
18797 10 : coninfo->condef);
18798 :
18799 10 : appendPQExpBuffer(delq, "ALTER DOMAIN %s ",
18800 10 : fmtQualifiedDumpable(tyinfo));
18801 10 : appendPQExpBuffer(delq, "DROP CONSTRAINT %s;\n",
18802 10 : fmtId(coninfo->dobj.name));
18803 :
18804 10 : tag = psprintf("%s %s", tyinfo->dobj.name, coninfo->dobj.name);
18805 :
18806 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
18807 10 : ArchiveEntry(fout, coninfo->dobj.catId, coninfo->dobj.dumpId,
18808 10 : ARCHIVE_OPTS(.tag = tag,
18809 : .namespace = tyinfo->dobj.namespace->dobj.name,
18810 : .owner = tyinfo->rolname,
18811 : .description = keyword,
18812 : .section = SECTION_POST_DATA,
18813 : .createStmt = q->data,
18814 : .dropStmt = delq->data));
18815 :
18816 10 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18817 : {
18818 10 : PQExpBuffer conprefix = createPQExpBuffer();
18819 10 : char *qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
18820 :
18821 10 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
18822 10 : fmtId(coninfo->dobj.name));
18823 :
18824 10 : dumpComment(fout, conprefix->data, qtypname,
18825 10 : tyinfo->dobj.namespace->dobj.name,
18826 : tyinfo->rolname,
18827 10 : coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
18828 10 : destroyPQExpBuffer(conprefix);
18829 10 : free(qtypname);
18830 : }
18831 : }
18832 : }
18833 : else
18834 : {
18835 0 : pg_fatal("unrecognized constraint type: %c",
18836 : coninfo->contype);
18837 : }
18838 :
18839 : /* Dump Constraint Comments --- only works for table constraints */
18840 4782 : if (tbinfo && coninfo->separate &&
18841 3468 : coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18842 94 : dumpTableConstraintComment(fout, coninfo);
18843 :
18844 4782 : free(tag);
18845 4782 : destroyPQExpBuffer(q);
18846 4782 : destroyPQExpBuffer(delq);
18847 : }
18848 :
18849 : /*
18850 : * dumpTableConstraintComment --- dump a constraint's comment if any
18851 : *
18852 : * This is split out because we need the function in two different places
18853 : * depending on whether the constraint is dumped as part of CREATE TABLE
18854 : * or as a separate ALTER command.
18855 : */
18856 : static void
18857 168 : dumpTableConstraintComment(Archive *fout, const ConstraintInfo *coninfo)
18858 : {
18859 168 : TableInfo *tbinfo = coninfo->contable;
18860 168 : PQExpBuffer conprefix = createPQExpBuffer();
18861 : char *qtabname;
18862 :
18863 168 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
18864 :
18865 168 : appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
18866 168 : fmtId(coninfo->dobj.name));
18867 :
18868 168 : if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
18869 168 : dumpComment(fout, conprefix->data, qtabname,
18870 168 : tbinfo->dobj.namespace->dobj.name,
18871 : tbinfo->rolname,
18872 : coninfo->dobj.catId, 0,
18873 168 : coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId);
18874 :
18875 168 : destroyPQExpBuffer(conprefix);
18876 168 : free(qtabname);
18877 168 : }
18878 :
18879 : static inline SeqType
18880 1276 : parse_sequence_type(const char *name)
18881 : {
18882 2850 : for (int i = 0; i < lengthof(SeqTypeNames); i++)
18883 : {
18884 2850 : if (strcmp(SeqTypeNames[i], name) == 0)
18885 1276 : return (SeqType) i;
18886 : }
18887 :
18888 0 : pg_fatal("unrecognized sequence type: %s", name);
18889 : return (SeqType) 0; /* keep compiler quiet */
18890 : }
18891 :
18892 : /*
18893 : * bsearch() comparator for SequenceItem
18894 : */
18895 : static int
18896 5908 : SequenceItemCmp(const void *p1, const void *p2)
18897 : {
18898 5908 : SequenceItem v1 = *((const SequenceItem *) p1);
18899 5908 : SequenceItem v2 = *((const SequenceItem *) p2);
18900 :
18901 5908 : return pg_cmp_u32(v1.oid, v2.oid);
18902 : }
18903 :
18904 : /*
18905 : * collectSequences
18906 : *
18907 : * Construct a table of sequence information. This table is sorted by OID for
18908 : * speed in lookup.
18909 : */
18910 : static void
18911 376 : collectSequences(Archive *fout)
18912 : {
18913 : PGresult *res;
18914 : const char *query;
18915 :
18916 : /*
18917 : * Before Postgres 10, sequence metadata is in the sequence itself. With
18918 : * some extra effort, we might be able to use the sorted table for those
18919 : * versions, but for now it seems unlikely to be worth it.
18920 : *
18921 : * Since version 18, we can gather the sequence data in this query with
18922 : * pg_get_sequence_data(), but we only do so for non-schema-only dumps.
18923 : */
18924 376 : if (fout->remoteVersion < 100000)
18925 0 : return;
18926 376 : else if (fout->remoteVersion < 180000 ||
18927 376 : (!fout->dopt->dumpData && !fout->dopt->sequence_data))
18928 16 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18929 : "seqstart, seqincrement, "
18930 : "seqmax, seqmin, "
18931 : "seqcache, seqcycle, "
18932 : "NULL, 'f' "
18933 : "FROM pg_catalog.pg_sequence "
18934 : "ORDER BY seqrelid";
18935 : else
18936 360 : query = "SELECT seqrelid, format_type(seqtypid, NULL), "
18937 : "seqstart, seqincrement, "
18938 : "seqmax, seqmin, "
18939 : "seqcache, seqcycle, "
18940 : "last_value, is_called "
18941 : "FROM pg_catalog.pg_sequence, "
18942 : "pg_get_sequence_data(seqrelid) "
18943 : "ORDER BY seqrelid;";
18944 :
18945 376 : res = ExecuteSqlQuery(fout, query, PGRES_TUPLES_OK);
18946 :
18947 376 : nsequences = PQntuples(res);
18948 376 : sequences = (SequenceItem *) pg_malloc(nsequences * sizeof(SequenceItem));
18949 :
18950 1652 : for (int i = 0; i < nsequences; i++)
18951 : {
18952 1276 : sequences[i].oid = atooid(PQgetvalue(res, i, 0));
18953 1276 : sequences[i].seqtype = parse_sequence_type(PQgetvalue(res, i, 1));
18954 1276 : sequences[i].startv = strtoi64(PQgetvalue(res, i, 2), NULL, 10);
18955 1276 : sequences[i].incby = strtoi64(PQgetvalue(res, i, 3), NULL, 10);
18956 1276 : sequences[i].maxv = strtoi64(PQgetvalue(res, i, 4), NULL, 10);
18957 1276 : sequences[i].minv = strtoi64(PQgetvalue(res, i, 5), NULL, 10);
18958 1276 : sequences[i].cache = strtoi64(PQgetvalue(res, i, 6), NULL, 10);
18959 1276 : sequences[i].cycled = (strcmp(PQgetvalue(res, i, 7), "t") == 0);
18960 1276 : sequences[i].last_value = strtoi64(PQgetvalue(res, i, 8), NULL, 10);
18961 1276 : sequences[i].is_called = (strcmp(PQgetvalue(res, i, 9), "t") == 0);
18962 : }
18963 :
18964 376 : PQclear(res);
18965 : }
18966 :
18967 : /*
18968 : * dumpSequence
18969 : * write the declaration (not data) of one user-defined sequence
18970 : */
18971 : static void
18972 750 : dumpSequence(Archive *fout, const TableInfo *tbinfo)
18973 : {
18974 750 : DumpOptions *dopt = fout->dopt;
18975 : SequenceItem *seq;
18976 : bool is_ascending;
18977 : int64 default_minv,
18978 : default_maxv;
18979 750 : PQExpBuffer query = createPQExpBuffer();
18980 750 : PQExpBuffer delqry = createPQExpBuffer();
18981 : char *qseqname;
18982 750 : TableInfo *owning_tab = NULL;
18983 :
18984 750 : qseqname = pg_strdup(fmtId(tbinfo->dobj.name));
18985 :
18986 : /*
18987 : * For versions >= 10, the sequence information is gathered in a sorted
18988 : * table before any calls to dumpSequence(). See collectSequences() for
18989 : * more information.
18990 : */
18991 750 : if (fout->remoteVersion >= 100000)
18992 : {
18993 750 : SequenceItem key = {0};
18994 :
18995 : Assert(sequences);
18996 :
18997 750 : key.oid = tbinfo->dobj.catId.oid;
18998 750 : seq = bsearch(&key, sequences, nsequences,
18999 : sizeof(SequenceItem), SequenceItemCmp);
19000 : }
19001 : else
19002 : {
19003 : PGresult *res;
19004 :
19005 : /*
19006 : * Before PostgreSQL 10, sequence metadata is in the sequence itself.
19007 : *
19008 : * Note: it might seem that 'bigint' potentially needs to be
19009 : * schema-qualified, but actually that's a keyword.
19010 : */
19011 0 : appendPQExpBuffer(query,
19012 : "SELECT 'bigint' AS sequence_type, "
19013 : "start_value, increment_by, max_value, min_value, "
19014 : "cache_value, is_cycled FROM %s",
19015 0 : fmtQualifiedDumpable(tbinfo));
19016 :
19017 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19018 :
19019 0 : if (PQntuples(res) != 1)
19020 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19021 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19022 : PQntuples(res)),
19023 : tbinfo->dobj.name, PQntuples(res));
19024 :
19025 0 : seq = pg_malloc0(sizeof(SequenceItem));
19026 0 : seq->seqtype = parse_sequence_type(PQgetvalue(res, 0, 0));
19027 0 : seq->startv = strtoi64(PQgetvalue(res, 0, 1), NULL, 10);
19028 0 : seq->incby = strtoi64(PQgetvalue(res, 0, 2), NULL, 10);
19029 0 : seq->maxv = strtoi64(PQgetvalue(res, 0, 3), NULL, 10);
19030 0 : seq->minv = strtoi64(PQgetvalue(res, 0, 4), NULL, 10);
19031 0 : seq->cache = strtoi64(PQgetvalue(res, 0, 5), NULL, 10);
19032 0 : seq->cycled = (strcmp(PQgetvalue(res, 0, 6), "t") == 0);
19033 :
19034 0 : PQclear(res);
19035 : }
19036 :
19037 : /* Calculate default limits for a sequence of this type */
19038 750 : is_ascending = (seq->incby >= 0);
19039 750 : if (seq->seqtype == SEQTYPE_SMALLINT)
19040 : {
19041 50 : default_minv = is_ascending ? 1 : PG_INT16_MIN;
19042 50 : default_maxv = is_ascending ? PG_INT16_MAX : -1;
19043 : }
19044 700 : else if (seq->seqtype == SEQTYPE_INTEGER)
19045 : {
19046 568 : default_minv = is_ascending ? 1 : PG_INT32_MIN;
19047 568 : default_maxv = is_ascending ? PG_INT32_MAX : -1;
19048 : }
19049 132 : else if (seq->seqtype == SEQTYPE_BIGINT)
19050 : {
19051 132 : default_minv = is_ascending ? 1 : PG_INT64_MIN;
19052 132 : default_maxv = is_ascending ? PG_INT64_MAX : -1;
19053 : }
19054 : else
19055 : {
19056 0 : pg_fatal("unrecognized sequence type: %d", seq->seqtype);
19057 : default_minv = default_maxv = 0; /* keep compiler quiet */
19058 : }
19059 :
19060 : /*
19061 : * Identity sequences are not to be dropped separately.
19062 : */
19063 750 : if (!tbinfo->is_identity_sequence)
19064 : {
19065 466 : appendPQExpBuffer(delqry, "DROP SEQUENCE %s;\n",
19066 466 : fmtQualifiedDumpable(tbinfo));
19067 : }
19068 :
19069 750 : resetPQExpBuffer(query);
19070 :
19071 750 : if (dopt->binary_upgrade)
19072 : {
19073 132 : binary_upgrade_set_pg_class_oids(fout, query,
19074 132 : tbinfo->dobj.catId.oid);
19075 :
19076 : /*
19077 : * In older PG versions a sequence will have a pg_type entry, but v14
19078 : * and up don't use that, so don't attempt to preserve the type OID.
19079 : */
19080 : }
19081 :
19082 750 : if (tbinfo->is_identity_sequence)
19083 : {
19084 284 : owning_tab = findTableByOid(tbinfo->owning_tab);
19085 :
19086 284 : appendPQExpBuffer(query,
19087 : "ALTER TABLE %s ",
19088 284 : fmtQualifiedDumpable(owning_tab));
19089 284 : appendPQExpBuffer(query,
19090 : "ALTER COLUMN %s ADD GENERATED ",
19091 284 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19092 284 : if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_ALWAYS)
19093 204 : appendPQExpBufferStr(query, "ALWAYS");
19094 80 : else if (owning_tab->attidentity[tbinfo->owning_col - 1] == ATTRIBUTE_IDENTITY_BY_DEFAULT)
19095 80 : appendPQExpBufferStr(query, "BY DEFAULT");
19096 284 : appendPQExpBuffer(query, " AS IDENTITY (\n SEQUENCE NAME %s\n",
19097 284 : fmtQualifiedDumpable(tbinfo));
19098 :
19099 : /*
19100 : * Emit persistence option only if it's different from the owning
19101 : * table's. This avoids using this new syntax unnecessarily.
19102 : */
19103 284 : if (tbinfo->relpersistence != owning_tab->relpersistence)
19104 20 : appendPQExpBuffer(query, " %s\n",
19105 20 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19106 : "UNLOGGED" : "LOGGED");
19107 : }
19108 : else
19109 : {
19110 466 : appendPQExpBuffer(query,
19111 : "CREATE %sSEQUENCE %s\n",
19112 466 : tbinfo->relpersistence == RELPERSISTENCE_UNLOGGED ?
19113 : "UNLOGGED " : "",
19114 466 : fmtQualifiedDumpable(tbinfo));
19115 :
19116 466 : if (seq->seqtype != SEQTYPE_BIGINT)
19117 364 : appendPQExpBuffer(query, " AS %s\n", SeqTypeNames[seq->seqtype]);
19118 : }
19119 :
19120 750 : appendPQExpBuffer(query, " START WITH " INT64_FORMAT "\n", seq->startv);
19121 :
19122 750 : appendPQExpBuffer(query, " INCREMENT BY " INT64_FORMAT "\n", seq->incby);
19123 :
19124 750 : if (seq->minv != default_minv)
19125 30 : appendPQExpBuffer(query, " MINVALUE " INT64_FORMAT "\n", seq->minv);
19126 : else
19127 720 : appendPQExpBufferStr(query, " NO MINVALUE\n");
19128 :
19129 750 : if (seq->maxv != default_maxv)
19130 30 : appendPQExpBuffer(query, " MAXVALUE " INT64_FORMAT "\n", seq->maxv);
19131 : else
19132 720 : appendPQExpBufferStr(query, " NO MAXVALUE\n");
19133 :
19134 750 : appendPQExpBuffer(query,
19135 : " CACHE " INT64_FORMAT "%s",
19136 750 : seq->cache, (seq->cycled ? "\n CYCLE" : ""));
19137 :
19138 750 : if (tbinfo->is_identity_sequence)
19139 284 : appendPQExpBufferStr(query, "\n);\n");
19140 : else
19141 466 : appendPQExpBufferStr(query, ";\n");
19142 :
19143 : /* binary_upgrade: no need to clear TOAST table oid */
19144 :
19145 750 : if (dopt->binary_upgrade)
19146 132 : binary_upgrade_extension_member(query, &tbinfo->dobj,
19147 : "SEQUENCE", qseqname,
19148 132 : tbinfo->dobj.namespace->dobj.name);
19149 :
19150 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19151 750 : ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
19152 750 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19153 : .namespace = tbinfo->dobj.namespace->dobj.name,
19154 : .owner = tbinfo->rolname,
19155 : .description = "SEQUENCE",
19156 : .section = SECTION_PRE_DATA,
19157 : .createStmt = query->data,
19158 : .dropStmt = delqry->data));
19159 :
19160 : /*
19161 : * If the sequence is owned by a table column, emit the ALTER for it as a
19162 : * separate TOC entry immediately following the sequence's own entry. It's
19163 : * OK to do this rather than using full sorting logic, because the
19164 : * dependency that tells us it's owned will have forced the table to be
19165 : * created first. We can't just include the ALTER in the TOC entry
19166 : * because it will fail if we haven't reassigned the sequence owner to
19167 : * match the table's owner.
19168 : *
19169 : * We need not schema-qualify the table reference because both sequence
19170 : * and table must be in the same schema.
19171 : */
19172 750 : if (OidIsValid(tbinfo->owning_tab) && !tbinfo->is_identity_sequence)
19173 : {
19174 274 : owning_tab = findTableByOid(tbinfo->owning_tab);
19175 :
19176 274 : if (owning_tab == NULL)
19177 0 : pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
19178 : tbinfo->owning_tab, tbinfo->dobj.catId.oid);
19179 :
19180 274 : if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
19181 : {
19182 270 : resetPQExpBuffer(query);
19183 270 : appendPQExpBuffer(query, "ALTER SEQUENCE %s",
19184 270 : fmtQualifiedDumpable(tbinfo));
19185 270 : appendPQExpBuffer(query, " OWNED BY %s",
19186 270 : fmtQualifiedDumpable(owning_tab));
19187 270 : appendPQExpBuffer(query, ".%s;\n",
19188 270 : fmtId(owning_tab->attnames[tbinfo->owning_col - 1]));
19189 :
19190 270 : if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19191 270 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19192 270 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19193 : .namespace = tbinfo->dobj.namespace->dobj.name,
19194 : .owner = tbinfo->rolname,
19195 : .description = "SEQUENCE OWNED BY",
19196 : .section = SECTION_PRE_DATA,
19197 : .createStmt = query->data,
19198 : .deps = &(tbinfo->dobj.dumpId),
19199 : .nDeps = 1));
19200 : }
19201 : }
19202 :
19203 : /* Dump Sequence Comments and Security Labels */
19204 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19205 0 : dumpComment(fout, "SEQUENCE", qseqname,
19206 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19207 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19208 :
19209 750 : if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19210 0 : dumpSecLabel(fout, "SEQUENCE", qseqname,
19211 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19212 0 : tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
19213 :
19214 750 : if (fout->remoteVersion < 100000)
19215 0 : pg_free(seq);
19216 750 : destroyPQExpBuffer(query);
19217 750 : destroyPQExpBuffer(delqry);
19218 750 : free(qseqname);
19219 750 : }
19220 :
19221 : /*
19222 : * dumpSequenceData
19223 : * write the data of one user-defined sequence
19224 : */
19225 : static void
19226 786 : dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
19227 : {
19228 786 : TableInfo *tbinfo = tdinfo->tdtable;
19229 : int64 last;
19230 : bool called;
19231 786 : PQExpBuffer query = createPQExpBuffer();
19232 :
19233 : /*
19234 : * For versions >= 18, the sequence information is gathered in the sorted
19235 : * array before any calls to dumpSequenceData(). See collectSequences()
19236 : * for more information.
19237 : *
19238 : * For older versions, we have to query the sequence relations
19239 : * individually.
19240 : */
19241 786 : if (fout->remoteVersion < 180000)
19242 : {
19243 : PGresult *res;
19244 :
19245 0 : appendPQExpBuffer(query,
19246 : "SELECT last_value, is_called FROM %s",
19247 0 : fmtQualifiedDumpable(tbinfo));
19248 :
19249 0 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19250 :
19251 0 : if (PQntuples(res) != 1)
19252 0 : pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
19253 : "query to get data of sequence \"%s\" returned %d rows (expected 1)",
19254 : PQntuples(res)),
19255 : tbinfo->dobj.name, PQntuples(res));
19256 :
19257 0 : last = strtoi64(PQgetvalue(res, 0, 0), NULL, 10);
19258 0 : called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
19259 :
19260 0 : PQclear(res);
19261 : }
19262 : else
19263 : {
19264 786 : SequenceItem key = {0};
19265 : SequenceItem *entry;
19266 :
19267 : Assert(sequences);
19268 : Assert(tbinfo->dobj.catId.oid);
19269 :
19270 786 : key.oid = tbinfo->dobj.catId.oid;
19271 786 : entry = bsearch(&key, sequences, nsequences,
19272 : sizeof(SequenceItem), SequenceItemCmp);
19273 :
19274 786 : last = entry->last_value;
19275 786 : called = entry->is_called;
19276 : }
19277 :
19278 786 : resetPQExpBuffer(query);
19279 786 : appendPQExpBufferStr(query, "SELECT pg_catalog.setval(");
19280 786 : appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
19281 786 : appendPQExpBuffer(query, ", " INT64_FORMAT ", %s);\n",
19282 : last, (called ? "true" : "false"));
19283 :
19284 786 : if (tdinfo->dobj.dump & DUMP_COMPONENT_DATA)
19285 786 : ArchiveEntry(fout, nilCatalogId, createDumpId(),
19286 786 : ARCHIVE_OPTS(.tag = tbinfo->dobj.name,
19287 : .namespace = tbinfo->dobj.namespace->dobj.name,
19288 : .owner = tbinfo->rolname,
19289 : .description = "SEQUENCE SET",
19290 : .section = SECTION_DATA,
19291 : .createStmt = query->data,
19292 : .deps = &(tbinfo->dobj.dumpId),
19293 : .nDeps = 1));
19294 :
19295 786 : destroyPQExpBuffer(query);
19296 786 : }
19297 :
19298 : /*
19299 : * dumpTrigger
19300 : * write the declaration of one user-defined table trigger
19301 : */
19302 : static void
19303 1046 : dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
19304 : {
19305 1046 : DumpOptions *dopt = fout->dopt;
19306 1046 : TableInfo *tbinfo = tginfo->tgtable;
19307 : PQExpBuffer query;
19308 : PQExpBuffer delqry;
19309 : PQExpBuffer trigprefix;
19310 : PQExpBuffer trigidentity;
19311 : char *qtabname;
19312 : char *tag;
19313 :
19314 : /* Do nothing if not dumping schema */
19315 1046 : if (!dopt->dumpSchema)
19316 62 : return;
19317 :
19318 984 : query = createPQExpBuffer();
19319 984 : delqry = createPQExpBuffer();
19320 984 : trigprefix = createPQExpBuffer();
19321 984 : trigidentity = createPQExpBuffer();
19322 :
19323 984 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19324 :
19325 984 : appendPQExpBuffer(trigidentity, "%s ", fmtId(tginfo->dobj.name));
19326 984 : appendPQExpBuffer(trigidentity, "ON %s", fmtQualifiedDumpable(tbinfo));
19327 :
19328 984 : appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
19329 984 : appendPQExpBuffer(delqry, "DROP TRIGGER %s;\n", trigidentity->data);
19330 :
19331 : /* Triggers can depend on extensions */
19332 984 : append_depends_on_extension(fout, query, &tginfo->dobj,
19333 : "pg_catalog.pg_trigger", "TRIGGER",
19334 984 : trigidentity->data);
19335 :
19336 984 : if (tginfo->tgispartition)
19337 : {
19338 : Assert(tbinfo->ispartition);
19339 :
19340 : /*
19341 : * Partition triggers only appear here because their 'tgenabled' flag
19342 : * differs from its parent's. The trigger is created already, so
19343 : * remove the CREATE and replace it with an ALTER. (Clear out the
19344 : * DROP query too, so that pg_dump --create does not cause errors.)
19345 : */
19346 218 : resetPQExpBuffer(query);
19347 218 : resetPQExpBuffer(delqry);
19348 218 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19349 218 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19350 218 : fmtQualifiedDumpable(tbinfo));
19351 218 : switch (tginfo->tgenabled)
19352 : {
19353 76 : case 'f':
19354 : case 'D':
19355 76 : appendPQExpBufferStr(query, "DISABLE");
19356 76 : break;
19357 0 : case 't':
19358 : case 'O':
19359 0 : appendPQExpBufferStr(query, "ENABLE");
19360 0 : break;
19361 66 : case 'R':
19362 66 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19363 66 : break;
19364 76 : case 'A':
19365 76 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19366 76 : break;
19367 : }
19368 218 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19369 218 : fmtId(tginfo->dobj.name));
19370 : }
19371 766 : else if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
19372 : {
19373 0 : appendPQExpBuffer(query, "\nALTER %sTABLE %s ",
19374 0 : tbinfo->relkind == RELKIND_FOREIGN_TABLE ? "FOREIGN " : "",
19375 0 : fmtQualifiedDumpable(tbinfo));
19376 0 : switch (tginfo->tgenabled)
19377 : {
19378 0 : case 'D':
19379 : case 'f':
19380 0 : appendPQExpBufferStr(query, "DISABLE");
19381 0 : break;
19382 0 : case 'A':
19383 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19384 0 : break;
19385 0 : case 'R':
19386 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19387 0 : break;
19388 0 : default:
19389 0 : appendPQExpBufferStr(query, "ENABLE");
19390 0 : break;
19391 : }
19392 0 : appendPQExpBuffer(query, " TRIGGER %s;\n",
19393 0 : fmtId(tginfo->dobj.name));
19394 : }
19395 :
19396 984 : appendPQExpBuffer(trigprefix, "TRIGGER %s ON",
19397 984 : fmtId(tginfo->dobj.name));
19398 :
19399 984 : tag = psprintf("%s %s", tbinfo->dobj.name, tginfo->dobj.name);
19400 :
19401 984 : if (tginfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19402 984 : ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId,
19403 984 : ARCHIVE_OPTS(.tag = tag,
19404 : .namespace = tbinfo->dobj.namespace->dobj.name,
19405 : .owner = tbinfo->rolname,
19406 : .description = "TRIGGER",
19407 : .section = SECTION_POST_DATA,
19408 : .createStmt = query->data,
19409 : .dropStmt = delqry->data));
19410 :
19411 984 : if (tginfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19412 0 : dumpComment(fout, trigprefix->data, qtabname,
19413 0 : tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
19414 0 : tginfo->dobj.catId, 0, tginfo->dobj.dumpId);
19415 :
19416 984 : free(tag);
19417 984 : destroyPQExpBuffer(query);
19418 984 : destroyPQExpBuffer(delqry);
19419 984 : destroyPQExpBuffer(trigprefix);
19420 984 : destroyPQExpBuffer(trigidentity);
19421 984 : free(qtabname);
19422 : }
19423 :
19424 : /*
19425 : * dumpEventTrigger
19426 : * write the declaration of one user-defined event trigger
19427 : */
19428 : static void
19429 84 : dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
19430 : {
19431 84 : DumpOptions *dopt = fout->dopt;
19432 : PQExpBuffer query;
19433 : PQExpBuffer delqry;
19434 : char *qevtname;
19435 :
19436 : /* Do nothing if not dumping schema */
19437 84 : if (!dopt->dumpSchema)
19438 12 : return;
19439 :
19440 72 : query = createPQExpBuffer();
19441 72 : delqry = createPQExpBuffer();
19442 :
19443 72 : qevtname = pg_strdup(fmtId(evtinfo->dobj.name));
19444 :
19445 72 : appendPQExpBufferStr(query, "CREATE EVENT TRIGGER ");
19446 72 : appendPQExpBufferStr(query, qevtname);
19447 72 : appendPQExpBufferStr(query, " ON ");
19448 72 : appendPQExpBufferStr(query, fmtId(evtinfo->evtevent));
19449 :
19450 72 : if (strcmp("", evtinfo->evttags) != 0)
19451 : {
19452 10 : appendPQExpBufferStr(query, "\n WHEN TAG IN (");
19453 10 : appendPQExpBufferStr(query, evtinfo->evttags);
19454 10 : appendPQExpBufferChar(query, ')');
19455 : }
19456 :
19457 72 : appendPQExpBufferStr(query, "\n EXECUTE FUNCTION ");
19458 72 : appendPQExpBufferStr(query, evtinfo->evtfname);
19459 72 : appendPQExpBufferStr(query, "();\n");
19460 :
19461 72 : if (evtinfo->evtenabled != 'O')
19462 : {
19463 0 : appendPQExpBuffer(query, "\nALTER EVENT TRIGGER %s ",
19464 : qevtname);
19465 0 : switch (evtinfo->evtenabled)
19466 : {
19467 0 : case 'D':
19468 0 : appendPQExpBufferStr(query, "DISABLE");
19469 0 : break;
19470 0 : case 'A':
19471 0 : appendPQExpBufferStr(query, "ENABLE ALWAYS");
19472 0 : break;
19473 0 : case 'R':
19474 0 : appendPQExpBufferStr(query, "ENABLE REPLICA");
19475 0 : break;
19476 0 : default:
19477 0 : appendPQExpBufferStr(query, "ENABLE");
19478 0 : break;
19479 : }
19480 0 : appendPQExpBufferStr(query, ";\n");
19481 : }
19482 :
19483 72 : appendPQExpBuffer(delqry, "DROP EVENT TRIGGER %s;\n",
19484 : qevtname);
19485 :
19486 72 : if (dopt->binary_upgrade)
19487 4 : binary_upgrade_extension_member(query, &evtinfo->dobj,
19488 : "EVENT TRIGGER", qevtname, NULL);
19489 :
19490 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19491 72 : ArchiveEntry(fout, evtinfo->dobj.catId, evtinfo->dobj.dumpId,
19492 72 : ARCHIVE_OPTS(.tag = evtinfo->dobj.name,
19493 : .owner = evtinfo->evtowner,
19494 : .description = "EVENT TRIGGER",
19495 : .section = SECTION_POST_DATA,
19496 : .createStmt = query->data,
19497 : .dropStmt = delqry->data));
19498 :
19499 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19500 0 : dumpComment(fout, "EVENT TRIGGER", qevtname,
19501 0 : NULL, evtinfo->evtowner,
19502 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19503 :
19504 72 : if (evtinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
19505 0 : dumpSecLabel(fout, "EVENT TRIGGER", qevtname,
19506 0 : NULL, evtinfo->evtowner,
19507 0 : evtinfo->dobj.catId, 0, evtinfo->dobj.dumpId);
19508 :
19509 72 : destroyPQExpBuffer(query);
19510 72 : destroyPQExpBuffer(delqry);
19511 72 : free(qevtname);
19512 : }
19513 :
19514 : /*
19515 : * dumpRule
19516 : * Dump a rule
19517 : */
19518 : static void
19519 2258 : dumpRule(Archive *fout, const RuleInfo *rinfo)
19520 : {
19521 2258 : DumpOptions *dopt = fout->dopt;
19522 2258 : TableInfo *tbinfo = rinfo->ruletable;
19523 : bool is_view;
19524 : PQExpBuffer query;
19525 : PQExpBuffer cmd;
19526 : PQExpBuffer delcmd;
19527 : PQExpBuffer ruleprefix;
19528 : char *qtabname;
19529 : PGresult *res;
19530 : char *tag;
19531 :
19532 : /* Do nothing if not dumping schema */
19533 2258 : if (!dopt->dumpSchema)
19534 120 : return;
19535 :
19536 : /*
19537 : * If it is an ON SELECT rule that is created implicitly by CREATE VIEW,
19538 : * we do not want to dump it as a separate object.
19539 : */
19540 2138 : if (!rinfo->separate)
19541 1716 : return;
19542 :
19543 : /*
19544 : * If it's an ON SELECT rule, we want to print it as a view definition,
19545 : * instead of a rule.
19546 : */
19547 422 : is_view = (rinfo->ev_type == '1' && rinfo->is_instead);
19548 :
19549 422 : query = createPQExpBuffer();
19550 422 : cmd = createPQExpBuffer();
19551 422 : delcmd = createPQExpBuffer();
19552 422 : ruleprefix = createPQExpBuffer();
19553 :
19554 422 : qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
19555 :
19556 422 : if (is_view)
19557 : {
19558 : PQExpBuffer result;
19559 :
19560 : /*
19561 : * We need OR REPLACE here because we'll be replacing a dummy view.
19562 : * Otherwise this should look largely like the regular view dump code.
19563 : */
19564 20 : appendPQExpBuffer(cmd, "CREATE OR REPLACE VIEW %s",
19565 20 : fmtQualifiedDumpable(tbinfo));
19566 20 : if (nonemptyReloptions(tbinfo->reloptions))
19567 : {
19568 0 : appendPQExpBufferStr(cmd, " WITH (");
19569 0 : appendReloptionsArrayAH(cmd, tbinfo->reloptions, "", fout);
19570 0 : appendPQExpBufferChar(cmd, ')');
19571 : }
19572 20 : result = createViewAsClause(fout, tbinfo);
19573 20 : appendPQExpBuffer(cmd, " AS\n%s", result->data);
19574 20 : destroyPQExpBuffer(result);
19575 20 : if (tbinfo->checkoption != NULL)
19576 0 : appendPQExpBuffer(cmd, "\n WITH %s CHECK OPTION",
19577 : tbinfo->checkoption);
19578 20 : appendPQExpBufferStr(cmd, ";\n");
19579 : }
19580 : else
19581 : {
19582 : /* In the rule case, just print pg_get_ruledef's result verbatim */
19583 402 : appendPQExpBuffer(query,
19584 : "SELECT pg_catalog.pg_get_ruledef('%u'::pg_catalog.oid)",
19585 402 : rinfo->dobj.catId.oid);
19586 :
19587 402 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19588 :
19589 402 : if (PQntuples(res) != 1)
19590 0 : pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
19591 : rinfo->dobj.name, tbinfo->dobj.name);
19592 :
19593 402 : printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
19594 :
19595 402 : PQclear(res);
19596 : }
19597 :
19598 : /*
19599 : * Add the command to alter the rules replication firing semantics if it
19600 : * differs from the default.
19601 : */
19602 422 : if (rinfo->ev_enabled != 'O')
19603 : {
19604 30 : appendPQExpBuffer(cmd, "ALTER TABLE %s ", fmtQualifiedDumpable(tbinfo));
19605 30 : switch (rinfo->ev_enabled)
19606 : {
19607 0 : case 'A':
19608 0 : appendPQExpBuffer(cmd, "ENABLE ALWAYS RULE %s;\n",
19609 0 : fmtId(rinfo->dobj.name));
19610 0 : break;
19611 0 : case 'R':
19612 0 : appendPQExpBuffer(cmd, "ENABLE REPLICA RULE %s;\n",
19613 0 : fmtId(rinfo->dobj.name));
19614 0 : break;
19615 30 : case 'D':
19616 30 : appendPQExpBuffer(cmd, "DISABLE RULE %s;\n",
19617 30 : fmtId(rinfo->dobj.name));
19618 30 : break;
19619 : }
19620 : }
19621 :
19622 422 : if (is_view)
19623 : {
19624 : /*
19625 : * We can't DROP a view's ON SELECT rule. Instead, use CREATE OR
19626 : * REPLACE VIEW to replace the rule with something with minimal
19627 : * dependencies.
19628 : */
19629 : PQExpBuffer result;
19630 :
19631 20 : appendPQExpBuffer(delcmd, "CREATE OR REPLACE VIEW %s",
19632 20 : fmtQualifiedDumpable(tbinfo));
19633 20 : result = createDummyViewAsClause(fout, tbinfo);
19634 20 : appendPQExpBuffer(delcmd, " AS\n%s;\n", result->data);
19635 20 : destroyPQExpBuffer(result);
19636 : }
19637 : else
19638 : {
19639 402 : appendPQExpBuffer(delcmd, "DROP RULE %s ",
19640 402 : fmtId(rinfo->dobj.name));
19641 402 : appendPQExpBuffer(delcmd, "ON %s;\n",
19642 402 : fmtQualifiedDumpable(tbinfo));
19643 : }
19644 :
19645 422 : appendPQExpBuffer(ruleprefix, "RULE %s ON",
19646 422 : fmtId(rinfo->dobj.name));
19647 :
19648 422 : tag = psprintf("%s %s", tbinfo->dobj.name, rinfo->dobj.name);
19649 :
19650 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
19651 422 : ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId,
19652 422 : ARCHIVE_OPTS(.tag = tag,
19653 : .namespace = tbinfo->dobj.namespace->dobj.name,
19654 : .owner = tbinfo->rolname,
19655 : .description = "RULE",
19656 : .section = SECTION_POST_DATA,
19657 : .createStmt = cmd->data,
19658 : .dropStmt = delcmd->data));
19659 :
19660 : /* Dump rule comments */
19661 422 : if (rinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
19662 0 : dumpComment(fout, ruleprefix->data, qtabname,
19663 0 : tbinfo->dobj.namespace->dobj.name,
19664 : tbinfo->rolname,
19665 0 : rinfo->dobj.catId, 0, rinfo->dobj.dumpId);
19666 :
19667 422 : free(tag);
19668 422 : destroyPQExpBuffer(query);
19669 422 : destroyPQExpBuffer(cmd);
19670 422 : destroyPQExpBuffer(delcmd);
19671 422 : destroyPQExpBuffer(ruleprefix);
19672 422 : free(qtabname);
19673 : }
19674 :
19675 : /*
19676 : * getExtensionMembership --- obtain extension membership data
19677 : *
19678 : * We need to identify objects that are extension members as soon as they're
19679 : * loaded, so that we can correctly determine whether they need to be dumped.
19680 : * Generally speaking, extension member objects will get marked as *not* to
19681 : * be dumped, as they will be recreated by the single CREATE EXTENSION
19682 : * command. However, in binary upgrade mode we still need to dump the members
19683 : * individually.
19684 : */
19685 : void
19686 378 : getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
19687 : int numExtensions)
19688 : {
19689 : PQExpBuffer query;
19690 : PGresult *res;
19691 : int ntups,
19692 : i;
19693 : int i_classid,
19694 : i_objid,
19695 : i_refobjid;
19696 : ExtensionInfo *ext;
19697 :
19698 : /* Nothing to do if no extensions */
19699 378 : if (numExtensions == 0)
19700 0 : return;
19701 :
19702 378 : query = createPQExpBuffer();
19703 :
19704 : /* refclassid constraint is redundant but may speed the search */
19705 378 : appendPQExpBufferStr(query, "SELECT "
19706 : "classid, objid, refobjid "
19707 : "FROM pg_depend "
19708 : "WHERE refclassid = 'pg_extension'::regclass "
19709 : "AND deptype = 'e' "
19710 : "ORDER BY 3");
19711 :
19712 378 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19713 :
19714 378 : ntups = PQntuples(res);
19715 :
19716 378 : i_classid = PQfnumber(res, "classid");
19717 378 : i_objid = PQfnumber(res, "objid");
19718 378 : i_refobjid = PQfnumber(res, "refobjid");
19719 :
19720 : /*
19721 : * Since we ordered the SELECT by referenced ID, we can expect that
19722 : * multiple entries for the same extension will appear together; this
19723 : * saves on searches.
19724 : */
19725 378 : ext = NULL;
19726 :
19727 3100 : for (i = 0; i < ntups; i++)
19728 : {
19729 : CatalogId objId;
19730 : Oid extId;
19731 :
19732 2722 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
19733 2722 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
19734 2722 : extId = atooid(PQgetvalue(res, i, i_refobjid));
19735 :
19736 2722 : if (ext == NULL ||
19737 2344 : ext->dobj.catId.oid != extId)
19738 438 : ext = findExtensionByOid(extId);
19739 :
19740 2722 : if (ext == NULL)
19741 : {
19742 : /* shouldn't happen */
19743 0 : pg_log_warning("could not find referenced extension %u", extId);
19744 0 : continue;
19745 : }
19746 :
19747 2722 : recordExtensionMembership(objId, ext);
19748 : }
19749 :
19750 378 : PQclear(res);
19751 :
19752 378 : destroyPQExpBuffer(query);
19753 : }
19754 :
19755 : /*
19756 : * processExtensionTables --- deal with extension configuration tables
19757 : *
19758 : * There are two parts to this process:
19759 : *
19760 : * 1. Identify and create dump records for extension configuration tables.
19761 : *
19762 : * Extensions can mark tables as "configuration", which means that the user
19763 : * is able and expected to modify those tables after the extension has been
19764 : * loaded. For these tables, we dump out only the data- the structure is
19765 : * expected to be handled at CREATE EXTENSION time, including any indexes or
19766 : * foreign keys, which brings us to-
19767 : *
19768 : * 2. Record FK dependencies between configuration tables.
19769 : *
19770 : * Due to the FKs being created at CREATE EXTENSION time and therefore before
19771 : * the data is loaded, we have to work out what the best order for reloading
19772 : * the data is, to avoid FK violations when the tables are restored. This is
19773 : * not perfect- we can't handle circular dependencies and if any exist they
19774 : * will cause an invalid dump to be produced (though at least all of the data
19775 : * is included for a user to manually restore). This is currently documented
19776 : * but perhaps we can provide a better solution in the future.
19777 : */
19778 : void
19779 376 : processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
19780 : int numExtensions)
19781 : {
19782 376 : DumpOptions *dopt = fout->dopt;
19783 : PQExpBuffer query;
19784 : PGresult *res;
19785 : int ntups,
19786 : i;
19787 : int i_conrelid,
19788 : i_confrelid;
19789 :
19790 : /* Nothing to do if no extensions */
19791 376 : if (numExtensions == 0)
19792 0 : return;
19793 :
19794 : /*
19795 : * Identify extension configuration tables and create TableDataInfo
19796 : * objects for them, ensuring their data will be dumped even though the
19797 : * tables themselves won't be.
19798 : *
19799 : * Note that we create TableDataInfo objects even in schema-only mode, ie,
19800 : * user data in a configuration table is treated like schema data. This
19801 : * seems appropriate since system data in a config table would get
19802 : * reloaded by CREATE EXTENSION. If the extension is not listed in the
19803 : * list of extensions to be included, none of its data is dumped.
19804 : */
19805 812 : for (i = 0; i < numExtensions; i++)
19806 : {
19807 436 : ExtensionInfo *curext = &(extinfo[i]);
19808 436 : char *extconfig = curext->extconfig;
19809 436 : char *extcondition = curext->extcondition;
19810 436 : char **extconfigarray = NULL;
19811 436 : char **extconditionarray = NULL;
19812 436 : int nconfigitems = 0;
19813 436 : int nconditionitems = 0;
19814 :
19815 : /*
19816 : * Check if this extension is listed as to include in the dump. If
19817 : * not, any table data associated with it is discarded.
19818 : */
19819 436 : if (extension_include_oids.head != NULL &&
19820 16 : !simple_oid_list_member(&extension_include_oids,
19821 : curext->dobj.catId.oid))
19822 12 : continue;
19823 :
19824 : /*
19825 : * Check if this extension is listed as to exclude in the dump. If
19826 : * yes, any table data associated with it is discarded.
19827 : */
19828 436 : if (extension_exclude_oids.head != NULL &&
19829 8 : simple_oid_list_member(&extension_exclude_oids,
19830 : curext->dobj.catId.oid))
19831 4 : continue;
19832 :
19833 424 : if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
19834 : {
19835 : int j;
19836 :
19837 40 : if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
19838 0 : pg_fatal("could not parse %s array", "extconfig");
19839 40 : if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
19840 0 : pg_fatal("could not parse %s array", "extcondition");
19841 40 : if (nconfigitems != nconditionitems)
19842 0 : pg_fatal("mismatched number of configurations and conditions for extension");
19843 :
19844 120 : for (j = 0; j < nconfigitems; j++)
19845 : {
19846 : TableInfo *configtbl;
19847 80 : Oid configtbloid = atooid(extconfigarray[j]);
19848 80 : bool dumpobj =
19849 80 : curext->dobj.dump & DUMP_COMPONENT_DEFINITION;
19850 :
19851 80 : configtbl = findTableByOid(configtbloid);
19852 80 : if (configtbl == NULL)
19853 0 : continue;
19854 :
19855 : /*
19856 : * Tables of not-to-be-dumped extensions shouldn't be dumped
19857 : * unless the table or its schema is explicitly included
19858 : */
19859 80 : if (!(curext->dobj.dump & DUMP_COMPONENT_DEFINITION))
19860 : {
19861 : /* check table explicitly requested */
19862 4 : if (table_include_oids.head != NULL &&
19863 0 : simple_oid_list_member(&table_include_oids,
19864 : configtbloid))
19865 0 : dumpobj = true;
19866 :
19867 : /* check table's schema explicitly requested */
19868 4 : if (configtbl->dobj.namespace->dobj.dump &
19869 : DUMP_COMPONENT_DATA)
19870 4 : dumpobj = true;
19871 : }
19872 :
19873 : /* check table excluded by an exclusion switch */
19874 88 : if (table_exclude_oids.head != NULL &&
19875 8 : simple_oid_list_member(&table_exclude_oids,
19876 : configtbloid))
19877 2 : dumpobj = false;
19878 :
19879 : /* check schema excluded by an exclusion switch */
19880 80 : if (simple_oid_list_member(&schema_exclude_oids,
19881 80 : configtbl->dobj.namespace->dobj.catId.oid))
19882 0 : dumpobj = false;
19883 :
19884 80 : if (dumpobj)
19885 : {
19886 78 : makeTableDataInfo(dopt, configtbl);
19887 78 : if (configtbl->dataObj != NULL)
19888 : {
19889 78 : if (strlen(extconditionarray[j]) > 0)
19890 0 : configtbl->dataObj->filtercond = pg_strdup(extconditionarray[j]);
19891 : }
19892 : }
19893 : }
19894 : }
19895 424 : if (extconfigarray)
19896 40 : free(extconfigarray);
19897 424 : if (extconditionarray)
19898 40 : free(extconditionarray);
19899 : }
19900 :
19901 : /*
19902 : * Now that all the TableDataInfo objects have been created for all the
19903 : * extensions, check their FK dependencies and register them to try and
19904 : * dump the data out in an order that they can be restored in.
19905 : *
19906 : * Note that this is not a problem for user tables as their FKs are
19907 : * recreated after the data has been loaded.
19908 : */
19909 :
19910 376 : query = createPQExpBuffer();
19911 :
19912 376 : printfPQExpBuffer(query,
19913 : "SELECT conrelid, confrelid "
19914 : "FROM pg_constraint "
19915 : "JOIN pg_depend ON (objid = confrelid) "
19916 : "WHERE contype = 'f' "
19917 : "AND refclassid = 'pg_extension'::regclass "
19918 : "AND classid = 'pg_class'::regclass;");
19919 :
19920 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
19921 376 : ntups = PQntuples(res);
19922 :
19923 376 : i_conrelid = PQfnumber(res, "conrelid");
19924 376 : i_confrelid = PQfnumber(res, "confrelid");
19925 :
19926 : /* Now get the dependencies and register them */
19927 376 : for (i = 0; i < ntups; i++)
19928 : {
19929 : Oid conrelid,
19930 : confrelid;
19931 : TableInfo *reftable,
19932 : *contable;
19933 :
19934 0 : conrelid = atooid(PQgetvalue(res, i, i_conrelid));
19935 0 : confrelid = atooid(PQgetvalue(res, i, i_confrelid));
19936 0 : contable = findTableByOid(conrelid);
19937 0 : reftable = findTableByOid(confrelid);
19938 :
19939 0 : if (reftable == NULL ||
19940 0 : reftable->dataObj == NULL ||
19941 0 : contable == NULL ||
19942 0 : contable->dataObj == NULL)
19943 0 : continue;
19944 :
19945 : /*
19946 : * Make referencing TABLE_DATA object depend on the referenced table's
19947 : * TABLE_DATA object.
19948 : */
19949 0 : addObjectDependency(&contable->dataObj->dobj,
19950 0 : reftable->dataObj->dobj.dumpId);
19951 : }
19952 376 : PQclear(res);
19953 376 : destroyPQExpBuffer(query);
19954 : }
19955 :
19956 : /*
19957 : * getDependencies --- obtain available dependency data
19958 : */
19959 : static void
19960 376 : getDependencies(Archive *fout)
19961 : {
19962 : PQExpBuffer query;
19963 : PGresult *res;
19964 : int ntups,
19965 : i;
19966 : int i_classid,
19967 : i_objid,
19968 : i_refclassid,
19969 : i_refobjid,
19970 : i_deptype;
19971 : DumpableObject *dobj,
19972 : *refdobj;
19973 :
19974 376 : pg_log_info("reading dependency data");
19975 :
19976 376 : query = createPQExpBuffer();
19977 :
19978 : /*
19979 : * Messy query to collect the dependency data we need. Note that we
19980 : * ignore the sub-object column, so that dependencies of or on a column
19981 : * look the same as dependencies of or on a whole table.
19982 : *
19983 : * PIN dependencies aren't interesting, and EXTENSION dependencies were
19984 : * already processed by getExtensionMembership.
19985 : */
19986 376 : appendPQExpBufferStr(query, "SELECT "
19987 : "classid, objid, refclassid, refobjid, deptype "
19988 : "FROM pg_depend "
19989 : "WHERE deptype != 'p' AND deptype != 'e'\n");
19990 :
19991 : /*
19992 : * Since we don't treat pg_amop entries as separate DumpableObjects, we
19993 : * have to translate their dependencies into dependencies of their parent
19994 : * opfamily. Ignore internal dependencies though, as those will point to
19995 : * their parent opclass, which we needn't consider here (and if we did,
19996 : * it'd just result in circular dependencies). Also, "loose" opfamily
19997 : * entries will have dependencies on their parent opfamily, which we
19998 : * should drop since they'd likewise become useless self-dependencies.
19999 : * (But be sure to keep deps on *other* opfamilies; see amopsortfamily.)
20000 : */
20001 376 : appendPQExpBufferStr(query, "UNION ALL\n"
20002 : "SELECT 'pg_opfamily'::regclass AS classid, amopfamily AS objid, refclassid, refobjid, deptype "
20003 : "FROM pg_depend d, pg_amop o "
20004 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20005 : "classid = 'pg_amop'::regclass AND objid = o.oid "
20006 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amopfamily = refobjid)\n");
20007 :
20008 : /* Likewise for pg_amproc entries */
20009 376 : appendPQExpBufferStr(query, "UNION ALL\n"
20010 : "SELECT 'pg_opfamily'::regclass AS classid, amprocfamily AS objid, refclassid, refobjid, deptype "
20011 : "FROM pg_depend d, pg_amproc p "
20012 : "WHERE deptype NOT IN ('p', 'e', 'i') AND "
20013 : "classid = 'pg_amproc'::regclass AND objid = p.oid "
20014 : "AND NOT (refclassid = 'pg_opfamily'::regclass AND amprocfamily = refobjid)\n");
20015 :
20016 : /* Sort the output for efficiency below */
20017 376 : appendPQExpBufferStr(query, "ORDER BY 1,2");
20018 :
20019 376 : res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
20020 :
20021 376 : ntups = PQntuples(res);
20022 :
20023 376 : i_classid = PQfnumber(res, "classid");
20024 376 : i_objid = PQfnumber(res, "objid");
20025 376 : i_refclassid = PQfnumber(res, "refclassid");
20026 376 : i_refobjid = PQfnumber(res, "refobjid");
20027 376 : i_deptype = PQfnumber(res, "deptype");
20028 :
20029 : /*
20030 : * Since we ordered the SELECT by referencing ID, we can expect that
20031 : * multiple entries for the same object will appear together; this saves
20032 : * on searches.
20033 : */
20034 376 : dobj = NULL;
20035 :
20036 811246 : for (i = 0; i < ntups; i++)
20037 : {
20038 : CatalogId objId;
20039 : CatalogId refobjId;
20040 : char deptype;
20041 :
20042 810870 : objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
20043 810870 : objId.oid = atooid(PQgetvalue(res, i, i_objid));
20044 810870 : refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
20045 810870 : refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
20046 810870 : deptype = *(PQgetvalue(res, i, i_deptype));
20047 :
20048 810870 : if (dobj == NULL ||
20049 761592 : dobj->catId.tableoid != objId.tableoid ||
20050 757412 : dobj->catId.oid != objId.oid)
20051 353638 : dobj = findObjectByCatalogId(objId);
20052 :
20053 : /*
20054 : * Failure to find objects mentioned in pg_depend is not unexpected,
20055 : * since for example we don't collect info about TOAST tables.
20056 : */
20057 810870 : if (dobj == NULL)
20058 : {
20059 : #ifdef NOT_USED
20060 : pg_log_warning("no referencing object %u %u",
20061 : objId.tableoid, objId.oid);
20062 : #endif
20063 50618 : continue;
20064 : }
20065 :
20066 761968 : refdobj = findObjectByCatalogId(refobjId);
20067 :
20068 761968 : if (refdobj == NULL)
20069 : {
20070 : #ifdef NOT_USED
20071 : pg_log_warning("no referenced object %u %u",
20072 : refobjId.tableoid, refobjId.oid);
20073 : #endif
20074 1716 : continue;
20075 : }
20076 :
20077 : /*
20078 : * For 'x' dependencies, mark the object for later; we still add the
20079 : * normal dependency, for possible ordering purposes. Currently
20080 : * pg_dump_sort.c knows to put extensions ahead of all object types
20081 : * that could possibly depend on them, but this is safer.
20082 : */
20083 760252 : if (deptype == 'x')
20084 88 : dobj->depends_on_ext = true;
20085 :
20086 : /*
20087 : * Ordinarily, table rowtypes have implicit dependencies on their
20088 : * tables. However, for a composite type the implicit dependency goes
20089 : * the other way in pg_depend; which is the right thing for DROP but
20090 : * it doesn't produce the dependency ordering we need. So in that one
20091 : * case, we reverse the direction of the dependency.
20092 : */
20093 760252 : if (deptype == 'i' &&
20094 213460 : dobj->objType == DO_TABLE &&
20095 2516 : refdobj->objType == DO_TYPE)
20096 364 : addObjectDependency(refdobj, dobj->dumpId);
20097 : else
20098 : /* normal case */
20099 759888 : addObjectDependency(dobj, refdobj->dumpId);
20100 : }
20101 :
20102 376 : PQclear(res);
20103 :
20104 376 : destroyPQExpBuffer(query);
20105 376 : }
20106 :
20107 :
20108 : /*
20109 : * createBoundaryObjects - create dummy DumpableObjects to represent
20110 : * dump section boundaries.
20111 : */
20112 : static DumpableObject *
20113 376 : createBoundaryObjects(void)
20114 : {
20115 : DumpableObject *dobjs;
20116 :
20117 376 : dobjs = (DumpableObject *) pg_malloc(2 * sizeof(DumpableObject));
20118 :
20119 376 : dobjs[0].objType = DO_PRE_DATA_BOUNDARY;
20120 376 : dobjs[0].catId = nilCatalogId;
20121 376 : AssignDumpId(dobjs + 0);
20122 376 : dobjs[0].name = pg_strdup("PRE-DATA BOUNDARY");
20123 :
20124 376 : dobjs[1].objType = DO_POST_DATA_BOUNDARY;
20125 376 : dobjs[1].catId = nilCatalogId;
20126 376 : AssignDumpId(dobjs + 1);
20127 376 : dobjs[1].name = pg_strdup("POST-DATA BOUNDARY");
20128 :
20129 376 : return dobjs;
20130 : }
20131 :
20132 : /*
20133 : * addBoundaryDependencies - add dependencies as needed to enforce the dump
20134 : * section boundaries.
20135 : */
20136 : static void
20137 376 : addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
20138 : DumpableObject *boundaryObjs)
20139 : {
20140 376 : DumpableObject *preDataBound = boundaryObjs + 0;
20141 376 : DumpableObject *postDataBound = boundaryObjs + 1;
20142 : int i;
20143 :
20144 1393768 : for (i = 0; i < numObjs; i++)
20145 : {
20146 1393392 : DumpableObject *dobj = dobjs[i];
20147 :
20148 : /*
20149 : * The classification of object types here must match the SECTION_xxx
20150 : * values assigned during subsequent ArchiveEntry calls!
20151 : */
20152 1393392 : switch (dobj->objType)
20153 : {
20154 1301586 : case DO_NAMESPACE:
20155 : case DO_EXTENSION:
20156 : case DO_TYPE:
20157 : case DO_SHELL_TYPE:
20158 : case DO_FUNC:
20159 : case DO_AGG:
20160 : case DO_OPERATOR:
20161 : case DO_ACCESS_METHOD:
20162 : case DO_OPCLASS:
20163 : case DO_OPFAMILY:
20164 : case DO_COLLATION:
20165 : case DO_CONVERSION:
20166 : case DO_TABLE:
20167 : case DO_TABLE_ATTACH:
20168 : case DO_ATTRDEF:
20169 : case DO_PROCLANG:
20170 : case DO_CAST:
20171 : case DO_DUMMY_TYPE:
20172 : case DO_TSPARSER:
20173 : case DO_TSDICT:
20174 : case DO_TSTEMPLATE:
20175 : case DO_TSCONFIG:
20176 : case DO_FDW:
20177 : case DO_FOREIGN_SERVER:
20178 : case DO_TRANSFORM:
20179 : /* Pre-data objects: must come before the pre-data boundary */
20180 1301586 : addObjectDependency(preDataBound, dobj->dumpId);
20181 1301586 : break;
20182 9598 : case DO_TABLE_DATA:
20183 : case DO_SEQUENCE_SET:
20184 : case DO_LARGE_OBJECT:
20185 : case DO_LARGE_OBJECT_DATA:
20186 : /* Data objects: must come between the boundaries */
20187 9598 : addObjectDependency(dobj, preDataBound->dumpId);
20188 9598 : addObjectDependency(postDataBound, dobj->dumpId);
20189 9598 : break;
20190 11486 : case DO_INDEX:
20191 : case DO_INDEX_ATTACH:
20192 : case DO_STATSEXT:
20193 : case DO_REFRESH_MATVIEW:
20194 : case DO_TRIGGER:
20195 : case DO_EVENT_TRIGGER:
20196 : case DO_DEFAULT_ACL:
20197 : case DO_POLICY:
20198 : case DO_PUBLICATION:
20199 : case DO_PUBLICATION_REL:
20200 : case DO_PUBLICATION_TABLE_IN_SCHEMA:
20201 : case DO_SUBSCRIPTION:
20202 : case DO_SUBSCRIPTION_REL:
20203 : /* Post-data objects: must come after the post-data boundary */
20204 11486 : addObjectDependency(dobj, postDataBound->dumpId);
20205 11486 : break;
20206 58310 : case DO_RULE:
20207 : /* Rules are post-data, but only if dumped separately */
20208 58310 : if (((RuleInfo *) dobj)->separate)
20209 1298 : addObjectDependency(dobj, postDataBound->dumpId);
20210 58310 : break;
20211 5066 : case DO_CONSTRAINT:
20212 : case DO_FK_CONSTRAINT:
20213 : /* Constraints are post-data, but only if dumped separately */
20214 5066 : if (((ConstraintInfo *) dobj)->separate)
20215 3650 : addObjectDependency(dobj, postDataBound->dumpId);
20216 5066 : break;
20217 376 : case DO_PRE_DATA_BOUNDARY:
20218 : /* nothing to do */
20219 376 : break;
20220 376 : case DO_POST_DATA_BOUNDARY:
20221 : /* must come after the pre-data boundary */
20222 376 : addObjectDependency(dobj, preDataBound->dumpId);
20223 376 : break;
20224 6594 : case DO_REL_STATS:
20225 : /* stats section varies by parent object type, DATA or POST */
20226 6594 : if (((RelStatsInfo *) dobj)->section == SECTION_DATA)
20227 : {
20228 4234 : addObjectDependency(dobj, preDataBound->dumpId);
20229 4234 : addObjectDependency(postDataBound, dobj->dumpId);
20230 : }
20231 : else
20232 2360 : addObjectDependency(dobj, postDataBound->dumpId);
20233 6594 : break;
20234 : }
20235 : }
20236 376 : }
20237 :
20238 :
20239 : /*
20240 : * BuildArchiveDependencies - create dependency data for archive TOC entries
20241 : *
20242 : * The raw dependency data obtained by getDependencies() is not terribly
20243 : * useful in an archive dump, because in many cases there are dependency
20244 : * chains linking through objects that don't appear explicitly in the dump.
20245 : * For example, a view will depend on its _RETURN rule while the _RETURN rule
20246 : * will depend on other objects --- but the rule will not appear as a separate
20247 : * object in the dump. We need to adjust the view's dependencies to include
20248 : * whatever the rule depends on that is included in the dump.
20249 : *
20250 : * Just to make things more complicated, there are also "special" dependencies
20251 : * such as the dependency of a TABLE DATA item on its TABLE, which we must
20252 : * not rearrange because pg_restore knows that TABLE DATA only depends on
20253 : * its table. In these cases we must leave the dependencies strictly as-is
20254 : * even if they refer to not-to-be-dumped objects.
20255 : *
20256 : * To handle this, the convention is that "special" dependencies are created
20257 : * during ArchiveEntry calls, and an archive TOC item that has any such
20258 : * entries will not be touched here. Otherwise, we recursively search the
20259 : * DumpableObject data structures to build the correct dependencies for each
20260 : * archive TOC item.
20261 : */
20262 : static void
20263 116 : BuildArchiveDependencies(Archive *fout)
20264 : {
20265 116 : ArchiveHandle *AH = (ArchiveHandle *) fout;
20266 : TocEntry *te;
20267 :
20268 : /* Scan all TOC entries in the archive */
20269 13324 : for (te = AH->toc->next; te != AH->toc; te = te->next)
20270 : {
20271 : DumpableObject *dobj;
20272 : DumpId *dependencies;
20273 : int nDeps;
20274 : int allocDeps;
20275 :
20276 : /* No need to process entries that will not be dumped */
20277 13208 : if (te->reqs == 0)
20278 6468 : continue;
20279 : /* Ignore entries that already have "special" dependencies */
20280 13182 : if (te->nDeps > 0)
20281 5558 : continue;
20282 : /* Otherwise, look up the item's original DumpableObject, if any */
20283 7624 : dobj = findObjectByDumpId(te->dumpId);
20284 7624 : if (dobj == NULL)
20285 700 : continue;
20286 : /* No work if it has no dependencies */
20287 6924 : if (dobj->nDeps <= 0)
20288 184 : continue;
20289 : /* Set up work array */
20290 6740 : allocDeps = 64;
20291 6740 : dependencies = (DumpId *) pg_malloc(allocDeps * sizeof(DumpId));
20292 6740 : nDeps = 0;
20293 : /* Recursively find all dumpable dependencies */
20294 6740 : findDumpableDependencies(AH, dobj,
20295 : &dependencies, &nDeps, &allocDeps);
20296 : /* And save 'em ... */
20297 6740 : if (nDeps > 0)
20298 : {
20299 5006 : dependencies = (DumpId *) pg_realloc(dependencies,
20300 : nDeps * sizeof(DumpId));
20301 5006 : te->dependencies = dependencies;
20302 5006 : te->nDeps = nDeps;
20303 : }
20304 : else
20305 1734 : free(dependencies);
20306 : }
20307 116 : }
20308 :
20309 : /* Recursive search subroutine for BuildArchiveDependencies */
20310 : static void
20311 16568 : findDumpableDependencies(ArchiveHandle *AH, const DumpableObject *dobj,
20312 : DumpId **dependencies, int *nDeps, int *allocDeps)
20313 : {
20314 : int i;
20315 :
20316 : /*
20317 : * Ignore section boundary objects: if we search through them, we'll
20318 : * report lots of bogus dependencies.
20319 : */
20320 16568 : if (dobj->objType == DO_PRE_DATA_BOUNDARY ||
20321 16534 : dobj->objType == DO_POST_DATA_BOUNDARY)
20322 2804 : return;
20323 :
20324 34178 : for (i = 0; i < dobj->nDeps; i++)
20325 : {
20326 20414 : DumpId depid = dobj->dependencies[i];
20327 :
20328 20414 : if (TocIDRequired(AH, depid) != 0)
20329 : {
20330 : /* Object will be dumped, so just reference it as a dependency */
20331 10586 : if (*nDeps >= *allocDeps)
20332 : {
20333 0 : *allocDeps *= 2;
20334 0 : *dependencies = (DumpId *) pg_realloc(*dependencies,
20335 0 : *allocDeps * sizeof(DumpId));
20336 : }
20337 10586 : (*dependencies)[*nDeps] = depid;
20338 10586 : (*nDeps)++;
20339 : }
20340 : else
20341 : {
20342 : /*
20343 : * Object will not be dumped, so recursively consider its deps. We
20344 : * rely on the assumption that sortDumpableObjects already broke
20345 : * any dependency loops, else we might recurse infinitely.
20346 : */
20347 9828 : DumpableObject *otherdobj = findObjectByDumpId(depid);
20348 :
20349 9828 : if (otherdobj)
20350 9828 : findDumpableDependencies(AH, otherdobj,
20351 : dependencies, nDeps, allocDeps);
20352 : }
20353 : }
20354 : }
20355 :
20356 :
20357 : /*
20358 : * getFormattedTypeName - retrieve a nicely-formatted type name for the
20359 : * given type OID.
20360 : *
20361 : * This does not guarantee to schema-qualify the output, so it should not
20362 : * be used to create the target object name for CREATE or ALTER commands.
20363 : *
20364 : * Note that the result is cached and must not be freed by the caller.
20365 : */
20366 : static const char *
20367 4610 : getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
20368 : {
20369 : TypeInfo *typeInfo;
20370 : char *result;
20371 : PQExpBuffer query;
20372 : PGresult *res;
20373 :
20374 4610 : if (oid == 0)
20375 : {
20376 0 : if ((opts & zeroAsStar) != 0)
20377 0 : return "*";
20378 0 : else if ((opts & zeroAsNone) != 0)
20379 0 : return "NONE";
20380 : }
20381 :
20382 : /* see if we have the result cached in the type's TypeInfo record */
20383 4610 : typeInfo = findTypeByOid(oid);
20384 4610 : if (typeInfo && typeInfo->ftypname)
20385 3676 : return typeInfo->ftypname;
20386 :
20387 934 : query = createPQExpBuffer();
20388 934 : appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
20389 : oid);
20390 :
20391 934 : res = ExecuteSqlQueryForSingleRow(fout, query->data);
20392 :
20393 : /* result of format_type is already quoted */
20394 934 : result = pg_strdup(PQgetvalue(res, 0, 0));
20395 :
20396 934 : PQclear(res);
20397 934 : destroyPQExpBuffer(query);
20398 :
20399 : /*
20400 : * Cache the result for re-use in later requests, if possible. If we
20401 : * don't have a TypeInfo for the type, the string will be leaked once the
20402 : * caller is done with it ... but that case really should not happen, so
20403 : * leaking if it does seems acceptable.
20404 : */
20405 934 : if (typeInfo)
20406 934 : typeInfo->ftypname = result;
20407 :
20408 934 : return result;
20409 : }
20410 :
20411 : /*
20412 : * Return a column list clause for the given relation.
20413 : *
20414 : * Special case: if there are no undropped columns in the relation, return
20415 : * "", not an invalid "()" column list.
20416 : */
20417 : static const char *
20418 16420 : fmtCopyColumnList(const TableInfo *ti, PQExpBuffer buffer)
20419 : {
20420 16420 : int numatts = ti->numatts;
20421 16420 : char **attnames = ti->attnames;
20422 16420 : bool *attisdropped = ti->attisdropped;
20423 16420 : char *attgenerated = ti->attgenerated;
20424 : bool needComma;
20425 : int i;
20426 :
20427 16420 : appendPQExpBufferChar(buffer, '(');
20428 16420 : needComma = false;
20429 80104 : for (i = 0; i < numatts; i++)
20430 : {
20431 63684 : if (attisdropped[i])
20432 1196 : continue;
20433 62488 : if (attgenerated[i])
20434 2208 : continue;
20435 60280 : if (needComma)
20436 44308 : appendPQExpBufferStr(buffer, ", ");
20437 60280 : appendPQExpBufferStr(buffer, fmtId(attnames[i]));
20438 60280 : needComma = true;
20439 : }
20440 :
20441 16420 : if (!needComma)
20442 448 : return ""; /* no undropped columns */
20443 :
20444 15972 : appendPQExpBufferChar(buffer, ')');
20445 15972 : return buffer->data;
20446 : }
20447 :
20448 : /*
20449 : * Check if a reloptions array is nonempty.
20450 : */
20451 : static bool
20452 26964 : nonemptyReloptions(const char *reloptions)
20453 : {
20454 : /* Don't want to print it if it's just "{}" */
20455 26964 : return (reloptions != NULL && strlen(reloptions) > 2);
20456 : }
20457 :
20458 : /*
20459 : * Format a reloptions array and append it to the given buffer.
20460 : *
20461 : * "prefix" is prepended to the option names; typically it's "" or "toast.".
20462 : */
20463 : static void
20464 418 : appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
20465 : const char *prefix, Archive *fout)
20466 : {
20467 : bool res;
20468 :
20469 418 : res = appendReloptionsArray(buffer, reloptions, prefix, fout->encoding,
20470 418 : fout->std_strings);
20471 418 : if (!res)
20472 0 : pg_log_warning("could not parse %s array", "reloptions");
20473 418 : }
20474 :
20475 : /*
20476 : * read_dump_filters - retrieve object identifier patterns from file
20477 : *
20478 : * Parse the specified filter file for include and exclude patterns, and add
20479 : * them to the relevant lists. If the filename is "-" then filters will be
20480 : * read from STDIN rather than a file.
20481 : */
20482 : static void
20483 52 : read_dump_filters(const char *filename, DumpOptions *dopt)
20484 : {
20485 : FilterStateData fstate;
20486 : char *objname;
20487 : FilterCommandType comtype;
20488 : FilterObjectType objtype;
20489 :
20490 52 : filter_init(&fstate, filename, exit_nicely);
20491 :
20492 168 : while (filter_read_item(&fstate, &objname, &comtype, &objtype))
20493 : {
20494 66 : if (comtype == FILTER_COMMAND_TYPE_INCLUDE)
20495 : {
20496 34 : switch (objtype)
20497 : {
20498 0 : case FILTER_OBJECT_TYPE_NONE:
20499 0 : break;
20500 0 : case FILTER_OBJECT_TYPE_DATABASE:
20501 : case FILTER_OBJECT_TYPE_FUNCTION:
20502 : case FILTER_OBJECT_TYPE_INDEX:
20503 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20504 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20505 : case FILTER_OBJECT_TYPE_TRIGGER:
20506 0 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20507 : "include",
20508 : filter_object_type_name(objtype));
20509 0 : exit_nicely(1);
20510 : break; /* unreachable */
20511 :
20512 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20513 2 : simple_string_list_append(&extension_include_patterns, objname);
20514 2 : break;
20515 2 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20516 2 : simple_string_list_append(&foreign_servers_include_patterns, objname);
20517 2 : break;
20518 2 : case FILTER_OBJECT_TYPE_SCHEMA:
20519 2 : simple_string_list_append(&schema_include_patterns, objname);
20520 2 : dopt->include_everything = false;
20521 2 : break;
20522 26 : case FILTER_OBJECT_TYPE_TABLE:
20523 26 : simple_string_list_append(&table_include_patterns, objname);
20524 26 : dopt->include_everything = false;
20525 26 : break;
20526 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20527 2 : simple_string_list_append(&table_include_patterns_and_children,
20528 : objname);
20529 2 : dopt->include_everything = false;
20530 2 : break;
20531 : }
20532 : }
20533 32 : else if (comtype == FILTER_COMMAND_TYPE_EXCLUDE)
20534 : {
20535 18 : switch (objtype)
20536 : {
20537 0 : case FILTER_OBJECT_TYPE_NONE:
20538 0 : break;
20539 2 : case FILTER_OBJECT_TYPE_DATABASE:
20540 : case FILTER_OBJECT_TYPE_FUNCTION:
20541 : case FILTER_OBJECT_TYPE_INDEX:
20542 : case FILTER_OBJECT_TYPE_TRIGGER:
20543 : case FILTER_OBJECT_TYPE_FOREIGN_DATA:
20544 2 : pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
20545 : "exclude",
20546 : filter_object_type_name(objtype));
20547 2 : exit_nicely(1);
20548 : break;
20549 :
20550 2 : case FILTER_OBJECT_TYPE_EXTENSION:
20551 2 : simple_string_list_append(&extension_exclude_patterns, objname);
20552 2 : break;
20553 2 : case FILTER_OBJECT_TYPE_TABLE_DATA:
20554 2 : simple_string_list_append(&tabledata_exclude_patterns,
20555 : objname);
20556 2 : break;
20557 2 : case FILTER_OBJECT_TYPE_TABLE_DATA_AND_CHILDREN:
20558 2 : simple_string_list_append(&tabledata_exclude_patterns_and_children,
20559 : objname);
20560 2 : break;
20561 4 : case FILTER_OBJECT_TYPE_SCHEMA:
20562 4 : simple_string_list_append(&schema_exclude_patterns, objname);
20563 4 : break;
20564 4 : case FILTER_OBJECT_TYPE_TABLE:
20565 4 : simple_string_list_append(&table_exclude_patterns, objname);
20566 4 : break;
20567 2 : case FILTER_OBJECT_TYPE_TABLE_AND_CHILDREN:
20568 2 : simple_string_list_append(&table_exclude_patterns_and_children,
20569 : objname);
20570 2 : break;
20571 : }
20572 : }
20573 : else
20574 : {
20575 : Assert(comtype == FILTER_COMMAND_TYPE_NONE);
20576 : Assert(objtype == FILTER_OBJECT_TYPE_NONE);
20577 : }
20578 :
20579 64 : if (objname)
20580 50 : free(objname);
20581 : }
20582 :
20583 44 : filter_free(&fstate);
20584 44 : }
|